summaryrefslogtreecommitdiffstats
path: root/3rdparty/assimp/code
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/assimp/code')
-rw-r--r--3rdparty/assimp/code/3DSConverter.cpp845
-rw-r--r--3rdparty/assimp/code/3DSHelper.h577
-rw-r--r--3rdparty/assimp/code/3DSLoader.cpp1381
-rw-r--r--3rdparty/assimp/code/3DSLoader.h280
-rw-r--r--3rdparty/assimp/code/ACLoader.cpp856
-rw-r--r--3rdparty/assimp/code/ACLoader.h271
-rw-r--r--3rdparty/assimp/code/ASELoader.cpp1302
-rw-r--r--3rdparty/assimp/code/ASELoader.h208
-rw-r--r--3rdparty/assimp/code/ASEParser.cpp2150
-rw-r--r--3rdparty/assimp/code/ASEParser.h669
-rw-r--r--3rdparty/assimp/code/Assimp.cpp731
-rw-r--r--3rdparty/assimp/code/AssimpPCH.cpp70
-rw-r--r--3rdparty/assimp/code/AssimpPCH.h149
-rw-r--r--3rdparty/assimp/code/B3DImporter.cpp672
-rw-r--r--3rdparty/assimp/code/B3DImporter.h126
-rw-r--r--3rdparty/assimp/code/BVHLoader.cpp505
-rw-r--r--3rdparty/assimp/code/BVHLoader.h173
-rw-r--r--3rdparty/assimp/code/BaseImporter.cpp527
-rw-r--r--3rdparty/assimp/code/BaseImporter.h499
-rw-r--r--3rdparty/assimp/code/BaseProcess.cpp96
-rw-r--r--3rdparty/assimp/code/BaseProcess.h289
-rw-r--r--3rdparty/assimp/code/BlenderDNA.cpp372
-rw-r--r--3rdparty/assimp/code/BlenderDNA.h798
-rw-r--r--3rdparty/assimp/code/BlenderDNA.inl706
-rw-r--r--3rdparty/assimp/code/BlenderIntermediate.h183
-rw-r--r--3rdparty/assimp/code/BlenderLoader.cpp1003
-rw-r--r--3rdparty/assimp/code/BlenderLoader.h261
-rw-r--r--3rdparty/assimp/code/BlenderModifier.cpp311
-rw-r--r--3rdparty/assimp/code/BlenderModifier.h155
-rw-r--r--3rdparty/assimp/code/BlenderScene.cpp596
-rw-r--r--3rdparty/assimp/code/BlenderScene.h683
-rw-r--r--3rdparty/assimp/code/BlenderSceneGen.h223
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/LICENSE_1_0.txt24
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/foreach.hpp93
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/format.hpp81
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/lexical_cast.hpp23
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/math/common_factor_rt.hpp37
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/pointer_cast.hpp45
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/scoped_array.hpp79
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/scoped_ptr.hpp79
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/shared_array.hpp228
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/shared_ptr.hpp257
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/static_assert.hpp20
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/timer.hpp72
-rw-r--r--3rdparty/assimp/code/BoostWorkaround/boost/tuple/tuple.hpp285
-rw-r--r--3rdparty/assimp/code/ByteSwap.h245
-rw-r--r--3rdparty/assimp/code/COBLoader.cpp1278
-rw-r--r--3rdparty/assimp/code/COBLoader.h175
-rw-r--r--3rdparty/assimp/code/COBScene.h271
-rw-r--r--3rdparty/assimp/code/CSMLoader.cpp281
-rw-r--r--3rdparty/assimp/code/CSMLoader.h88
-rw-r--r--3rdparty/assimp/code/CalcTangentsProcess.cpp283
-rw-r--r--3rdparty/assimp/code/CalcTangentsProcess.h118
-rw-r--r--3rdparty/assimp/code/ColladaHelper.h601
-rw-r--r--3rdparty/assimp/code/ColladaLoader.cpp1487
-rw-r--r--3rdparty/assimp/code/ColladaLoader.h241
-rw-r--r--3rdparty/assimp/code/ColladaParser.cpp2796
-rw-r--r--3rdparty/assimp/code/ColladaParser.h341
-rw-r--r--3rdparty/assimp/code/ComputeUVMappingProcess.cpp504
-rw-r--r--3rdparty/assimp/code/ComputeUVMappingProcess.h149
-rw-r--r--3rdparty/assimp/code/ConvertToLHProcess.cpp318
-rw-r--r--3rdparty/assimp/code/ConvertToLHProcess.h169
-rw-r--r--3rdparty/assimp/code/DXFLoader.cpp609
-rw-r--r--3rdparty/assimp/code/DXFLoader.h184
-rw-r--r--3rdparty/assimp/code/DefaultIOStream.cpp139
-rw-r--r--3rdparty/assimp/code/DefaultIOStream.h133
-rw-r--r--3rdparty/assimp/code/DefaultIOSystem.cpp167
-rw-r--r--3rdparty/assimp/code/DefaultIOSystem.h83
-rw-r--r--3rdparty/assimp/code/DefaultLogger.cpp419
-rw-r--r--3rdparty/assimp/code/DefaultProgressHandler.h64
-rw-r--r--3rdparty/assimp/code/Exceptional.h122
-rw-r--r--3rdparty/assimp/code/FileLogStream.h64
-rw-r--r--3rdparty/assimp/code/FileSystemFilter.h244
-rw-r--r--3rdparty/assimp/code/FindDegenerates.cpp216
-rw-r--r--3rdparty/assimp/code/FindDegenerates.h110
-rw-r--r--3rdparty/assimp/code/FindInstancesProcess.cpp288
-rw-r--r--3rdparty/assimp/code/FindInstancesProcess.h140
-rw-r--r--3rdparty/assimp/code/FindInvalidDataProcess.cpp419
-rw-r--r--3rdparty/assimp/code/FindInvalidDataProcess.h111
-rw-r--r--3rdparty/assimp/code/FixNormalsStep.cpp176
-rw-r--r--3rdparty/assimp/code/FixNormalsStep.h96
-rw-r--r--3rdparty/assimp/code/GenFaceNormalsProcess.cpp138
-rw-r--r--3rdparty/assimp/code/GenFaceNormalsProcess.h88
-rw-r--r--3rdparty/assimp/code/GenVertexNormalsProcess.cpp228
-rw-r--r--3rdparty/assimp/code/GenVertexNormalsProcess.h118
-rw-r--r--3rdparty/assimp/code/GenericProperty.h112
-rw-r--r--3rdparty/assimp/code/HMPFileData.h134
-rw-r--r--3rdparty/assimp/code/HMPLoader.cpp497
-rw-r--r--3rdparty/assimp/code/HMPLoader.h159
-rw-r--r--3rdparty/assimp/code/HalfLifeFileData.h150
-rw-r--r--3rdparty/assimp/code/Hash.h108
-rw-r--r--3rdparty/assimp/code/IFF.h102
-rw-r--r--3rdparty/assimp/code/IRRLoader.cpp1462
-rw-r--r--3rdparty/assimp/code/IRRLoader.h312
-rw-r--r--3rdparty/assimp/code/IRRMeshLoader.cpp499
-rw-r--r--3rdparty/assimp/code/IRRMeshLoader.h99
-rw-r--r--3rdparty/assimp/code/IRRShared.cpp496
-rw-r--r--3rdparty/assimp/code/IRRShared.h115
-rw-r--r--3rdparty/assimp/code/Importer.cpp1415
-rw-r--r--3rdparty/assimp/code/ImproveCacheLocality.cpp378
-rw-r--r--3rdparty/assimp/code/ImproveCacheLocality.h102
-rw-r--r--3rdparty/assimp/code/JoinVerticesProcess.cpp395
-rw-r--r--3rdparty/assimp/code/JoinVerticesProcess.h105
-rw-r--r--3rdparty/assimp/code/LWOAnimation.cpp585
-rw-r--r--3rdparty/assimp/code/LWOAnimation.h336
-rw-r--r--3rdparty/assimp/code/LWOBLoader.cpp396
-rw-r--r--3rdparty/assimp/code/LWOFileData.h699
-rw-r--r--3rdparty/assimp/code/LWOLoader.cpp1403
-rw-r--r--3rdparty/assimp/code/LWOLoader.h490
-rw-r--r--3rdparty/assimp/code/LWOMaterial.cpp898
-rw-r--r--3rdparty/assimp/code/LWSLoader.cpp885
-rw-r--r--3rdparty/assimp/code/LWSLoader.h242
-rw-r--r--3rdparty/assimp/code/LimitBoneWeightsProcess.cpp204
-rw-r--r--3rdparty/assimp/code/LimitBoneWeightsProcess.h146
-rw-r--r--3rdparty/assimp/code/LineSplitter.h217
-rw-r--r--3rdparty/assimp/code/MD2FileData.h163
-rw-r--r--3rdparty/assimp/code/MD2Loader.cpp414
-rw-r--r--3rdparty/assimp/code/MD2Loader.h126
-rw-r--r--3rdparty/assimp/code/MD2NormalTable.h217
-rw-r--r--3rdparty/assimp/code/MD3FileData.h315
-rw-r--r--3rdparty/assimp/code/MD3Loader.cpp1043
-rw-r--r--3rdparty/assimp/code/MD3Loader.h331
-rw-r--r--3rdparty/assimp/code/MD4FileData.h218
-rw-r--r--3rdparty/assimp/code/MD5Loader.cpp727
-rw-r--r--3rdparty/assimp/code/MD5Loader.h194
-rw-r--r--3rdparty/assimp/code/MD5Parser.cpp471
-rw-r--r--3rdparty/assimp/code/MD5Parser.h460
-rw-r--r--3rdparty/assimp/code/MDCFileData.h199
-rw-r--r--3rdparty/assimp/code/MDCLoader.cpp472
-rw-r--r--3rdparty/assimp/code/MDCLoader.h132
-rw-r--r--3rdparty/assimp/code/MDCNormalTable.h299
-rw-r--r--3rdparty/assimp/code/MDLDefaultColorMap.h118
-rw-r--r--3rdparty/assimp/code/MDLFileData.h959
-rw-r--r--3rdparty/assimp/code/MDLLoader.cpp1929
-rw-r--r--3rdparty/assimp/code/MDLLoader.h460
-rw-r--r--3rdparty/assimp/code/MDLMaterialLoader.cpp823
-rw-r--r--3rdparty/assimp/code/MS3DLoader.cpp646
-rw-r--r--3rdparty/assimp/code/MS3DLoader.h158
-rw-r--r--3rdparty/assimp/code/MakeVerboseFormat.cpp218
-rw-r--r--3rdparty/assimp/code/MakeVerboseFormat.h106
-rw-r--r--3rdparty/assimp/code/MaterialSystem.cpp612
-rw-r--r--3rdparty/assimp/code/MaterialSystem.h246
-rw-r--r--3rdparty/assimp/code/MemoryIOWrapper.h181
-rw-r--r--3rdparty/assimp/code/NDOLoader.cpp289
-rw-r--r--3rdparty/assimp/code/NDOLoader.h116
-rw-r--r--3rdparty/assimp/code/NFFLoader.cpp1256
-rw-r--r--3rdparty/assimp/code/NFFLoader.h216
-rw-r--r--3rdparty/assimp/code/OFFLoader.cpp211
-rw-r--r--3rdparty/assimp/code/OFFLoader.h96
-rw-r--r--3rdparty/assimp/code/ObjFileData.h324
-rw-r--r--3rdparty/assimp/code/ObjFileImporter.cpp521
-rw-r--r--3rdparty/assimp/code/ObjFileImporter.h136
-rw-r--r--3rdparty/assimp/code/ObjFileMtlImporter.cpp294
-rw-r--r--3rdparty/assimp/code/ObjFileMtlImporter.h115
-rw-r--r--3rdparty/assimp/code/ObjFileParser.cpp664
-rw-r--r--3rdparty/assimp/code/ObjFileParser.h137
-rw-r--r--3rdparty/assimp/code/ObjTools.h220
-rw-r--r--3rdparty/assimp/code/OgreImporter.cpp873
-rw-r--r--3rdparty/assimp/code/OgreImporter.h151
-rw-r--r--3rdparty/assimp/code/OgreImporterMaterial.cpp255
-rw-r--r--3rdparty/assimp/code/OgreXmlHelper.h79
-rw-r--r--3rdparty/assimp/code/OptimizeGraph.cpp347
-rw-r--r--3rdparty/assimp/code/OptimizeGraph.h147
-rw-r--r--3rdparty/assimp/code/OptimizeMeshes.cpp243
-rw-r--r--3rdparty/assimp/code/OptimizeMeshes.h187
-rw-r--r--3rdparty/assimp/code/ParsingUtils.h181
-rw-r--r--3rdparty/assimp/code/PlyLoader.cpp1050
-rw-r--r--3rdparty/assimp/code/PlyLoader.h174
-rw-r--r--3rdparty/assimp/code/PlyParser.cpp919
-rw-r--r--3rdparty/assimp/code/PlyParser.h501
-rw-r--r--3rdparty/assimp/code/PretransformVertices.cpp711
-rw-r--r--3rdparty/assimp/code/PretransformVertices.h166
-rw-r--r--3rdparty/assimp/code/ProcessHelper.h564
-rw-r--r--3rdparty/assimp/code/Profiler.h97
-rw-r--r--3rdparty/assimp/code/Q3BSPFileData.h228
-rw-r--r--3rdparty/assimp/code/Q3BSPFileImporter.cpp731
-rw-r--r--3rdparty/assimp/code/Q3BSPFileImporter.h110
-rw-r--r--3rdparty/assimp/code/Q3BSPFileParser.cpp272
-rw-r--r--3rdparty/assimp/code/Q3BSPFileParser.h89
-rw-r--r--3rdparty/assimp/code/Q3BSPZipArchive.cpp196
-rw-r--r--3rdparty/assimp/code/Q3BSPZipArchive.h182
-rw-r--r--3rdparty/assimp/code/Q3DLoader.cpp600
-rw-r--r--3rdparty/assimp/code/Q3DLoader.h135
-rw-r--r--3rdparty/assimp/code/RawLoader.cpp312
-rw-r--r--3rdparty/assimp/code/RawLoader.h123
-rw-r--r--3rdparty/assimp/code/RemoveComments.cpp108
-rw-r--r--3rdparty/assimp/code/RemoveComments.h88
-rw-r--r--3rdparty/assimp/code/RemoveRedundantMaterials.cpp204
-rw-r--r--3rdparty/assimp/code/RemoveRedundantMaterials.h107
-rw-r--r--3rdparty/assimp/code/RemoveVCProcess.cpp328
-rw-r--r--3rdparty/assimp/code/RemoveVCProcess.h123
-rw-r--r--3rdparty/assimp/code/SGSpatialSort.cpp169
-rw-r--r--3rdparty/assimp/code/SGSpatialSort.h139
-rw-r--r--3rdparty/assimp/code/SMDLoader.cpp1129
-rw-r--r--3rdparty/assimp/code/SMDLoader.h420
-rw-r--r--3rdparty/assimp/code/STLLoader.cpp398
-rw-r--r--3rdparty/assimp/code/STLLoader.h119
-rw-r--r--3rdparty/assimp/code/SceneCombiner.cpp1133
-rw-r--r--3rdparty/assimp/code/SceneCombiner.h365
-rw-r--r--3rdparty/assimp/code/ScenePreprocessor.cpp281
-rw-r--r--3rdparty/assimp/code/ScenePreprocessor.h116
-rw-r--r--3rdparty/assimp/code/SkeletonMeshBuilder.cpp267
-rw-r--r--3rdparty/assimp/code/SkeletonMeshBuilder.h122
-rw-r--r--3rdparty/assimp/code/SmoothingGroups.h103
-rw-r--r--3rdparty/assimp/code/SmoothingGroups.inl138
-rw-r--r--3rdparty/assimp/code/SortByPTypeProcess.cpp405
-rw-r--r--3rdparty/assimp/code/SortByPTypeProcess.h88
-rw-r--r--3rdparty/assimp/code/SpatialSort.cpp342
-rw-r--r--3rdparty/assimp/code/SpatialSort.h170
-rw-r--r--3rdparty/assimp/code/SplitLargeMeshes.cpp677
-rw-r--r--3rdparty/assimp/code/SplitLargeMeshes.h216
-rw-r--r--3rdparty/assimp/code/StandardShapes.cpp502
-rw-r--r--3rdparty/assimp/code/StandardShapes.h196
-rw-r--r--3rdparty/assimp/code/StdOStreamLogStream.h52
-rw-r--r--3rdparty/assimp/code/StreamReader.h384
-rw-r--r--3rdparty/assimp/code/StringComparison.h217
-rw-r--r--3rdparty/assimp/code/Subdivision.cpp589
-rw-r--r--3rdparty/assimp/code/Subdivision.h124
-rw-r--r--3rdparty/assimp/code/TargetAnimation.cpp246
-rw-r--r--3rdparty/assimp/code/TargetAnimation.h179
-rw-r--r--3rdparty/assimp/code/TerragenLoader.cpp254
-rw-r--r--3rdparty/assimp/code/TerragenLoader.h107
-rw-r--r--3rdparty/assimp/code/TextureTransform.cpp563
-rw-r--r--3rdparty/assimp/code/TextureTransform.h225
-rw-r--r--3rdparty/assimp/code/TinyFormatter.h141
-rw-r--r--3rdparty/assimp/code/TriangulateProcess.cpp401
-rw-r--r--3rdparty/assimp/code/TriangulateProcess.h98
-rw-r--r--3rdparty/assimp/code/UnrealLoader.cpp426
-rw-r--r--3rdparty/assimp/code/UnrealLoader.h204
-rw-r--r--3rdparty/assimp/code/ValidateDataStructure.cpp968
-rw-r--r--3rdparty/assimp/code/ValidateDataStructure.h187
-rw-r--r--3rdparty/assimp/code/Vertex.h301
-rw-r--r--3rdparty/assimp/code/VertexTriangleAdjacency.cpp132
-rw-r--r--3rdparty/assimp/code/VertexTriangleAdjacency.h124
-rw-r--r--3rdparty/assimp/code/Win32DebugLogStream.h50
-rw-r--r--3rdparty/assimp/code/XFileHelper.h200
-rw-r--r--3rdparty/assimp/code/XFileImporter.cpp672
-rw-r--r--3rdparty/assimp/code/XFileImporter.h159
-rw-r--r--3rdparty/assimp/code/XFileParser.cpp1434
-rw-r--r--3rdparty/assimp/code/XFileParser.h162
-rw-r--r--3rdparty/assimp/code/aiAssert.cpp68
-rw-r--r--3rdparty/assimp/code/assbin_chunks.h196
-rw-r--r--3rdparty/assimp/code/fast_atof.h306
-rw-r--r--3rdparty/assimp/code/irrXMLWrapper.h131
-rw-r--r--3rdparty/assimp/code/makefile111
-rw-r--r--3rdparty/assimp/code/makefile.mingw105
-rw-r--r--3rdparty/assimp/code/pstdint.h729
-rw-r--r--3rdparty/assimp/code/qnan.h110
-rw-r--r--3rdparty/assimp/code/res/assimp.rc80
-rw-r--r--3rdparty/assimp/code/res/resource.h14
250 files changed, 91189 insertions, 0 deletions
diff --git a/3rdparty/assimp/code/3DSConverter.cpp b/3rdparty/assimp/code/3DSConverter.cpp
new file mode 100644
index 000000000..f4bd56f18
--- /dev/null
+++ b/3rdparty/assimp/code/3DSConverter.cpp
@@ -0,0 +1,845 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the 3ds importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
+
+// internal headers
+#include "3DSLoader.h"
+#include "TargetAnimation.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Setup final material indices, generae a default material if necessary
+void Discreet3DSImporter::ReplaceDefaultMaterial()
+{
+
+ // Try to find an existing material that matches the
+ // typical default material setting:
+ // - no textures
+ // - diffuse color (in grey!)
+ // NOTE: This is here to workaround the fact that some
+ // exporters are writing a default material, too.
+ unsigned int idx = 0xcdcdcdcd;
+ for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
+ {
+ std::string s = mScene->mMaterials[i].mName;
+ for (std::string::iterator it = s.begin(); it != s.end(); ++it)
+ *it = ::tolower(*it);
+
+ if (std::string::npos == s.find("default"))continue;
+
+ if (mScene->mMaterials[i].mDiffuse.r !=
+ mScene->mMaterials[i].mDiffuse.g ||
+ mScene->mMaterials[i].mDiffuse.r !=
+ mScene->mMaterials[i].mDiffuse.b)continue;
+
+ if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 ||
+ mScene->mMaterials[i].sTexBump.mMapName.length() != 0 ||
+ mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 ||
+ mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 ||
+ mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 ||
+ mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 )
+ {
+ continue;
+ }
+ idx = i;
+ }
+ if (0xcdcdcdcd == idx)idx = (unsigned int)mScene->mMaterials.size();
+
+ // now iterate through all meshes and through all faces and
+ // find all faces that are using the default material
+ unsigned int cnt = 0;
+ for (std::vector<D3DS::Mesh>::iterator
+ i = mScene->mMeshes.begin();
+ i != mScene->mMeshes.end();++i)
+ {
+ for (std::vector<unsigned int>::iterator
+ a = (*i).mFaceMaterials.begin();
+ a != (*i).mFaceMaterials.end();++a)
+ {
+ // NOTE: The additional check seems to be necessary,
+ // some exporters seem to generate invalid data here
+ if (0xcdcdcdcd == (*a))
+ {
+ (*a) = idx;
+ ++cnt;
+ }
+ else if ( (*a) >= mScene->mMaterials.size())
+ {
+ (*a) = idx;
+ DefaultLogger::get()->warn("Material index overflow in 3DS file. Using default material");
+ ++cnt;
+ }
+ }
+ }
+ if (cnt && idx == mScene->mMaterials.size())
+ {
+ // We need to create our own default material
+ D3DS::Material sMat;
+ sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f);
+ sMat.mName = "%%%DEFAULT";
+ mScene->mMaterials.push_back(sMat);
+
+ DefaultLogger::get()->info("3DS: Generating default material");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether all indices are valid. Otherwise we'd crash before the validation step is reached
+void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh)
+{
+ for (std::vector< D3DS::Face >::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i)
+ {
+ // check whether all indices are in range
+ for (unsigned int a = 0; a < 3;++a)
+ {
+ if ((*i).mIndices[a] >= sMesh.mPositions.size())
+ {
+ DefaultLogger::get()->warn("3DS: Vertex index overflow)");
+ (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1;
+ }
+ if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size())
+ {
+ DefaultLogger::get()->warn("3DS: Texture coordinate index overflow)");
+ (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Generate out unique verbose format representation
+void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh)
+{
+ // TODO: really necessary? I don't think. Just a waste of memory and time
+ // to do it now in a separate buffer.
+
+ // Allocate output storage
+ std::vector<aiVector3D> vNew (sMesh.mFaces.size() * 3);
+ std::vector<aiVector3D> vNew2;
+ if (sMesh.mTexCoords.size())
+ vNew2.resize(sMesh.mFaces.size() * 3);
+
+ for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i)
+ {
+ D3DS::Face& face = sMesh.mFaces[i];
+
+ // Positions
+ for (unsigned int a = 0; a < 3;++a,++base)
+ {
+ vNew[base] = sMesh.mPositions[face.mIndices[a]];
+ if (sMesh.mTexCoords.size())
+ vNew2[base] = sMesh.mTexCoords[face.mIndices[a]];
+
+ face.mIndices[a] = base;
+ }
+ }
+ sMesh.mPositions = vNew;
+ sMesh.mTexCoords = vNew2;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a 3DS texture to texture keys in an aiMaterial
+void CopyTexture(MaterialHelper& mat, D3DS::Texture& texture, aiTextureType type)
+{
+ // Setup the texture name
+ aiString tex;
+ tex.Set( texture.mMapName);
+ mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0));
+
+ // Setup the texture blend factor
+ if (is_not_qnan(texture.mTextureBlend))
+ mat.AddProperty<float>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0));
+
+ // Setup the texture mapping mode
+ mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0));
+ mat.AddProperty<int>((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0));
+
+ // Mirroring - double the scaling values
+ // FIXME: this is not really correct ...
+ if (texture.mMapMode == aiTextureMapMode_Mirror)
+ {
+ texture.mScaleU *= 2.f;
+ texture.mScaleV *= 2.f;
+ texture.mOffsetU /= 2.f;
+ texture.mOffsetV /= 2.f;
+ }
+
+ // Setup texture UV transformations
+ mat.AddProperty<float>(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a 3DS material to an aiMaterial
+void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat,
+ MaterialHelper& mat)
+{
+ // NOTE: Pass the background image to the viewer by bypassing the
+ // material system. This is an evil hack, never do it again!
+ if (0 != mBackgroundImage.length() && bHasBG)
+ {
+ aiString tex;
+ tex.Set( mBackgroundImage);
+ mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE);
+
+ // Be sure this is only done for the first material
+ mBackgroundImage = std::string("");
+ }
+
+ // At first add the base ambient color of the scene to the material
+ oldMat.mAmbient.r += mClrAmbient.r;
+ oldMat.mAmbient.g += mClrAmbient.g;
+ oldMat.mAmbient.b += mClrAmbient.b;
+
+ aiString name;
+ name.Set( oldMat.mName);
+ mat.AddProperty( &name, AI_MATKEY_NAME);
+
+ // Material colors
+ mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
+ mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
+ mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+
+ // Phong shininess and shininess strength
+ if (D3DS::Discreet3DS::Phong == oldMat.mShading ||
+ D3DS::Discreet3DS::Metal == oldMat.mShading)
+ {
+ if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength)
+ {
+ oldMat.mShading = D3DS::Discreet3DS::Gouraud;
+ }
+ else
+ {
+ mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
+ mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
+ }
+ }
+
+ // Opacity
+ mat.AddProperty<float>( &oldMat.mTransparency,1,AI_MATKEY_OPACITY);
+
+ // Bump height scaling
+ mat.AddProperty<float>( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING);
+
+ // Two sided rendering?
+ if (oldMat.mTwoSided)
+ {
+ int i = 1;
+ mat.AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
+ }
+
+ // Shading mode
+ aiShadingMode eShading = aiShadingMode_NoShading;
+ switch (oldMat.mShading)
+ {
+ case D3DS::Discreet3DS::Flat:
+ eShading = aiShadingMode_Flat; break;
+
+ // I don't know what "Wire" shading should be,
+ // assume it is simple lambertian diffuse shading
+ case D3DS::Discreet3DS::Wire:
+ {
+ // Set the wireframe flag
+ unsigned int iWire = 1;
+ mat.AddProperty<int>( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME);
+ }
+
+ case D3DS::Discreet3DS::Gouraud:
+ eShading = aiShadingMode_Gouraud; break;
+
+ // assume cook-torrance shading for metals.
+ case D3DS::Discreet3DS::Phong :
+ eShading = aiShadingMode_Phong; break;
+
+ case D3DS::Discreet3DS::Metal :
+ eShading = aiShadingMode_CookTorrance; break;
+
+ // FIX to workaround a warning with GCC 4 who complained
+ // about a missing case Blinn: here - Blinn isn't a valid
+ // value in the 3DS Loader, it is just needed for ASE
+ case D3DS::Discreet3DS::Blinn :
+ eShading = aiShadingMode_Blinn; break;
+ }
+ mat.AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
+
+ // DIFFUSE texture
+ if ( oldMat.sTexDiffuse.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE);
+
+ // SPECULAR texture
+ if ( oldMat.sTexSpecular.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR);
+
+ // OPACITY texture
+ if ( oldMat.sTexOpacity.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY);
+
+ // EMISSIVE texture
+ if ( oldMat.sTexEmissive.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE);
+
+ // BUMP texture
+ if ( oldMat.sTexBump.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT);
+
+ // SHININESS texture
+ if ( oldMat.sTexShininess.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS);
+
+ // REFLECTION texture
+ if ( oldMat.sTexReflective.mMapName.length() > 0)
+ CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION);
+
+ // Store the name of the material itself, too
+ if ( oldMat.mName.length()) {
+ aiString tex;
+ tex.Set( oldMat.mName);
+ mat.AddProperty( &tex, AI_MATKEY_NAME);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Split meshes by their materials and generate output aiMesh'es
+void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut)
+{
+ std::vector<aiMesh*> avOutMeshes;
+ avOutMeshes.reserve(mScene->mMeshes.size() * 2);
+
+ unsigned int iFaceCnt = 0,num = 0;
+ aiString name;
+
+ // we need to split all meshes by their materials
+ for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i) {
+ boost::scoped_array< std::vector<unsigned int> > aiSplit(new std::vector<unsigned int>[mScene->mMaterials.size()]);
+
+ name.length = ASSIMP_itoa10(name.data,num++);
+
+ unsigned int iNum = 0;
+ for (std::vector<unsigned int>::const_iterator a = (*i).mFaceMaterials.begin();
+ a != (*i).mFaceMaterials.end();++a,++iNum)
+ {
+ aiSplit[*a].push_back(iNum);
+ }
+ // now generate submeshes
+ for (unsigned int p = 0; p < mScene->mMaterials.size();++p)
+ {
+ if (aiSplit[p].empty()) {
+ continue;
+ }
+ aiMesh* meshOut = new aiMesh();
+
+ std::string name_parts((*i).mName);
+ name_parts.append("::");
+ name_parts.append(mScene->mMaterials.at(p).mName);
+
+ meshOut->mName.Set(name_parts);
+ meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // be sure to setup the correct material index
+ meshOut->mMaterialIndex = p;
+
+ // use the color data as temporary storage
+ meshOut->mColors[0] = (aiColor4D*)(&*i);
+ avOutMeshes.push_back(meshOut);
+
+ // convert vertices
+ meshOut->mNumFaces = (unsigned int)aiSplit[p].size();
+ meshOut->mNumVertices = meshOut->mNumFaces*3;
+
+ // allocate enough storage for faces
+ meshOut->mFaces = new aiFace[meshOut->mNumFaces];
+ iFaceCnt += meshOut->mNumFaces;
+
+ meshOut->mVertices = new aiVector3D[meshOut->mNumVertices];
+ meshOut->mNormals = new aiVector3D[meshOut->mNumVertices];
+ if ((*i).mTexCoords.size())
+ {
+ meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices];
+ }
+ for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q)
+ {
+ register unsigned int index = aiSplit[p][q];
+ aiFace& face = meshOut->mFaces[q];
+
+ face.mIndices = new unsigned int[3];
+ face.mNumIndices = 3;
+
+ for (unsigned int a = 0; a < 3;++a,++base)
+ {
+ unsigned int idx = (*i).mFaces[index].mIndices[a];
+ meshOut->mVertices[base] = (*i).mPositions[idx];
+ meshOut->mNormals [base] = (*i).mNormals[idx];
+
+ if ((*i).mTexCoords.size())
+ meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx];
+
+ face.mIndices[a] = base;
+ }
+ }
+ }
+ }
+
+ // Copy them to the output array
+ pcOut->mNumMeshes = (unsigned int)avOutMeshes.size();
+ pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes]();
+ for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) {
+ pcOut->mMeshes[a] = avOutMeshes[a];
+ }
+
+ // We should have at least one face here
+ if (!iFaceCnt) {
+ throw DeadlyImportError("No faces loaded. The mesh is empty");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add a node to the scenegraph and setup its final transformation
+void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,
+ D3DS::Node* pcIn, aiMatrix4x4& /* absTrafo */)
+{
+ std::vector<unsigned int> iArray;
+ iArray.reserve(3);
+
+ aiMatrix4x4 abs;
+
+ // Find all meshes with the same name as the node
+ for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a)
+ {
+ const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0];
+ ai_assert(NULL != pcMesh);
+
+ if (pcIn->mName == pcMesh->mName)
+ iArray.push_back(a);
+ }
+ if (!iArray.empty())
+ {
+ // The matrix should be identical for all meshes with the
+ // same name. It HAS to be identical for all meshes .....
+ D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]);
+
+ // Compute the inverse of the transformation matrix to move the
+ // vertices back to their relative and local space
+ aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat;
+ mInv.Inverse();mInvTransposed.Transpose();
+ aiVector3D pivot = pcIn->vPivot;
+
+ pcOut->mNumMeshes = (unsigned int)iArray.size();
+ pcOut->mMeshes = new unsigned int[iArray.size()];
+ for (unsigned int i = 0;i < iArray.size();++i) {
+ const unsigned int iIndex = iArray[i];
+ aiMesh* const mesh = pcSOut->mMeshes[iIndex];
+
+ // Transform the vertices back into their local space
+ // fixme: consider computing normals after this, so we don't need to transform them
+ const aiVector3D* const pvEnd = mesh->mVertices+mesh->mNumVertices;
+ aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals;
+
+ for (;pvCurrent != pvEnd;++pvCurrent,++t2) {
+ *pvCurrent = mInv * (*pvCurrent);
+ *t2 = mInvTransposed * (*t2);
+ }
+
+ // Handle negative transformation matrix determinant -> invert vertex x
+ if (imesh->mMat.Determinant() < 0.0f)
+ {
+ /* we *must* have normals */
+ for (pvCurrent = mesh->mVertices,t2 = mesh->mNormals;pvCurrent != pvEnd;++pvCurrent,++t2) {
+ pvCurrent->x *= -1.f;
+ t2->x *= -1.f;
+ }
+ DefaultLogger::get()->info("3DS: Flipping mesh X-Axis");
+ }
+
+ // Handle pivot point
+ if (pivot.x || pivot.y || pivot.z)
+ {
+ for (pvCurrent = mesh->mVertices;pvCurrent != pvEnd;++pvCurrent) {
+ *pvCurrent -= pivot;
+ }
+ }
+
+ // Setup the mesh index
+ pcOut->mMeshes[i] = iIndex;
+ }
+ }
+
+ // Setup the name of the node
+ pcOut->mName.Set(pcIn->mName);
+
+ // Now build the transformation matrix of the node
+ // ROTATION
+ if (pcIn->aRotationKeys.size()){
+
+ // FIX to get to Assimp's quaternion conventions
+ for (std::vector<aiQuatKey>::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) {
+ (*it).mValue.w *= -1.f;
+ }
+
+ pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() );
+ }
+ else if (pcIn->aCameraRollKeys.size())
+ {
+ aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue),
+ pcOut->mTransformation);
+ }
+
+ // SCALING
+ aiMatrix4x4& m = pcOut->mTransformation;
+ if (pcIn->aScalingKeys.size())
+ {
+ const aiVector3D& v = pcIn->aScalingKeys[0].mValue;
+ m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x;
+ m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y;
+ m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z;
+ }
+
+ // TRANSLATION
+ if (pcIn->aPositionKeys.size())
+ {
+ const aiVector3D& v = pcIn->aPositionKeys[0].mValue;
+ m.a4 += v.x;
+ m.b4 += v.y;
+ m.c4 += v.z;
+ }
+
+ // Generate animation channels for the node
+ if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 ||
+ pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 ||
+ pcIn->aTargetPositionKeys.size() > 1)
+ {
+ aiAnimation* anim = pcSOut->mAnimations[0];
+ ai_assert(NULL != anim);
+
+ if (pcIn->aCameraRollKeys.size() > 1)
+ {
+ DefaultLogger::get()->debug("3DS: Converting camera roll track ...");
+
+ // Camera roll keys - in fact they're just rotations
+ // around the camera's z axis. The angles are given
+ // in degrees (and they're clockwise).
+ pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size());
+ for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i)
+ {
+ aiQuatKey& q = pcIn->aRotationKeys[i];
+ aiFloatKey& f = pcIn->aCameraRollKeys[i];
+
+ q.mTime = f.mTime;
+
+ // FIX to get to Assimp quaternion conventions
+ q.mValue = aiQuaternion(0.f,0.f,AI_DEG_TO_RAD( /*-*/ f.mValue));
+ }
+ }
+#if 0
+ if (pcIn->aTargetPositionKeys.size() > 1)
+ {
+ DefaultLogger::get()->debug("3DS: Converting target track ...");
+
+ // Camera or spot light - need to convert the separate
+ // target position channel to our representation
+ TargetAnimationHelper helper;
+
+ if (pcIn->aPositionKeys.empty())
+ {
+ // We can just pass zero here ...
+ helper.SetFixedMainAnimationChannel(aiVector3D());
+ }
+ else helper.SetMainAnimationChannel(&pcIn->aPositionKeys);
+ helper.SetTargetAnimationChannel(&pcIn->aTargetPositionKeys);
+
+ // Do the conversion
+ std::vector<aiVectorKey> distanceTrack;
+ helper.Process(&distanceTrack);
+
+ // Now add a new node as child, name it <ourName>.Target
+ // and assign the distance track to it. This is that the
+ // information where the target is and how it moves is
+ // not lost
+ D3DS::Node* nd = new D3DS::Node();
+ pcIn->push_back(nd);
+
+ nd->mName = pcIn->mName + ".Target";
+
+ aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
+ nda->mNodeName.Set(nd->mName);
+
+ nda->mNumPositionKeys = (unsigned int)distanceTrack.size();
+ nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
+ ::memcpy(nda->mPositionKeys,&distanceTrack[0],
+ sizeof(aiVectorKey)*nda->mNumPositionKeys);
+ }
+#endif
+
+ // Cameras or lights define their transformation in their parent node and in the
+ // corresponding light or camera chunks. However, we read and process the latter
+ // to to be able to return valid cameras/lights even if no scenegraph is given.
+ for (unsigned int n = 0; n < pcSOut->mNumCameras;++n) {
+ if (pcSOut->mCameras[n]->mName == pcOut->mName) {
+ pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f,0.f,1.f);
+ }
+ }
+ for (unsigned int n = 0; n < pcSOut->mNumLights;++n) {
+ if (pcSOut->mLights[n]->mName == pcOut->mName) {
+ pcSOut->mLights[n]->mDirection = aiVector3D(0.f,0.f,1.f);
+ }
+ }
+
+ // Allocate a new node anim and setup its name
+ aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
+ nda->mNodeName.Set(pcIn->mName);
+
+ // POSITION keys
+ if (pcIn->aPositionKeys.size() > 0)
+ {
+ nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size();
+ nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys];
+ ::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0],
+ sizeof(aiVectorKey)*nda->mNumPositionKeys);
+ }
+
+ // ROTATION keys
+ if (pcIn->aRotationKeys.size() > 0)
+ {
+ nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size();
+ nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys];
+
+ // Rotations are quaternion offsets
+ aiQuaternion abs;
+ for (unsigned int n = 0; n < nda->mNumRotationKeys;++n)
+ {
+ const aiQuatKey& q = pcIn->aRotationKeys[n];
+
+ abs = (n ? abs * q.mValue : q.mValue);
+ nda->mRotationKeys[n].mTime = q.mTime;
+ nda->mRotationKeys[n].mValue = abs.Normalize();
+ }
+ }
+
+ // SCALING keys
+ if (pcIn->aScalingKeys.size() > 0)
+ {
+ nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size();
+ nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys];
+ ::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0],
+ sizeof(aiVectorKey)*nda->mNumScalingKeys);
+ }
+ }
+
+ // Allocate storage for children
+ pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size();
+ pcOut->mChildren = new aiNode*[pcIn->mChildren.size()];
+
+ // Recursively process all children
+ const unsigned int size = pcIn->mChildren.size();
+ for (unsigned int i = 0; i < size;++i)
+ {
+ pcOut->mChildren[i] = new aiNode();
+ pcOut->mChildren[i]->mParent = pcOut;
+ AddNodeToGraph(pcSOut,pcOut->mChildren[i],pcIn->mChildren[i],abs);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Find out how many node animation channels we'll have finally
+void CountTracks(D3DS::Node* node, unsigned int& cnt)
+{
+ //////////////////////////////////////////////////////////////////////////////
+ // We will never generate more than one channel for a node, so
+ // this is rather easy here.
+
+ if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 ||
+ node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 ||
+ node->aTargetPositionKeys.size() > 1)
+ {
+ ++cnt;
+
+ // account for the additional channel for the camera/spotlight target position
+ if (node->aTargetPositionKeys.size() > 1)++cnt;
+ }
+
+ // Recursively process all children
+ for (unsigned int i = 0; i < node->mChildren.size();++i)
+ CountTracks(node->mChildren[i],cnt);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Generate the output node graph
+void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut)
+{
+ pcOut->mRootNode = new aiNode();
+ if (0 == mRootNode->mChildren.size())
+ {
+ //////////////////////////////////////////////////////////////////////////////
+ // It seems the file is so fucked up that it has not even a hierarchy.
+ // generate a flat hiearachy which looks like this:
+ //
+ // ROOT_NODE
+ // |
+ // ----------------------------------------
+ // | | | | |
+ // MESH_0 MESH_1 MESH_2 ... MESH_N CAMERA_0 ....
+ //
+ DefaultLogger::get()->warn("No hierarchy information has been found in the file. ");
+
+ pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes +
+ mScene->mCameras.size() + mScene->mLights.size();
+
+ pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mRootNode->mNumChildren ];
+ pcOut->mRootNode->mName.Set("<3DSDummyRoot>");
+
+ // Build dummy nodes for all meshes
+ unsigned int a = 0;
+ for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a)
+ {
+ aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
+ pcNode->mParent = pcOut->mRootNode;
+ pcNode->mMeshes = new unsigned int[1];
+ pcNode->mMeshes[0] = i;
+ pcNode->mNumMeshes = 1;
+
+ // Build a name for the node
+ pcNode->mName.length = sprintf(pcNode->mName.data,"3DSMesh_%i",i);
+ //fprintf(stderr, "XXXX created dummy node: %s\n", pcNode->mName.data);
+ }
+
+ // Build dummy nodes for all cameras
+ for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a)
+ {
+ aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
+ pcNode->mParent = pcOut->mRootNode;
+
+ // Build a name for the node
+ pcNode->mName = mScene->mCameras[i]->mName;
+ }
+
+ // Build dummy nodes for all lights
+ for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a)
+ {
+ aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode();
+ pcNode->mParent = pcOut->mRootNode;
+
+ // Build a name for the node
+ pcNode->mName = mScene->mLights[i]->mName;
+ }
+ }
+ else
+ {
+ // First of all: find out how many scaling, rotation and translation
+ // animation tracks we'll have afterwards
+ unsigned int numChannel = 0;
+ CountTracks(mRootNode,numChannel);
+
+ if (numChannel)
+ {
+ // Allocate a primary animation channel
+ pcOut->mNumAnimations = 1;
+ pcOut->mAnimations = new aiAnimation*[1];
+ aiAnimation* anim = pcOut->mAnimations[0] = new aiAnimation();
+
+ anim->mName.Set("3DSMasterAnim");
+
+ // Allocate enough storage for all node animation channels,
+ // but don't set the mNumChannels member - we'll use it to
+ // index into the array
+ anim->mChannels = new aiNodeAnim*[numChannel];
+ }
+
+ aiMatrix4x4 m;
+ AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode,m);
+ }
+
+ // We used the first vertex color set to store some emporary values so we need to cleanup here
+ for (unsigned int a = 0; a < pcOut->mNumMeshes;++a)
+ pcOut->mMeshes[a]->mColors[0] = NULL;
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ pcOut->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation;
+
+ // If the root node is unnamed name it "<3DSRoot>"
+ if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) ||
+ (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') )
+ {
+ pcOut->mRootNode->mName.Set("<3DSRoot>");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert all meshes in the scene and generate the final output scene.
+void Discreet3DSImporter::ConvertScene(aiScene* pcOut)
+{
+ // Allocate enough storage for all output materials
+ pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size();
+ pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials];
+
+ // ... and convert the 3DS materials to aiMaterial's
+ for (unsigned int i = 0; i < pcOut->mNumMaterials;++i)
+ {
+ MaterialHelper* pcNew = new MaterialHelper();
+ ConvertMaterial(mScene->mMaterials[i],*pcNew);
+ pcOut->mMaterials[i] = pcNew;
+ }
+
+ // Generate the output mesh list
+ ConvertMeshes(pcOut);
+
+ // Now copy all light sources to the output scene
+ pcOut->mNumLights = (unsigned int)mScene->mLights.size();
+ if (pcOut->mNumLights)
+ {
+ pcOut->mLights = new aiLight*[pcOut->mNumLights];
+ ::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights);
+ }
+
+ // Now copy all cameras to the output scene
+ pcOut->mNumCameras = (unsigned int)mScene->mCameras.size();
+ if (pcOut->mNumCameras)
+ {
+ pcOut->mCameras = new aiCamera*[pcOut->mNumCameras];
+ ::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras);
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
diff --git a/3rdparty/assimp/code/3DSHelper.h b/3rdparty/assimp/code/3DSHelper.h
new file mode 100644
index 000000000..db37415fd
--- /dev/null
+++ b/3rdparty/assimp/code/3DSHelper.h
@@ -0,0 +1,577 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines helper data structures for the import of 3DS files */
+
+#ifndef AI_3DSFILEHELPER_H_INC
+#define AI_3DSFILEHELPER_H_INC
+
+
+#include "SpatialSort.h"
+#include "SmoothingGroups.h"
+
+namespace Assimp {
+namespace D3DS {
+
+#include "./../include/Compiler/pushpack1.h"
+
+// ---------------------------------------------------------------------------
+/** Discreet3DS class: Helper class for loading 3ds files. Defines chunks
+* and data structures.
+*/
+class Discreet3DS
+{
+private:
+ inline Discreet3DS() {}
+
+public:
+
+ //! data structure for a single chunk in a .3ds file
+ struct Chunk
+ {
+ uint16_t Flag;
+ uint32_t Size;
+ } PACK_STRUCT;
+
+
+ //! Used for shading field in material3ds structure
+ //! From AutoDesk 3ds SDK
+ typedef enum
+ {
+ // translated to gouraud shading with wireframe active
+ Wire = 0x0,
+
+ // if this material is set, no vertex normals will
+ // be calculated for the model. Face normals + gouraud
+ Flat = 0x1,
+
+ // standard gouraud shading
+ Gouraud = 0x2,
+
+ // phong shading
+ Phong = 0x3,
+
+ // cooktorrance or anistropic phong shading ...
+ // the exact meaning is unknown, if you know it
+ // feel free to tell me ;-)
+ Metal = 0x4,
+
+ // required by the ASE loader
+ Blinn = 0x5
+ } shadetype3ds;
+
+ // Flags for animated keys
+ enum
+ {
+ KEY_USE_TENS = 0x1,
+ KEY_USE_CONT = 0x2,
+ KEY_USE_BIAS = 0x4,
+ KEY_USE_EASE_TO = 0x8,
+ KEY_USE_EASE_FROM = 0x10
+ } ;
+
+ enum
+ {
+
+ // ********************************************************************
+ // Basic chunks which can be found everywhere in the file
+ CHUNK_VERSION = 0x0002,
+ CHUNK_RGBF = 0x0010, // float4 R; float4 G; float4 B
+ CHUNK_RGBB = 0x0011, // int1 R; int1 G; int B
+
+ // Linear color values (gamma = 2.2?)
+ CHUNK_LINRGBF = 0x0013, // float4 R; float4 G; float4 B
+ CHUNK_LINRGBB = 0x0012, // int1 R; int1 G; int B
+
+ CHUNK_PERCENTW = 0x0030, // int2 percentage
+ CHUNK_PERCENTF = 0x0031, // float4 percentage
+ // ********************************************************************
+
+ // Prj master chunk
+ CHUNK_PRJ = 0xC23D,
+
+ // MDLI master chunk
+ CHUNK_MLI = 0x3DAA,
+
+ // Primary main chunk of the .3ds file
+ CHUNK_MAIN = 0x4D4D,
+
+ // Mesh main chunk
+ CHUNK_OBJMESH = 0x3D3D,
+
+ // Specifies the background color of the .3ds file
+ // This is passed through the material system for
+ // viewing purposes.
+ CHUNK_BKGCOLOR = 0x1200,
+
+ // Specifies the ambient base color of the scene.
+ // This is added to all materials in the file
+ CHUNK_AMBCOLOR = 0x2100,
+
+ // Specifies the background image for the whole scene
+ // This value is passed through the material system
+ // to the viewer
+ CHUNK_BIT_MAP = 0x1100,
+ CHUNK_BIT_MAP_EXISTS = 0x1101,
+
+ // ********************************************************************
+ // Viewport related stuff. Ignored
+ CHUNK_DEFAULT_VIEW = 0x3000,
+ CHUNK_VIEW_TOP = 0x3010,
+ CHUNK_VIEW_BOTTOM = 0x3020,
+ CHUNK_VIEW_LEFT = 0x3030,
+ CHUNK_VIEW_RIGHT = 0x3040,
+ CHUNK_VIEW_FRONT = 0x3050,
+ CHUNK_VIEW_BACK = 0x3060,
+ CHUNK_VIEW_USER = 0x3070,
+ CHUNK_VIEW_CAMERA = 0x3080,
+ // ********************************************************************
+
+ // Mesh chunks
+ CHUNK_OBJBLOCK = 0x4000,
+ CHUNK_TRIMESH = 0x4100,
+ CHUNK_VERTLIST = 0x4110,
+ CHUNK_VERTFLAGS = 0x4111,
+ CHUNK_FACELIST = 0x4120,
+ CHUNK_FACEMAT = 0x4130,
+ CHUNK_MAPLIST = 0x4140,
+ CHUNK_SMOOLIST = 0x4150,
+ CHUNK_TRMATRIX = 0x4160,
+ CHUNK_MESHCOLOR = 0x4165,
+ CHUNK_TXTINFO = 0x4170,
+ CHUNK_LIGHT = 0x4600,
+ CHUNK_CAMERA = 0x4700,
+ CHUNK_HIERARCHY = 0x4F00,
+
+ // Specifies the global scaling factor. This is applied
+ // to the root node's transformation matrix
+ CHUNK_MASTER_SCALE = 0x0100,
+
+ // ********************************************************************
+ // Material chunks
+ CHUNK_MAT_MATERIAL = 0xAFFF,
+
+ // asciiz containing the name of the material
+ CHUNK_MAT_MATNAME = 0xA000,
+ CHUNK_MAT_AMBIENT = 0xA010, // followed by color chunk
+ CHUNK_MAT_DIFFUSE = 0xA020, // followed by color chunk
+ CHUNK_MAT_SPECULAR = 0xA030, // followed by color chunk
+
+ // Specifies the shininess of the material
+ // followed by percentage chunk
+ CHUNK_MAT_SHININESS = 0xA040,
+ CHUNK_MAT_SHININESS_PERCENT = 0xA041 ,
+
+ // Specifies the shading mode to be used
+ // followed by a short
+ CHUNK_MAT_SHADING = 0xA100,
+
+ // NOTE: Emissive color (self illumination) seems not
+ // to be a color but a single value, type is unknown.
+ // Make the parser accept both of them.
+ // followed by percentage chunk (?)
+ CHUNK_MAT_SELF_ILLUM = 0xA080,
+
+ // Always followed by percentage chunk (?)
+ CHUNK_MAT_SELF_ILPCT = 0xA084,
+
+ // Always followed by percentage chunk
+ CHUNK_MAT_TRANSPARENCY = 0xA050,
+
+ // Diffuse texture channel 0
+ CHUNK_MAT_TEXTURE = 0xA200,
+
+ // Contains opacity information for each texel
+ CHUNK_MAT_OPACMAP = 0xA210,
+
+ // Contains a reflection map to be used to reflect
+ // the environment. This is partially supported.
+ CHUNK_MAT_REFLMAP = 0xA220,
+
+ // Self Illumination map (emissive colors)
+ CHUNK_MAT_SELFIMAP = 0xA33d,
+
+ // Bumpmap. Not specified whether it is a heightmap
+ // or a normal map. Assme it is a heightmap since
+ // artist normally prefer this format.
+ CHUNK_MAT_BUMPMAP = 0xA230,
+
+ // Specular map. Seems to influence the specular color
+ CHUNK_MAT_SPECMAP = 0xA204,
+
+ // Holds shininess data.
+ CHUNK_MAT_MAT_SHINMAP = 0xA33C,
+
+ // Scaling in U/V direction.
+ // (need to gen separate UV coordinate set
+ // and do this by hand)
+ CHUNK_MAT_MAP_USCALE = 0xA354,
+ CHUNK_MAT_MAP_VSCALE = 0xA356,
+
+ // Translation in U/V direction.
+ // (need to gen separate UV coordinate set
+ // and do this by hand)
+ CHUNK_MAT_MAP_UOFFSET = 0xA358,
+ CHUNK_MAT_MAP_VOFFSET = 0xA35a,
+
+ // UV-coordinates rotation around the z-axis
+ // Assumed to be in radians.
+ CHUNK_MAT_MAP_ANG = 0xA35C,
+
+ // Tiling flags for 3DS files
+ CHUNK_MAT_MAP_TILING = 0xa351,
+
+ // Specifies the file name of a texture
+ CHUNK_MAPFILE = 0xA300,
+
+ // Specifies whether a materail requires two-sided rendering
+ CHUNK_MAT_TWO_SIDE = 0xA081,
+ // ********************************************************************
+
+ // Main keyframer chunk. Contains translation/rotation/scaling data
+ CHUNK_KEYFRAMER = 0xB000,
+
+ // Supported sub chunks
+ CHUNK_TRACKINFO = 0xB002,
+ CHUNK_TRACKOBJNAME = 0xB010,
+ CHUNK_TRACKDUMMYOBJNAME = 0xB011,
+ CHUNK_TRACKPIVOT = 0xB013,
+ CHUNK_TRACKPOS = 0xB020,
+ CHUNK_TRACKROTATE = 0xB021,
+ CHUNK_TRACKSCALE = 0xB022,
+
+ // ********************************************************************
+ // Keyframes for various other stuff in the file
+ // Partially ignored
+ CHUNK_AMBIENTKEY = 0xB001,
+ CHUNK_TRACKMORPH = 0xB026,
+ CHUNK_TRACKHIDE = 0xB029,
+ CHUNK_OBJNUMBER = 0xB030,
+ CHUNK_TRACKCAMERA = 0xB003,
+ CHUNK_TRACKFOV = 0xB023,
+ CHUNK_TRACKROLL = 0xB024,
+ CHUNK_TRACKCAMTGT = 0xB004,
+ CHUNK_TRACKLIGHT = 0xB005,
+ CHUNK_TRACKLIGTGT = 0xB006,
+ CHUNK_TRACKSPOTL = 0xB007,
+ CHUNK_FRAMES = 0xB008,
+ // ********************************************************************
+
+ // light sub-chunks
+ CHUNK_DL_OFF = 0x4620,
+ CHUNK_DL_OUTER_RANGE = 0x465A,
+ CHUNK_DL_INNER_RANGE = 0x4659,
+ CHUNK_DL_MULTIPLIER = 0x465B,
+ CHUNK_DL_EXCLUDE = 0x4654,
+ CHUNK_DL_ATTENUATE = 0x4625,
+ CHUNK_DL_SPOTLIGHT = 0x4610,
+
+ // camera sub-chunks
+ CHUNK_CAM_RANGES = 0x4720
+ };
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a 3ds mesh face */
+struct Face : public FaceWithSmoothingGroup
+{
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a texture */
+struct Texture
+{
+ //! Default constructor
+ Texture()
+ : mOffsetU (0.0f)
+ , mOffsetV (0.0f)
+ , mScaleU (1.0f)
+ , mScaleV (1.0f)
+ , mRotation (0.0f)
+ , mMapMode (aiTextureMapMode_Wrap)
+ , iUVSrc (0)
+ {
+ mTextureBlend = get_qnan();
+ }
+
+ //! Specifies the blend factor for the texture
+ float mTextureBlend;
+
+ //! Specifies the filename of the texture
+ std::string mMapName;
+
+ //! Specifies texture coordinate offsets/scaling/rotations
+ float mOffsetU;
+ float mOffsetV;
+ float mScaleU;
+ float mScaleV;
+ float mRotation;
+
+ //! Specifies the mapping mode to be used for the texture
+ aiTextureMapMode mMapMode;
+
+ //! Used internally
+ bool bPrivate;
+ int iUVSrc;
+};
+
+#include "./../include/Compiler/poppack1.h"
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a 3ds material */
+struct Material
+{
+ //! Default constructor. Builds a default name for the material
+ Material()
+ :
+ mDiffuse (0.6f,0.6f,0.6f), // FIX ... we won't want object to be black
+ mSpecularExponent (0.0f),
+ mShininessStrength (1.0f),
+ mShading(Discreet3DS::Gouraud),
+ mTransparency (1.0f),
+ mBumpHeight (1.0f),
+ mTwoSided (false)
+ {
+ static int iCnt = 0;
+
+ char szTemp[128];
+ sprintf(szTemp,"UNNAMED_%i",iCnt++);
+ mName = szTemp;
+ }
+
+ //! Name of the material
+ std::string mName;
+ //! Diffuse color of the material
+ aiColor3D mDiffuse;
+ //! Specular exponent
+ float mSpecularExponent;
+ //! Shininess strength, in percent
+ float mShininessStrength;
+ //! Specular color of the material
+ aiColor3D mSpecular;
+ //! Ambient color of the material
+ aiColor3D mAmbient;
+ //! Shading type to be used
+ Discreet3DS::shadetype3ds mShading;
+ //! Opacity of the material
+ float mTransparency;
+ //! Diffuse texture channel
+ Texture sTexDiffuse;
+ //! Opacity texture channel
+ Texture sTexOpacity;
+ //! Specular texture channel
+ Texture sTexSpecular;
+ //! Reflective texture channel
+ Texture sTexReflective;
+ //! Bump texture channel
+ Texture sTexBump;
+ //! Emissive texture channel
+ Texture sTexEmissive;
+ //! Shininess texture channel
+ Texture sTexShininess;
+ //! Scaling factor for the bump values
+ float mBumpHeight;
+ //! Emissive color
+ aiColor3D mEmissive;
+ //! Ambient texture channel
+ //! (used by the ASE format)
+ Texture sTexAmbient;
+ //! True if the material must be rendered from two sides
+ bool mTwoSided;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent a 3ds file mesh */
+struct Mesh : public MeshWithSmoothingGroups<D3DS::Face>
+{
+ //! Default constructor
+ Mesh()
+ {
+ static int iCnt = 0;
+
+ // Generate a default name for the mesh
+ char szTemp[128];
+ ::sprintf(szTemp,"UNNAMED_%i",iCnt++);
+ mName = szTemp;
+ }
+
+ //! Name of the mesh
+ std::string mName;
+
+ //! Texture coordinates
+ std::vector<aiVector3D> mTexCoords;
+
+ //! Face materials
+ std::vector<unsigned int> mFaceMaterials;
+
+ //! Local transformation matrix
+ aiMatrix4x4 mMat;
+};
+
+// ---------------------------------------------------------------------------
+/** Float key - quite similar to aiVectorKey and aiQuatKey. Both are in the
+ C-API, so it would be difficult to make them a template. */
+struct aiFloatKey
+{
+ double mTime; ///< The time of this key
+ float mValue; ///< The value of this key
+
+#ifdef __cplusplus
+
+ // time is not compared
+ bool operator == (const aiFloatKey& o) const
+ {return o.mValue == this->mValue;}
+
+ bool operator != (const aiFloatKey& o) const
+ {return o.mValue != this->mValue;}
+
+ // Only time is compared. This operator is defined
+ // for use with std::sort
+ bool operator < (const aiFloatKey& o) const
+ {return mTime < o.mTime;}
+
+ bool operator > (const aiFloatKey& o) const
+ {return mTime < o.mTime;}
+
+#endif
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent a 3ds file node */
+struct Node
+{
+ Node()
+
+ : mHierarchyPos (0)
+ , mHierarchyIndex (0)
+
+ {
+ static int iCnt = 0;
+
+ // Generate a default name for the node
+ char szTemp[128];
+ ::sprintf(szTemp,"UNNAMED_%i",iCnt++);
+ mName = szTemp;
+
+ aRotationKeys.reserve (20);
+ aPositionKeys.reserve (20);
+ aScalingKeys.reserve (20);
+ }
+
+ ~Node()
+ {
+ for (unsigned int i = 0; i < mChildren.size();++i)
+ delete mChildren[i];
+ }
+
+ //! Pointer to the parent node
+ Node* mParent;
+
+ //! Holds all child nodes
+ std::vector<Node*> mChildren;
+
+ //! Name of the node
+ std::string mName;
+
+ //! Dummy nodes: real name to be combined with the $$$DUMMY
+ std::string mDummyName;
+
+ //! Position of the node in the hierarchy (tree depth)
+ int16_t mHierarchyPos;
+
+ //! Index of the node
+ int16_t mHierarchyIndex;
+
+ //! Rotation keys loaded from the file
+ std::vector<aiQuatKey> aRotationKeys;
+
+ //! Position keys loaded from the file
+ std::vector<aiVectorKey> aPositionKeys;
+
+ //! Scaling keys loaded from the file
+ std::vector<aiVectorKey> aScalingKeys;
+
+
+ // For target lights (spot lights and directional lights):
+ // The position of the target
+ std::vector< aiVectorKey > aTargetPositionKeys;
+
+ // For cameras: the camera roll angle
+ std::vector< aiFloatKey > aCameraRollKeys;
+
+ //! Pivot position loaded from the file
+ aiVector3D vPivot;
+
+ //! Add a child node, setup the right parent node for it
+ //! \param pc Node to be 'adopted'
+ inline Node& push_back(Node* pc)
+ {
+ mChildren.push_back(pc);
+ pc->mParent = this;
+ return *this;
+ }
+};
+// ---------------------------------------------------------------------------
+/** Helper structure analogue to aiScene */
+struct Scene
+{
+ //! List of all materials loaded
+ //! NOTE: 3ds references materials globally
+ std::vector<Material> mMaterials;
+
+ //! List of all meshes loaded
+ std::vector<Mesh> mMeshes;
+
+ //! List of all cameras loaded
+ std::vector<aiCamera*> mCameras;
+
+ //! List of all lights loaded
+ std::vector<aiLight*> mLights;
+
+ //! Pointer to the root node of the scene
+ // --- moved to main class
+ // Node* pcRootNode;
+};
+
+
+} // end of namespace D3DS
+} // end of namespace Assimp
+
+#endif // AI_XFILEHELPER_H_INC
diff --git a/3rdparty/assimp/code/3DSLoader.cpp b/3rdparty/assimp/code/3DSLoader.cpp
new file mode 100644
index 000000000..eb69bd5db
--- /dev/null
+++ b/3rdparty/assimp/code/3DSLoader.cpp
@@ -0,0 +1,1381 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 3DSLoader.cpp
+ * @brief Implementation of the 3ds importer class
+ *
+ * http://www.the-labs.com/Blender/3DS-details.html
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
+
+// internal headers
+#include "3DSLoader.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Begins a new parsing block
+// - Reads the current chunk and validates it
+// - computes its length
+#define ASSIMP_3DS_BEGIN_CHUNK() \
+ while (true) { \
+ if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)){ \
+ return; \
+ } \
+ Discreet3DS::Chunk chunk; \
+ ReadChunk(&chunk); \
+ int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \
+ const int oldReadLimit = stream->GetReadLimit(); \
+ stream->SetReadLimit(stream->GetCurrentPos() + chunkSize); \
+
+
+// ------------------------------------------------------------------------------------------------
+// End a parsing block
+// Must follow at the end of each parsing block, reset chunk end marker to previous value
+#define ASSIMP_3DS_END_CHUNK() \
+ stream->SkipToReadLimit(); \
+ stream->SetReadLimit(oldReadLimit); \
+ if (stream->GetRemainingSizeToLimit() == 0) \
+ return; \
+ }
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+Discreet3DSImporter::Discreet3DSImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+Discreet3DSImporter::~Discreet3DSImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ std::string extension = GetExtension(pFile);
+ if (extension == "3ds" || extension == "prj" ) {
+ return true;
+ }
+ if (!extension.length() || checkSig) {
+ uint16_t token[3];
+ token[0] = 0x4d4d;
+ token[1] = 0x3dc2;
+ //token[2] = 0x3daa;
+ return CheckMagicToken(pIOHandler,pFile,token,2,0,2);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get list of all extension supported by this loader
+void Discreet3DSImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("3ds");
+ extensions.insert("prj");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void Discreet3DSImporter::SetupProperties(const Importer* /* pImp */)
+{
+ // nothing to be done for the moment
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void Discreet3DSImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ StreamReaderLE stream(pIOHandler->Open(pFile,"rb"));
+ this->stream = &stream;
+
+ // We should have at least one chunk
+ if (stream.GetRemainingSize() < 16) {
+ throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile);
+ }
+
+ // Allocate our temporary 3DS representation
+ mScene = new D3DS::Scene();
+
+ // Initialize members
+ mLastNodeIndex = -1;
+ mCurrentNode = new D3DS::Node();
+ mRootNode = mCurrentNode;
+ mRootNode->mHierarchyPos = -1;
+ mRootNode->mHierarchyIndex = -1;
+ mRootNode->mParent = NULL;
+ mMasterScale = 1.0f;
+ mBackgroundImage = "";
+ bHasBG = false;
+ bIsPrj = false;
+
+ // Parse the file
+ ParseMainChunk();
+
+ // Process all meshes in the file. First check whether all
+ // face indices haev valid values. The generate our
+ // internal verbose representation. Finally compute normal
+ // vectors from the smoothing groups we read from the
+ // file.
+ for (std::vector<D3DS::Mesh>::iterator i = mScene->mMeshes.begin(),
+ end = mScene->mMeshes.end(); i != end;++i) {
+ CheckIndices(*i);
+ MakeUnique (*i);
+ ComputeNormalsWithSmoothingsGroups<D3DS::Face>(*i);
+ }
+
+ // Replace all occurences of the default material with a
+ // valid material. Generate it if no material containing
+ // DEFAULT in its name has been found in the file
+ ReplaceDefaultMaterial();
+
+ // Convert the scene from our internal representation to an
+ // aiScene object. This involves copying all meshes, lights
+ // and cameras to the scene
+ ConvertScene(pScene);
+
+ // Generate the node graph for the scene. This is a little bit
+ // tricky since we'll need to split some meshes into submeshes
+ GenerateNodeGraph(pScene);
+
+ // Now apply the master scaling factor to the scene
+ ApplyMasterScale(pScene);
+
+ // Delete our internal scene representation and the root
+ // node, so the whole hierarchy will follow
+ delete mRootNode;
+ delete mScene;
+
+ AI_DEBUG_INVALIDATE_PTR(mRootNode);
+ AI_DEBUG_INVALIDATE_PTR(mScene);
+ AI_DEBUG_INVALIDATE_PTR(this->stream);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Applies a master-scaling factor to the imported scene
+void Discreet3DSImporter::ApplyMasterScale(aiScene* pScene)
+{
+ // There are some 3DS files with a zero scaling factor
+ if (!mMasterScale)mMasterScale = 1.0f;
+ else mMasterScale = 1.0f / mMasterScale;
+
+ // Construct an uniform scaling matrix and multiply with it
+ pScene->mRootNode->mTransformation *= aiMatrix4x4(
+ mMasterScale,0.0f, 0.0f, 0.0f,
+ 0.0f, mMasterScale,0.0f, 0.0f,
+ 0.0f, 0.0f, mMasterScale,0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+
+ // Check whether a scaling track is assigned to the root node.
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a new chunk from the file
+void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk* pcOut)
+{
+ ai_assert(pcOut != NULL);
+
+ pcOut->Flag = stream->GetI2();
+ pcOut->Size = stream->GetI4();
+
+ if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize())
+ throw DeadlyImportError("Chunk is too large");
+
+ if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit())
+ DefaultLogger::get()->error("3DS: Chunk overflow");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skip a chunk
+void Discreet3DSImporter::SkipChunk()
+{
+ Discreet3DS::Chunk psChunk;
+ ReadChunk(&psChunk);
+
+ stream->IncPtr(psChunk.Size-sizeof(Discreet3DS::Chunk));
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Process the primary chunk of the file
+void Discreet3DSImporter::ParseMainChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+
+ case Discreet3DS::CHUNK_PRJ:
+ bIsPrj = true;
+ case Discreet3DS::CHUNK_MAIN:
+ ParseEditorChunk();
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+ // recursively continue processing this hierarchy level
+ return ParseMainChunk();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseEditorChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_OBJMESH:
+
+ ParseObjectChunk();
+ break;
+
+ // NOTE: In several documentations in the internet this
+ // chunk appears at different locations
+ case Discreet3DS::CHUNK_KEYFRAMER:
+
+ ParseKeyframeChunk();
+ break;
+
+ case Discreet3DS::CHUNK_VERSION:
+ {
+ // print the version number
+ char buff[10];
+ ASSIMP_itoa10(buff,stream->GetI2());
+ DefaultLogger::get()->info(std::string("3DS file format version: ") + buff);
+ }
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseObjectChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_OBJBLOCK:
+ {
+ unsigned int cnt = 0;
+ const char* sz = (const char*)stream->GetPtr();
+
+ // Get the name of the geometry object
+ while (stream->GetI1())++cnt;
+ ParseChunk(sz,cnt);
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MATERIAL:
+
+ // Add a new material to the list
+ mScene->mMaterials.push_back(D3DS::Material());
+ ParseMaterialChunk();
+ break;
+
+ case Discreet3DS::CHUNK_AMBCOLOR:
+
+ // This is the ambient base color of the scene.
+ // We add it to the ambient color of all materials
+ ParseColorChunk(&mClrAmbient,true);
+ if (is_qnan(mClrAmbient.r))
+ {
+ // We failed to read the ambient base color.
+ DefaultLogger::get()->error("3DS: Failed to read ambient base color");
+ mClrAmbient.r = mClrAmbient.g = mClrAmbient.b = 0.0f;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_BIT_MAP:
+ {
+ // Specifies the background image. The string should already be
+ // properly 0 terminated but we need to be sure
+ unsigned int cnt = 0;
+ const char* sz = (const char*)stream->GetPtr();
+ while (stream->GetI1())++cnt;
+ mBackgroundImage = std::string(sz,cnt);
+ }
+ break;
+
+ case Discreet3DS::CHUNK_BIT_MAP_EXISTS:
+ bHasBG = true;
+ break;
+
+ case Discreet3DS::CHUNK_MASTER_SCALE:
+ // Scene master scaling factor
+ mMasterScale = stream->GetF4();
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+#include <QtCore/qstring.h>
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num)
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // IMPLEMENTATION NOTE;
+ // Cameras or lights define their transformation in their parent node and in the
+ // corresponding light or camera chunks. However, we read and process the latter
+ // to to be able to return valid cameras/lights even if no scenegraph is given.
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_TRIMESH:
+ {
+ // this starts a new triangle mesh
+ mScene->mMeshes.push_back(D3DS::Mesh());
+ D3DS::Mesh& m = mScene->mMeshes.back();
+
+ // Setup the name of the mesh
+ m.mName = std::string(name, num);
+
+ // Read mesh chunks
+ ParseMeshChunk();
+ }
+ break;
+
+ case Discreet3DS::CHUNK_LIGHT:
+ {
+ // This starts a new light
+ aiLight* light = new aiLight();
+ mScene->mLights.push_back(light);
+
+ light->mName.Set(std::string(name, num));
+
+ // First read the position of the light
+ light->mPosition.x = stream->GetF4();
+ light->mPosition.y = stream->GetF4();
+ light->mPosition.z = stream->GetF4();
+
+ light->mColorDiffuse = aiColor3D(1.f,1.f,1.f);
+
+ // Now check for further subchunks
+ if (!bIsPrj) /* fixme */
+ ParseLightChunk();
+
+ // The specular light color is identical the the diffuse light color. The ambient light color
+ // is equal to the ambient base color of the whole scene.
+ light->mColorSpecular = light->mColorDiffuse;
+ light->mColorAmbient = mClrAmbient;
+
+ if (light->mType == aiLightSource_UNDEFINED)
+ {
+ // It must be a point light
+ light->mType = aiLightSource_POINT;
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_CAMERA:
+ {
+ // This starts a new camera
+ aiCamera* camera = new aiCamera();
+ mScene->mCameras.push_back(camera);
+ camera->mName.Set(std::string(name, num));
+
+ // First read the position of the camera
+ camera->mPosition.x = stream->GetF4();
+ camera->mPosition.y = stream->GetF4();
+ camera->mPosition.z = stream->GetF4();
+
+ // Then the camera target
+ camera->mLookAt.x = stream->GetF4() - camera->mPosition.x;
+ camera->mLookAt.y = stream->GetF4() - camera->mPosition.y;
+ camera->mLookAt.z = stream->GetF4() - camera->mPosition.z;
+ float len = camera->mLookAt.Length();
+ if (len < 1e-5f) {
+
+ // There are some files with lookat == position. Don't know why or whether it's ok or not.
+ DefaultLogger::get()->error("3DS: Unable to read proper camera look-at vector");
+ camera->mLookAt = aiVector3D(0.f,1.f,0.f);
+
+ }
+ else camera->mLookAt /= len;
+
+ // And finally - the camera rotation angle, in counter clockwise direction
+ const float angle = AI_DEG_TO_RAD( stream->GetF4() );
+ aiQuaternion quat(camera->mLookAt,angle);
+ camera->mUp = quat.GetMatrix() * aiVector3D(0.f,1.f,0.f);
+
+ // Read the lense angle
+ camera->mHorizontalFOV = AI_DEG_TO_RAD ( stream->GetF4() );
+ if (camera->mHorizontalFOV < 0.001f) {
+ camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f);
+ }
+
+ // Now check for further subchunks
+ if (!bIsPrj) /* fixme */ {
+ ParseCameraChunk();
+ }}
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseLightChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+ aiLight* light = mScene->mLights.back();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_DL_SPOTLIGHT:
+ // Now we can be sure that the light is a spot light
+ light->mType = aiLightSource_SPOT;
+
+ // We wouldn't need to normalize here, but we do it
+ light->mDirection.x = stream->GetF4() - light->mPosition.x;
+ light->mDirection.y = stream->GetF4() - light->mPosition.y;
+ light->mDirection.z = stream->GetF4() - light->mPosition.z;
+ light->mDirection.Normalize();
+
+ // Now the hotspot and falloff angles - in degrees
+ light->mAngleInnerCone = AI_DEG_TO_RAD( stream->GetF4() );
+
+ // FIX: the falloff angle is just an offset
+ light->mAngleOuterCone = light->mAngleInnerCone+AI_DEG_TO_RAD( stream->GetF4() );
+ break;
+
+ // intensity multiplier
+ case Discreet3DS::CHUNK_DL_MULTIPLIER:
+ light->mColorDiffuse = light->mColorDiffuse * stream->GetF4();
+ break;
+
+ // light color
+ case Discreet3DS::CHUNK_RGBF:
+ case Discreet3DS::CHUNK_LINRGBF:
+ light->mColorDiffuse.r *= stream->GetF4();
+ light->mColorDiffuse.g *= stream->GetF4();
+ light->mColorDiffuse.b *= stream->GetF4();
+ break;
+
+ // light attenuation
+ case Discreet3DS::CHUNK_DL_ATTENUATE:
+ light->mAttenuationLinear = stream->GetF4();
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseCameraChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+ aiCamera* camera = mScene->mCameras.back();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ // near and far clip plane
+ case Discreet3DS::CHUNK_CAM_RANGES:
+ camera->mClipPlaneNear = stream->GetF4();
+ camera->mClipPlaneFar = stream->GetF4();
+ break;
+ }
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseKeyframeChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_TRACKCAMTGT:
+ case Discreet3DS::CHUNK_TRACKSPOTL:
+ case Discreet3DS::CHUNK_TRACKCAMERA:
+ case Discreet3DS::CHUNK_TRACKINFO:
+ case Discreet3DS::CHUNK_TRACKLIGHT:
+ case Discreet3DS::CHUNK_TRACKLIGTGT:
+
+ // this starts a new mesh hierarchy chunk
+ ParseHierarchyChunk(chunk.Flag);
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Little helper function for ParseHierarchyChunk
+void Discreet3DSImporter::InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent)
+{
+ if (!pcCurrent) {
+ mRootNode->push_back(pcNode);
+ return;
+ }
+
+ if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos) {
+ if (pcCurrent->mParent) {
+ pcCurrent->mParent->push_back(pcNode);
+ }
+ else pcCurrent->push_back(pcNode);
+ return;
+ }
+ return InverseNodeSearch(pcNode,pcCurrent->mParent);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Find a node with a specific name in the import hierarchy
+D3DS::Node* FindNode(D3DS::Node* root, const std::string& name)
+{
+ if (root->mName == name)
+ return root;
+ for (std::vector<D3DS::Node*>::iterator it = root->mChildren.begin();it != root->mChildren.end(); ++it) {
+ D3DS::Node* nd;
+ if (( nd = FindNode(*it,name)))
+ return nd;
+ }
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Binary predicate for std::unique()
+template <class T>
+bool KeyUniqueCompare(const T& first, const T& second)
+{
+ return first.mTime == second.mTime;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skip some additional import data.
+void Discreet3DSImporter::SkipTCBInfo()
+{
+ unsigned int flags = stream->GetI2();
+
+ if (!flags) {
+ // Currently we can't do anything with these values. They occur
+ // quite rare, so it wouldn't be worth the effort implementing
+ // them. 3DS ist not really suitable for complex animations,
+ // so full support is not required.
+ DefaultLogger::get()->warn("3DS: Skipping TCB animation info");
+ }
+
+ if (flags & Discreet3DS::KEY_USE_TENS) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_BIAS) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_CONT) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_EASE_FROM) {
+ stream->IncPtr(4);
+ }
+ if (flags & Discreet3DS::KEY_USE_EASE_TO) {
+ stream->IncPtr(4);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read hierarchy and keyframe info
+void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent)
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_TRACKOBJNAME:
+
+ // This is the name of the object to which the track applies. The chunk also
+ // defines the position of this object in the hierarchy.
+ {
+
+ // First of all: get the name of the object
+ unsigned int cnt = 0;
+ const char* sz = (const char*)stream->GetPtr();
+
+ while (stream->GetI1())++cnt;
+ std::string name = std::string(sz,cnt);
+
+ // Now find out whether we have this node already (target animation channels
+ // are stored with a separate object ID)
+ D3DS::Node* pcNode = FindNode(mRootNode,name);
+ if (pcNode)
+ {
+ // Make this node the current node
+ mCurrentNode = pcNode;
+ break;
+ }
+ pcNode = new D3DS::Node();
+ pcNode->mName = name;
+
+ // There are two unknown values which we can safely ignore
+ stream->IncPtr(4);
+
+ // Now read the hierarchy position of the object
+ uint16_t hierarchy = stream->GetI2() + 1;
+ pcNode->mHierarchyPos = hierarchy;
+ pcNode->mHierarchyIndex = mLastNodeIndex;
+
+ // And find a proper position in the graph for it
+ if (mCurrentNode && mCurrentNode->mHierarchyPos == hierarchy) {
+
+ // add to the parent of the last touched node
+ mCurrentNode->mParent->push_back(pcNode);
+ mLastNodeIndex++;
+ }
+ else if (hierarchy >= mLastNodeIndex) {
+
+ // place it at the current position in the hierarchy
+ mCurrentNode->push_back(pcNode);
+ mLastNodeIndex = hierarchy;
+ }
+ else {
+ // need to go back to the specified position in the hierarchy.
+ InverseNodeSearch(pcNode,mCurrentNode);
+ mLastNodeIndex++;
+ }
+ // Make this node the current node
+ mCurrentNode = pcNode;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_TRACKDUMMYOBJNAME:
+
+ // This is the "real" name of a $$$DUMMY object
+ {
+ const char* sz = (const char*) stream->GetPtr();
+ while (stream->GetI1()) {};
+
+ // If object name is DUMMY, take this one instead
+ if (mCurrentNode->mName == "$$$DUMMY") {
+ //DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object");
+ mCurrentNode->mName = std::string(sz);
+ break;
+ }
+ }
+ break;
+
+ case Discreet3DS::CHUNK_TRACKPIVOT:
+
+ if ( Discreet3DS::CHUNK_TRACKINFO != parent)
+ {
+ DefaultLogger::get()->warn("3DS: Skipping pivot subchunk for non usual object");
+ break;
+ }
+
+ // Pivot = origin of rotation and scaling
+ mCurrentNode->vPivot.x = stream->GetF4();
+ mCurrentNode->vPivot.y = stream->GetF4();
+ mCurrentNode->vPivot.z = stream->GetF4();
+ break;
+
+
+ // ////////////////////////////////////////////////////////////////////
+ // POSITION KEYFRAME
+ case Discreet3DS::CHUNK_TRACKPOS:
+ {
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI4();
+ bool sortKeys = false;
+
+ // This could also be meant as the target position for
+ // (targeted) lights and cameras
+ std::vector<aiVectorKey>* l;
+ if ( Discreet3DS::CHUNK_TRACKCAMTGT == parent || Discreet3DS::CHUNK_TRACKLIGTGT == parent) {
+ l = & mCurrentNode->aTargetPositionKeys;
+ }
+ else l = & mCurrentNode->aPositionKeys;
+
+ l->reserve(numFrames);
+ for (unsigned int i = 0; i < numFrames;++i) {
+ const unsigned int fidx = stream->GetI4();
+
+ // Setup a new position key
+ aiVectorKey v;
+ v.mTime = (double)fidx;
+
+ SkipTCBInfo();
+ v.mValue.x = stream->GetF4();
+ v.mValue.y = stream->GetF4();
+ v.mValue.z = stream->GetF4();
+
+ // check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // Add the new keyframe to the list
+ l->push_back(v);
+ }
+
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(),l->end());
+ l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiVectorKey>), l->end() );
+ }}
+
+ break;
+
+ // ////////////////////////////////////////////////////////////////////
+ // CAMERA ROLL KEYFRAME
+ case Discreet3DS::CHUNK_TRACKROLL:
+ {
+ // roll keys are accepted for cameras only
+ if (parent != Discreet3DS::CHUNK_TRACKCAMERA) {
+ DefaultLogger::get()->warn("3DS: Ignoring roll track for non-camera object");
+ break;
+ }
+ bool sortKeys = false;
+ std::vector<aiFloatKey>* l = &mCurrentNode->aCameraRollKeys;
+
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI4();
+ l->reserve(numFrames);
+ for (unsigned int i = 0; i < numFrames;++i) {
+ const unsigned int fidx = stream->GetI4();
+
+ // Setup a new position key
+ aiFloatKey v;
+ v.mTime = (double)fidx;
+
+ // This is just a single float
+ SkipTCBInfo();
+ v.mValue = stream->GetF4();
+
+ // Check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // Add the new keyframe to the list
+ l->push_back(v);
+ }
+
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(),l->end());
+ l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiFloatKey>), l->end() );
+ }}
+ break;
+
+
+ // ////////////////////////////////////////////////////////////////////
+ // CAMERA FOV KEYFRAME
+ case Discreet3DS::CHUNK_TRACKFOV:
+ {
+ DefaultLogger::get()->error("3DS: Skipping FOV animation track. "
+ "This is not supported");
+ }
+ break;
+
+
+ // ////////////////////////////////////////////////////////////////////
+ // ROTATION KEYFRAME
+ case Discreet3DS::CHUNK_TRACKROTATE:
+ {
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI4();
+
+ bool sortKeys = false;
+ std::vector<aiQuatKey>* l = &mCurrentNode->aRotationKeys;
+ l->reserve(numFrames);
+
+ for (unsigned int i = 0; i < numFrames;++i) {
+ const unsigned int fidx = stream->GetI4();
+ SkipTCBInfo();
+
+ aiQuatKey v;
+ v.mTime = (double)fidx;
+
+ // The rotation keyframe is given as an axis-angle pair
+ const float rad = stream->GetF4();
+ aiVector3D axis;
+ axis.x = stream->GetF4();
+ axis.y = stream->GetF4();
+ axis.z = stream->GetF4();
+
+ if (!axis.x && !axis.y && !axis.z)
+ axis.y = 1.f;
+
+ // Construct a rotation quaternion from the axis-angle pair
+ v.mValue = aiQuaternion(axis,rad);
+
+ // Check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // add the new keyframe to the list
+ l->push_back(v);
+ }
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(),l->end());
+ l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiQuatKey>), l->end() );
+ }}
+ break;
+
+ // ////////////////////////////////////////////////////////////////////
+ // SCALING KEYFRAME
+ case Discreet3DS::CHUNK_TRACKSCALE:
+ {
+ stream->IncPtr(10);
+ const unsigned int numFrames = stream->GetI2();
+ stream->IncPtr(2);
+
+ bool sortKeys = false;
+ std::vector<aiVectorKey>* l = &mCurrentNode->aScalingKeys;
+ l->reserve(numFrames);
+
+ for (unsigned int i = 0; i < numFrames;++i) {
+ const unsigned int fidx = stream->GetI4();
+ SkipTCBInfo();
+
+ // Setup a new key
+ aiVectorKey v;
+ v.mTime = (double)fidx;
+
+ // ... and read its value
+ v.mValue.x = stream->GetF4();
+ v.mValue.y = stream->GetF4();
+ v.mValue.z = stream->GetF4();
+
+ // check whether we'll need to sort the keys
+ if (!l->empty() && v.mTime <= l->back().mTime)
+ sortKeys = true;
+
+ // Remove zero-scalings on singular axes - they've been reported to be there erroneously in some strange files
+ if (!v.mValue.x) v.mValue.x = 1.f;
+ if (!v.mValue.y) v.mValue.y = 1.f;
+ if (!v.mValue.z) v.mValue.z = 1.f;
+
+ l->push_back(v);
+ }
+ // Sort all keys with ascending time values and remove duplicates?
+ if (sortKeys) {
+ std::stable_sort(l->begin(),l->end());
+ l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare<aiVectorKey>), l->end() );
+ }}
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a face chunk - it contains smoothing groups and material assignments
+void Discreet3DSImporter::ParseFaceChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // Get the mesh we're currently working on
+ D3DS::Mesh& mMesh = mScene->mMeshes.back();
+
+ // Get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_SMOOLIST:
+ {
+ // This is the list of smoothing groups - a bitfield for every face.
+ // Up to 32 smoothing groups assigned to a single face.
+ unsigned int num = chunkSize/4, m = 0;
+ for (std::vector<D3DS::Face>::iterator i = mMesh.mFaces.begin(); m != num;++i, ++m) {
+ // nth bit is set for nth smoothing group
+ (*i).iSmoothGroup = stream->GetI4();
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_FACEMAT:
+ {
+ // at fist an asciiz with the material name
+ const char* sz = (const char*)stream->GetPtr();
+ while (stream->GetI1()) {};
+
+ // find the index of the material
+ unsigned int idx = 0xcdcdcdcd, cnt = 0;
+ for (std::vector<D3DS::Material>::const_iterator i = mScene->mMaterials.begin();i != mScene->mMaterials.end();++i,++cnt) {
+ // use case independent comparisons. hopefully it will work.
+ if ((*i).mName.length() && !ASSIMP_stricmp(sz, (*i).mName.c_str())) {
+ idx = cnt;
+ break;
+ }
+ }
+ if (0xcdcdcdcd == idx) {
+ DefaultLogger::get()->error(std::string("3DS: Unknown material: ") + sz);
+ }
+
+ // Now continue and read all material indices
+ cnt = (uint16_t)stream->GetI2();
+ for (unsigned int i = 0; i < cnt;++i) {
+ unsigned int fidx = (uint16_t)stream->GetI2();
+
+ // check range
+ if (fidx >= mMesh.mFaceMaterials.size()) {
+ DefaultLogger::get()->error("3DS: Invalid face index in face material list");
+ }
+ else mMesh.mFaceMaterials[fidx] = idx;
+ }}
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a mesh chunk. Here's the actual mesh data
+void Discreet3DSImporter::ParseMeshChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // Get the mesh we're currently working on
+ D3DS::Mesh& mMesh = mScene->mMeshes.back();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_VERTLIST:
+ {
+ // This is the list of all vertices in the current mesh
+ int num = (int)(uint16_t)stream->GetI2();
+ mMesh.mPositions.reserve(num);
+ while (num-- > 0) {
+ aiVector3D v;
+ v.x = stream->GetF4();
+ v.y = stream->GetF4();
+ v.z = stream->GetF4();
+ mMesh.mPositions.push_back(v);
+ }}
+ break;
+ case Discreet3DS::CHUNK_TRMATRIX:
+ {
+ // This is the RLEATIVE transformation matrix of the current mesh. Vertices are
+ // pretransformed by this matrix wonder.
+ mMesh.mMat.a1 = stream->GetF4();
+ mMesh.mMat.b1 = stream->GetF4();
+ mMesh.mMat.c1 = stream->GetF4();
+ mMesh.mMat.a2 = stream->GetF4();
+ mMesh.mMat.b2 = stream->GetF4();
+ mMesh.mMat.c2 = stream->GetF4();
+ mMesh.mMat.a3 = stream->GetF4();
+ mMesh.mMat.b3 = stream->GetF4();
+ mMesh.mMat.c3 = stream->GetF4();
+ mMesh.mMat.a4 = stream->GetF4();
+ mMesh.mMat.b4 = stream->GetF4();
+ mMesh.mMat.c4 = stream->GetF4();
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAPLIST:
+ {
+ // This is the list of all UV coords in the current mesh
+ int num = (int)(uint16_t)stream->GetI2();
+ mMesh.mTexCoords.reserve(num);
+ while (num-- > 0) {
+ aiVector3D v;
+ v.x = stream->GetF4();
+ v.y = stream->GetF4();
+ mMesh.mTexCoords.push_back(v);
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_FACELIST:
+ {
+ // This is the list of all faces in the current mesh
+ int num = (int)(uint16_t)stream->GetI2();
+ mMesh.mFaces.reserve(num);
+ while (num-- > 0) {
+ // 3DS faces are ALWAYS triangles
+ mMesh.mFaces.push_back(D3DS::Face());
+ D3DS::Face& sFace = mMesh.mFaces.back();
+
+ sFace.mIndices[0] = (uint16_t)stream->GetI2();
+ sFace.mIndices[1] = (uint16_t)stream->GetI2();
+ sFace.mIndices[2] = (uint16_t)stream->GetI2();
+
+ stream->IncPtr(2); // skip edge visibility flag
+ }
+
+ // Resize the material array (0xcdcdcdcd marks the default material; so if a face is
+ // not referenced by a material, $$DEFAULT will be assigned to it)
+ mMesh.mFaceMaterials.resize(mMesh.mFaces.size(),0xcdcdcdcd);
+
+ // Larger 3DS files could have multiple FACE chunks here
+ chunkSize = stream->GetRemainingSizeToLimit();
+ if ( chunkSize > (int) sizeof(Discreet3DS::Chunk ) )
+ ParseFaceChunk();
+ }
+
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a 3DS material chunk
+void Discreet3DSImporter::ParseMaterialChunk()
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_MAT_MATNAME:
+
+ {
+ // The material name string is already zero-terminated, but we need to be sure ...
+ const char* sz = (const char*)stream->GetPtr();
+ unsigned int cnt = 0;
+ while (stream->GetI1())
+ ++cnt;
+
+ if (!cnt) {
+ // This may not be, we use the default name instead
+ DefaultLogger::get()->error("3DS: Empty material name");
+ }
+ else mScene->mMaterials.back().mName = std::string(sz,cnt);
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_DIFFUSE:
+ {
+ // This is the diffuse material color
+ aiColor3D* pc = &mScene->mMaterials.back().mDiffuse;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ DefaultLogger::get()->error("3DS: Unable to read DIFFUSE chunk");
+ pc->r = pc->g = pc->b = 1.0f;
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SPECULAR:
+ {
+ // This is the specular material color
+ aiColor3D* pc = &mScene->mMaterials.back().mSpecular;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ DefaultLogger::get()->error("3DS: Unable to read SPECULAR chunk");
+ pc->r = pc->g = pc->b = 1.0f;
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_MAT_AMBIENT:
+ {
+ // This is the ambient material color
+ aiColor3D* pc = &mScene->mMaterials.back().mAmbient;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ DefaultLogger::get()->error("3DS: Unable to read AMBIENT chunk");
+ pc->r = pc->g = pc->b = 0.0f;
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SELF_ILLUM:
+ {
+ // This is the emissive material color
+ aiColor3D* pc = &mScene->mMaterials.back().mEmissive;
+ ParseColorChunk(pc);
+ if (is_qnan(pc->r)) {
+ // color chunk is invalid. Simply ignore it
+ DefaultLogger::get()->error("3DS: Unable to read EMISSIVE chunk");
+ pc->r = pc->g = pc->b = 0.0f;
+ }}
+ break;
+
+ case Discreet3DS::CHUNK_MAT_TRANSPARENCY:
+ {
+ // This is the material's transparency
+ float* pcf = &mScene->mMaterials.back().mTransparency;
+ *pcf = ParsePercentageChunk();
+
+ // NOTE: transparency, not opacity
+ if (is_qnan(*pcf))
+ *pcf = 1.0f;
+ else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SHADING:
+ // This is the material shading mode
+ mScene->mMaterials.back().mShading = (D3DS::Discreet3DS::shadetype3ds)stream->GetI2();
+ break;
+
+ case Discreet3DS::CHUNK_MAT_TWO_SIDE:
+ // This is the two-sided flag
+ mScene->mMaterials.back().mTwoSided = true;
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SHININESS:
+ { // This is the shininess of the material
+ float* pcf = &mScene->mMaterials.back().mSpecularExponent;
+ *pcf = ParsePercentageChunk();
+ if (is_qnan(*pcf))
+ *pcf = 0.0f;
+ else *pcf *= (float)0xFFFF;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT:
+ { // This is the shininess strength of the material
+ float* pcf = &mScene->mMaterials.back().mShininessStrength;
+ *pcf = ParsePercentageChunk();
+ if (is_qnan(*pcf))
+ *pcf = 0.0f;
+ else *pcf *= (float)0xffff / 100.0f;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_SELF_ILPCT:
+ { // This is the self illumination strength of the material
+ float f = ParsePercentageChunk();
+ if (is_qnan(f))
+ f = 0.0f;
+ else f *= (float)0xFFFF / 100.0f;
+ mScene->mMaterials.back().mEmissive = aiColor3D(f,f,f);
+ }
+ break;
+
+ // Parse texture chunks
+ case Discreet3DS::CHUNK_MAT_TEXTURE:
+ // Diffuse texture
+ ParseTextureChunk(&mScene->mMaterials.back().sTexDiffuse);
+ break;
+ case Discreet3DS::CHUNK_MAT_BUMPMAP:
+ // Height map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexBump);
+ break;
+ case Discreet3DS::CHUNK_MAT_OPACMAP:
+ // Opacity texture
+ ParseTextureChunk(&mScene->mMaterials.back().sTexOpacity);
+ break;
+ case Discreet3DS::CHUNK_MAT_MAT_SHINMAP:
+ // Shininess map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexShininess);
+ break;
+ case Discreet3DS::CHUNK_MAT_SPECMAP:
+ // Specular map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexSpecular);
+ break;
+ case Discreet3DS::CHUNK_MAT_SELFIMAP:
+ // Self-illumination (emissive) map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexEmissive);
+ break;
+ case Discreet3DS::CHUNK_MAT_REFLMAP:
+ // Reflection map
+ ParseTextureChunk(&mScene->mMaterials.back().sTexReflective);
+ break;
+ };
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut)
+{
+ ASSIMP_3DS_BEGIN_CHUNK();
+
+ // get chunk type
+ switch (chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_MAPFILE:
+ {
+ // The material name string is already zero-terminated, but we need to be sure ...
+ const char* sz = (const char*)stream->GetPtr();
+ unsigned int cnt = 0;
+ while (stream->GetI1())
+ ++cnt;
+ pcOut->mMapName = std::string(sz,cnt);
+ }
+ break;
+
+
+ case Discreet3DS::CHUNK_PERCENTF:
+ // Manually parse the blend factor
+ pcOut->mTextureBlend = stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_PERCENTW:
+ // Manually parse the blend factor
+ pcOut->mTextureBlend = (float)((uint16_t)stream->GetI2()) / 100.0f;
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_USCALE:
+ // Texture coordinate scaling in the U direction
+ pcOut->mScaleU = stream->GetF4();
+ if (0.0f == pcOut->mScaleU)
+ {
+ DefaultLogger::get()->warn("Texture coordinate scaling in the x direction is zero. Assuming 1.");
+ pcOut->mScaleU = 1.0f;
+ }
+ break;
+ case Discreet3DS::CHUNK_MAT_MAP_VSCALE:
+ // Texture coordinate scaling in the V direction
+ pcOut->mScaleV = stream->GetF4();
+ if (0.0f == pcOut->mScaleV)
+ {
+ DefaultLogger::get()->warn("Texture coordinate scaling in the y direction is zero. Assuming 1.");
+ pcOut->mScaleV = 1.0f;
+ }
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_UOFFSET:
+ // Texture coordinate offset in the U direction
+ pcOut->mOffsetU = -stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_VOFFSET:
+ // Texture coordinate offset in the V direction
+ pcOut->mOffsetV = stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_ANG:
+ // Texture coordinate rotation, CCW in DEGREES
+ pcOut->mRotation = -AI_DEG_TO_RAD( stream->GetF4() );
+ break;
+
+ case Discreet3DS::CHUNK_MAT_MAP_TILING:
+ {
+ const uint16_t iFlags = stream->GetI2();
+
+ // Get the mapping mode (for both axes)
+ if (iFlags & 0x2u)
+ pcOut->mMapMode = aiTextureMapMode_Mirror;
+
+ else if (iFlags & 0x10u)
+ pcOut->mMapMode = aiTextureMapMode_Decal;
+
+ // wrapping in all remaining cases
+ else pcOut->mMapMode = aiTextureMapMode_Wrap;
+ }
+ break;
+ };
+
+ ASSIMP_3DS_END_CHUNK();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a percentage chunk
+float Discreet3DSImporter::ParsePercentageChunk()
+{
+ Discreet3DS::Chunk chunk;
+ ReadChunk(&chunk);
+
+ if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag)
+ return stream->GetF4();
+ else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag)
+ return (float)((uint16_t)stream->GetI2()) / (float)0xFFFF;
+ return get_qnan();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a color chunk. If a percentage chunk is found instead it is read as a grayscale color
+void Discreet3DSImporter::ParseColorChunk(aiColor3D* out,
+ bool acceptPercent)
+{
+ ai_assert(out != NULL);
+
+ // error return value
+ const float qnan = get_qnan();
+ static const aiColor3D clrError = aiColor3D(qnan,qnan,qnan);
+
+ Discreet3DS::Chunk chunk;
+ ReadChunk(&chunk);
+ const unsigned int diff = chunk.Size - sizeof(Discreet3DS::Chunk);
+
+ bool bGamma = false;
+
+ // Get the type of the chunk
+ switch(chunk.Flag)
+ {
+ case Discreet3DS::CHUNK_LINRGBF:
+ bGamma = true;
+
+ case Discreet3DS::CHUNK_RGBF:
+ if (sizeof(float) * 3 > diff) {
+ *out = clrError;
+ return;
+ }
+ out->r = stream->GetF4();
+ out->g = stream->GetF4();
+ out->b = stream->GetF4();
+ break;
+
+ case Discreet3DS::CHUNK_LINRGBB:
+ bGamma = true;
+ case Discreet3DS::CHUNK_RGBB:
+ if (sizeof(char) * 3 > diff) {
+ *out = clrError;
+ return;
+ }
+ out->r = (float)(uint8_t)stream->GetI1() / 255.0f;
+ out->g = (float)(uint8_t)stream->GetI1() / 255.0f;
+ out->b = (float)(uint8_t)stream->GetI1() / 255.0f;
+ break;
+
+ // Percentage chunks are accepted, too.
+ case Discreet3DS::CHUNK_PERCENTF:
+ if (acceptPercent && 4 <= diff) {
+ out->g = out->b = out->r = stream->GetF4();
+ break;
+ }
+ *out = clrError;
+ return;
+
+ case Discreet3DS::CHUNK_PERCENTW:
+ if (acceptPercent && 1 <= diff) {
+ out->g = out->b = out->r = (float)(uint8_t)stream->GetI1() / 255.0f;
+ break;
+ }
+ *out = clrError;
+ return;
+
+ default:
+ stream->IncPtr(diff);
+ // Skip unknown chunks, hope this won't cause any problems.
+ return ParseColorChunk(out,acceptPercent);
+ };
+}
+
+#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
diff --git a/3rdparty/assimp/code/3DSLoader.h b/3rdparty/assimp/code/3DSLoader.h
new file mode 100644
index 000000000..3fd3aee3b
--- /dev/null
+++ b/3rdparty/assimp/code/3DSLoader.h
@@ -0,0 +1,280 @@
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 3DSLoader.h
+ * @brief 3DS File format loader
+ */
+#ifndef AI_3DSIMPORTER_H_INC
+#define AI_3DSIMPORTER_H_INC
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+#include "3DSHelper.h"
+
+namespace Assimp {
+class MaterialHelper;
+
+using namespace D3DS;
+
+// ---------------------------------------------------------------------------------
+/** Importer class for 3D Studio r3 and r4 3DS files
+ */
+class Discreet3DSImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ Discreet3DSImporter();
+
+ /** Destructor, private as well */
+ ~Discreet3DSImporter();
+
+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;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Converts a temporary material to the outer representation
+ */
+ void ConvertMaterial(D3DS::Material& p_cMat,
+ MaterialHelper& p_pcOut);
+
+ // -------------------------------------------------------------------
+ /** Read a chunk
+ *
+ * @param pcOut Receives the current chunk
+ */
+ void ReadChunk(Discreet3DS::Chunk* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Parse a percentage chunk. mCurrent will point to the next
+ * chunk behind afterwards. If no percentage chunk is found
+ * QNAN is returned.
+ */
+ float ParsePercentageChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a color chunk. mCurrent will point to the next
+ * chunk behind afterwards. If no color chunk is found
+ * QNAN is returned in all members.
+ */
+ void ParseColorChunk(aiColor3D* p_pcOut,
+ bool p_bAcceptPercent = true);
+
+
+ // -------------------------------------------------------------------
+ /** Skip a chunk in the file
+ */
+ void SkipChunk();
+
+ // -------------------------------------------------------------------
+ /** Generate the nodegraph
+ */
+ void GenerateNodeGraph(aiScene* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Parse a main top-level chunk in the file
+ */
+ void ParseMainChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a top-level chunk in the file
+ */
+ void ParseChunk(const char* name, unsigned int num);
+
+ // -------------------------------------------------------------------
+ /** Parse a top-level editor chunk in the file
+ */
+ void ParseEditorChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a top-level object chunk in the file
+ */
+ void ParseObjectChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a material chunk in the file
+ */
+ void ParseMaterialChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a mesh chunk in the file
+ */
+ void ParseMeshChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a light chunk in the file
+ */
+ void ParseLightChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a camera chunk in the file
+ */
+ void ParseCameraChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a face list chunk in the file
+ */
+ void ParseFaceChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a keyframe chunk in the file
+ */
+ void ParseKeyframeChunk();
+
+ // -------------------------------------------------------------------
+ /** Parse a hierarchy chunk in the file
+ */
+ void ParseHierarchyChunk(uint16_t parent);
+
+ // -------------------------------------------------------------------
+ /** Parse a texture chunk in the file
+ */
+ void ParseTextureChunk(D3DS::Texture* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Convert the meshes in the file
+ */
+ void ConvertMeshes(aiScene* pcOut);
+
+ // -------------------------------------------------------------------
+ /** Replace the default material in the scene
+ */
+ void ReplaceDefaultMaterial();
+
+ // -------------------------------------------------------------------
+ /** Convert the whole scene
+ */
+ void ConvertScene(aiScene* pcOut);
+
+ // -------------------------------------------------------------------
+ /** generate unique vertices for a mesh
+ */
+ void MakeUnique(D3DS::Mesh& sMesh);
+
+ // -------------------------------------------------------------------
+ /** Add a node to the node graph
+ */
+ void AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,D3DS::Node* pcIn,
+ aiMatrix4x4& absTrafo);
+
+ // -------------------------------------------------------------------
+ /** Search for a node in the graph.
+ * Called recursively
+ */
+ void InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent);
+
+ // -------------------------------------------------------------------
+ /** Apply the master scaling factor to the mesh
+ */
+ void ApplyMasterScale(aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ /** Clamp all indices in the file to a valid range
+ */
+ void CheckIndices(D3DS::Mesh& sMesh);
+
+ // -------------------------------------------------------------------
+ /** Skip the TCB info in a track key
+ */
+ void SkipTCBInfo();
+
+protected:
+
+ /** Stream to read from */
+ StreamReaderLE* stream;
+
+ /** Last touched node index */
+ short mLastNodeIndex;
+
+ /** Current node, root node */
+ D3DS::Node* mCurrentNode, *mRootNode;
+
+ /** Scene under construction */
+ D3DS::Scene* mScene;
+
+ /** Ambient base color of the scene */
+ aiColor3D mClrAmbient;
+
+ /** Master scaling factor of the scene */
+ float mMasterScale;
+
+ /** Path to the background image of the scene */
+ std::string mBackgroundImage;
+ bool bHasBG;
+
+ /** true if PRJ file */
+ bool bIsPrj;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/ACLoader.cpp b/3rdparty/assimp/code/ACLoader.cpp
new file mode 100644
index 000000000..318e2bfe8
--- /dev/null
+++ b/3rdparty/assimp/code/ACLoader.cpp
@@ -0,0 +1,856 @@
+
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the AC3D importer class */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_AC_IMPORTER
+
+// internal headers
+#include "ACLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+#include "Subdivision.h"
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// skip to the next token
+#define AI_AC_SKIP_TO_NEXT_TOKEN() \
+ if (!SkipSpaces(&buffer)) \
+ { \
+ DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL"); \
+ continue; \
+ }
+
+// ------------------------------------------------------------------------------------------------
+// read a string (may be enclosed in double quotation marks). buffer must point to "
+#define AI_AC_GET_STRING(out) \
+ ++buffer; \
+ const char* sz = buffer; \
+ while ('\"' != *buffer) \
+ { \
+ if (IsLineEnd( *buffer )) \
+ { \
+ DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL in string"); \
+ out = "ERROR"; \
+ break; \
+ } \
+ ++buffer; \
+ } \
+ if (IsLineEnd( *buffer ))continue; \
+ out = std::string(sz,(unsigned int)(buffer-sz)); \
+ ++buffer;
+
+
+// ------------------------------------------------------------------------------------------------
+// read 1 to n floats prefixed with an optional predefined identifier
+#define AI_AC_CHECKED_LOAD_FLOAT_ARRAY(name,name_length,num,out) \
+ AI_AC_SKIP_TO_NEXT_TOKEN(); \
+ if (name_length) \
+ { \
+ if (strncmp(buffer,name,name_length) || !IsSpace(buffer[name_length])) \
+ { \
+ DefaultLogger::get()->error("AC3D: Unexpexted token. " name " was expected."); \
+ continue; \
+ } \
+ buffer += name_length+1; \
+ } \
+ for (unsigned int i = 0; i < num;++i) \
+ { \
+ AI_AC_SKIP_TO_NEXT_TOKEN(); \
+ buffer = fast_atof_move(buffer,((float*)out)[i]); \
+ }
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+AC3DImporter::AC3DImporter()
+{
+ // nothing to be done here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+AC3DImporter::~AC3DImporter()
+{
+ // nothing to be done here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ std::string extension = GetExtension(pFile);
+
+ // fixme: are acc and ac3d *really* used? Some sources say they are
+ if (extension == "ac" || extension == "ac3d" || extension == "acc") {
+ return true;
+ }
+ if (!extension.length() || checkSig) {
+ uint32_t token = AI_MAKE_MAGIC("AC3D");
+ return CheckMagicToken(pIOHandler,pFile,&token,1,0);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get list of file extensions handled by this loader
+void AC3DImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ac");
+ extensions.insert("acc");
+ extensions.insert("ac3d");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a pointer to the next line from the file
+bool AC3DImporter::GetNextLine( )
+{
+ SkipLine(&buffer);
+ return SkipSpaces(&buffer);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse an object section in an AC file
+void AC3DImporter::LoadObjectSection(std::vector<Object>& objects)
+{
+ if (!TokenMatch(buffer,"OBJECT",6))
+ return;
+
+ SkipSpaces(&buffer);
+
+ ++mNumMeshes;
+
+ objects.push_back(Object());
+ Object& obj = objects.back();
+
+ aiLight* light = NULL;
+ if (!ASSIMP_strincmp(buffer,"light",5))
+ {
+ // This is a light source. Add it to the list
+ mLights->push_back(light = new aiLight());
+
+ // Return a point light with no attenuation
+ light->mType = aiLightSource_POINT;
+ light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f,1.f,1.f);
+ light->mAttenuationConstant = 1.f;
+
+ // Generate a default name for both the light source and the node
+ // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version.
+ light->mName.length = ::sprintf(light->mName.data,"ACLight_%i",static_cast<unsigned int>(mLights->size())-1);
+ obj.name = std::string( light->mName.data );
+
+ DefaultLogger::get()->debug("AC3D: Light source encountered");
+ obj.type = Object::Light;
+ }
+ else if (!ASSIMP_strincmp(buffer,"group",5))
+ {
+ obj.type = Object::Group;
+ }
+ else if (!ASSIMP_strincmp(buffer,"world",5))
+ {
+ obj.type = Object::World;
+ }
+ else obj.type = Object::Poly;
+ while (GetNextLine())
+ {
+ if (TokenMatch(buffer,"kids",4))
+ {
+ SkipSpaces(&buffer);
+ unsigned int num = strtol10(buffer,&buffer);
+ GetNextLine();
+ if (num)
+ {
+ // load the children of this object recursively
+ obj.children.reserve(num);
+ for (unsigned int i = 0; i < num; ++i)
+ LoadObjectSection(obj.children);
+ }
+ return;
+ }
+ else if (TokenMatch(buffer,"name",4))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_GET_STRING(obj.name);
+
+ // If this is a light source, we'll also need to store
+ // the name of the node in it.
+ if (light)
+ {
+ light->mName.Set(obj.name);
+ }
+ }
+ else if (TokenMatch(buffer,"texture",7))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_GET_STRING(obj.texture);
+ }
+ else if (TokenMatch(buffer,"texrep",6))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texRepeat);
+ if (!obj.texRepeat.x || !obj.texRepeat.y)
+ obj.texRepeat = aiVector2D (1.f,1.f);
+ }
+ else if (TokenMatch(buffer,"texoff",6))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texOffset);
+ }
+ else if (TokenMatch(buffer,"rot",3))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,9,&obj.rotation);
+ }
+ else if (TokenMatch(buffer,"loc",3))
+ {
+ SkipSpaces(&buffer);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&obj.translation);
+ }
+ else if (TokenMatch(buffer,"subdiv",6))
+ {
+ SkipSpaces(&buffer);
+ obj.subDiv = strtol10(buffer,&buffer);
+ }
+ else if (TokenMatch(buffer,"crease",6))
+ {
+ SkipSpaces(&buffer);
+ obj.crease = fast_atof(buffer);
+ }
+ else if (TokenMatch(buffer,"numvert",7))
+ {
+ SkipSpaces(&buffer);
+
+ unsigned int t = strtol10(buffer,&buffer);
+ obj.vertices.reserve(t);
+ for (unsigned int i = 0; i < t;++i)
+ {
+ if (!GetNextLine())
+ {
+ DefaultLogger::get()->error("AC3D: Unexpected EOF: not all vertices have been parsed yet");
+ break;
+ }
+ else if (!IsNumeric(*buffer))
+ {
+ DefaultLogger::get()->error("AC3D: Unexpected token: not all vertices have been parsed yet");
+ --buffer; // make sure the line is processed a second time
+ break;
+ }
+ obj.vertices.push_back(aiVector3D());
+ aiVector3D& v = obj.vertices.back();
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&v.x);
+ }
+ }
+ else if (TokenMatch(buffer,"numsurf",7))
+ {
+ SkipSpaces(&buffer);
+
+ bool Q3DWorkAround = false;
+
+ const unsigned int t = strtol10(buffer,&buffer);
+ obj.surfaces.reserve(t);
+ for (unsigned int i = 0; i < t;++i)
+ {
+ GetNextLine();
+ if (!TokenMatch(buffer,"SURF",4))
+ {
+ // FIX: this can occur for some files - Quick 3D for
+ // example writes no surf chunks
+ if (!Q3DWorkAround)
+ {
+ DefaultLogger::get()->warn("AC3D: SURF token was expected");
+ DefaultLogger::get()->debug("Continuing with Quick3D Workaround enabled");
+ }
+ --buffer; // make sure the line is processed a second time
+ // break; --- see fix notes above
+
+ Q3DWorkAround = true;
+ }
+ SkipSpaces(&buffer);
+ obj.surfaces.push_back(Surface());
+ Surface& surf = obj.surfaces.back();
+ surf.flags = strtol_cppstyle(buffer);
+
+ while (1)
+ {
+ if (!GetNextLine())
+ {
+ DefaultLogger::get()->error("AC3D: Unexpected EOF: surface is incomplete");
+ break;
+ }
+ if (TokenMatch(buffer,"mat",3))
+ {
+ SkipSpaces(&buffer);
+ surf.mat = strtol10(buffer);
+ }
+ else if (TokenMatch(buffer,"refs",4))
+ {
+ // --- see fix notes above
+ if (Q3DWorkAround)
+ {
+ if (!surf.entries.empty())
+ {
+ buffer -= 6;
+ break;
+ }
+ }
+
+ SkipSpaces(&buffer);
+ const unsigned int m = strtol10(buffer);
+ surf.entries.reserve(m);
+
+ obj.numRefs += m;
+
+ for (unsigned int k = 0; k < m; ++k)
+ {
+ if (!GetNextLine())
+ {
+ DefaultLogger::get()->error("AC3D: Unexpected EOF: surface references are incomplete");
+ break;
+ }
+ surf.entries.push_back(Surface::SurfaceEntry());
+ Surface::SurfaceEntry& entry = surf.entries.back();
+
+ entry.first = strtol10(buffer,&buffer);
+ SkipSpaces(&buffer);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&entry.second);
+ }
+ }
+ else
+ {
+
+ --buffer; // make sure the line is processed a second time
+ break;
+ }
+ }
+ }
+ }
+ }
+ DefaultLogger::get()->error("AC3D: Unexpected EOF: \'kids\' line was expected");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a material from AC3DImporter::Material to aiMaterial
+void AC3DImporter::ConvertMaterial(const Object& object,
+ const Material& matSrc,
+ MaterialHelper& matDest)
+{
+ aiString s;
+
+ if (matSrc.name.length())
+ {
+ s.Set(matSrc.name);
+ matDest.AddProperty(&s,AI_MATKEY_NAME);
+ }
+ if (object.texture.length())
+ {
+ s.Set(object.texture);
+ matDest.AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ // UV transformation
+ if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y ||
+ object.texOffset.x || object.texOffset.y)
+ {
+ aiUVTransform transform;
+ transform.mScaling = object.texRepeat;
+ transform.mTranslation = object.texOffset;
+ matDest.AddProperty(&transform,1,AI_MATKEY_UVTRANSFORM_DIFFUSE(0));
+ }
+ }
+
+ matDest.AddProperty<aiColor3D>(&matSrc.rgb,1, AI_MATKEY_COLOR_DIFFUSE);
+ matDest.AddProperty<aiColor3D>(&matSrc.amb,1, AI_MATKEY_COLOR_AMBIENT);
+ matDest.AddProperty<aiColor3D>(&matSrc.emis,1,AI_MATKEY_COLOR_EMISSIVE);
+ matDest.AddProperty<aiColor3D>(&matSrc.spec,1,AI_MATKEY_COLOR_SPECULAR);
+
+ int n;
+ if (matSrc.shin)
+ {
+ n = aiShadingMode_Phong;
+ matDest.AddProperty<float>(&matSrc.shin,1,AI_MATKEY_SHININESS);
+ }
+ else n = aiShadingMode_Gouraud;
+ matDest.AddProperty<int>(&n,1,AI_MATKEY_SHADING_MODEL);
+
+ float f = 1.f - matSrc.trans;
+ matDest.AddProperty<float>(&f,1,AI_MATKEY_OPACITY);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts the loaded data to the internal verbose representation
+aiNode* AC3DImporter::ConvertObjectSection(Object& object,
+ std::vector<aiMesh*>& meshes,
+ std::vector<MaterialHelper*>& outMaterials,
+ const std::vector<Material>& materials,
+ aiNode* parent)
+{
+ aiNode* node = new aiNode();
+ node->mParent = parent;
+ if (object.vertices.size())
+ {
+ if (!object.surfaces.size() || !object.numRefs)
+ {
+ /* " An object with 7 vertices (no surfaces, no materials defined).
+ This is a good way of getting point data into AC3D.
+ The Vertex->create convex-surface/object can be used on these
+ vertices to 'wrap' a 3d shape around them "
+ (http://www.opencity.info/html/ac3dfileformat.html)
+
+ therefore: if no surfaces are defined return point data only
+ */
+
+ DefaultLogger::get()->info("AC3D: No surfaces defined in object definition, "
+ "a point list is returned");
+
+ meshes.push_back(new aiMesh());
+ aiMesh* mesh = meshes.back();
+
+ mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size();
+ aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
+ aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+
+ for (unsigned int i = 0; i < mesh->mNumVertices;++i,++faces,++verts)
+ {
+ *verts = object.vertices[i];
+ faces->mNumIndices = 1;
+ faces->mIndices = new unsigned int[1];
+ faces->mIndices[0] = i;
+ }
+
+ // use the primary material in this case. this should be the
+ // default material if all objects of the file contain points
+ // and no faces.
+ mesh->mMaterialIndex = 0;
+ outMaterials.push_back(new MaterialHelper());
+ ConvertMaterial(object, materials[0], *outMaterials.back());
+ }
+ else
+ {
+ // need to generate one or more meshes for this object.
+ // find out how many different materials we have
+ typedef std::pair< unsigned int, unsigned int > IntPair;
+ typedef std::vector< IntPair > MatTable;
+ MatTable needMat(materials.size(),IntPair(0,0));
+
+ std::vector<Surface>::iterator it,end = object.surfaces.end();
+ std::vector<Surface::SurfaceEntry>::iterator it2,end2;
+
+ for (it = object.surfaces.begin(); it != end; ++it)
+ {
+ register unsigned int idx = (*it).mat;
+ if (idx >= needMat.size())
+ {
+ DefaultLogger::get()->error("AC3D: material index is out of range");
+ idx = 0;
+ }
+ if ((*it).entries.empty())
+ {
+ DefaultLogger::get()->warn("AC3D: surface her zero vertex references");
+ }
+
+ // validate all vertex indices to make sure we won't crash here
+ for (it2 = (*it).entries.begin(),
+ end2 = (*it).entries.end(); it2 != end2; ++it2)
+ {
+ if ((*it2).first >= object.vertices.size())
+ {
+ DefaultLogger::get()->warn("AC3D: Invalid vertex reference");
+ (*it2).first = 0;
+ }
+ }
+
+ if (!needMat[idx].first)++node->mNumMeshes;
+
+ switch ((*it).flags & 0xf)
+ {
+ // closed line
+ case 0x1:
+
+ needMat[idx].first += (unsigned int)(*it).entries.size();
+ needMat[idx].second += (unsigned int)(*it).entries.size()<<1u;
+ break;
+
+ // unclosed line
+ case 0x2:
+
+ needMat[idx].first += (unsigned int)(*it).entries.size()-1;
+ needMat[idx].second += ((unsigned int)(*it).entries.size()-1)<<1u;
+ break;
+
+ // 0 == polygon, else unknown
+ default:
+
+ if ((*it).flags & 0xf)
+ {
+ DefaultLogger::get()->warn("AC3D: The type flag of a surface is unknown");
+ (*it).flags &= ~(0xf);
+ }
+
+ // the number of faces increments by one, the number
+ // of vertices by surface.numref.
+ needMat[idx].first++;
+ needMat[idx].second += (unsigned int)(*it).entries.size();
+ };
+ }
+ unsigned int* pip = node->mMeshes = new unsigned int[node->mNumMeshes];
+ unsigned int mat = 0;
+ const size_t oldm = meshes.size();
+ for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end();
+ cit != cend; ++cit, ++mat)
+ {
+ if (!(*cit).first)continue;
+
+ // allocate a new aiMesh object
+ *pip++ = (unsigned int)meshes.size();
+ aiMesh* mesh = new aiMesh();
+ meshes.push_back(mesh);
+
+ mesh->mMaterialIndex = (unsigned int)outMaterials.size();
+ outMaterials.push_back(new MaterialHelper());
+ ConvertMaterial(object, materials[mat], *outMaterials.back());
+
+ // allocate storage for vertices and normals
+ mesh->mNumFaces = (*cit).first;
+ aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
+
+ mesh->mNumVertices = (*cit).second;
+ aiVector3D* vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ unsigned int cur = 0;
+
+ // allocate UV coordinates, but only if the texture name for the
+ // surface is not empty
+ aiVector3D* uv = NULL;
+ if (object.texture.length())
+ {
+ uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+ mesh->mNumUVComponents[0] = 2;
+ }
+
+ for (it = object.surfaces.begin(); it != end; ++it)
+ {
+ if (mat == (*it).mat)
+ {
+ const Surface& src = *it;
+
+ // closed polygon
+ unsigned int type = (*it).flags & 0xf;
+ if (!type)
+ {
+ aiFace& face = *faces++;
+ if ((face.mNumIndices = (unsigned int)src.entries.size()))
+ {
+ face.mIndices = new unsigned int[face.mNumIndices];
+ for (unsigned int i = 0; i < face.mNumIndices;++i,++vertices)
+ {
+ const Surface::SurfaceEntry& entry = src.entries[i];
+ face.mIndices[i] = cur++;
+
+ // copy vertex positions
+ *vertices = object.vertices[entry.first] + object.translation;
+
+
+ // copy texture coordinates
+ if (uv)
+ {
+ uv->x = entry.second.x;
+ uv->y = entry.second.y;
+ ++uv;
+ }
+ }
+ }
+ }
+ else
+ {
+
+ it2 = (*it).entries.begin();
+
+ // either a closed or an unclosed line
+ register unsigned int tmp = (unsigned int)(*it).entries.size();
+ if (0x2 == type)--tmp;
+ for (unsigned int m = 0; m < tmp;++m)
+ {
+ aiFace& face = *faces++;
+
+ face.mNumIndices = 2;
+ face.mIndices = new unsigned int[2];
+ face.mIndices[0] = cur++;
+ face.mIndices[1] = cur++;
+
+ // copy vertex positions
+ *vertices++ = object.vertices[(*it2).first];
+
+ // copy texture coordinates
+ if (uv)
+ {
+ uv->x = (*it2).second.x;
+ uv->y = (*it2).second.y;
+ ++uv;
+ }
+
+
+ if (0x1 == type && tmp-1 == m)
+ {
+ // if this is a closed line repeat its beginning now
+ it2 = (*it).entries.begin();
+ }
+ else ++it2;
+
+ // second point
+ *vertices++ = object.vertices[(*it2).first];
+
+ if (uv)
+ {
+ uv->x = (*it2).second.x;
+ uv->y = (*it2).second.y;
+ ++uv;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Now apply catmull clark subdivision if necessary. We split meshes into
+ // materials which is not done by AC3D during smoothing, so we need to
+ // collect all meshes using the same material group.
+ if (object.subDiv) {
+ if (configEvalSubdivision) {
+ boost::scoped_ptr<Subdivider> div(Subdivider::Create(Subdivider::CATMULL_CLARKE));
+ DefaultLogger::get()->info("AC3D: Evaluating subdivision surface: "+object.name);
+
+ std::vector<aiMesh*> cpy(meshes.size()-oldm,NULL);
+ div->Subdivide(&meshes[oldm],cpy.size(),&cpy.front(),object.subDiv,true);
+ std::copy(cpy.begin(),cpy.end(),meshes.begin()+oldm);
+
+ // previous meshes are deleted vy Subdivide().
+ }
+ else {
+ DefaultLogger::get()->info("AC3D: Letting the subdivision surface untouched due to my configuration: "
+ +object.name);
+ }
+ }
+ }
+ }
+
+ if (object.name.length())
+ node->mName.Set(object.name);
+ else
+ {
+ // generate a name depending on the type of the node
+ switch (object.type)
+ {
+ case Object::Group:
+ node->mName.length = ::sprintf(node->mName.data,"ACGroup_%i",groups++);
+ break;
+ case Object::Poly:
+ node->mName.length = ::sprintf(node->mName.data,"ACPoly_%i",polys++);
+ break;
+ case Object::Light:
+ node->mName.length = ::sprintf(node->mName.data,"ACLight_%i",lights++);
+ break;
+
+ // there shouldn't be more than one world, but we don't care
+ case Object::World:
+ node->mName.length = ::sprintf(node->mName.data,"ACWorld_%i",worlds++);
+ break;
+ }
+ }
+
+
+ // setup the local transformation matrix of the object
+ // compute the transformation offset to the parent node
+ node->mTransformation = aiMatrix4x4 ( object.rotation );
+
+ if (object.type == Object::Group || !object.numRefs)
+ {
+ node->mTransformation.a4 = object.translation.x;
+ node->mTransformation.b4 = object.translation.y;
+ node->mTransformation.c4 = object.translation.z;
+ }
+
+ // add children to the object
+ if (object.children.size())
+ {
+ node->mNumChildren = (unsigned int)object.children.size();
+ node->mChildren = new aiNode*[node->mNumChildren];
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ {
+ node->mChildren[i] = ConvertObjectSection(object.children[i],meshes,outMaterials,materials,node);
+ }
+ }
+
+ return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+void AC3DImporter::SetupProperties(const Importer* pImp)
+{
+ configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL,1) ? true : false;
+ configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION,1) ? true : false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void AC3DImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open AC3D file " + pFile + ".");
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+
+ buffer = &mBuffer2[0];
+ mNumMeshes = 0;
+
+ lights = polys = worlds = groups = 0;
+
+ if (::strncmp(buffer,"AC3D",4)) {
+ throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found");
+ }
+
+ // print the file format version to the console
+ unsigned int version = HexDigitToDecimal( buffer[4] );
+ char msg[3];
+ ASSIMP_itoa10(msg,3,version);
+ DefaultLogger::get()->info(std::string("AC3D file format version: ") + msg);
+
+ std::vector<Material> materials;
+ materials.reserve(5);
+
+ std::vector<Object> rootObjects;
+ rootObjects.reserve(5);
+
+ std::vector<aiLight*> lights;
+ mLights = & lights;
+
+ while (GetNextLine())
+ {
+ if (TokenMatch(buffer,"MATERIAL",8))
+ {
+ materials.push_back(Material());
+ Material& mat = materials.back();
+
+ // manually parse the material ... sscanf would use the buldin atof ...
+ // Format: (name) rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f
+
+ AI_AC_SKIP_TO_NEXT_TOKEN();
+ if ('\"' == *buffer)
+ {
+ AI_AC_GET_STRING(mat.name);
+ AI_AC_SKIP_TO_NEXT_TOKEN();
+ }
+
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("rgb",3,3,&mat.rgb);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("amb",3,3,&mat.amb);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("emis",4,3,&mat.emis);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("spec",4,3,&mat.spec);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("shi",3,1,&mat.shin);
+ AI_AC_CHECKED_LOAD_FLOAT_ARRAY("trans",5,1,&mat.trans);
+ }
+ LoadObjectSection(rootObjects);
+ }
+
+ if (rootObjects.empty() || !mNumMeshes)
+ {
+ throw DeadlyImportError("AC3D: No meshes have been loaded");
+ }
+ if (materials.empty())
+ {
+ DefaultLogger::get()->warn("AC3D: No material has been found");
+ materials.push_back(Material());
+ }
+
+ mNumMeshes += (mNumMeshes>>2u) + 1;
+ std::vector<aiMesh*> meshes;
+ meshes.reserve(mNumMeshes);
+
+ std::vector<MaterialHelper*> omaterials;
+ materials.reserve(mNumMeshes);
+
+ // generate a dummy root if there are multiple objects on the top layer
+ Object* root;
+ if (1 == rootObjects.size())
+ root = &rootObjects[0];
+ else
+ {
+ root = new Object();
+ }
+
+ // now convert the imported stuff to our output data structure
+ pScene->mRootNode = ConvertObjectSection(*root,meshes,omaterials,materials);
+ if (1 != rootObjects.size())delete root;
+
+ if (!::strncmp( pScene->mRootNode->mName.data, "Node", 4))
+ pScene->mRootNode->mName.Set("<AC3DWorld>");
+
+ // copy meshes
+ if (meshes.empty())
+ {
+ throw DeadlyImportError("An unknown error occured during converting");
+ }
+ pScene->mNumMeshes = (unsigned int)meshes.size();
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ ::memcpy(pScene->mMeshes,&meshes[0],pScene->mNumMeshes*sizeof(void*));
+
+ // copy materials
+ pScene->mNumMaterials = (unsigned int)omaterials.size();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ ::memcpy(pScene->mMaterials,&omaterials[0],pScene->mNumMaterials*sizeof(void*));
+
+ // copy lights
+ pScene->mNumLights = (unsigned int)lights.size();
+ if (lights.size())
+ {
+ pScene->mLights = new aiLight*[lights.size()];
+ ::memcpy(pScene->mLights,&lights[0],lights.size()*sizeof(void*));
+ }
+}
+
+#endif //!defined ASSIMP_BUILD_NO_AC_IMPORTER
diff --git a/3rdparty/assimp/code/ACLoader.h b/3rdparty/assimp/code/ACLoader.h
new file mode 100644
index 000000000..0f994710c
--- /dev/null
+++ b/3rdparty/assimp/code/ACLoader.h
@@ -0,0 +1,271 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 ACLoader.h
+ * @brief Declaration of the .ac importer class.
+ */
+#ifndef AI_AC3DLOADER_H_INCLUDED
+#define AI_AC3DLOADER_H_INCLUDED
+
+#include <vector>
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** AC3D (*.ac) importer class
+*/
+class AC3DImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ AC3DImporter();
+
+ /** Destructor, private as well */
+ ~AC3DImporter();
+
+
+ // Represents an AC3D material
+ struct Material
+ {
+ Material()
+ : rgb (0.6f,0.6f,0.6f)
+ , spec (1.f,1.f,1.f)
+ , shin (0.f)
+ , trans (0.f)
+ {}
+
+ // base color of the material
+ aiColor3D rgb;
+
+ // ambient color of the material
+ aiColor3D amb;
+
+ // emissive color of the material
+ aiColor3D emis;
+
+ // specular color of the material
+ aiColor3D spec;
+
+ // shininess exponent
+ float shin;
+
+ // transparency. 0 == opaque
+ float trans;
+
+ // name of the material. optional.
+ std::string name;
+ };
+
+ // Represents an AC3D surface
+ struct Surface
+ {
+ Surface()
+ : mat (0)
+ , flags (0)
+ {}
+
+ unsigned int mat,flags;
+
+ typedef std::pair<unsigned int, aiVector2D > SurfaceEntry;
+ std::vector< SurfaceEntry > entries;
+ };
+
+ // Represents an AC3D object
+ struct Object
+ {
+ Object()
+ : type (World)
+ , name( "" )
+ , children()
+ , texture( "" )
+ , texRepeat( 1.f, 1.f )
+ , texOffset( 0.0f, 0.0f )
+ , rotation()
+ , translation()
+ , vertices()
+ , surfaces()
+ , numRefs (0)
+ , subDiv (0)
+ {}
+
+ // Type description
+ enum Type
+ {
+ World = 0x0,
+ Poly = 0x1,
+ Group = 0x2,
+ Light = 0x4
+ } type;
+
+ // name of the object
+ std::string name;
+
+ // object children
+ std::vector<Object> children;
+
+ // texture to be assigned to all surfaces of the object
+ std::string texture;
+
+ // texture repat factors (scaling for all coordinates)
+ aiVector2D texRepeat, texOffset;
+
+ // rotation matrix
+ aiMatrix3x3 rotation;
+
+ // translation vector
+ aiVector3D translation;
+
+ // vertices
+ std::vector<aiVector3D> vertices;
+
+ // surfaces
+ std::vector<Surface> surfaces;
+
+ // number of indices (= num verts in verbose format)
+ unsigned int numRefs;
+
+ // number of subdivisions to be performed on the
+ // imported data
+ unsigned int subDiv;
+
+ // max angle limit for smoothing
+ float crease;
+ };
+
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details*/
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.*/
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+ // -------------------------------------------------------------------
+ /** Get the next line from the file.
+ * @return false if the end of the file was reached*/
+ bool GetNextLine();
+
+ // -------------------------------------------------------------------
+ /** Load the object section. This method is called recursively to
+ * load subobjects, the method returns after a 'kids 0' was
+ * encountered.
+ * @objects List of output objects*/
+ void LoadObjectSection(std::vector<Object>& objects);
+
+ // -------------------------------------------------------------------
+ /** Convert all objects into meshes and nodes.
+ * @param object Current object to work on
+ * @param meshes Pointer to the list of output meshes
+ * @param outMaterials List of output materials
+ * @param materials Material list
+ * @param Scenegraph node for the object */
+ aiNode* ConvertObjectSection(Object& object,
+ std::vector<aiMesh*>& meshes,
+ std::vector<MaterialHelper*>& outMaterials,
+ const std::vector<Material>& materials,
+ aiNode* parent = NULL);
+
+ // -------------------------------------------------------------------
+ /** Convert a material
+ * @param object Current object
+ * @param matSrc Source material description
+ * @param matDest Destination material to be filled */
+ void ConvertMaterial(const Object& object,
+ const Material& matSrc,
+ MaterialHelper& matDest);
+
+private:
+
+
+ // points to the next data line
+ const char* buffer;
+
+ // Configuration option: if enabled, up to two meshes
+ // are generated per material: those faces who have
+ // their bf cull flags set are separated.
+ bool configSplitBFCull;
+
+ // Configuration switch: subdivision surfaces are only
+ // evaluated if the value is true.
+ bool configEvalSubdivision;
+
+ // counts how many objects we have in the tree.
+ // basing on this information we can find a
+ // good estimate how many meshes we'll have in the final scene.
+ unsigned int mNumMeshes;
+
+ // current list of light sources
+ std::vector<aiLight*>* mLights;
+
+ // name counters
+ unsigned int lights, groups, polys, worlds;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_AC3DIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/ASELoader.cpp b/3rdparty/assimp/code/ASELoader.cpp
new file mode 100644
index 000000000..6f63c8942
--- /dev/null
+++ b/3rdparty/assimp/code/ASELoader.cpp
@@ -0,0 +1,1302 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 ASELoader.cpp
+ * @brief Implementation of the ASE importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
+
+// internal headers
+#include "ASELoader.h"
+#include "MaterialSystem.h"
+#include "StringComparison.h"
+#include "SkeletonMeshBuilder.h"
+#include "TargetAnimation.h"
+
+// utilities
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace Assimp::ASE;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ASEImporter::ASEImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ASEImporter::~ASEImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+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")
+ return true;
+
+ if ((!extension.length() || cs) && pIOHandler) {
+ const char* tokens[] = {"*3dsmax_asciiexport"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ASEImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ase");
+ extensions.insert("ask");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration options
+void ASEImporter::SetupProperties(const Importer* pImp)
+{
+ configRecomputeNormals = (pImp->GetPropertyInteger(
+ AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void ASEImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open ASE file " + pFile + ".");
+ }
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+
+ this->mBuffer = &mBuffer2[0];
+ this->pcScene = pScene;
+
+ // ------------------------------------------------------------------
+ // Guess the file format by looking at the extension
+ // ASC is considered to be the older format 110,
+ // ASE is the actual version 200 (that is currently written by max)
+ // ------------------------------------------------------------------
+ unsigned int defaultFormat;
+ std::string::size_type s = pFile.length()-1;
+ switch (pFile.c_str()[s]) {
+
+ case 'C':
+ case 'c':
+ defaultFormat = AI_ASE_OLD_FILE_FORMAT;
+ break;
+ default:
+ defaultFormat = AI_ASE_NEW_FILE_FORMAT;
+ };
+
+ // Construct an ASE parser and parse the file
+ ASE::Parser parser(mBuffer,defaultFormat);
+ mParser = &parser;
+ mParser->Parse();
+
+ //------------------------------------------------------------------
+ // Check whether we god at least one mesh. If we did - generate
+ // materials and copy meshes.
+ // ------------------------------------------------------------------
+ if ( !mParser->m_vMeshes.empty()) {
+
+ // If absolutely no material has been loaded from the file
+ // we need to generate a default material
+ GenerateDefaultMaterial();
+
+ // process all meshes
+ bool tookNormals = false;
+ std::vector<aiMesh*> avOutMeshes;
+ avOutMeshes.reserve(mParser->m_vMeshes.size()*2);
+ for (std::vector<ASE::Mesh>::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) {
+ if ((*i).bSkip) {
+ continue;
+ }
+ BuildUniqueRepresentation(*i);
+
+ // Need to generate proper vertex normals if necessary
+ if (GenerateNormals(*i)) {
+ tookNormals = true;
+ }
+
+ // Convert all meshes to aiMesh objects
+ ConvertMeshes(*i,avOutMeshes);
+ }
+ if (tookNormals) {
+ DefaultLogger::get()->debug("ASE: Taking normals from the file. Use "
+ "the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS setting if you "
+ "experience problems");
+ }
+
+ // Now build the output mesh list. Remove dummies
+ pScene->mNumMeshes = (unsigned int)avOutMeshes.size();
+ aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ for (std::vector<aiMesh*>::const_iterator i = avOutMeshes.begin();i != avOutMeshes.end();++i) {
+ if (!(*i)->mNumFaces) {
+ continue;
+ }
+ *pp++ = *i;
+ }
+ pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes);
+
+ // Build final material indices (remove submaterials and setup
+ // the final list)
+ BuildMaterialIndices();
+ }
+
+ // ------------------------------------------------------------------
+ // Copy all scene graph nodes - lights, cameras, dummies and meshes
+ // into one huge list.
+ //------------------------------------------------------------------
+ std::vector<BaseNode*> nodes;
+ nodes.reserve(mParser->m_vMeshes.size() +mParser->m_vLights.size()
+ + mParser->m_vCameras.size() + mParser->m_vDummies.size());
+
+ // Lights
+ for (std::vector<ASE::Light>::iterator it = mParser->m_vLights.begin(),
+ end = mParser->m_vLights.end();it != end; ++it)nodes.push_back(&(*it));
+ // Cameras
+ for (std::vector<ASE::Camera>::iterator it = mParser->m_vCameras.begin(),
+ end = mParser->m_vCameras.end();it != end; ++it)nodes.push_back(&(*it));
+ // Meshes
+ for (std::vector<ASE::Mesh>::iterator it = mParser->m_vMeshes.begin(),
+ end = mParser->m_vMeshes.end();it != end; ++it)nodes.push_back(&(*it));
+ // Dummies
+ for (std::vector<ASE::Dummy>::iterator it = mParser->m_vDummies.begin(),
+ end = mParser->m_vDummies.end();it != end; ++it)nodes.push_back(&(*it));
+
+ // build the final node graph
+ BuildNodes(nodes);
+
+ // build output animations
+ BuildAnimations(nodes);
+
+ // build output cameras
+ BuildCameras();
+
+ // build output lights
+ BuildLights();
+
+ // ------------------------------------------------------------------
+ // If we have no meshes use the SkeletonMeshBuilder helper class
+ // to build a mesh for the animation skeleton
+ // FIXME: very strange results
+ // ------------------------------------------------------------------
+ if (!pScene->mNumMeshes) {
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ SkeletonMeshBuilder skeleton(pScene);
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void ASEImporter::GenerateDefaultMaterial()
+{
+ ai_assert(NULL != mParser);
+
+ bool bHas = false;
+ for (std::vector<ASE::Mesh>::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) {
+ if ((*i).bSkip)continue;
+ if (ASE::Face::DEFAULT_MATINDEX == (*i).iMaterialIndex) {
+ (*i).iMaterialIndex = (unsigned int)mParser->m_vMaterials.size();
+ bHas = true;
+ }
+ }
+ if (bHas || mParser->m_vMaterials.empty()) {
+ // add a simple material without submaterials to the parser's list
+ mParser->m_vMaterials.push_back ( ASE::Material() );
+ ASE::Material& mat = mParser->m_vMaterials.back();
+
+ mat.mDiffuse = aiColor3D(0.6f,0.6f,0.6f);
+ mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f);
+ mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f);
+ mat.mShading = Discreet3DS::Gouraud;
+ mat.mName = AI_DEFAULT_MATERIAL_NAME;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ASEImporter::BuildAnimations(const std::vector<BaseNode*>& nodes)
+{
+ // check whether we have at least one mesh which has animations
+ std::vector<ASE::BaseNode*>::const_iterator i = nodes.begin();
+ unsigned int iNum = 0;
+ for (;i != nodes.end();++i) {
+
+ // TODO: Implement Bezier & TCB support
+ if ((*i)->mAnim.mPositionType != ASE::Animation::TRACK) {
+ DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. "
+ "This is not supported.");
+ }
+ if ((*i)->mAnim.mRotationType != ASE::Animation::TRACK) {
+ DefaultLogger::get()->warn("ASE: Rotation controller uses Bezier/TCB keys. "
+ "This is not supported.");
+ }
+ if ((*i)->mAnim.mScalingType != ASE::Animation::TRACK) {
+ DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. "
+ "This is not supported.");
+ }
+
+ // We compare against 1 here - firstly one key is not
+ // really an animation and secondly MAX writes dummies
+ // that represent the node transformation.
+ if ((*i)->mAnim.akeyPositions.size()>1 || (*i)->mAnim.akeyRotations.size()>1 || (*i)->mAnim.akeyScaling.size()>1){
+ ++iNum;
+ }
+ if ((*i)->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( (*i)->mTargetPosition.x )) {
+ ++iNum;
+ }
+ }
+ if (iNum) {
+ // Generate a new animation channel and setup everything for it
+ pcScene->mNumAnimations = 1;
+ pcScene->mAnimations = new aiAnimation*[1];
+ aiAnimation* pcAnim = pcScene->mAnimations[0] = new aiAnimation();
+ pcAnim->mNumChannels = iNum;
+ pcAnim->mChannels = new aiNodeAnim*[iNum];
+ pcAnim->mTicksPerSecond = mParser->iFrameSpeed * mParser->iTicksPerFrame;
+
+ iNum = 0;
+
+ // Now iterate through all meshes and collect all data we can find
+ for (i = nodes.begin();i != nodes.end();++i) {
+
+ ASE::BaseNode* me = *i;
+ if ( me->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( me->mTargetPosition.x )) {
+ // Generate an extra channel for the camera/light target.
+ // BuildNodes() does also generate an extra node, named
+ // <baseName>.Target.
+ aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim();
+ nd->mNodeName.Set(me->mName + ".Target");
+
+ // If there is no input position channel we will need
+ // to supply the default position from the node's
+ // local transformation matrix.
+ /*TargetAnimationHelper helper;
+ if (me->mAnim.akeyPositions.empty())
+ {
+ aiMatrix4x4& mat = (*i)->mTransform;
+ helper.SetFixedMainAnimationChannel(aiVector3D(
+ mat.a4, mat.b4, mat.c4));
+ }
+ else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions);
+ helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions);
+
+ helper.Process(&me->mTargetAnim.akeyPositions);*/
+
+ // Allocate the key array and fill it
+ nd->mNumPositionKeys = (unsigned int) me->mTargetAnim.akeyPositions.size();
+ nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
+
+ ::memcpy(nd->mPositionKeys,&me->mTargetAnim.akeyPositions[0],
+ nd->mNumPositionKeys * sizeof(aiVectorKey));
+ }
+
+ if (me->mAnim.akeyPositions.size() > 1 || me->mAnim.akeyRotations.size() > 1 || me->mAnim.akeyScaling.size() > 1) {
+ // Begin a new node animation channel for this node
+ aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim();
+ nd->mNodeName.Set(me->mName);
+
+ // copy position keys
+ if (me->mAnim.akeyPositions.size() > 1 )
+ {
+ // Allocate the key array and fill it
+ nd->mNumPositionKeys = (unsigned int) me->mAnim.akeyPositions.size();
+ nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
+
+ ::memcpy(nd->mPositionKeys,&me->mAnim.akeyPositions[0],
+ nd->mNumPositionKeys * sizeof(aiVectorKey));
+ }
+ // copy rotation keys
+ if (me->mAnim.akeyRotations.size() > 1 ) {
+ // Allocate the key array and fill it
+ nd->mNumRotationKeys = (unsigned int) me->mAnim.akeyRotations.size();
+ nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys];
+
+ // --------------------------------------------------------------------
+ // Rotation keys are offsets to the previous keys.
+ // We have the quaternion representations of all
+ // of them, so we just need to concatenate all
+ // (unit-length) quaternions to get the absolute
+ // rotations.
+ // Rotation keys are ABSOLUTE for older files
+ // --------------------------------------------------------------------
+
+ aiQuaternion cur;
+ for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) {
+ aiQuatKey q = me->mAnim.akeyRotations[a];
+
+ if (mParser->iFileFormat > 110) {
+ cur = (a ? cur*q.mValue : q.mValue);
+ q.mValue = cur.Normalize();
+ }
+ nd->mRotationKeys[a] = q;
+
+ // need this to get to Assimp quaternion conventions
+ nd->mRotationKeys[a].mValue.w *= -1.f;
+ }
+ }
+ // copy scaling keys
+ if (me->mAnim.akeyScaling.size() > 1 ) {
+ // Allocate the key array and fill it
+ nd->mNumScalingKeys = (unsigned int) me->mAnim.akeyScaling.size();
+ nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys];
+
+ ::memcpy(nd->mScalingKeys,&me->mAnim.akeyScaling[0],
+ nd->mNumScalingKeys * sizeof(aiVectorKey));
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build output cameras
+void ASEImporter::BuildCameras()
+{
+ if (!mParser->m_vCameras.empty()) {
+ pcScene->mNumCameras = (unsigned int)mParser->m_vCameras.size();
+ pcScene->mCameras = new aiCamera*[pcScene->mNumCameras];
+
+ for (unsigned int i = 0; i < pcScene->mNumCameras;++i) {
+ aiCamera* out = pcScene->mCameras[i] = new aiCamera();
+ ASE::Camera& in = mParser->m_vCameras[i];
+
+ // copy members
+ out->mClipPlaneFar = in.mFar;
+ out->mClipPlaneNear = (in.mNear ? in.mNear : 0.1f);
+ out->mHorizontalFOV = in.mFOV;
+
+ out->mName.Set(in.mName);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build output lights
+void ASEImporter::BuildLights()
+{
+ if (!mParser->m_vLights.empty()) {
+ pcScene->mNumLights = (unsigned int)mParser->m_vLights.size();
+ pcScene->mLights = new aiLight*[pcScene->mNumLights];
+
+ for (unsigned int i = 0; i < pcScene->mNumLights;++i) {
+ aiLight* out = pcScene->mLights[i] = new aiLight();
+ ASE::Light& in = mParser->m_vLights[i];
+
+ // The direction is encoded in the transformation matrix of the node.
+ // In 3DS MAX the light source points into negative Z direction if
+ // the node transformation is the identity.
+ out->mDirection = aiVector3D(0.f,0.f,-1.f);
+
+ out->mName.Set(in.mName);
+ switch (in.mLightType)
+ {
+ case ASE::Light::TARGET:
+ out->mType = aiLightSource_SPOT;
+ out->mAngleInnerCone = AI_DEG_TO_RAD(in.mAngle);
+ out->mAngleOuterCone = (in.mFalloff ? AI_DEG_TO_RAD(in.mFalloff) : out->mAngleInnerCone);
+ break;
+
+ case ASE::Light::DIRECTIONAL:
+ out->mType = aiLightSource_DIRECTIONAL;
+ break;
+
+ default:
+ //case ASE::Light::OMNI:
+ out->mType = aiLightSource_POINT;
+ break;
+ };
+ out->mColorDiffuse = out->mColorSpecular = in.mColor * in.mIntensity;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ASEImporter::AddNodes(const std::vector<BaseNode*>& nodes,
+ aiNode* pcParent,const char* szName)
+{
+ aiMatrix4x4 m;
+ AddNodes(nodes,pcParent,szName,m);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add meshes to a given node
+void ASEImporter::AddMeshes(const ASE::BaseNode* snode,aiNode* node)
+{
+ for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) {
+ // Get the name of the mesh (the mesh instance has been temporarily stored in the third vertex color)
+ const aiMesh* pcMesh = pcScene->mMeshes[i];
+ const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2];
+
+ if (mesh == snode) {
+ ++node->mNumMeshes;
+ }
+ }
+
+ if (node->mNumMeshes) {
+ node->mMeshes = new unsigned int[node->mNumMeshes];
+ for (unsigned int i = 0, p = 0; i < pcScene->mNumMeshes;++i) {
+
+ const aiMesh* pcMesh = pcScene->mMeshes[i];
+ const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2];
+ if (mesh == snode) {
+ node->mMeshes[p++] = i;
+
+ // Transform all vertices of the mesh back into their local space ->
+ // at the moment they are pretransformed
+ aiMatrix4x4 m = mesh->mTransform;
+ m.Inverse();
+
+ aiVector3D* pvCurPtr = pcMesh->mVertices;
+ const aiVector3D* pvEndPtr = pvCurPtr + pcMesh->mNumVertices;
+ while (pvCurPtr != pvEndPtr) {
+ *pvCurPtr = m * (*pvCurPtr);
+ pvCurPtr++;
+ }
+
+ // Do the same for the normal vectors, if we have them.
+ // As always, inverse transpose.
+ if (pcMesh->mNormals) {
+ aiMatrix3x3 m3 = aiMatrix3x3( mesh->mTransform );
+ m3.Transpose();
+
+ pvCurPtr = pcMesh->mNormals;
+ pvEndPtr = pvCurPtr + pcMesh->mNumVertices;
+ while (pvCurPtr != pvEndPtr) {
+ *pvCurPtr = m3 * (*pvCurPtr);
+ pvCurPtr++;
+ }
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add child nodes to a given parent node
+void ASEImporter::AddNodes (const std::vector<BaseNode*>& nodes,
+ aiNode* pcParent, const char* szName,
+ const aiMatrix4x4& mat)
+{
+ const size_t len = szName ? ::strlen(szName) : 0;
+ ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS);
+
+ // Receives child nodes for the pcParent node
+ std::vector<aiNode*> apcNodes;
+
+ // Now iterate through all nodes in the scene and search for one
+ // which has *us* as parent.
+ for (std::vector<BaseNode*>::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) {
+ const BaseNode* snode = *it;
+ if (szName) {
+ if (len != snode->mParent.length() || ::strcmp(szName,snode->mParent.c_str()))
+ continue;
+ }
+ else if (snode->mParent.length())
+ continue;
+
+ (*it)->mProcessed = true;
+
+ // Allocate a new node and add it to the output data structure
+ apcNodes.push_back(new aiNode());
+ aiNode* node = apcNodes.back();
+
+ node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node"));
+ node->mParent = pcParent;
+
+ // Setup the transformation matrix of the node
+ aiMatrix4x4 mParentAdjust = mat;
+ mParentAdjust.Inverse();
+ node->mTransformation = mParentAdjust*snode->mTransform;
+
+ // Add sub nodes - prevent stack overflow due to recursive parenting
+ if (node->mName != node->mParent->mName) {
+ AddNodes(nodes,node,node->mName.data,snode->mTransform);
+ }
+
+ // Further processing depends on the type of the node
+ if (snode->mType == ASE::BaseNode::Mesh) {
+ // If the type of this node is "Mesh" we need to search
+ // the list of output meshes in the data structure for
+ // all those that belonged to this node once. This is
+ // slightly inconvinient here and a better solution should
+ // be used when this code is refactored next.
+ AddMeshes(snode,node);
+ }
+ else if (is_not_qnan( snode->mTargetPosition.x )) {
+ // If this is a target camera or light we generate a small
+ // child node which marks the position of the camera
+ // target (the direction information is contained in *this*
+ // node's animation track but the exact target position
+ // would be lost otherwise)
+ if (!node->mNumChildren) {
+ node->mChildren = new aiNode*[1];
+ }
+
+ aiNode* nd = new aiNode();
+
+ nd->mName.Set ( snode->mName + ".Target" );
+
+ nd->mTransformation.a4 = snode->mTargetPosition.x - snode->mTransform.a4;
+ nd->mTransformation.b4 = snode->mTargetPosition.y - snode->mTransform.b4;
+ nd->mTransformation.c4 = snode->mTargetPosition.z - snode->mTransform.c4;
+
+ nd->mParent = node;
+
+ // The .Target node is always the first child node
+ for (unsigned int m = 0; m < node->mNumChildren;++m)
+ node->mChildren[m+1] = node->mChildren[m];
+
+ node->mChildren[0] = nd;
+ node->mNumChildren++;
+
+ // What we did is so great, it is at least worth a debug message
+ DefaultLogger::get()->debug("ASE: Generating separate target node ("+snode->mName+")");
+ }
+ }
+
+ // Allocate enough space for the child nodes
+ // We allocate one slot more in case this is a target camera/light
+ pcParent->mNumChildren = (unsigned int)apcNodes.size();
+ if (pcParent->mNumChildren) {
+ pcParent->mChildren = new aiNode*[apcNodes.size()+1 /* PLUS ONE !!! */];
+
+ // now build all nodes for our nice new children
+ for (unsigned int p = 0; p < apcNodes.size();++p)
+ pcParent->mChildren[p] = apcNodes[p];
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build the output node graph
+void ASEImporter::BuildNodes(std::vector<BaseNode*>& nodes) {
+ ai_assert(NULL != pcScene);
+
+ // allocate the one and only root node
+ aiNode* root = pcScene->mRootNode = new aiNode();
+ root->mName.Set("<ASERoot>");
+
+ // Setup the coordinate system transformation
+ pcScene->mRootNode->mNumChildren = 1;
+ pcScene->mRootNode->mChildren = new aiNode*[1];
+ aiNode* ch = pcScene->mRootNode->mChildren[0] = new aiNode();
+ ch->mParent = root;
+
+ // Change the transformation matrix of all nodes
+ for (std::vector<BaseNode*>::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) {
+ aiMatrix4x4& m = (*it)->mTransform;
+ m.Transpose(); // row-order vs column-order
+ }
+
+ // add all nodes
+ AddNodes(nodes,ch,NULL);
+
+ // now iterate through al nodes and find those that have not yet
+ // been added to the nodegraph (= their parent could not be recognized)
+ std::vector<const BaseNode*> aiList;
+ for (std::vector<BaseNode*>::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) {
+ if ((*it)->mProcessed) {
+ continue;
+ }
+
+ // check whether our parent is known
+ bool bKnowParent = false;
+
+ // search the list another time, starting *here* and try to find out whether
+ // there is a node that references *us* as a parent
+ for (std::vector<BaseNode*>::const_iterator it2 = nodes.begin();it2 != end; ++it2) {
+ if (it2 == it) {
+ continue;
+ }
+
+ if ((*it2)->mName == (*it)->mParent) {
+ bKnowParent = true;
+ break;
+ }
+ }
+ if (!bKnowParent) {
+ aiList.push_back(*it);
+ }
+ }
+
+ // Are there ane orphaned nodes?
+ if (!aiList.empty()) {
+ std::vector<aiNode*> apcNodes;
+ apcNodes.reserve(aiList.size() + pcScene->mRootNode->mNumChildren);
+
+ for (unsigned int i = 0; i < pcScene->mRootNode->mNumChildren;++i)
+ apcNodes.push_back(pcScene->mRootNode->mChildren[i]);
+
+ delete[] pcScene->mRootNode->mChildren;
+ for (std::vector<const BaseNode*>::/*const_*/iterator i = aiList.begin();i != aiList.end();++i) {
+ const ASE::BaseNode* src = *i;
+
+ // The parent is not known, so we can assume that we must add
+ // this node to the root node of the whole scene
+ aiNode* pcNode = new aiNode();
+ pcNode->mParent = pcScene->mRootNode;
+ pcNode->mName.Set(src->mName);
+ AddMeshes(src,pcNode);
+ AddNodes(nodes,pcNode,pcNode->mName.data);
+ apcNodes.push_back(pcNode);
+ }
+
+ // Regenerate our output array
+ pcScene->mRootNode->mChildren = new aiNode*[apcNodes.size()];
+ for (unsigned int i = 0; i < apcNodes.size();++i)
+ pcScene->mRootNode->mChildren[i] = apcNodes[i];
+
+ pcScene->mRootNode->mNumChildren = (unsigned int)apcNodes.size();
+ }
+
+ // Reset the third color set to NULL - we used this field to store a temporary pointer
+ for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
+ pcScene->mMeshes[i]->mColors[2] = NULL;
+
+ // The root node should not have at least one child or the file is valid
+ if (!pcScene->mRootNode->mNumChildren) {
+ throw DeadlyImportError("ASE: No nodes loaded. The file is either empty or corrupt");
+ }
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ pcScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert the imported data to the internal verbose representation
+void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) {
+ // allocate output storage
+ std::vector<aiVector3D> mPositions;
+ std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ std::vector<aiColor4D> mVertexColors;
+ std::vector<aiVector3D> mNormals;
+ std::vector<BoneVertex> mBoneVertices;
+
+ unsigned int iSize = (unsigned int)mesh.mFaces.size() * 3;
+ mPositions.resize(iSize);
+
+ // optional texture coordinates
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) {
+ if (!mesh.amTexCoords[i].empty()) {
+ amTexCoords[i].resize(iSize);
+ }
+ }
+ // optional vertex colors
+ if (!mesh.mVertexColors.empty()) {
+ mVertexColors.resize(iSize);
+ }
+
+ // optional vertex normals (vertex normals can simply be copied)
+ if (!mesh.mNormals.empty()) {
+ mNormals.resize(iSize);
+ }
+ // bone vertices. There is no need to change the bone list
+ if (!mesh.mBoneVertices.empty()) {
+ mBoneVertices.resize(iSize);
+ }
+
+ // iterate through all faces in the mesh
+ unsigned int iCurrent = 0, fi = 0;
+ for (std::vector<ASE::Face>::iterator i = mesh.mFaces.begin();i != mesh.mFaces.end();++i,++fi) {
+ for (unsigned int n = 0; n < 3;++n,++iCurrent)
+ {
+ mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]];
+
+ // add texture coordinates
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+ if (mesh.amTexCoords[c].empty())break;
+ amTexCoords[c][iCurrent] = mesh.amTexCoords[c][(*i).amUVIndices[c][n]];
+ }
+ // add vertex colors
+ if (!mesh.mVertexColors.empty()) {
+ mVertexColors[iCurrent] = mesh.mVertexColors[(*i).mColorIndices[n]];
+ }
+ // add normal vectors
+ if (!mesh.mNormals.empty()) {
+ mNormals[iCurrent] = mesh.mNormals[fi*3+n];
+ mNormals[iCurrent].Normalize();
+ }
+
+ // handle bone vertices
+ if ((*i).mIndices[n] < mesh.mBoneVertices.size()) {
+ // (sometimes this will cause bone verts to be duplicated
+ // however, I' quite sure Schrompf' JoinVerticesStep
+ // will fix that again ...)
+ mBoneVertices[iCurrent] = mesh.mBoneVertices[(*i).mIndices[n]];
+ }
+ (*i).mIndices[n] = iCurrent;
+ }
+ }
+
+ // replace the old arrays
+ mesh.mNormals = mNormals;
+ mesh.mPositions = mPositions;
+ mesh.mVertexColors = mVertexColors;
+
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
+ mesh.amTexCoords[c] = amTexCoords[c];
+}
+
+// ------------------------------------------------------------------------------------------------
+// Copy a texture from the ASE structs to the output material
+void CopyASETexture(MaterialHelper& mat, ASE::Texture& texture, aiTextureType type)
+{
+ // Setup the texture name
+ aiString tex;
+ tex.Set( texture.mMapName);
+ mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0));
+
+ // Setup the texture blend factor
+ if (is_not_qnan(texture.mTextureBlend))
+ mat.AddProperty<float>( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0));
+
+ // Setup texture UV transformations
+ mat.AddProperty<float>(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert from ASE material to output material
+void ASEImporter::ConvertMaterial(ASE::Material& mat)
+{
+ // LARGE TODO: Much code her is copied from 3DS ... join them maybe?
+
+ // Allocate the output material
+ mat.pcInstance = new MaterialHelper();
+
+ // At first add the base ambient color of the
+ // scene to the material
+ mat.mAmbient.r += mParser->m_clrAmbient.r;
+ mat.mAmbient.g += mParser->m_clrAmbient.g;
+ mat.mAmbient.b += mParser->m_clrAmbient.b;
+
+ aiString name;
+ name.Set( mat.mName);
+ mat.pcInstance->AddProperty( &name, AI_MATKEY_NAME);
+
+ // material colors
+ mat.pcInstance->AddProperty( &mat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
+ mat.pcInstance->AddProperty( &mat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat.pcInstance->AddProperty( &mat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
+ mat.pcInstance->AddProperty( &mat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+
+ // shininess
+ if (0.0f != mat.mSpecularExponent && 0.0f != mat.mShininessStrength)
+ {
+ mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
+ mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH);
+ }
+ // If there is no shininess, we can disable phong lighting
+ else if (D3DS::Discreet3DS::Metal == mat.mShading ||
+ D3DS::Discreet3DS::Phong == mat.mShading ||
+ D3DS::Discreet3DS::Blinn == mat.mShading)
+ {
+ mat.mShading = D3DS::Discreet3DS::Gouraud;
+ }
+
+ // opacity
+ mat.pcInstance->AddProperty<float>( &mat.mTransparency,1,AI_MATKEY_OPACITY);
+
+ // Two sided rendering?
+ if (mat.mTwoSided)
+ {
+ int i = 1;
+ mat.pcInstance->AddProperty<int>(&i,1,AI_MATKEY_TWOSIDED);
+ }
+
+ // shading mode
+ aiShadingMode eShading = aiShadingMode_NoShading;
+ switch (mat.mShading)
+ {
+ case D3DS::Discreet3DS::Flat:
+ eShading = aiShadingMode_Flat; break;
+ case D3DS::Discreet3DS::Phong :
+ eShading = aiShadingMode_Phong; break;
+ case D3DS::Discreet3DS::Blinn :
+ eShading = aiShadingMode_Blinn; break;
+
+ // I don't know what "Wire" shading should be,
+ // assume it is simple lambertian diffuse (L dot N) shading
+ case D3DS::Discreet3DS::Wire:
+ {
+ // set the wireframe flag
+ unsigned int iWire = 1;
+ mat.pcInstance->AddProperty<int>( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME);
+ }
+ case D3DS::Discreet3DS::Gouraud:
+ eShading = aiShadingMode_Gouraud; break;
+ case D3DS::Discreet3DS::Metal :
+ eShading = aiShadingMode_CookTorrance; break;
+ }
+ mat.pcInstance->AddProperty<int>( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL);
+
+ // DIFFUSE texture
+ if ( mat.sTexDiffuse.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexDiffuse, aiTextureType_DIFFUSE);
+
+ // SPECULAR texture
+ if ( mat.sTexSpecular.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexSpecular, aiTextureType_SPECULAR);
+
+ // AMBIENT texture
+ if ( mat.sTexAmbient.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexAmbient, aiTextureType_AMBIENT);
+
+ // OPACITY texture
+ if ( mat.sTexOpacity.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexOpacity, aiTextureType_OPACITY);
+
+ // EMISSIVE texture
+ if ( mat.sTexEmissive.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexEmissive, aiTextureType_EMISSIVE);
+
+ // BUMP texture
+ if ( mat.sTexBump.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexBump, aiTextureType_HEIGHT);
+
+ // SHININESS texture
+ if ( mat.sTexShininess.mMapName.length() > 0)
+ CopyASETexture(*mat.pcInstance,mat.sTexShininess, aiTextureType_SHININESS);
+
+ // store the name of the material itself, too
+ if ( mat.mName.length() > 0) {
+ aiString tex;tex.Set( mat.mName);
+ mat.pcInstance->AddProperty( &tex, AI_MATKEY_NAME);
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build output meshes
+void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOutMeshes)
+{
+ // validate the material index of the mesh
+ if (mesh.iMaterialIndex >= mParser->m_vMaterials.size()) {
+ mesh.iMaterialIndex = (unsigned int)mParser->m_vMaterials.size()-1;
+ DefaultLogger::get()->warn("Material index is out of range");
+ }
+
+ // If the material the mesh is assigned to is consisting of submeshes, split it
+ if (!mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials.empty()) {
+ std::vector<ASE::Material> vSubMaterials = mParser->
+ m_vMaterials[mesh.iMaterialIndex].avSubMaterials;
+
+ std::vector<unsigned int>* aiSplit = new std::vector<unsigned int>[vSubMaterials.size()];
+
+ // build a list of all faces per submaterial
+ for (unsigned int i = 0; i < mesh.mFaces.size();++i) {
+ // check range
+ if (mesh.mFaces[i].iMaterial >= vSubMaterials.size()) {
+ DefaultLogger::get()->warn("Submaterial index is out of range");
+
+ // use the last material instead
+ aiSplit[vSubMaterials.size()-1].push_back(i);
+ }
+ else aiSplit[mesh.mFaces[i].iMaterial].push_back(i);
+ }
+
+ // now generate submeshes
+ for (unsigned int p = 0; p < vSubMaterials.size();++p) {
+ if (!aiSplit[p].empty()) {
+
+ aiMesh* p_pcOut = new aiMesh();
+ p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // let the sub material index
+ p_pcOut->mMaterialIndex = p;
+
+ // we will need this material
+ mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials[p].bNeed = true;
+
+ // store the real index here ... color channel 3
+ p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex;
+
+ // store a pointer to the mesh in color channel 2
+ p_pcOut->mColors[2] = (aiColor4D*) &mesh;
+ avOutMeshes.push_back(p_pcOut);
+
+ // convert vertices
+ p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3;
+ p_pcOut->mNumFaces = (unsigned int)aiSplit[p].size();
+
+ // receive output vertex weights
+ std::vector<std::pair<unsigned int, float> > *avOutputBones = NULL;
+ if (!mesh.mBones.empty()) {
+ avOutputBones = new std::vector<std::pair<unsigned int, float> >[mesh.mBones.size()];
+ }
+
+ // allocate enough storage for faces
+ p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces];
+
+ unsigned int iBase = 0,iIndex;
+ if (p_pcOut->mNumVertices) {
+ p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices];
+ p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices];
+ for (unsigned int q = 0; q < aiSplit[p].size();++q) {
+
+ iIndex = aiSplit[p][q];
+
+ p_pcOut->mFaces[q].mIndices = new unsigned int[3];
+ p_pcOut->mFaces[q].mNumIndices = 3;
+
+ for (unsigned int t = 0; t < 3;++t, ++iBase) {
+ const uint32_t iIndex2 = mesh.mFaces[iIndex].mIndices[t];
+
+ p_pcOut->mVertices[iBase] = mesh.mPositions [iIndex2];
+ p_pcOut->mNormals [iBase] = mesh.mNormals [iIndex2];
+
+ // convert bones, if existing
+ if (!mesh.mBones.empty()) {
+ // check whether there is a vertex weight for this vertex index
+ if (iIndex2 < mesh.mBoneVertices.size()) {
+
+ for (std::vector<std::pair<int,float> >::const_iterator
+ blubb = mesh.mBoneVertices[iIndex2].mBoneWeights.begin();
+ blubb != mesh.mBoneVertices[iIndex2].mBoneWeights.end();++blubb) {
+
+ // NOTE: illegal cases have already been filtered out
+ avOutputBones[(*blubb).first].push_back(std::pair<unsigned int, float>(
+ iBase,(*blubb).second));
+ }
+ }
+ }
+ p_pcOut->mFaces[q].mIndices[t] = iBase;
+ }
+ }
+ }
+ // convert texture coordinates (up to AI_MAX_NUMBER_OF_TEXTURECOORDS sets supported)
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+ if (!mesh.amTexCoords[c].empty())
+ {
+ p_pcOut->mTextureCoords[c] = new aiVector3D[p_pcOut->mNumVertices];
+ iBase = 0;
+ for (unsigned int q = 0; q < aiSplit[p].size();++q) {
+ iIndex = aiSplit[p][q];
+ for (unsigned int t = 0; t < 3;++t) {
+ p_pcOut->mTextureCoords[c][iBase++] = mesh.amTexCoords[c][mesh.mFaces[iIndex].mIndices[t]];
+ }
+ }
+ // Setup the number of valid vertex components
+ p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c];
+ }
+ }
+
+ // Convert vertex colors (only one set supported)
+ if (!mesh.mVertexColors.empty()){
+ p_pcOut->mColors[0] = new aiColor4D[p_pcOut->mNumVertices];
+ iBase = 0;
+ for (unsigned int q = 0; q < aiSplit[p].size();++q) {
+ iIndex = aiSplit[p][q];
+ for (unsigned int t = 0; t < 3;++t) {
+ p_pcOut->mColors[0][iBase++] = mesh.mVertexColors[mesh.mFaces[iIndex].mIndices[t]];
+ }
+ }
+ }
+ // Copy bones
+ if (!mesh.mBones.empty()) {
+ p_pcOut->mNumBones = 0;
+ for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock)
+ if (!avOutputBones[mrspock].empty())p_pcOut->mNumBones++;
+
+ p_pcOut->mBones = new aiBone* [ p_pcOut->mNumBones ];
+ aiBone** pcBone = p_pcOut->mBones;
+ for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock)
+ {
+ if (!avOutputBones[mrspock].empty()) {
+ // we will need this bone. add it to the output mesh and
+ // add all per-vertex weights
+ aiBone* pc = *pcBone = new aiBone();
+ pc->mName.Set(mesh.mBones[mrspock].mName);
+
+ pc->mNumWeights = (unsigned int)avOutputBones[mrspock].size();
+ pc->mWeights = new aiVertexWeight[pc->mNumWeights];
+
+ for (unsigned int captainkirk = 0; captainkirk < pc->mNumWeights;++captainkirk)
+ {
+ const std::pair<unsigned int,float>& ref = avOutputBones[mrspock][captainkirk];
+ pc->mWeights[captainkirk].mVertexId = ref.first;
+ pc->mWeights[captainkirk].mWeight = ref.second;
+ }
+ ++pcBone;
+ }
+ }
+ // delete allocated storage
+ delete[] avOutputBones;
+ }
+ }
+ }
+ // delete storage
+ delete[] aiSplit;
+ }
+ else
+ {
+ // Otherwise we can simply copy the data to one output mesh
+ // This codepath needs less memory and uses fast memcpy()s
+ // to do the actual copying. So I think it is worth the
+ // effort here.
+
+ aiMesh* p_pcOut = new aiMesh();
+ p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // set an empty sub material index
+ p_pcOut->mMaterialIndex = ASE::Face::DEFAULT_MATINDEX;
+ mParser->m_vMaterials[mesh.iMaterialIndex].bNeed = true;
+
+ // store the real index here ... in color channel 3
+ p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex;
+
+ // store a pointer to the mesh in color channel 2
+ p_pcOut->mColors[2] = (aiColor4D*) &mesh;
+ avOutMeshes.push_back(p_pcOut);
+
+ // If the mesh hasn't faces or vertices, there are two cases
+ // possible: 1. the model is invalid. 2. This is a dummy
+ // helper object which we are going to remove later ...
+ if (mesh.mFaces.empty() || mesh.mPositions.empty()) {
+ return;
+ }
+
+ // convert vertices
+ p_pcOut->mNumVertices = (unsigned int)mesh.mPositions.size();
+ p_pcOut->mNumFaces = (unsigned int)mesh.mFaces.size();
+
+ // allocate enough storage for faces
+ p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces];
+
+ // copy vertices
+ p_pcOut->mVertices = new aiVector3D[mesh.mPositions.size()];
+ memcpy(p_pcOut->mVertices,&mesh.mPositions[0],
+ mesh.mPositions.size() * sizeof(aiVector3D));
+
+ // copy normals
+ p_pcOut->mNormals = new aiVector3D[mesh.mNormals.size()];
+ memcpy(p_pcOut->mNormals,&mesh.mNormals[0],
+ mesh.mNormals.size() * sizeof(aiVector3D));
+
+ // copy texture coordinates
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) {
+ if (!mesh.amTexCoords[c].empty()) {
+ p_pcOut->mTextureCoords[c] = new aiVector3D[mesh.amTexCoords[c].size()];
+ memcpy(p_pcOut->mTextureCoords[c],&mesh.amTexCoords[c][0],
+ mesh.amTexCoords[c].size() * sizeof(aiVector3D));
+
+ // setup the number of valid vertex components
+ p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c];
+ }
+ }
+
+ // copy vertex colors
+ if (!mesh.mVertexColors.empty()) {
+ p_pcOut->mColors[0] = new aiColor4D[mesh.mVertexColors.size()];
+ memcpy(p_pcOut->mColors[0],&mesh.mVertexColors[0],
+ mesh.mVertexColors.size() * sizeof(aiColor4D));
+ }
+
+ // copy faces
+ for (unsigned int iFace = 0; iFace < p_pcOut->mNumFaces;++iFace) {
+ p_pcOut->mFaces[iFace].mNumIndices = 3;
+ p_pcOut->mFaces[iFace].mIndices = new unsigned int[3];
+
+ // copy indices
+ p_pcOut->mFaces[iFace].mIndices[0] = mesh.mFaces[iFace].mIndices[0];
+ p_pcOut->mFaces[iFace].mIndices[1] = mesh.mFaces[iFace].mIndices[1];
+ p_pcOut->mFaces[iFace].mIndices[2] = mesh.mFaces[iFace].mIndices[2];
+ }
+
+ // copy vertex bones
+ if (!mesh.mBones.empty() && !mesh.mBoneVertices.empty()) {
+ std::vector<aiVertexWeight>* avBonesOut = new std::vector<aiVertexWeight>[mesh.mBones.size()];
+
+ // find all vertex weights for this bone
+ unsigned int quak = 0;
+ for (std::vector<BoneVertex>::const_iterator harrypotter = mesh.mBoneVertices.begin();
+ harrypotter != mesh.mBoneVertices.end();++harrypotter,++quak) {
+
+ for (std::vector<std::pair<int,float> >::const_iterator
+ ronaldweasley = (*harrypotter).mBoneWeights.begin();
+ ronaldweasley != (*harrypotter).mBoneWeights.end();++ronaldweasley)
+ {
+ aiVertexWeight weight;
+ weight.mVertexId = quak;
+ weight.mWeight = (*ronaldweasley).second;
+ avBonesOut[(*ronaldweasley).first].push_back(weight);
+ }
+ }
+
+ // now build a final bone list
+ p_pcOut->mNumBones = 0;
+ for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy)
+ if (!avBonesOut[jfkennedy].empty())p_pcOut->mNumBones++;
+
+ p_pcOut->mBones = new aiBone*[p_pcOut->mNumBones];
+ aiBone** pcBone = p_pcOut->mBones;
+ for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) {
+ if (!avBonesOut[jfkennedy].empty()) {
+ aiBone* pc = *pcBone = new aiBone();
+ pc->mName.Set(mesh.mBones[jfkennedy].mName);
+ pc->mNumWeights = (unsigned int)avBonesOut[jfkennedy].size();
+ pc->mWeights = new aiVertexWeight[pc->mNumWeights];
+ ::memcpy(pc->mWeights,&avBonesOut[jfkennedy][0],
+ sizeof(aiVertexWeight) * pc->mNumWeights);
+ ++pcBone;
+ }
+ }
+
+ // delete allocated storage
+ delete[] avBonesOut;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup proper material indices and build output materials
+void ASEImporter::BuildMaterialIndices()
+{
+ ai_assert(NULL != pcScene);
+
+ // iterate through all materials and check whether we need them
+ for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat)
+ {
+ ASE::Material& mat = mParser->m_vMaterials[iMat];
+ if (mat.bNeed) {
+ // Convert it to the aiMaterial layout
+ ConvertMaterial(mat);
+ ++pcScene->mNumMaterials;
+ }
+ for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat)
+ {
+ ASE::Material& submat = mat.avSubMaterials[iSubMat];
+ if (submat.bNeed) {
+ // Convert it to the aiMaterial layout
+ ConvertMaterial(submat);
+ ++pcScene->mNumMaterials;
+ }
+ }
+ }
+
+ // allocate the output material array
+ pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials];
+ D3DS::Material** pcIntMaterials = new D3DS::Material*[pcScene->mNumMaterials];
+
+ unsigned int iNum = 0;
+ for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) {
+ ASE::Material& mat = mParser->m_vMaterials[iMat];
+ if (mat.bNeed)
+ {
+ ai_assert(NULL != mat.pcInstance);
+ pcScene->mMaterials[iNum] = mat.pcInstance;
+
+ // Store the internal material, too
+ pcIntMaterials[iNum] = &mat;
+
+ // Iterate through all meshes and search for one which is using
+ // this top-level material index
+ for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh)
+ {
+ aiMesh* mesh = pcScene->mMeshes[iMesh];
+ if (ASE::Face::DEFAULT_MATINDEX == mesh->mMaterialIndex &&
+ iMat == (uintptr_t)mesh->mColors[3])
+ {
+ mesh->mMaterialIndex = iNum;
+ mesh->mColors[3] = NULL;
+ }
+ }
+ iNum++;
+ }
+ for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) {
+ ASE::Material& submat = mat.avSubMaterials[iSubMat];
+ if (submat.bNeed) {
+ ai_assert(NULL != submat.pcInstance);
+ pcScene->mMaterials[iNum] = submat.pcInstance;
+
+ // Store the internal material, too
+ pcIntMaterials[iNum] = &submat;
+
+ // Iterate through all meshes and search for one which is using
+ // this sub-level material index
+ for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) {
+ aiMesh* mesh = pcScene->mMeshes[iMesh];
+
+ if (iSubMat == mesh->mMaterialIndex && iMat == (uintptr_t)mesh->mColors[3]) {
+ mesh->mMaterialIndex = iNum;
+ mesh->mColors[3] = NULL;
+ }
+ }
+ iNum++;
+ }
+ }
+ }
+
+ // Dekete our temporary array
+ delete[] pcIntMaterials;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Generate normal vectors basing on smoothing groups
+bool ASEImporter::GenerateNormals(ASE::Mesh& mesh) {
+
+ if (!mesh.mNormals.empty() && !configRecomputeNormals)
+ {
+ // Check whether there are only uninitialized normals. If there are
+ // some, skip all normals from the file and compute them on our own
+ for (std::vector<aiVector3D>::const_iterator qq = mesh.mNormals.begin();qq != mesh.mNormals.end();++qq) {
+ if ((*qq).x || (*qq).y || (*qq).z)
+ {
+ return true;
+ }
+ }
+ }
+ // The array is reused.
+ ComputeNormalsWithSmoothingsGroups<ASE::Face>(mesh);
+ return false;
+}
+
+#endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER
diff --git a/3rdparty/assimp/code/ASELoader.h b/3rdparty/assimp/code/ASELoader.h
new file mode 100644
index 000000000..884e7df84
--- /dev/null
+++ b/3rdparty/assimp/code/ASELoader.h
@@ -0,0 +1,208 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 ASELoader.h
+ * @brief Definition of the .ASE importer class.
+ */
+#ifndef AI_ASELOADER_H_INCLUDED
+#define AI_ASELOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+#include "ASEParser.h"
+
+namespace Assimp {
+class MaterialHelper;
+
+// --------------------------------------------------------------------------------
+/** Importer class for the 3DS ASE ASCII format.
+ *
+ */
+class ASEImporter : public BaseImporter {
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ ASEImporter();
+
+ /** Destructor, private as well */
+ ~ASEImporter();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+
+private:
+
+ // -------------------------------------------------------------------
+ /** Generate normal vectors basing on smoothing groups
+ * (in some cases the normal are already contained in the file)
+ * \param mesh Mesh to work on
+ * \return false if the normals have been recomputed
+ */
+ bool GenerateNormals(ASE::Mesh& mesh);
+
+
+ // -------------------------------------------------------------------
+ /** Create valid vertex/normal/UV/color/face lists.
+ * All elements are unique, faces have only one set of indices
+ * after this step occurs.
+ * \param mesh Mesh to work on
+ */
+ void BuildUniqueRepresentation(ASE::Mesh& mesh);
+
+
+ /** Create one-material-per-mesh meshes ;-)
+ * \param mesh Mesh to work with
+ * \param Receives the list of all created meshes
+ */
+ void ConvertMeshes(ASE::Mesh& mesh, std::vector<aiMesh*>& avOut);
+
+
+ // -------------------------------------------------------------------
+ /** Convert a material to a MaterialHelper object
+ * \param mat Input material
+ */
+ void ConvertMaterial(ASE::Material& mat);
+
+
+ // -------------------------------------------------------------------
+ /** Setup the final material indices for each mesh
+ */
+ void BuildMaterialIndices();
+
+
+ // -------------------------------------------------------------------
+ /** Build the node graph
+ */
+ void BuildNodes(std::vector<ASE::BaseNode*>& nodes);
+
+
+ // -------------------------------------------------------------------
+ /** Build output cameras
+ */
+ void BuildCameras();
+
+
+ // -------------------------------------------------------------------
+ /** Build output lights
+ */
+ void BuildLights();
+
+
+ // -------------------------------------------------------------------
+ /** Build output animations
+ */
+ void BuildAnimations(const std::vector<ASE::BaseNode*>& nodes);
+
+
+ // -------------------------------------------------------------------
+ /** Add sub nodes to a node
+ * \param pcParent parent node to be filled
+ * \param szName Name of the parent node
+ * \param matrix Current transform
+ */
+ void AddNodes(const std::vector<ASE::BaseNode*>& nodes,
+ aiNode* pcParent,const char* szName);
+
+ void AddNodes(const std::vector<ASE::BaseNode*>& nodes,
+ aiNode* pcParent,const char* szName,
+ const aiMatrix4x4& matrix);
+
+ void AddMeshes(const ASE::BaseNode* snode,aiNode* node);
+
+ // -------------------------------------------------------------------
+ /** Generate a default material and add it to the parser's list
+ * Called if no material has been found in the file (rare for ASE,
+ * but not impossible)
+ */
+ void GenerateDefaultMaterial();
+
+protected:
+
+ /** Parser instance */
+ ASE::Parser* mParser;
+
+ /** Buffer to hold the loaded file */
+ char* mBuffer;
+
+ /** Scene to be filled */
+ aiScene* pcScene;
+
+ /** Config options: Recompute the normals in every case - WA
+ for 3DS Max broken ASE normal export */
+ bool configRecomputeNormals;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/ASEParser.cpp b/3rdparty/assimp/code/ASEParser.cpp
new file mode 100644
index 000000000..0b3584a2e
--- /dev/null
+++ b/3rdparty/assimp/code/ASEParser.cpp
@@ -0,0 +1,2150 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 ASEParser.cpp
+ * @brief Implementation of the ASE parser class
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "TextureTransform.h"
+#include "ASELoader.h"
+#include "MaterialSystem.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace Assimp::ASE;
+
+
+// ------------------------------------------------------------------------------------------------
+// Begin an ASE parsing function
+
+#define AI_ASE_PARSER_INIT() \
+ int iDepth = 0;
+
+// ------------------------------------------------------------------------------------------------
+// Handle a "top-level" section in the file. EOF is no error in this case.
+
+#define AI_ASE_HANDLE_TOP_LEVEL_SECTION() \
+ else if ('{' == *filePtr)iDepth++; \
+ else if ('}' == *filePtr) \
+ { \
+ if (0 == --iDepth) \
+ { \
+ ++filePtr; \
+ SkipToNextToken(); \
+ return; \
+ } \
+ } \
+ else if ('\0' == *filePtr) \
+ { \
+ return; \
+ } \
+ if (IsLineEnd(*filePtr) && !bLastWasEndLine) \
+ { \
+ ++iLineNumber; \
+ bLastWasEndLine = true; \
+ } else bLastWasEndLine = false; \
+ ++filePtr;
+
+// ------------------------------------------------------------------------------------------------
+// Handle a nested section in the file. EOF is an error in this case
+// @param level "Depth" of the section
+// @param msg Full name of the section (including the asterisk)
+
+#define AI_ASE_HANDLE_SECTION(level, msg) \
+ if ('{' == *filePtr)iDepth++; \
+ else if ('}' == *filePtr) \
+ { \
+ if (0 == --iDepth) \
+ { \
+ ++filePtr; \
+ SkipToNextToken(); \
+ return; \
+ } \
+ } \
+ else if ('\0' == *filePtr) \
+ { \
+ LogError("Encountered unexpected EOL while parsing a " msg \
+ " chunk (Level " level ")"); \
+ } \
+ if (IsLineEnd(*filePtr) && !bLastWasEndLine) \
+ { \
+ ++iLineNumber; \
+ bLastWasEndLine = true; \
+ } else bLastWasEndLine = false; \
+ ++filePtr;
+
+// ------------------------------------------------------------------------------------------------
+Parser::Parser (const char* szFile, unsigned int fileFormatDefault)
+{
+ ai_assert(NULL != szFile);
+ filePtr = szFile;
+ iFileFormat = fileFormatDefault;
+
+ // make sure that the color values are invalid
+ m_clrBackground.r = get_qnan();
+ m_clrAmbient.r = get_qnan();
+
+ // setup some default values
+ iLineNumber = 0;
+ iFirstFrame = 0;
+ iLastFrame = 0;
+ iFrameSpeed = 30; // use 30 as default value for this property
+ iTicksPerFrame = 1; // use 1 as default value for this property
+ bLastWasEndLine = false; // need to handle \r\n seqs due to binary file mapping
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::LogWarning(const char* szWarn)
+{
+ ai_assert(NULL != szWarn);
+
+ char szTemp[1024];
+#if _MSC_VER >= 1400
+ sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn);
+#else
+ snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn);
+#endif
+
+ // output the warning to the logger ...
+ DefaultLogger::get()->warn(szTemp);
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::LogInfo(const char* szWarn)
+{
+ ai_assert(NULL != szWarn);
+
+ char szTemp[1024];
+#if _MSC_VER >= 1400
+ sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn);
+#else
+ snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn);
+#endif
+
+ // output the information to the logger ...
+ DefaultLogger::get()->info(szTemp);
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::LogError(const char* szWarn)
+{
+ ai_assert(NULL != szWarn);
+
+ char szTemp[1024];
+#if _MSC_VER >= 1400
+ sprintf_s(szTemp,"Line %i: %s",iLineNumber,szWarn);
+#else
+ snprintf(szTemp,1024,"Line %i: %s",iLineNumber,szWarn);
+#endif
+
+ // throw an exception
+ throw DeadlyImportError(szTemp);
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Parser::SkipToNextToken()
+{
+ while (true)
+ {
+ char me = *filePtr;
+
+ // increase the line number counter if necessary
+ if (IsLineEnd(me) && !bLastWasEndLine)
+ {
+ ++iLineNumber;
+ bLastWasEndLine = true;
+ }
+ else bLastWasEndLine = false;
+ if ('*' == me || '}' == me || '{' == me)return true;
+ if ('\0' == me)return false;
+
+ ++filePtr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Parser::SkipSection()
+{
+ // must handle subsections ...
+ int iCnt = 0;
+ while (true)
+ {
+ if ('}' == *filePtr)
+ {
+ --iCnt;
+ if (0 == iCnt)
+ {
+ // go to the next valid token ...
+ ++filePtr;
+ SkipToNextToken();
+ return true;
+ }
+ }
+ else if ('{' == *filePtr)
+ {
+ ++iCnt;
+ }
+ else if ('\0' == *filePtr)
+ {
+ LogWarning("Unable to parse block: Unexpected EOF, closing bracket \'}\' was expected [#1]");
+ return false;
+ }
+ else if (IsLineEnd(*filePtr))++iLineNumber;
+ ++filePtr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::Parse()
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Version should be 200. Validate this ...
+ if (TokenMatch(filePtr,"3DSMAX_ASCIIEXPORT",18))
+ {
+ unsigned int fmt;
+ ParseLV4MeshLong(fmt);
+
+ if (fmt > 200)
+ {
+ LogWarning("Unknown file format version: *3DSMAX_ASCIIEXPORT should \
+ be <= 200");
+ }
+ // *************************************************************
+ // - fmt will be 0 if we're unable to read the version number
+ // there are some faulty files without a version number ...
+ // in this case we'll guess the exact file format by looking
+ // at the file extension (ASE, ASK, ASC)
+ // *************************************************************
+
+ if (fmt)iFileFormat = fmt;
+ continue;
+ }
+ // main scene information
+ if (TokenMatch(filePtr,"SCENE",5))
+ {
+ ParseLV1SceneBlock();
+ continue;
+ }
+ // "group" - no implementation yet, in facte
+ // we're just ignoring them for the moment
+ if (TokenMatch(filePtr,"GROUP",5))
+ {
+ Parse();
+ continue;
+ }
+ // material list
+ if (TokenMatch(filePtr,"MATERIAL_LIST",13))
+ {
+ ParseLV1MaterialListBlock();
+ continue;
+ }
+ // geometric object (mesh)
+ if (TokenMatch(filePtr,"GEOMOBJECT",10))
+
+ {
+ m_vMeshes.push_back(Mesh());
+ ParseLV1ObjectBlock(m_vMeshes.back());
+ continue;
+ }
+ // helper object = dummy in the hierarchy
+ if (TokenMatch(filePtr,"HELPEROBJECT",12))
+
+ {
+ m_vDummies.push_back(Dummy());
+ ParseLV1ObjectBlock(m_vDummies.back());
+ continue;
+ }
+ // light object
+ if (TokenMatch(filePtr,"LIGHTOBJECT",11))
+
+ {
+ m_vLights.push_back(Light());
+ ParseLV1ObjectBlock(m_vLights.back());
+ continue;
+ }
+ // camera object
+ if (TokenMatch(filePtr,"CAMERAOBJECT",12))
+ {
+ m_vCameras.push_back(Camera());
+ ParseLV1ObjectBlock(m_vCameras.back());
+ continue;
+ }
+ // comment - print it on the console
+ if (TokenMatch(filePtr,"COMMENT",7))
+ {
+ std::string out = "<unknown>";
+ ParseString(out,"*COMMENT");
+ LogInfo(("Comment: " + out).c_str());
+ continue;
+ }
+ // ASC bone weights
+ if (AI_ASE_IS_OLD_FILE_FORMAT() && TokenMatch(filePtr,"MESH_SOFTSKINVERTS",18))
+ {
+ ParseLV1SoftSkinBlock();
+ }
+ }
+ AI_ASE_HANDLE_TOP_LEVEL_SECTION();
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV1SoftSkinBlock()
+{
+ // TODO: fix line counting here
+
+ // **************************************************************
+ // The soft skin block is formatted differently. There are no
+ // nested sections supported and the single elements aren't
+ // marked by keywords starting with an asterisk.
+
+ /**
+ FORMAT BEGIN
+
+ *MESH_SOFTSKINVERTS {
+ <nodename>
+ <number of vertices>
+
+ [for <number of vertices> times:]
+ <number of weights> [for <number of weights> times:] <bone name> <weight>
+ }
+
+ FORMAT END
+ */
+ // **************************************************************
+ while (true)
+ {
+ if (*filePtr == '}' ) {++filePtr;return;}
+ else if (*filePtr == '\0') return;
+ else if (*filePtr == '{' ) ++filePtr;
+
+ else // if (!IsSpace(*filePtr) && !IsLineEnd(*filePtr))
+ {
+ ASE::Mesh* curMesh = NULL;
+ unsigned int numVerts = 0;
+
+ const char* sz = filePtr;
+ while (!IsSpaceOrNewLine(*filePtr))++filePtr;
+
+ const unsigned int diff = (unsigned int)(filePtr-sz);
+ if (diff)
+ {
+ std::string name = std::string(sz,diff);
+ for (std::vector<ASE::Mesh>::iterator it = m_vMeshes.begin();
+ it != m_vMeshes.end(); ++it)
+ {
+ if ((*it).mName == name)
+ {
+ curMesh = & (*it);
+ break;
+ }
+ }
+ if (!curMesh)
+ {
+ LogWarning("Encountered unknown mesh in *MESH_SOFTSKINVERTS section");
+
+ // Skip the mesh data - until we find a new mesh
+ // or the end of the *MESH_SOFTSKINVERTS section
+ while (true)
+ {
+ SkipSpacesAndLineEnd(&filePtr);
+ if (*filePtr == '}')
+ {++filePtr;return;}
+ else if (!IsNumeric(*filePtr))
+ break;
+
+ SkipLine(&filePtr);
+ }
+ }
+ else
+ {
+ SkipSpacesAndLineEnd(&filePtr);
+ ParseLV4MeshLong(numVerts);
+
+ // Reserve enough storage
+ curMesh->mBoneVertices.reserve(numVerts);
+
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ SkipSpacesAndLineEnd(&filePtr);
+ unsigned int numWeights;
+ ParseLV4MeshLong(numWeights);
+
+ curMesh->mBoneVertices.push_back(ASE::BoneVertex());
+ ASE::BoneVertex& vert = curMesh->mBoneVertices.back();
+
+ // Reserve enough storage
+ vert.mBoneWeights.reserve(numWeights);
+
+ for (unsigned int w = 0; w < numWeights;++w)
+ {
+ std::string bone;
+ ParseString(bone,"*MESH_SOFTSKINVERTS.Bone");
+
+ // Find the bone in the mesh's list
+ std::pair<int,float> me;
+ me.first = -1;
+
+ for (unsigned int n = 0; n < curMesh->mBones.size();++n)
+ {
+ if (curMesh->mBones[n].mName == bone)
+ {
+ me.first = n;
+ break;
+ }
+ }
+ if (-1 == me.first)
+ {
+ // We don't have this bone yet, so add it to the list
+ me.first = (int)curMesh->mBones.size();
+ curMesh->mBones.push_back(ASE::Bone(bone));
+ }
+ ParseLV4MeshFloat( me.second );
+
+ // Add the new bone weight to list
+ vert.mBoneWeights.push_back(me);
+ }
+ }
+ }
+ }
+ }
+ ++filePtr;
+ SkipSpacesAndLineEnd(&filePtr);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV1SceneBlock()
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"SCENE_BACKGROUND_STATIC",23))
+
+ {
+ // parse a color triple and assume it is really the bg color
+ ParseLV4MeshFloatTriple( &m_clrBackground.r );
+ continue;
+ }
+ if (TokenMatch(filePtr,"SCENE_AMBIENT_STATIC",20))
+
+ {
+ // parse a color triple and assume it is really the bg color
+ ParseLV4MeshFloatTriple( &m_clrAmbient.r );
+ continue;
+ }
+ if (TokenMatch(filePtr,"SCENE_FIRSTFRAME",16))
+ {
+ ParseLV4MeshLong(iFirstFrame);
+ continue;
+ }
+ if (TokenMatch(filePtr,"SCENE_LASTFRAME",15))
+ {
+ ParseLV4MeshLong(iLastFrame);
+ continue;
+ }
+ if (TokenMatch(filePtr,"SCENE_FRAMESPEED",16))
+ {
+ ParseLV4MeshLong(iFrameSpeed);
+ continue;
+ }
+ if (TokenMatch(filePtr,"SCENE_TICKSPERFRAME",19))
+ {
+ ParseLV4MeshLong(iTicksPerFrame);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_TOP_LEVEL_SECTION();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV1MaterialListBlock()
+{
+ AI_ASE_PARSER_INIT();
+
+ unsigned int iMaterialCount = 0;
+ unsigned int iOldMaterialCount = (unsigned int)m_vMaterials.size();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"MATERIAL_COUNT",14))
+ {
+ ParseLV4MeshLong(iMaterialCount);
+
+ // now allocate enough storage to hold all materials
+ m_vMaterials.resize(iOldMaterialCount+iMaterialCount);
+ continue;
+ }
+ if (TokenMatch(filePtr,"MATERIAL",8))
+ {
+ unsigned int iIndex = 0;
+ ParseLV4MeshLong(iIndex);
+
+ if (iIndex >= iMaterialCount)
+ {
+ LogWarning("Out of range: material index is too large");
+ iIndex = iMaterialCount-1;
+ }
+
+ // get a reference to the material
+ Material& sMat = m_vMaterials[iIndex+iOldMaterialCount];
+ // parse the material block
+ ParseLV2MaterialBlock(sMat);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_TOP_LEVEL_SECTION();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2MaterialBlock(ASE::Material& mat)
+{
+ AI_ASE_PARSER_INIT();
+
+ unsigned int iNumSubMaterials = 0;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"MATERIAL_NAME",13))
+ {
+ if (!ParseString(mat.mName,"*MATERIAL_NAME"))
+ SkipToNextToken();
+ continue;
+ }
+ // ambient material color
+ if (TokenMatch(filePtr,"MATERIAL_AMBIENT",16))
+ {
+ ParseLV4MeshFloatTriple(&mat.mAmbient.r);
+ continue;
+ }
+ // diffuse material color
+ if (TokenMatch(filePtr,"MATERIAL_DIFFUSE",16) )
+ {
+ ParseLV4MeshFloatTriple(&mat.mDiffuse.r);
+ continue;
+ }
+ // specular material color
+ if (TokenMatch(filePtr,"MATERIAL_SPECULAR",17))
+ {
+ ParseLV4MeshFloatTriple(&mat.mSpecular.r);
+ continue;
+ }
+ // material shading type
+ if (TokenMatch(filePtr,"MATERIAL_SHADING",16))
+ {
+ if (TokenMatch(filePtr,"Blinn",5))
+ {
+ mat.mShading = Discreet3DS::Blinn;
+ }
+ else if (TokenMatch(filePtr,"Phong",5))
+ {
+ mat.mShading = Discreet3DS::Phong;
+ }
+ else if (TokenMatch(filePtr,"Flat",4))
+ {
+ mat.mShading = Discreet3DS::Flat;
+ }
+ else if (TokenMatch(filePtr,"Wire",4))
+ {
+ mat.mShading = Discreet3DS::Wire;
+ }
+ else
+ {
+ // assume gouraud shading
+ mat.mShading = Discreet3DS::Gouraud;
+ SkipToNextToken();
+ }
+ continue;
+ }
+ // material transparency
+ if (TokenMatch(filePtr,"MATERIAL_TRANSPARENCY",21))
+ {
+ ParseLV4MeshFloat(mat.mTransparency);
+ mat.mTransparency = 1.0f - mat.mTransparency;continue;
+ }
+ // material self illumination
+ if (TokenMatch(filePtr,"MATERIAL_SELFILLUM",18))
+ {
+ float f = 0.0f;
+ ParseLV4MeshFloat(f);
+
+ mat.mEmissive.r = f;
+ mat.mEmissive.g = f;
+ mat.mEmissive.b = f;
+ continue;
+ }
+ // material shininess
+ if (TokenMatch(filePtr,"MATERIAL_SHINE",14) )
+ {
+ ParseLV4MeshFloat(mat.mSpecularExponent);
+ mat.mSpecularExponent *= 15;
+ continue;
+ }
+ // two-sided material
+ if (TokenMatch(filePtr,"MATERIAL_TWOSIDED",17) )
+ {
+ mat.mTwoSided = true;
+ continue;
+ }
+ // material shininess strength
+ if (TokenMatch(filePtr,"MATERIAL_SHINESTRENGTH",22))
+ {
+ ParseLV4MeshFloat(mat.mShininessStrength);
+ continue;
+ }
+ // diffuse color map
+ if (TokenMatch(filePtr,"MAP_DIFFUSE",11))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexDiffuse);
+ continue;
+ }
+ // ambient color map
+ if (TokenMatch(filePtr,"MAP_AMBIENT",11))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexAmbient);
+ continue;
+ }
+ // specular color map
+ if (TokenMatch(filePtr,"MAP_SPECULAR",12))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexSpecular);
+ continue;
+ }
+ // opacity map
+ if (TokenMatch(filePtr,"MAP_OPACITY",11))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexOpacity);
+ continue;
+ }
+ // emissive map
+ if (TokenMatch(filePtr,"MAP_SELFILLUM",13))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexEmissive);
+ continue;
+ }
+ // bump map
+ if (TokenMatch(filePtr,"MAP_BUMP",8))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexBump);
+ }
+ // specular/shininess map
+ if (TokenMatch(filePtr,"MAP_SHINESTRENGTH",17))
+ {
+ // parse the texture block
+ ParseLV3MapBlock(mat.sTexShininess);
+ continue;
+ }
+ // number of submaterials
+ if (TokenMatch(filePtr,"NUMSUBMTLS",10))
+ {
+ ParseLV4MeshLong(iNumSubMaterials);
+
+ // allocate enough storage
+ mat.avSubMaterials.resize(iNumSubMaterials);
+ }
+ // submaterial chunks
+ if (TokenMatch(filePtr,"SUBMATERIAL",11))
+ {
+
+ unsigned int iIndex = 0;
+ ParseLV4MeshLong(iIndex);
+
+ if (iIndex >= iNumSubMaterials)
+ {
+ LogWarning("Out of range: submaterial index is too large");
+ iIndex = iNumSubMaterials-1;
+ }
+
+ // get a reference to the material
+ Material& sMat = mat.avSubMaterials[iIndex];
+
+ // parse the material block
+ ParseLV2MaterialBlock(sMat);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","*MATERIAL");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MapBlock(Texture& map)
+{
+ AI_ASE_PARSER_INIT();
+
+ // ***********************************************************
+ // *BITMAP should not be there if *MAP_CLASS is not BITMAP,
+ // but we need to expect that case ... if the path is
+ // empty the texture won't be used later.
+ // ***********************************************************
+ bool parsePath = true;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ // type of map
+ if (TokenMatch(filePtr,"MAP_CLASS" ,9))
+ {
+ std::string temp;
+ if (!ParseString(temp,"*MAP_CLASS"))
+ SkipToNextToken();
+ if (temp != "Bitmap")
+ {
+ DefaultLogger::get()->warn("ASE: Skipping unknown map type: " + temp);
+ parsePath = false;
+ }
+ continue;
+ }
+ // path to the texture
+ if (parsePath && TokenMatch(filePtr,"BITMAP" ,6))
+ {
+ if (!ParseString(map.mMapName,"*BITMAP"))
+ SkipToNextToken();
+
+ if (map.mMapName == "None")
+ {
+ // Files with 'None' as map name are produced by
+ // an Maja to ASE exporter which name I forgot ..
+ DefaultLogger::get()->warn("ASE: Skipping invalid map entry");
+ map.mMapName = "";
+ }
+
+ continue;
+ }
+ // offset on the u axis
+ if (TokenMatch(filePtr,"UVW_U_OFFSET" ,12))
+ {
+ ParseLV4MeshFloat(map.mOffsetU);
+ continue;
+ }
+ // offset on the v axis
+ if (TokenMatch(filePtr,"UVW_V_OFFSET" ,12))
+ {
+ ParseLV4MeshFloat(map.mOffsetV);
+ continue;
+ }
+ // tiling on the u axis
+ if (TokenMatch(filePtr,"UVW_U_TILING" ,12))
+ {
+ ParseLV4MeshFloat(map.mScaleU);
+ continue;
+ }
+ // tiling on the v axis
+ if (TokenMatch(filePtr,"UVW_V_TILING" ,12))
+ {
+ ParseLV4MeshFloat(map.mScaleV);
+ continue;
+ }
+ // rotation around the z-axis
+ if (TokenMatch(filePtr,"UVW_ANGLE" ,9))
+ {
+ ParseLV4MeshFloat(map.mRotation);
+ continue;
+ }
+ // map blending factor
+ if (TokenMatch(filePtr,"MAP_AMOUNT" ,10))
+ {
+ ParseLV4MeshFloat(map.mTextureBlend);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MAP_XXXXXX");
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Parser::ParseString(std::string& out,const char* szName)
+{
+ char szBuffer[1024];
+ if (!SkipSpaces(&filePtr))
+ {
+
+ sprintf(szBuffer,"Unable to parse %s block: Unexpected EOL",szName);
+ LogWarning(szBuffer);
+ return false;
+ }
+ // there must be '"'
+ if ('\"' != *filePtr)
+ {
+
+ sprintf(szBuffer,"Unable to parse %s block: Strings are expected "
+ "to be enclosed in double quotation marks",szName);
+ LogWarning(szBuffer);
+ return false;
+ }
+ ++filePtr;
+ const char* sz = filePtr;
+ while (true)
+ {
+ if ('\"' == *sz)break;
+ else if ('\0' == *sz)
+ {
+ sprintf(szBuffer,"Unable to parse %s block: Strings are expected to "
+ "be enclosed in double quotation marks but EOF was reached before "
+ "a closing quotation mark was encountered",szName);
+ LogWarning(szBuffer);
+ return false;
+ }
+ sz++;
+ }
+ out = std::string(filePtr,(uintptr_t)sz-(uintptr_t)filePtr);
+ filePtr = sz+1;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV1ObjectBlock(ASE::BaseNode& node)
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // first process common tokens such as node name and transform
+ // name of the mesh/node
+ if (TokenMatch(filePtr,"NODE_NAME" ,9))
+ {
+ if (!ParseString(node.mName,"*NODE_NAME"))
+ SkipToNextToken();
+ continue;
+ }
+ // name of the parent of the node
+ if (TokenMatch(filePtr,"NODE_PARENT" ,11) )
+ {
+ if (!ParseString(node.mParent,"*NODE_PARENT"))
+ SkipToNextToken();
+ continue;
+ }
+ // transformation matrix of the node
+ if (TokenMatch(filePtr,"NODE_TM" ,7))
+ {
+ ParseLV2NodeTransformBlock(node);
+ continue;
+ }
+ // animation data of the node
+ if (TokenMatch(filePtr,"TM_ANIMATION" ,12))
+ {
+ ParseLV2AnimationBlock(node);
+ continue;
+ }
+
+ if (node.mType == BaseNode::Light)
+ {
+ // light settings
+ if (TokenMatch(filePtr,"LIGHT_SETTINGS" ,14))
+ {
+ ParseLV2LightSettingsBlock((ASE::Light&)node);
+ continue;
+ }
+ // type of the light source
+ if (TokenMatch(filePtr,"LIGHT_TYPE" ,10))
+ {
+ if (!ASSIMP_strincmp("omni",filePtr,4))
+ {
+ ((ASE::Light&)node).mLightType = ASE::Light::OMNI;
+ }
+ else if (!ASSIMP_strincmp("target",filePtr,6))
+ {
+ ((ASE::Light&)node).mLightType = ASE::Light::TARGET;
+ }
+ else if (!ASSIMP_strincmp("free",filePtr,4))
+ {
+ ((ASE::Light&)node).mLightType = ASE::Light::FREE;
+ }
+ else if (!ASSIMP_strincmp("directional",filePtr,11))
+ {
+ ((ASE::Light&)node).mLightType = ASE::Light::DIRECTIONAL;
+ }
+ else
+ {
+ LogWarning("Unknown kind of light source");
+ }
+ continue;
+ }
+ }
+ else if (node.mType == BaseNode::Camera)
+ {
+ // Camera settings
+ if (TokenMatch(filePtr,"CAMERA_SETTINGS" ,15))
+ {
+ ParseLV2CameraSettingsBlock((ASE::Camera&)node);
+ continue;
+ }
+ else if (TokenMatch(filePtr,"CAMERA_TYPE" ,11))
+ {
+ if (!ASSIMP_strincmp("target",filePtr,6))
+ {
+ ((ASE::Camera&)node).mCameraType = ASE::Camera::TARGET;
+ }
+ else if (!ASSIMP_strincmp("free",filePtr,4))
+ {
+ ((ASE::Camera&)node).mCameraType = ASE::Camera::FREE;
+ }
+ else
+ {
+ LogWarning("Unknown kind of camera");
+ }
+ continue;
+ }
+ }
+ else if (node.mType == BaseNode::Mesh)
+ {
+ // mesh data
+ // FIX: Older files use MESH_SOFTSKIN
+ if (TokenMatch(filePtr,"MESH" ,4) ||
+ TokenMatch(filePtr,"MESH_SOFTSKIN",13))
+ {
+ ParseLV2MeshBlock((ASE::Mesh&)node);
+ continue;
+ }
+ // mesh material index
+ if (TokenMatch(filePtr,"MATERIAL_REF" ,12))
+ {
+ ParseLV4MeshLong(((ASE::Mesh&)node).iMaterialIndex);
+ continue;
+ }
+ }
+ }
+ AI_ASE_HANDLE_TOP_LEVEL_SECTION();
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2CameraSettingsBlock(ASE::Camera& camera)
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"CAMERA_NEAR" ,11))
+ {
+ ParseLV4MeshFloat(camera.mNear);
+ continue;
+ }
+ if (TokenMatch(filePtr,"CAMERA_FAR" ,10))
+ {
+ ParseLV4MeshFloat(camera.mFar);
+ continue;
+ }
+ if (TokenMatch(filePtr,"CAMERA_FOV" ,10))
+ {
+ ParseLV4MeshFloat(camera.mFOV);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","CAMERA_SETTINGS");
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2LightSettingsBlock(ASE::Light& light)
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"LIGHT_COLOR" ,11))
+ {
+ ParseLV4MeshFloatTriple(&light.mColor.r);
+ continue;
+ }
+ if (TokenMatch(filePtr,"LIGHT_INTENS" ,12))
+ {
+ ParseLV4MeshFloat(light.mIntensity);
+ continue;
+ }
+ if (TokenMatch(filePtr,"LIGHT_HOTSPOT" ,13))
+ {
+ ParseLV4MeshFloat(light.mAngle);
+ continue;
+ }
+ if (TokenMatch(filePtr,"LIGHT_FALLOFF" ,13))
+ {
+ ParseLV4MeshFloat(light.mFalloff);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","LIGHT_SETTINGS");
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2AnimationBlock(ASE::BaseNode& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ ASE::Animation* anim = &mesh.mAnim;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ if (TokenMatch(filePtr,"NODE_NAME" ,9))
+ {
+ std::string temp;
+ if (!ParseString(temp,"*NODE_NAME"))
+ SkipToNextToken();
+
+ // If the name of the node contains .target it
+ // represents an animated camera or spot light
+ // target.
+ if (std::string::npos != temp.find(".Target"))
+ {
+ if ((mesh.mType != BaseNode::Camera || ((ASE::Camera&)mesh).mCameraType != ASE::Camera::TARGET) &&
+ ( mesh.mType != BaseNode::Light || ((ASE::Light&)mesh).mLightType != ASE::Light::TARGET))
+ {
+
+ DefaultLogger::get()->error("ASE: Found target animation channel "
+ "but the node is neither a camera nor a spot light");
+ anim = NULL;
+ }
+ else anim = &mesh.mTargetAnim;
+ }
+ continue;
+ }
+
+ // position keyframes
+ if (TokenMatch(filePtr,"CONTROL_POS_TRACK" ,17) ||
+ TokenMatch(filePtr,"CONTROL_POS_BEZIER" ,18) ||
+ TokenMatch(filePtr,"CONTROL_POS_TCB" ,15))
+ {
+ if (!anim)SkipSection();
+ else ParseLV3PosAnimationBlock(*anim);
+ continue;
+ }
+ // scaling keyframes
+ if (TokenMatch(filePtr,"CONTROL_SCALE_TRACK" ,19) ||
+ TokenMatch(filePtr,"CONTROL_SCALE_BEZIER" ,20) ||
+ TokenMatch(filePtr,"CONTROL_SCALE_TCB" ,17))
+ {
+ if (!anim || anim == &mesh.mTargetAnim)
+ {
+ // Target animation channels may have no rotation channels
+ DefaultLogger::get()->error("ASE: Ignoring scaling channel in target animation");
+ SkipSection();
+ }
+ else ParseLV3ScaleAnimationBlock(*anim);
+ continue;
+ }
+ // rotation keyframes
+ if (TokenMatch(filePtr,"CONTROL_ROT_TRACK" ,17) ||
+ TokenMatch(filePtr,"CONTROL_ROT_BEZIER" ,18) ||
+ TokenMatch(filePtr,"CONTROL_ROT_TCB" ,15))
+ {
+ if (!anim || anim == &mesh.mTargetAnim)
+ {
+ // Target animation channels may have no rotation channels
+ DefaultLogger::get()->error("ASE: Ignoring rotation channel in target animation");
+ SkipSection();
+ }
+ else ParseLV3RotAnimationBlock(*anim);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","TM_ANIMATION");
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3ScaleAnimationBlock(ASE::Animation& anim)
+{
+ AI_ASE_PARSER_INIT();
+ unsigned int iIndex;
+
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ bool b = false;
+
+ // For the moment we're just reading the three floats -
+ // we ignore the dditional information for bezier's and TCBs
+
+ // simple scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_SCALE_SAMPLE" ,20))
+ {
+ b = true;
+ anim.mScalingType = ASE::Animation::TRACK;
+ }
+
+ // Bezier scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_BEZIER_SCALE_KEY" ,24))
+ {
+ b = true;
+ anim.mScalingType = ASE::Animation::BEZIER;
+ }
+ // TCB scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_TCB_SCALE_KEY" ,21))
+ {
+ b = true;
+ anim.mScalingType = ASE::Animation::TCB;
+ }
+ if (b)
+ {
+ anim.akeyScaling.push_back(aiVectorKey());
+ aiVectorKey& key = anim.akeyScaling.back();
+ ParseLV4MeshFloatTriple(&key.mValue.x,iIndex);
+ key.mTime = (double)iIndex;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK");
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3PosAnimationBlock(ASE::Animation& anim)
+{
+ AI_ASE_PARSER_INIT();
+ unsigned int iIndex;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ bool b = false;
+
+ // For the moment we're just reading the three floats -
+ // we ignore the dditional information for bezier's and TCBs
+
+ // simple scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_POS_SAMPLE" ,18))
+ {
+ b = true;
+ anim.mPositionType = ASE::Animation::TRACK;
+ }
+
+ // Bezier scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_BEZIER_POS_KEY" ,22))
+ {
+ b = true;
+ anim.mPositionType = ASE::Animation::BEZIER;
+ }
+ // TCB scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_TCB_POS_KEY" ,19))
+ {
+ b = true;
+ anim.mPositionType = ASE::Animation::TCB;
+ }
+ if (b)
+ {
+ anim.akeyPositions.push_back(aiVectorKey());
+ aiVectorKey& key = anim.akeyPositions.back();
+ ParseLV4MeshFloatTriple(&key.mValue.x,iIndex);
+ key.mTime = (double)iIndex;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK");
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3RotAnimationBlock(ASE::Animation& anim)
+{
+ AI_ASE_PARSER_INIT();
+ unsigned int iIndex;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ bool b = false;
+
+ // For the moment we're just reading the floats -
+ // we ignore the dditional information for bezier's and TCBs
+
+ // simple scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_ROT_SAMPLE" ,18))
+ {
+ b = true;
+ anim.mRotationType = ASE::Animation::TRACK;
+ }
+
+ // Bezier scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_BEZIER_ROT_KEY" ,22))
+ {
+ b = true;
+ anim.mRotationType = ASE::Animation::BEZIER;
+ }
+ // TCB scaling keyframe
+ if (TokenMatch(filePtr,"CONTROL_TCB_ROT_KEY" ,19))
+ {
+ b = true;
+ anim.mRotationType = ASE::Animation::TCB;
+ }
+ if (b)
+ {
+ anim.akeyRotations.push_back(aiQuatKey());
+ aiQuatKey& key = anim.akeyRotations.back();
+ aiVector3D v;float f;
+ ParseLV4MeshFloatTriple(&v.x,iIndex);
+ ParseLV4MeshFloat(f);
+ key.mTime = (double)iIndex;
+ key.mValue = aiQuaternion(v,f);
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*CONTROL_ROT_TRACK");
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode& mesh)
+{
+ AI_ASE_PARSER_INIT();
+ int mode = 0;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ // name of the node
+ if (TokenMatch(filePtr,"NODE_NAME" ,9))
+ {
+ std::string temp;
+ if (!ParseString(temp,"*NODE_NAME"))
+ SkipToNextToken();
+
+ std::string::size_type s;
+ if (temp == mesh.mName)
+ {
+ mode = 1;
+ }
+ else if (std::string::npos != (s = temp.find(".Target")) &&
+ mesh.mName == temp.substr(0,s))
+ {
+ // This should be either a target light or a target camera
+ if ( (mesh.mType == BaseNode::Light && ((ASE::Light&)mesh) .mLightType == ASE::Light::TARGET) ||
+ (mesh.mType == BaseNode::Camera && ((ASE::Camera&)mesh).mCameraType == ASE::Camera::TARGET))
+ {
+ mode = 2;
+ }
+ else DefaultLogger::get()->error("ASE: Ignoring target transform, "
+ "this is no spot light or target camera");
+ }
+ else
+ {
+ DefaultLogger::get()->error("ASE: Unknown node transformation: " + temp);
+ // mode = 0
+ }
+ continue;
+ }
+ if (mode)
+ {
+ // fourth row of the transformation matrix - and also the
+ // only information here that is interesting for targets
+ if (TokenMatch(filePtr,"TM_ROW3" ,7))
+ {
+ ParseLV4MeshFloatTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x));
+ continue;
+ }
+ if (mode == 1)
+ {
+ // first row of the transformation matrix
+ if (TokenMatch(filePtr,"TM_ROW0" ,7))
+ {
+ ParseLV4MeshFloatTriple(mesh.mTransform[0]);
+ continue;
+ }
+ // second row of the transformation matrix
+ if (TokenMatch(filePtr,"TM_ROW1" ,7))
+ {
+ ParseLV4MeshFloatTriple(mesh.mTransform[1]);
+ continue;
+ }
+ // third row of the transformation matrix
+ if (TokenMatch(filePtr,"TM_ROW2" ,7))
+ {
+ ParseLV4MeshFloatTriple(mesh.mTransform[2]);
+ continue;
+ }
+ // inherited position axes
+ if (TokenMatch(filePtr,"INHERIT_POS" ,11))
+ {
+ unsigned int aiVal[3];
+ ParseLV4MeshLongTriple(aiVal);
+
+ for (unsigned int i = 0; i < 3;++i)
+ mesh.inherit.abInheritPosition[i] = aiVal[i] != 0;
+ continue;
+ }
+ // inherited rotation axes
+ if (TokenMatch(filePtr,"INHERIT_ROT" ,11))
+ {
+ unsigned int aiVal[3];
+ ParseLV4MeshLongTriple(aiVal);
+
+ for (unsigned int i = 0; i < 3;++i)
+ mesh.inherit.abInheritRotation[i] = aiVal[i] != 0;
+ continue;
+ }
+ // inherited scaling axes
+ if (TokenMatch(filePtr,"INHERIT_SCL" ,11))
+ {
+ unsigned int aiVal[3];
+ ParseLV4MeshLongTriple(aiVal);
+
+ for (unsigned int i = 0; i < 3;++i)
+ mesh.inherit.abInheritScaling[i] = aiVal[i] != 0;
+ continue;
+ }
+ }
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","*NODE_TM");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV2MeshBlock(ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ unsigned int iNumVertices = 0;
+ unsigned int iNumFaces = 0;
+ unsigned int iNumTVertices = 0;
+ unsigned int iNumTFaces = 0;
+ unsigned int iNumCVertices = 0;
+ unsigned int iNumCFaces = 0;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+ // Number of vertices in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14))
+ {
+ ParseLV4MeshLong(iNumVertices);
+ continue;
+ }
+ // Number of texture coordinates in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15))
+ {
+ ParseLV4MeshLong(iNumTVertices);
+ continue;
+ }
+ // Number of vertex colors in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMCVERTEX" ,15))
+ {
+ ParseLV4MeshLong(iNumCVertices);
+ continue;
+ }
+ // Number of regular faces in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMFACES" ,13))
+ {
+ ParseLV4MeshLong(iNumFaces);
+ continue;
+ }
+ // Number of UVWed faces in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15))
+ {
+ ParseLV4MeshLong(iNumTFaces);
+ continue;
+ }
+ // Number of colored faces in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMCVFACES" ,15))
+ {
+ ParseLV4MeshLong(iNumCFaces);
+ continue;
+ }
+ // mesh vertex list block
+ if (TokenMatch(filePtr,"MESH_VERTEX_LIST" ,16))
+ {
+ ParseLV3MeshVertexListBlock(iNumVertices,mesh);
+ continue;
+ }
+ // mesh face list block
+ if (TokenMatch(filePtr,"MESH_FACE_LIST" ,14))
+ {
+ ParseLV3MeshFaceListBlock(iNumFaces,mesh);
+ continue;
+ }
+ // mesh texture vertex list block
+ if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14))
+ {
+ ParseLV3MeshTListBlock(iNumTVertices,mesh);
+ continue;
+ }
+ // mesh texture face block
+ if (TokenMatch(filePtr,"MESH_TFACELIST" ,14))
+ {
+ ParseLV3MeshTFaceListBlock(iNumTFaces,mesh);
+ continue;
+ }
+ // mesh color vertex list block
+ if (TokenMatch(filePtr,"MESH_CVERTLIST" ,14))
+ {
+ ParseLV3MeshCListBlock(iNumCVertices,mesh);
+ continue;
+ }
+ // mesh color face block
+ if (TokenMatch(filePtr,"MESH_CFACELIST" ,14))
+ {
+ ParseLV3MeshCFaceListBlock(iNumCFaces,mesh);
+ continue;
+ }
+ // mesh normals
+ if (TokenMatch(filePtr,"MESH_NORMALS" ,12))
+ {
+ ParseLV3MeshNormalListBlock(mesh);
+ continue;
+ }
+ // another mesh UV channel ...
+ if (TokenMatch(filePtr,"MESH_MAPPINGCHANNEL" ,19))
+ {
+
+ unsigned int iIndex = 0;
+ ParseLV4MeshLong(iIndex);
+
+ if (iIndex < 2)
+ {
+ LogWarning("Mapping channel has an invalid index. Skipping UV channel");
+ // skip it ...
+ SkipSection();
+ }
+ if (iIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS)
+ {
+ LogWarning("Too many UV channels specified. Skipping channel ..");
+ // skip it ...
+ SkipSection();
+ }
+ else
+ {
+ // parse the mapping channel
+ ParseLV3MappingChannel(iIndex-1,mesh);
+ }
+ continue;
+ }
+ // mesh animation keyframe. Not supported
+ if (TokenMatch(filePtr,"MESH_ANIMATION" ,14))
+ {
+
+ LogWarning("Found *MESH_ANIMATION element in ASE/ASK file. "
+ "Keyframe animation is not supported by Assimp, this element "
+ "will be ignored");
+ //SkipSection();
+ continue;
+ }
+ if (TokenMatch(filePtr,"MESH_WEIGHTS" ,12))
+ {
+ ParseLV3MeshWeightsBlock(mesh);continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("2","*MESH");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ unsigned int iNumVertices = 0, iNumBones = 0;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Number of bone vertices ...
+ if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14))
+ {
+ ParseLV4MeshLong(iNumVertices);
+ continue;
+ }
+ // Number of bones
+ if (TokenMatch(filePtr,"MESH_NUMBONE" ,11))
+ {
+ ParseLV4MeshLong(iNumBones);
+ continue;
+ }
+ // parse the list of bones
+ if (TokenMatch(filePtr,"MESH_BONE_LIST" ,14))
+ {
+ ParseLV4MeshBones(iNumBones,mesh);
+ continue;
+ }
+ // parse the list of bones vertices
+ if (TokenMatch(filePtr,"MESH_BONE_VERTEX_LIST" ,21) )
+ {
+ ParseLV4MeshBonesVertices(iNumVertices,mesh);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_WEIGHTS");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshBones(unsigned int iNumBones,ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+ mesh.mBones.resize(iNumBones);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Mesh bone with name ...
+ if (TokenMatch(filePtr,"MESH_BONE_NAME" ,16))
+ {
+ // parse an index ...
+ if (SkipSpaces(&filePtr))
+ {
+ unsigned int iIndex = strtol10(filePtr,&filePtr);
+ if (iIndex >= iNumBones)
+ {
+ continue;
+ LogWarning("Bone index is out of bounds");
+ }
+ if (!ParseString(mesh.mBones[iIndex].mName,"*MESH_BONE_NAME"))
+ SkipToNextToken();
+ continue;
+ }
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_BONE_LIST");
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices,ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+ mesh.mBoneVertices.resize(iNumVertices);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Mesh bone vertex
+ if (TokenMatch(filePtr,"MESH_BONE_VERTEX" ,16))
+ {
+ // read the vertex index
+ unsigned int iIndex = strtol10(filePtr,&filePtr);
+ if (iIndex >= mesh.mPositions.size())
+ {
+ iIndex = (unsigned int)mesh.mPositions.size()-1;
+ LogWarning("Bone vertex index is out of bounds. Using the largest valid "
+ "bone vertex index instead");
+ }
+
+ // --- ignored
+ float afVert[3];
+ ParseLV4MeshFloatTriple(afVert);
+
+ std::pair<int,float> pairOut;
+ while (true)
+ {
+ // first parse the bone index ...
+ if (!SkipSpaces(&filePtr))break;
+ pairOut.first = strtol10(filePtr,&filePtr);
+
+ // then parse the vertex weight
+ if (!SkipSpaces(&filePtr))break;
+ filePtr = fast_atof_move(filePtr,pairOut.second);
+
+ // -1 marks unused entries
+ if (-1 != pairOut.first)
+ {
+ mesh.mBoneVertices[iIndex].mBoneWeights.push_back(pairOut);
+ }
+ }
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("4","*MESH_BONE_VERTEX");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshVertexListBlock(
+ unsigned int iNumVertices, ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ // allocate enough storage in the array
+ mesh.mPositions.resize(iNumVertices);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Vertex entry
+ if (TokenMatch(filePtr,"MESH_VERTEX" ,11))
+ {
+
+ aiVector3D vTemp;
+ unsigned int iIndex;
+ ParseLV4MeshFloatTriple(&vTemp.x,iIndex);
+
+ if (iIndex >= iNumVertices)
+ {
+ LogWarning("Invalid vertex index. It will be ignored");
+ }
+ else mesh.mPositions[iIndex] = vTemp;
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_VERTEX_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ // allocate enough storage in the face array
+ mesh.mFaces.resize(iNumFaces);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Face entry
+ if (TokenMatch(filePtr,"MESH_FACE" ,9))
+ {
+
+ ASE::Face mFace;
+ ParseLV4MeshFace(mFace);
+
+ if (mFace.iFace >= iNumFaces)
+ {
+ LogWarning("Face has an invalid index. It will be ignored");
+ }
+ else mesh.mFaces[mFace.iFace] = mFace;
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_FACE_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices,
+ ASE::Mesh& mesh, unsigned int iChannel)
+{
+ AI_ASE_PARSER_INIT();
+
+ // allocate enough storage in the array
+ mesh.amTexCoords[iChannel].resize(iNumVertices);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Vertex entry
+ if (TokenMatch(filePtr,"MESH_TVERT" ,10))
+ {
+ aiVector3D vTemp;
+ unsigned int iIndex;
+ ParseLV4MeshFloatTriple(&vTemp.x,iIndex);
+
+ if (iIndex >= iNumVertices)
+ {
+ LogWarning("Tvertex has an invalid index. It will be ignored");
+ }
+ else mesh.amTexCoords[iChannel][iIndex] = vTemp;
+
+ if (0.0f != vTemp.z)
+ {
+ // we need 3 coordinate channels
+ mesh.mNumUVComponents[iChannel] = 3;
+ }
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_TVERT_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces,
+ ASE::Mesh& mesh, unsigned int iChannel)
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Face entry
+ if (TokenMatch(filePtr,"MESH_TFACE" ,10))
+ {
+ unsigned int aiValues[3];
+ unsigned int iIndex = 0;
+
+ ParseLV4MeshLongTriple(aiValues,iIndex);
+ if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size())
+ {
+ LogWarning("UV-Face has an invalid index. It will be ignored");
+ }
+ else
+ {
+ // copy UV indices
+ mesh.mFaces[iIndex].amUVIndices[iChannel][0] = aiValues[0];
+ mesh.mFaces[iIndex].amUVIndices[iChannel][1] = aiValues[1];
+ mesh.mFaces[iIndex].amUVIndices[iChannel][2] = aiValues[2];
+ }
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_TFACE_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ unsigned int iNumTVertices = 0;
+ unsigned int iNumTFaces = 0;
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Number of texture coordinates in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15))
+ {
+ ParseLV4MeshLong(iNumTVertices);
+ continue;
+ }
+ // Number of UVWed faces in the mesh
+ if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15))
+ {
+ ParseLV4MeshLong(iNumTFaces);
+ continue;
+ }
+ // mesh texture vertex list block
+ if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14))
+ {
+ ParseLV3MeshTListBlock(iNumTVertices,mesh,iChannel);
+ continue;
+ }
+ // mesh texture face block
+ if (TokenMatch(filePtr,"MESH_TFACELIST" ,14))
+ {
+ ParseLV3MeshTFaceListBlock(iNumTFaces,mesh, iChannel);
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_MAPPING_CHANNEL");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ // allocate enough storage in the array
+ mesh.mVertexColors.resize(iNumVertices);
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Vertex entry
+ if (TokenMatch(filePtr,"MESH_VERTCOL" ,12))
+ {
+ aiColor4D vTemp;
+ vTemp.a = 1.0f;
+ unsigned int iIndex;
+ ParseLV4MeshFloatTriple(&vTemp.r,iIndex);
+
+ if (iIndex >= iNumVertices)
+ {
+ LogWarning("Vertex color has an invalid index. It will be ignored");
+ }
+ else mesh.mVertexColors[iIndex] = vTemp;
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_CVERTEX_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh)
+{
+ AI_ASE_PARSER_INIT();
+ while (true)
+ {
+ if ('*' == *filePtr)
+ {
+ ++filePtr;
+
+ // Face entry
+ if (TokenMatch(filePtr,"MESH_CFACE" ,11))
+ {
+ unsigned int aiValues[3];
+ unsigned int iIndex = 0;
+
+ ParseLV4MeshLongTriple(aiValues,iIndex);
+ if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size())
+ {
+ LogWarning("UV-Face has an invalid index. It will be ignored");
+ }
+ else
+ {
+ // copy color indices
+ mesh.mFaces[iIndex].mColorIndices[0] = aiValues[0];
+ mesh.mFaces[iIndex].mColorIndices[1] = aiValues[1];
+ mesh.mFaces[iIndex].mColorIndices[2] = aiValues[2];
+ }
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_CFACE_LIST");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh)
+{
+ AI_ASE_PARSER_INIT();
+
+ // Allocate enough storage for the normals
+ sMesh.mNormals.resize(sMesh.mFaces.size()*3,aiVector3D( 0.f, 0.f, 0.f ));
+ unsigned int index, faceIdx = 0xffffffff;
+
+ // FIXME: rewrite this and find out how to interpret the normals
+ // correctly. This is crap.
+
+ // Smooth the vertex and face normals together. The result
+ // will be edgy then, but otherwise everything would be soft ...
+ while (true) {
+ if ('*' == *filePtr) {
+ ++filePtr;
+ if (faceIdx != 0xffffffff && TokenMatch(filePtr,"MESH_VERTEXNORMAL",17)) {
+ aiVector3D vNormal;
+ ParseLV4MeshFloatTriple(&vNormal.x,index);
+ if (faceIdx >= sMesh.mFaces.size())
+ continue;
+
+ // Make sure we assign it to the correct face
+ const ASE::Face& face = sMesh.mFaces[faceIdx];
+ if (index == face.mIndices[0])
+ index = 0;
+ else if (index == face.mIndices[1])
+ index = 1;
+ else if (index == face.mIndices[2])
+ index = 2;
+ else {
+ DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_VERTEXNORMAL section");
+ continue;
+ }
+ // We'll renormalize later
+ sMesh.mNormals[faceIdx*3+index] += vNormal;
+ continue;
+ }
+ if (TokenMatch(filePtr,"MESH_FACENORMAL",15)) {
+ aiVector3D vNormal;
+ ParseLV4MeshFloatTriple(&vNormal.x,faceIdx);
+
+ if (faceIdx >= sMesh.mFaces.size()) {
+ DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_FACENORMAL section");
+ continue;
+ }
+
+ // We'll renormalize later
+ sMesh.mNormals[faceIdx*3] += vNormal;
+ sMesh.mNormals[faceIdx*3+1] += vNormal;
+ sMesh.mNormals[faceIdx*3+2] += vNormal;
+ continue;
+ }
+ }
+ AI_ASE_HANDLE_SECTION("3","*MESH_NORMALS");
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshFace(ASE::Face& out)
+{
+ // skip spaces and tabs
+ if (!SkipSpaces(&filePtr))
+ {
+ LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL [#1]");
+ SkipToNextToken();
+ return;
+ }
+
+ // parse the face index
+ out.iFace = strtol10(filePtr,&filePtr);
+
+ // next character should be ':'
+ if (!SkipSpaces(&filePtr))
+ {
+ // FIX: there are some ASE files which haven't got : here ....
+ LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. \':\' expected [#2]");
+ SkipToNextToken();
+ return;
+ }
+ // FIX: There are some ASE files which haven't got ':' here
+ if (':' == *filePtr)++filePtr;
+
+ // Parse all mesh indices
+ for (unsigned int i = 0; i < 3;++i)
+ {
+ unsigned int iIndex = 0;
+ if (!SkipSpaces(&filePtr))
+ {
+ LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL");
+ SkipToNextToken();
+ return;
+ }
+ switch (*filePtr)
+ {
+ case 'A':
+ case 'a':
+ break;
+ case 'B':
+ case 'b':
+ iIndex = 1;
+ break;
+ case 'C':
+ case 'c':
+ iIndex = 2;
+ break;
+ default:
+ LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. "
+ "A,B or C expected [#3]");
+ SkipToNextToken();
+ return;
+ };
+ ++filePtr;
+
+ // next character should be ':'
+ if (!SkipSpaces(&filePtr) || ':' != *filePtr)
+ {
+ LogWarning("Unable to parse *MESH_FACE Element: "
+ "Unexpected EOL. \':\' expected [#2]");
+ SkipToNextToken();
+ return;
+ }
+
+ ++filePtr;
+ if (!SkipSpaces(&filePtr))
+ {
+ LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. "
+ "Vertex index ecpected [#4]");
+ SkipToNextToken();
+ return;
+ }
+ out.mIndices[iIndex] = strtol10(filePtr,&filePtr);
+ }
+
+ // now we need to skip the AB, BC, CA blocks.
+ while (true)
+ {
+ if ('*' == *filePtr)break;
+ if (IsLineEnd(*filePtr))
+ {
+ //iLineNumber++;
+ return;
+ }
+ filePtr++;
+ }
+
+ // parse the smoothing group of the face
+ if (TokenMatch(filePtr,"*MESH_SMOOTHING",15))
+ {
+ if (!SkipSpaces(&filePtr))
+ {
+ LogWarning("Unable to parse *MESH_SMOOTHING Element: "
+ "Unexpected EOL. Smoothing group(s) expected [#5]");
+ SkipToNextToken();
+ return;
+ }
+
+ // Parse smoothing groups until we don't anymore see commas
+ // FIX: There needn't always be a value, sad but true
+ while (true)
+ {
+ if (*filePtr < '9' && *filePtr >= '0')
+ {
+ out.iSmoothGroup |= (1 << strtol10(filePtr,&filePtr));
+ }
+ SkipSpaces(&filePtr);
+ if (',' != *filePtr)
+ {
+ break;
+ }
+ ++filePtr;
+ SkipSpaces(&filePtr);
+ }
+ }
+
+ // *MESH_MTLID is optional, too
+ while (true)
+ {
+ if ('*' == *filePtr)break;
+ if (IsLineEnd(*filePtr))
+ {
+ return;
+ }
+ filePtr++;
+ }
+
+ if (TokenMatch(filePtr,"*MESH_MTLID",11))
+ {
+ if (!SkipSpaces(&filePtr))
+ {
+ LogWarning("Unable to parse *MESH_MTLID Element: Unexpected EOL. "
+ "Material index expected [#6]");
+ SkipToNextToken();
+ return;
+ }
+ out.iMaterial = strtol10(filePtr,&filePtr);
+ }
+ return;
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshLongTriple(unsigned int* apOut)
+{
+ ai_assert(NULL != apOut);
+
+ for (unsigned int i = 0; i < 3;++i)
+ ParseLV4MeshLong(apOut[i]);
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut)
+{
+ ai_assert(NULL != apOut);
+
+ // parse the index
+ ParseLV4MeshLong(rIndexOut);
+
+ // parse the three others
+ ParseLV4MeshLongTriple(apOut);
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut)
+{
+ ai_assert(NULL != apOut);
+
+ // parse the index
+ ParseLV4MeshLong(rIndexOut);
+
+ // parse the three others
+ ParseLV4MeshFloatTriple(apOut);
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshFloatTriple(float* apOut)
+{
+ ai_assert(NULL != apOut);
+
+ for (unsigned int i = 0; i < 3;++i)
+ ParseLV4MeshFloat(apOut[i]);
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshFloat(float& fOut)
+{
+ // skip spaces and tabs
+ if (!SkipSpaces(&filePtr))
+ {
+ // LOG
+ LogWarning("Unable to parse float: unexpected EOL [#1]");
+ fOut = 0.0f;
+ ++iLineNumber;
+ return;
+ }
+ // parse the first float
+ filePtr = fast_atof_move(filePtr,fOut);
+}
+// ------------------------------------------------------------------------------------------------
+void Parser::ParseLV4MeshLong(unsigned int& iOut)
+{
+ // Skip spaces and tabs
+ if (!SkipSpaces(&filePtr))
+ {
+ // LOG
+ LogWarning("Unable to parse long: unexpected EOL [#1]");
+ iOut = 0;
+ ++iLineNumber;
+ return;
+ }
+ // parse the value
+ iOut = strtol10(filePtr,&filePtr);
+}
diff --git a/3rdparty/assimp/code/ASEParser.h b/3rdparty/assimp/code/ASEParser.h
new file mode 100644
index 000000000..a221a8e81
--- /dev/null
+++ b/3rdparty/assimp/code/ASEParser.h
@@ -0,0 +1,669 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines the helper data structures for importing ASE files */
+#ifndef AI_ASEFILEHELPER_H_INC
+#define AI_ASEFILEHELPER_H_INC
+
+// STL/CRT headers
+#include <string>
+#include <vector>
+#include <list>
+
+// public ASSIMP headers
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+// for some helper routines like IsSpace()
+#include "ParsingUtils.h"
+#include "qnan.h"
+
+// ASE is quite similar to 3ds. We can reuse some structures
+#include "3DSLoader.h"
+
+namespace Assimp {
+namespace ASE {
+
+using namespace D3DS;
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing an ASE material */
+struct Material : public D3DS::Material
+{
+ //! Default constructor
+ Material() : pcInstance(NULL), bNeed (false)
+ {}
+
+ //! Contains all sub materials of this material
+ std::vector<Material> avSubMaterials;
+
+ //! MaterialHelper object
+ MaterialHelper* pcInstance;
+
+ //! Can we remove this material?
+ bool bNeed;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file face */
+struct Face : public FaceWithSmoothingGroup
+{
+ //! Default constructor. Initializes everything with 0
+ Face()
+ {
+ mColorIndices[0] = mColorIndices[1] = mColorIndices[2] = 0;
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
+ {
+ amUVIndices[i][0] = amUVIndices[i][1] = amUVIndices[i][2] = 0;
+ }
+
+ iMaterial = DEFAULT_MATINDEX;
+ iFace = 0;
+ }
+
+ //! special value to indicate that no material index has
+ //! been assigned to a face. The default material index
+ //! will replace this value later.
+ static const unsigned int DEFAULT_MATINDEX = 0xFFFFFFFF;
+
+
+
+ //! Indices into each list of texture coordinates
+ unsigned int amUVIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS][3];
+
+ //! Index into the list of vertex colors
+ unsigned int mColorIndices[3];
+
+ //! (Sub)Material index to be assigned to this face
+ unsigned int iMaterial;
+
+ //! Index of the face. It is not specified whether it is
+ //! a requirement of the file format that all faces are
+ //! written in sequential order, so we have to expect this case
+ unsigned int iFace;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file bone */
+struct Bone
+{
+ //! Constructor
+ Bone()
+ {
+ static int iCnt = 0;
+
+ // Generate a default name for the bone
+ char szTemp[128];
+ ::sprintf(szTemp,"UNNAMED_%i",iCnt++);
+ mName = szTemp;
+ }
+
+ //! Construction from an existing name
+ Bone( const std::string& name)
+ : mName (name)
+ {}
+
+ //! Name of the bone
+ std::string mName;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file bone vertex */
+struct BoneVertex
+{
+ //! Bone and corresponding vertex weight.
+ //! -1 for unrequired bones ....
+ std::vector<std::pair<int,float> > mBoneWeights;
+
+ //! Position of the bone vertex.
+ //! MUST be identical to the vertex position
+ //aiVector3D mPosition;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file animation */
+struct Animation
+{
+ enum Type
+ {
+ TRACK = 0x0,
+ BEZIER = 0x1,
+ TCB = 0x2
+ } mRotationType, mScalingType, mPositionType;
+
+ Animation()
+ : mRotationType (TRACK)
+ , mScalingType (TRACK)
+ , mPositionType (TRACK)
+ {}
+
+ //! List of track rotation keyframes
+ std::vector< aiQuatKey > akeyRotations;
+
+ //! List of track position keyframes
+ std::vector< aiVectorKey > akeyPositions;
+
+ //! List of track scaling keyframes
+ std::vector< aiVectorKey > akeyScaling;
+
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent the inheritance information of an ASE node */
+struct InheritanceInfo
+{
+ //! Default constructor
+ InheritanceInfo()
+ {
+ // set the inheritance flag for all axes by default to true
+ for (unsigned int i = 0; i < 3;++i)
+ abInheritPosition[i] = abInheritRotation[i] = abInheritScaling[i] = true;
+ }
+
+ //! Inherit the parent's position?, axis order is x,y,z
+ bool abInheritPosition[3];
+
+ //! Inherit the parent's rotation?, axis order is x,y,z
+ bool abInheritRotation[3];
+
+ //! Inherit the parent's scaling?, axis order is x,y,z
+ bool abInheritScaling[3];
+};
+
+// ---------------------------------------------------------------------------
+/** Represents an ASE file node. Base class for mesh, light and cameras */
+struct BaseNode
+{
+ enum Type {Light, Camera, Mesh, Dummy} mType;
+
+ //! Constructor. Creates a default name for the node
+ BaseNode(Type _mType)
+ : mType (_mType)
+ , mProcessed (false)
+ {
+ // generate a default name for the node
+ static int iCnt = 0;
+ char szTemp[128]; // should be sufficiently large
+ ::sprintf(szTemp,"UNNAMED_%i",iCnt++);
+ mName = szTemp;
+
+ // Set mTargetPosition to qnan
+ const float qnan = get_qnan();
+ mTargetPosition.x = qnan;
+ }
+
+ //! Name of the mesh
+ std::string mName;
+
+ //! Name of the parent of the node
+ //! "" if there is no parent ...
+ std::string mParent;
+
+ //! Transformation matrix of the node
+ aiMatrix4x4 mTransform;
+
+ //! Target position (target lights and cameras)
+ aiVector3D mTargetPosition;
+
+ //! Specifies which axes transformations a node inherits
+ //! from its parent ...
+ InheritanceInfo inherit;
+
+ //! Animation channels for the node
+ Animation mAnim;
+
+ //! Needed for lights and cameras: target animation channel
+ //! Should contain position keys only.
+ Animation mTargetAnim;
+
+ bool mProcessed;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE file mesh */
+struct Mesh : public MeshWithSmoothingGroups<ASE::Face>, public BaseNode
+{
+ //! Constructor.
+ Mesh()
+ : BaseNode (BaseNode::Mesh)
+ , bSkip (false)
+ {
+ // use 2 texture vertex components by default
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
+ this->mNumUVComponents[c] = 2;
+
+ // setup the default material index by default
+ iMaterialIndex = Face::DEFAULT_MATINDEX;
+ }
+
+ //! List of all texture coordinate sets
+ std::vector<aiVector3D> amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+ //! List of all vertex color sets.
+ std::vector<aiColor4D> mVertexColors;
+
+ //! List of all bone vertices
+ std::vector<BoneVertex> mBoneVertices;
+
+ //! List of all bones
+ std::vector<Bone> mBones;
+
+ //! Material index of the mesh
+ unsigned int iMaterialIndex;
+
+ //! Number of vertex components for each UVW set
+ unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+ //! used internally
+ bool bSkip;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE light source */
+struct Light : public BaseNode
+{
+ enum LightType
+ {
+ OMNI,
+ TARGET,
+ FREE,
+ DIRECTIONAL
+ };
+
+ //! Constructor.
+ Light()
+ : BaseNode (BaseNode::Light)
+ , mLightType (OMNI)
+ , mColor (1.f,1.f,1.f)
+ , mIntensity (1.f) // light is white by default
+ , mAngle (45.f)
+ , mFalloff (0.f)
+ {
+ }
+
+ LightType mLightType;
+ aiColor3D mColor;
+ float mIntensity;
+ float mAngle; // in degrees
+ float mFalloff;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE camera */
+struct Camera : public BaseNode
+{
+ enum CameraType
+ {
+ FREE,
+ TARGET
+ };
+
+ //! Constructor
+ Camera()
+ : BaseNode (BaseNode::Camera)
+ , mFOV (0.75f) // in radians
+ , mNear (0.1f)
+ , mFar (1000.f) // could be zero
+ , mCameraType (FREE)
+ {
+ }
+
+ float mFOV, mNear, mFar;
+ CameraType mCameraType;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure to represent an ASE helper object (dummy) */
+struct Dummy : public BaseNode
+{
+ //! Constructor
+ Dummy()
+ : BaseNode (BaseNode::Dummy)
+ {
+ }
+};
+
+// Parameters to Parser::Parse()
+#define AI_ASE_NEW_FILE_FORMAT 200
+#define AI_ASE_OLD_FILE_FORMAT 110
+
+// Internally we're a little bit more tolerant
+#define AI_ASE_IS_NEW_FILE_FORMAT() (iFileFormat >= 200)
+#define AI_ASE_IS_OLD_FILE_FORMAT() (iFileFormat < 200)
+
+// -------------------------------------------------------------------------------
+/** \brief Class to parse ASE files
+ */
+class Parser
+{
+
+private:
+
+ Parser() {}
+
+public:
+
+ // -------------------------------------------------------------------
+ //! Construct a parser from a given input file which is
+ //! guaranted to be terminated with zero.
+ //! @param szFile Input file
+ //! @param fileFormatDefault Assumed file format version. If the
+ //! file format is specified in the file the new value replaces
+ //! the default value.
+ Parser (const char* szFile, unsigned int fileFormatDefault);
+
+ // -------------------------------------------------------------------
+ //! Parses the file into the parsers internal representation
+ void Parse();
+
+
+private:
+
+ // -------------------------------------------------------------------
+ //! Parse the *SCENE block in a file
+ void ParseLV1SceneBlock();
+
+ // -------------------------------------------------------------------
+ //! Parse the *MESH_SOFTSKINVERTS block in a file
+ void ParseLV1SoftSkinBlock();
+
+ // -------------------------------------------------------------------
+ //! Parse the *MATERIAL_LIST block in a file
+ void ParseLV1MaterialListBlock();
+
+ // -------------------------------------------------------------------
+ //! Parse a *<xxx>OBJECT block in a file
+ //! \param mesh Node to be filled
+ void ParseLV1ObjectBlock(BaseNode& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MATERIAL blocks in a material list
+ //! \param mat Material structure to be filled
+ void ParseLV2MaterialBlock(Material& mat);
+
+ // -------------------------------------------------------------------
+ //! Parse a *NODE_TM block in a file
+ //! \param mesh Node (!) object to be filled
+ void ParseLV2NodeTransformBlock(BaseNode& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *TM_ANIMATION block in a file
+ //! \param mesh Mesh object to be filled
+ void ParseLV2AnimationBlock(BaseNode& mesh);
+ void ParseLV3PosAnimationBlock(ASE::Animation& anim);
+ void ParseLV3ScaleAnimationBlock(ASE::Animation& anim);
+ void ParseLV3RotAnimationBlock(ASE::Animation& anim);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH block in a file
+ //! \param mesh Mesh object to be filled
+ void ParseLV2MeshBlock(Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *LIGHT_SETTINGS block in a file
+ //! \param light Light object to be filled
+ void ParseLV2LightSettingsBlock(Light& light);
+
+ // -------------------------------------------------------------------
+ //! Parse a *CAMERA_SETTINGS block in a file
+ //! \param cam Camera object to be filled
+ void ParseLV2CameraSettingsBlock(Camera& cam);
+
+ // -------------------------------------------------------------------
+ //! Parse the *MAP_XXXXXX blocks in a material
+ //! \param map Texture structure to be filled
+ void ParseLV3MapBlock(Texture& map);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_VERTEX_LIST block in a file
+ //! \param iNumVertices Value of *MESH_NUMVERTEX, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshVertexListBlock(
+ unsigned int iNumVertices,Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_FACE_LIST block in a file
+ //! \param iNumFaces Value of *MESH_NUMFACES, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshFaceListBlock(
+ unsigned int iNumFaces,Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_TVERT_LIST block in a file
+ //! \param iNumVertices Value of *MESH_NUMTVERTEX, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ //! \param iChannel Output UVW channel
+ void ParseLV3MeshTListBlock(
+ unsigned int iNumVertices,Mesh& mesh, unsigned int iChannel = 0);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_TFACELIST block in a file
+ //! \param iNumFaces Value of *MESH_NUMTVFACES, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ //! \param iChannel Output UVW channel
+ void ParseLV3MeshTFaceListBlock(
+ unsigned int iNumFaces,Mesh& mesh, unsigned int iChannel = 0);
+
+ // -------------------------------------------------------------------
+ //! Parse an additional mapping channel
+ //! (specified via *MESH_MAPPINGCHANNEL)
+ //! \param iChannel Channel index to be filled
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MappingChannel(
+ unsigned int iChannel, Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_CVERTLIST block in a file
+ //! \param iNumVertices Value of *MESH_NUMCVERTEX, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshCListBlock(
+ unsigned int iNumVertices, Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_CFACELIST block in a file
+ //! \param iNumFaces Value of *MESH_NUMCVFACES, if present.
+ //! Otherwise zero. This is used to check the consistency of the file.
+ //! A warning is sent to the logger if the validations fails.
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshCFaceListBlock(
+ unsigned int iNumFaces, Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_NORMALS block in a file
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshNormalListBlock(Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_WEIGHTSblock in a file
+ //! \param mesh Mesh object to be filled
+ void ParseLV3MeshWeightsBlock(Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse the bone list of a file
+ //! \param mesh Mesh object to be filled
+ //! \param iNumBones Number of bones in the mesh
+ void ParseLV4MeshBones(unsigned int iNumBones,Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse the bone vertices list of a file
+ //! \param mesh Mesh object to be filled
+ //! \param iNumVertices Number of vertices to be parsed
+ void ParseLV4MeshBonesVertices(unsigned int iNumVertices,Mesh& mesh);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_FACE block in a file
+ //! \param out receive the face data
+ void ParseLV4MeshFace(ASE::Face& out);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_VERT block in a file
+ //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...)
+ //! \param apOut Output buffer (3 floats)
+ //! \param rIndexOut Output index
+ void ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_VERT block in a file
+ //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...)
+ //! \param apOut Output buffer (3 floats)
+ void ParseLV4MeshFloatTriple(float* apOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_TFACE block in a file
+ //! (also works for MESH_CFACE)
+ //! \param apOut Output buffer (3 ints)
+ //! \param rIndexOut Output index
+ void ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a *MESH_TFACE block in a file
+ //! (also works for MESH_CFACE)
+ //! \param apOut Output buffer (3 ints)
+ void ParseLV4MeshLongTriple(unsigned int* apOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a single float element
+ //! \param fOut Output float
+ void ParseLV4MeshFloat(float& fOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a single int element
+ //! \param iOut Output integer
+ void ParseLV4MeshLong(unsigned int& iOut);
+
+ // -------------------------------------------------------------------
+ //! Skip everything to the next: '*' or '\0'
+ bool SkipToNextToken();
+
+ // -------------------------------------------------------------------
+ //! Skip the current section until the token after the closing }.
+ //! This function handles embedded subsections correctly
+ bool SkipSection();
+
+ // -------------------------------------------------------------------
+ //! Output a warning to the logger
+ //! \param szWarn Warn message
+ void LogWarning(const char* szWarn);
+
+ // -------------------------------------------------------------------
+ //! Output a message to the logger
+ //! \param szWarn Message
+ void LogInfo(const char* szWarn);
+
+ // -------------------------------------------------------------------
+ //! Output an error to the logger
+ //! \param szWarn Error message
+ void LogError(const char* szWarn);
+
+ // -------------------------------------------------------------------
+ //! Parse a string, enclosed in double quotation marks
+ //! \param out Output string
+ //! \param szName Name of the enclosing element -> used in error
+ //! messages.
+ //! \return false if an error occured
+ bool ParseString(std::string& out,const char* szName);
+
+public:
+
+ //! Pointer to current data
+ const char* filePtr;
+
+ //! background color to be passed to the viewer
+ //! QNAN if none was found
+ aiColor3D m_clrBackground;
+
+ //! Base ambient color to be passed to all materials
+ //! QNAN if none was found
+ aiColor3D m_clrAmbient;
+
+ //! List of all materials found in the file
+ std::vector<Material> m_vMaterials;
+
+ //! List of all meshes found in the file
+ std::vector<Mesh> m_vMeshes;
+
+ //! List of all dummies found in the file
+ std::vector<Dummy> m_vDummies;
+
+ //! List of all lights found in the file
+ std::vector<Light> m_vLights;
+
+ //! List of all cameras found in the file
+ std::vector<Camera> m_vCameras;
+
+ //! Current line in the file
+ unsigned int iLineNumber;
+
+ //! First frame
+ unsigned int iFirstFrame;
+
+ //! Last frame
+ unsigned int iLastFrame;
+
+ //! Frame speed - frames per second
+ unsigned int iFrameSpeed;
+
+ //! Ticks per frame
+ unsigned int iTicksPerFrame;
+
+ //! true if the last character read was an end-line character
+ bool bLastWasEndLine;
+
+ //! File format version
+ unsigned int iFileFormat;
+};
+
+
+} // Namespace ASE
+} // Namespace ASSIMP
+
+#endif // !! include guard
diff --git a/3rdparty/assimp/code/Assimp.cpp b/3rdparty/assimp/code/Assimp.cpp
new file mode 100644
index 000000000..c00dc63b7
--- /dev/null
+++ b/3rdparty/assimp/code/Assimp.cpp
@@ -0,0 +1,731 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Assimp.cpp
+ * @brief Implementation of the Plain-C API
+ */
+
+#include "AssimpPCH.h"
+#include "../include/assimp.h"
+#include "../include/aiFileIO.h"
+
+#include "GenericProperty.h"
+
+// ------------------------------------------------------------------------------------------------
+#ifdef AI_C_THREADSAFE
+# include <boost/thread/thread.hpp>
+# include <boost/thread/mutex.hpp>
+#endif
+// ------------------------------------------------------------------------------------------------
+using namespace Assimp;
+
+namespace Assimp
+{
+ /** Stores the importer objects for all active import processes */
+ typedef std::map<const aiScene*, Assimp::Importer*> ImporterMap;
+
+ /** Stores the LogStream objects for all active C log streams */
+ struct mpred {
+ bool operator () (const aiLogStream& s0, const aiLogStream& s1) const {
+ return s0.callback<s1.callback&&s0.user<s1.user;
+ }
+ };
+ typedef std::map<aiLogStream, Assimp::LogStream*, mpred> LogStreamMap;
+
+ /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */
+ typedef std::list<Assimp::LogStream*> PredefLogStreamMap;
+
+ /** Local storage of all active import processes */
+ static ImporterMap gActiveImports;
+
+ /** Local storage of all active log streams */
+ static LogStreamMap gActiveLogStreams;
+
+ /** Local storage of LogStreams allocated by #aiGetPredefinedLogStream */
+ static PredefLogStreamMap gPredefinedStreams;
+
+ /** Error message of the last failed import process */
+ static std::string gLastErrorString;
+
+ /** Verbose logging active or not? */
+ static aiBool gVerboseLogging = false;
+}
+
+/** Configuration properties */
+static ImporterPimpl::IntPropertyMap gIntProperties;
+static ImporterPimpl::FloatPropertyMap gFloatProperties;
+static ImporterPimpl::StringPropertyMap gStringProperties;
+
+#ifdef AI_C_THREADSAFE
+/** Global mutex to manage the access to the importer map */
+static boost::mutex gMutex;
+
+/** Global mutex to manage the access to the logstream map */
+static boost::mutex gLogStreamMutex;
+#endif
+
+class CIOSystemWrapper;
+class CIOStreamWrapper;
+
+// ------------------------------------------------------------------------------------------------
+// Custom IOStream implementation for the C-API
+class CIOStreamWrapper : public IOStream
+{
+ friend class CIOSystemWrapper;
+public:
+
+ CIOStreamWrapper(aiFile* pFile)
+ : mFile(pFile)
+ {}
+
+ // ...................................................................
+ size_t Read(void* pvBuffer,
+ size_t pSize,
+ size_t pCount
+ ){
+ // need to typecast here as C has no void*
+ return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount);
+ }
+
+ // ...................................................................
+ size_t Write(const void* pvBuffer,
+ size_t pSize,
+ size_t pCount
+ ){
+ // need to typecast here as C has no void*
+ return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount);
+ }
+
+ // ...................................................................
+ aiReturn Seek(size_t pOffset,
+ aiOrigin pOrigin
+ ){
+ return mFile->SeekProc(mFile,pOffset,pOrigin);
+ }
+
+ // ...................................................................
+ size_t Tell(void) const {
+ return mFile->TellProc(mFile);
+ }
+
+ // ...................................................................
+ size_t FileSize() const {
+ return mFile->FileSizeProc(mFile);
+ }
+
+ // ...................................................................
+ void Flush () {
+ return mFile->FlushProc(mFile);
+ }
+
+private:
+ aiFile* mFile;
+};
+
+// ------------------------------------------------------------------------------------------------
+// Custom IOStream implementation for the C-API
+class CIOSystemWrapper : public IOSystem
+{
+public:
+ CIOSystemWrapper(aiFileIO* pFile)
+ : mFileSystem(pFile)
+ {}
+
+ // ...................................................................
+ bool Exists( const char* pFile) const {
+ CIOSystemWrapper* pip = const_cast<CIOSystemWrapper*>(this);
+ IOStream* p = pip->Open(pFile);
+ if (p){
+ pip->Close(p);
+ return true;
+ }
+ return false;
+ }
+
+ // ...................................................................
+ char getOsSeparator() const {
+#ifndef _WIN32
+ return '/';
+#else
+ return '\\';
+#endif
+ }
+
+ // ...................................................................
+ IOStream* Open(const char* pFile,const char* pMode = "rb") {
+ aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,pMode);
+ if (!p) {
+ return NULL;
+ }
+ return new CIOStreamWrapper(p);
+ }
+
+ // ...................................................................
+ void Close( IOStream* pFile) {
+ if (!pFile) {
+ return;
+ }
+ mFileSystem->CloseProc(mFileSystem,((CIOStreamWrapper*) pFile)->mFile);
+ delete pFile;
+ }
+private:
+ aiFileIO* mFileSystem;
+};
+
+// ------------------------------------------------------------------------------------------------
+// Custom LogStream implementation for the C-API
+class LogToCallbackRedirector : public LogStream
+{
+public:
+ LogToCallbackRedirector(const aiLogStream& s)
+ : stream (s) {
+ ai_assert(NULL != s.callback);
+ }
+
+ ~LogToCallbackRedirector() {
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gLogStreamMutex);
+#endif
+ // (HACK) Check whether the 'stream.user' pointer points to a
+ // custom LogStream allocated by #aiGetPredefinedLogStream.
+ // In this case, we need to delete it, too. Of course, this
+ // might cause strange problems, but the chance is quite low.
+
+ PredefLogStreamMap::iterator it = std::find(gPredefinedStreams.begin(),
+ gPredefinedStreams.end(), (Assimp::LogStream*)stream.user);
+
+ if (it != gPredefinedStreams.end()) {
+ delete *it;
+ gPredefinedStreams.erase(it);
+ }
+ }
+
+ /** @copydoc LogStream::write */
+ void write(const char* message) {
+ stream.callback(message,stream.user);
+ }
+
+private:
+ aiLogStream stream;
+};
+
+// ------------------------------------------------------------------------------------------------
+void ReportSceneNotFoundError()
+{
+ DefaultLogger::get()->error("Unable to find the Assimp::Importer for this aiScene. "
+ "Are you playing fools with us? Don't mix cpp and c API. Thanks.");
+
+ assert(false);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the given file and returns its content.
+const aiScene* aiImportFile( const char* pFile, unsigned int pFlags)
+{
+ return aiImportFileEx(pFile,pFlags,NULL);
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags,
+ aiFileIO* pFS)
+{
+ ai_assert(NULL != pFile);
+
+ const aiScene* scene = NULL;
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // create an Importer for this file
+ Assimp::Importer* imp = new Assimp::Importer();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ // copy the global property lists to the Importer instance
+ imp->pimpl->mIntProperties = gIntProperties;
+ imp->pimpl->mFloatProperties = gFloatProperties;
+ imp->pimpl->mStringProperties = gStringProperties;
+
+#ifdef AI_C_THREADSAFE
+ lock.unlock();
+#endif
+
+ // setup a custom IO system if necessary
+ if (pFS) {
+ imp->SetIOHandler( new CIOSystemWrapper (pFS) );
+ }
+
+ // and have it read the file
+ scene = imp->ReadFile( pFile, pFlags);
+
+ // if succeeded, place it in the collection of active processes
+ if ( scene) {
+#ifdef AI_C_THREADSAFE
+ lock.lock();
+#endif
+ gActiveImports[scene] = imp;
+ }
+ else {
+ // if failed, extract error code and destroy the import
+ gLastErrorString = imp->GetErrorString();
+ delete imp;
+ }
+
+ // return imported data. If the import failed the pointer is NULL anyways
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+ return scene;
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* aiImportFileFromMemory(
+ const char* pBuffer,
+ unsigned int pLength,
+ unsigned int pFlags,
+ const char* pHint)
+{
+ ai_assert(NULL != pBuffer && 0 != pLength);
+
+ const aiScene* scene = NULL;
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // create an Importer for this file
+ Assimp::Importer* imp = new Assimp::Importer();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ // copy the global property lists to the Importer instance
+ imp->pimpl->mIntProperties = gIntProperties;
+ imp->pimpl->mFloatProperties = gFloatProperties;
+ imp->pimpl->mStringProperties = gStringProperties;
+
+#ifdef AI_C_THREADSAFE
+ lock.unlock();
+#endif
+
+ // and have it read the file from the memory buffer
+ scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint);
+
+ // if succeeded, place it in the collection of active processes
+ if ( scene) {
+#ifdef AI_C_THREADSAFE
+ lock.lock();
+#endif
+ gActiveImports[scene] = imp;
+ }
+ else {
+ // if failed, extract error code and destroy the import
+ gLastErrorString = imp->GetErrorString();
+ delete imp;
+ }
+ // return imported data. If the import failed the pointer is NULL anyways
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+ return scene;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Releases all resources associated with the given import process.
+void aiReleaseImport( const aiScene* pScene)
+{
+ if (!pScene) {
+ return;
+ }
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+
+ // find the importer associated with this data
+ ImporterMap::iterator it = gActiveImports.find( pScene);
+ // it should be there... else the user is playing fools with us
+ if ( it == gActiveImports.end()) {
+ ReportSceneNotFoundError();
+ return;
+ }
+
+ // kill the importer, the data dies with it
+ delete it->second;
+ gActiveImports.erase( it);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API const aiScene* aiApplyPostProcessing(const aiScene* pScene,
+ unsigned int pFlags)
+{
+ const aiScene* sc = NULL;
+
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ // find the importer associated with this data
+ ImporterMap::iterator it = gActiveImports.find( pScene);
+ // it should be there... else the user is playing fools with us
+ if ( it == gActiveImports.end()) {
+ ReportSceneNotFoundError();
+ return NULL;
+ }
+#ifdef AI_C_THREADSAFE
+ lock.unlock();
+#endif
+ sc = it->second->ApplyPostProcessing(pFlags);
+#ifdef AI_C_THREADSAFE
+ lock.lock();
+#endif
+ if (!sc) {
+ // kill the importer, the data dies with it
+ delete it->second;
+ gActiveImports.erase( it);
+ return NULL;
+ }
+
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+ return sc;
+}
+
+// ------------------------------------------------------------------------------------------------
+void CallbackToLogRedirector (const char* msg, char* dt)
+{
+ ai_assert(NULL != msg && NULL != dt);
+ LogStream* s = (LogStream*)dt;
+
+ s->write(msg);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream,const char* file)
+{
+ aiLogStream sout;
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ LogStream* stream = LogStream::createDefaultStream(pStream,file);
+ if (!stream) {
+ sout.callback = NULL;
+ sout.user = NULL;
+ }
+ else {
+ sout.callback = &CallbackToLogRedirector;
+ sout.user = (char*)stream;
+ }
+ gPredefinedStreams.push_back(stream);
+ ASSIMP_END_EXCEPTION_REGION(aiLogStream);
+ return sout;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiAttachLogStream( const aiLogStream* stream )
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gLogStreamMutex);
+#endif
+
+ LogStream* lg = new LogToCallbackRedirector(*stream);
+ gActiveLogStreams[*stream] = lg;
+
+ if (DefaultLogger::isNullLogger()) {
+ DefaultLogger::create(NULL,(gVerboseLogging == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL));
+ }
+ DefaultLogger::get()->attachStream(lg);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiReturn aiDetachLogStream( const aiLogStream* stream)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gLogStreamMutex);
+#endif
+ // find the logstream associated with this data
+ LogStreamMap::iterator it = gActiveLogStreams.find( *stream);
+ // it should be there... else the user is playing fools with us
+ if ( it == gActiveLogStreams.end()) {
+ return AI_FAILURE;
+ }
+ DefaultLogger::get()->detatchStream( it->second );
+ delete it->second;
+
+ gActiveLogStreams.erase( it);
+
+ if (gActiveLogStreams.empty()) {
+ DefaultLogger::kill();
+ }
+ ASSIMP_END_EXCEPTION_REGION(aiReturn);
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiDetachAllLogStreams(void)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gLogStreamMutex);
+#endif
+ for (LogStreamMap::iterator it = gActiveLogStreams.begin(); it != gActiveLogStreams.end(); ++it) {
+ DefaultLogger::get()->detatchStream( it->second );
+ delete it->second;
+ }
+ gActiveLogStreams.clear();
+ DefaultLogger::kill();
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiEnableVerboseLogging(aiBool d)
+{
+ if (!DefaultLogger::isNullLogger()) {
+ DefaultLogger::get()->setLogSeverity((d == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL));
+ }
+ gVerboseLogging = d;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the error text of the last failed import process.
+const char* aiGetErrorString()
+{
+ return gLastErrorString.c_str();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the error text of the last failed import process.
+aiBool aiIsExtensionSupported(const char* szExtension)
+{
+ ai_assert(NULL != szExtension);
+ aiBool candoit=AI_FALSE;
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+
+ if (!gActiveImports.empty()) {
+ return ((*(gActiveImports.begin())).second->IsExtensionSupported( szExtension )) ? AI_TRUE : AI_FALSE;
+ }
+
+ // fixme: no need to create a temporary Importer instance just for that ..
+ Assimp::Importer tmp;
+ candoit = tmp.IsExtensionSupported(std::string(szExtension)) ? AI_TRUE : AI_FALSE;
+
+ ASSIMP_END_EXCEPTION_REGION(aiBool);
+ return candoit;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all file extensions supported by ASSIMP
+void aiGetExtensionList(aiString* szOut)
+{
+ ai_assert(NULL != szOut);
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+
+ if (!gActiveImports.empty()) {
+ (*(gActiveImports.begin())).second->GetExtensionList(*szOut);
+ return;
+ }
+ // fixme: no need to create a temporary Importer instance just for that ..
+ Assimp::Importer tmp;
+ tmp.GetExtensionList(*szOut);
+
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the memory requirements for a particular import.
+void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn,
+ C_STRUCT aiMemoryInfo* in)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+
+ // find the importer associated with this data
+ ImporterMap::iterator it = gActiveImports.find( pIn);
+ // it should be there... else the user is playing fools with us
+ if ( it == gActiveImports.end()) {
+ ReportSceneNotFoundError();
+ return;
+ }
+ // get memory statistics
+#ifdef AI_C_THREADSAFE
+ lock.unlock();
+#endif
+ it->second->GetMemoryRequirements(*in);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyInteger
+ASSIMP_API void aiSetImportPropertyInteger(const char* szName, int value)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ SetGenericProperty<int>(gIntProperties,szName,value,NULL);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyFloat
+ASSIMP_API void aiSetImportPropertyFloat(const char* szName, float value)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ SetGenericProperty<float>(gFloatProperties,szName,value,NULL);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyString
+ASSIMP_API void aiSetImportPropertyString(const char* szName,
+ const C_STRUCT aiString* st)
+{
+ if (!st) {
+ return;
+ }
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+#ifdef AI_C_THREADSAFE
+ boost::mutex::scoped_lock lock(gMutex);
+#endif
+ SetGenericProperty<std::string>(gStringProperties,szName,
+ std::string( st->data ),NULL);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Rotation matrix to quaternion
+ASSIMP_API void aiCreateQuaternionFromMatrix(aiQuaternion* quat,const aiMatrix3x3* mat)
+{
+ ai_assert(NULL != quat && NULL != mat);
+ *quat = aiQuaternion(*mat);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix decomposition
+ASSIMP_API void aiDecomposeMatrix(const aiMatrix4x4* mat,aiVector3D* scaling,
+ aiQuaternion* rotation,
+ aiVector3D* position)
+{
+ ai_assert(NULL != rotation && NULL != position && NULL != scaling && NULL != mat);
+ mat->Decompose(*scaling,*rotation,*position);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix transpose
+ASSIMP_API void aiTransposeMatrix3(aiMatrix3x3* mat)
+{
+ ai_assert(NULL != mat);
+ mat->Transpose();
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiTransposeMatrix4(aiMatrix4x4* mat)
+{
+ ai_assert(NULL != mat);
+ mat->Transpose();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Vector transformation
+ASSIMP_API void aiTransformVecByMatrix3(C_STRUCT aiVector3D* vec,
+ const C_STRUCT aiMatrix3x3* mat)
+{
+ ai_assert(NULL != mat && NULL != vec);
+ *vec *= (*mat);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiTransformVecByMatrix4(C_STRUCT aiVector3D* vec,
+ const C_STRUCT aiMatrix4x4* mat)
+{
+ ai_assert(NULL != mat && NULL != vec);
+ *vec *= (*mat);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix multiplication
+ASSIMP_API void aiMultiplyMatrix4(
+ C_STRUCT aiMatrix4x4* dst,
+ const C_STRUCT aiMatrix4x4* src)
+{
+ ai_assert(NULL != dst && NULL != src);
+ *dst = (*dst) * (*src);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiMultiplyMatrix3(
+ C_STRUCT aiMatrix3x3* dst,
+ const C_STRUCT aiMatrix3x3* src)
+{
+ ai_assert(NULL != dst && NULL != src);
+ *dst = (*dst) * (*src);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix identity
+ASSIMP_API void aiIdentityMatrix3(
+ C_STRUCT aiMatrix3x3* mat)
+{
+ ai_assert(NULL != mat);
+ *mat = aiMatrix3x3();
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiIdentityMatrix4(
+ C_STRUCT aiMatrix4x4* mat)
+{
+ ai_assert(NULL != mat);
+ *mat = aiMatrix4x4();
+}
+
+
diff --git a/3rdparty/assimp/code/AssimpPCH.cpp b/3rdparty/assimp/code/AssimpPCH.cpp
new file mode 100644
index 000000000..5ccfb4efe
--- /dev/null
+++ b/3rdparty/assimp/code/AssimpPCH.cpp
@@ -0,0 +1,70 @@
+
+// Actually just a dummy, used by the compiler to build the precompiled header.
+
+#include "AssimpPCH.h"
+#include "./../include/aiVersion.h"
+
+// --------------------------------------------------------------------------------
+// Legal information string - dont't remove from image!
+static const char* LEGAL_INFORMATION =
+
+"Open Asset Import Library (Assimp).\n"
+"A free C/C++ library to import various 3D file formats into applications\n\n"
+
+"(c) ASSIMP Development Team, 2008-2010\n"
+"License: 3-clause BSD license\n"
+"Website: http://assimp.sourceforge.net\n"
+;
+
+// ------------------------------------------------------------------------------------------------
+// Get legal string
+ASSIMP_API const char* aiGetLegalString () {
+ return LEGAL_INFORMATION;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get Assimp minor version
+ASSIMP_API unsigned int aiGetVersionMinor () {
+ return 1;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get Assimp major version
+ASSIMP_API unsigned int aiGetVersionMajor () {
+ return 1;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get flags used for compilation
+ASSIMP_API unsigned int aiGetCompileFlags () {
+
+ unsigned int flags = 0;
+
+#ifdef ASSIMP_BUILD_BOOST_WORKAROUND
+ flags |= ASSIMP_CFLAGS_NOBOOST;
+#endif
+#ifdef ASSIMP_BUILD_SINGLETHREADED
+ flags |= ASSIMP_CFLAGS_SINGLETHREADED;
+#endif
+#ifdef ASSIMP_BUILD_DEBUG
+ flags |= ASSIMP_CFLAGS_DEBUG;
+#endif
+#ifdef ASSIMP_BUILD_DLL_EXPORT
+ flags |= ASSIMP_CFLAGS_SHARED;
+#endif
+#ifdef _STLPORT_VERSION
+ flags |= ASSIMP_CFLAGS_STLPORT;
+#endif
+
+ return flags;
+}
+
+// include current build revision, which is even updated from time to time -- :-)
+#include "../revision.h"
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API unsigned int aiGetVersionRevision ()
+{
+ return SVNRevision;
+}
+
diff --git a/3rdparty/assimp/code/AssimpPCH.h b/3rdparty/assimp/code/AssimpPCH.h
new file mode 100644
index 000000000..779f681cc
--- /dev/null
+++ b/3rdparty/assimp/code/AssimpPCH.h
@@ -0,0 +1,149 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 AssimpPCH.h
+ * PCH master include. Every unit in Assimp has to include it.
+ */
+
+#ifndef ASSIMP_PCH_INCLUDED
+#define ASSIMP_PCH_INCLUDED
+#define ASSIMP_INTERNAL_BUILD
+
+// ----------------------------------------------------------------------------------------
+/* General compile config taken from aiDefines.h. It is important that the user compiles
+ * using exactly the same settings in aiDefines.h. Settings in AssimpPCH.h may differ,
+ * they won't affect the public API.
+ */
+#include "../include/aiDefines.h"
+
+/* Include our stdint.h replacement header for MSVC, take the global header for gcc/mingw
+ */
+#ifdef _MSC_VER
+# include "pstdint.h"
+#else
+# include <stdint.h>
+#endif
+
+/* Undefine the min/max macros defined by some platform headers (namely Windows.h) to
+ * avoid obvious conflicts with std::min() and std::max().
+ */
+#undef min
+#undef max
+
+/* Concatenate two tokens after evaluating them
+ */
+#define _AI_CONCAT(a,b) a ## b
+#define AI_CONCAT(a,b) _AI_CONCAT(a,b)
+
+/* Helper macro to set a pointer to NULL in debug builds
+ */
+#if (defined _DEBUG)
+# define AI_DEBUG_INVALIDATE_PTR(x) x = NULL;
+#else
+# define AI_DEBUG_INVALIDATE_PTR(x)
+#endif
+
+/* Beginning with MSVC8 some C string manipulation functions are mapped to their _safe_
+ * counterparts (e.g. _itoa_s). This avoids a lot of trouble with deprecation warnings.
+ */
+#if _MSC_VER >= 1400 && !(defined _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
+# define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
+#endif
+
+/* size_t to unsigned int, possible loss of data. The compiler is right with his warning
+ * but this loss of data won't be a problem for us. So shut up, little boy.
+ */
+#ifdef _MSC_VER
+# pragma warning (disable : 4267)
+#endif
+
+// ----------------------------------------------------------------------------------------
+/* Actually that's not required for MSVC. It is included somewhere in the deeper parts of
+ * the MSVC STL but it's necessary for proper build with STLport.
+ */
+#include <ctype.h>
+
+// Runtime/STL headers
+#include <vector>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+#include <sstream>
+#include <iomanip>
+#include <cassert>
+#include <stack>
+#include <queue>
+#include <iostream>
+#include <algorithm>
+#include <numeric>
+#include <new>
+#include <cstdio>
+
+// Boost headers
+#include <boost/pointer_cast.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/scoped_array.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/shared_array.hpp>
+//#include <boost/make_shared.hpp>
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
+#include <boost/static_assert.hpp>
+#include <boost/lexical_cast.hpp>
+
+// Public ASSIMP headers
+#include "../include/DefaultLogger.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
+#include "../include/aiScene.h"
+#include "../include/aiPostProcess.h"
+#include "../include/assimp.hpp"
+
+// Internal utility headers
+#include "BaseImporter.h"
+#include "MaterialSystem.h"
+#include "StringComparison.h"
+#include "StreamReader.h"
+#include "qnan.h"
+
+
+#endif // !! ASSIMP_PCH_INCLUDED
diff --git a/3rdparty/assimp/code/B3DImporter.cpp b/3rdparty/assimp/code/B3DImporter.cpp
new file mode 100644
index 000000000..0609c2752
--- /dev/null
+++ b/3rdparty/assimp/code/B3DImporter.cpp
@@ -0,0 +1,672 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 B3DImporter.cpp
+ * @brief Implementation of the b3d importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
+
+// internal headers
+#include "B3DImporter.h"
+#include "TextureTransform.h"
+#include "ConvertToLHProcess.h"
+
+using namespace Assimp;
+using namespace std;
+
+// (fixme, Aramis) quick workaround to get rid of all those signed to unsigned warnings
+#ifdef _MSC_VER
+# pragma warning (disable: 4018)
+#endif
+
+//#define DEBUG_B3D
+
+// ------------------------------------------------------------------------------------------------
+bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /* pIOHandler */, bool /* checkSig */) const{
+
+ size_t pos=pFile.find_last_of( '.' );
+ if ( pos==string::npos ) return false;
+
+ string ext=pFile.substr( pos+1 );
+ if ( ext.size()!=3 ) return false;
+
+ return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D');
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::GetExtensionList( std::set<std::string>& extensions ){
+ extensions.insert("b3d");
+}
+
+#ifdef DEBUG_B3D
+ extern "C"{ void _stdcall AllocConsole(); }
+#endif
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){
+
+#ifdef DEBUG_B3D
+ AllocConsole();
+ freopen( "conin$","r",stdin );
+ freopen( "conout$","w",stdout );
+ freopen( "conout$","w",stderr );
+ cout<<"Hello world from the B3DImporter!"<<endl;
+#endif
+
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open B3D file " + pFile + ".");
+
+ // check whether the .b3d file is large enough to contain
+ // at least one chunk.
+ size_t fileSize = file->FileSize();
+ if ( fileSize<8 ) throw DeadlyImportError( "B3D File is too small.");
+
+ _pos=0;
+ _buf.resize( fileSize );
+ file->Read( &_buf[0],1,fileSize );
+ _stack.clear();
+
+ ReadBB3D( pScene );
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::Oops(){
+ throw DeadlyImportError( "B3D Importer - INTERNAL ERROR" );
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::Fail( string str ){
+#ifdef DEBUG_B3D
+ cout<<"Error in B3D file data: "<<str<<endl;
+#endif
+ throw DeadlyImportError( "B3D Importer - error in B3D file data: "+str );
+}
+
+// ------------------------------------------------------------------------------------------------
+int B3DImporter::ReadByte(){
+ if ( _pos<_buf.size() ) return _buf[_pos++];
+ Fail( "EOF" );
+ return 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+int B3DImporter::ReadInt(){
+ if ( _pos+4<=_buf.size() ){
+ int n=*(int*)&_buf[_pos];
+ _pos+=4;
+ return n;
+ }
+ Fail( "EOF" );
+ return 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+float B3DImporter::ReadFloat(){
+ if ( _pos+4<=_buf.size() ){
+ float n=*(float*)&_buf[_pos];
+ _pos+=4;
+ return n;
+ }
+ Fail( "EOF" );
+ return 0.0f;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector2D B3DImporter::ReadVec2(){
+ float x=ReadFloat();
+ float y=ReadFloat();
+ return aiVector2D( x,y );
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector3D B3DImporter::ReadVec3(){
+ float x=ReadFloat();
+ float y=ReadFloat();
+ float z=ReadFloat();
+ return aiVector3D( x,y,z );
+}
+
+// ------------------------------------------------------------------------------------------------
+aiQuaternion B3DImporter::ReadQuat(){
+ // (aramis_acg) Fix to adapt the loader to changed quat orientation
+ float w=-ReadFloat();
+ float x=ReadFloat();
+ float y=ReadFloat();
+ float z=ReadFloat();
+ return aiQuaternion( w,x,y,z );
+}
+
+// ------------------------------------------------------------------------------------------------
+string B3DImporter::ReadString(){
+ string str;
+ while ( _pos<_buf.size() ){
+ char c=(char)ReadByte();
+ if ( !c ) return str;
+ str+=c;
+ }
+ Fail( "EOF" );
+ return string();
+}
+
+// ------------------------------------------------------------------------------------------------
+string B3DImporter::ReadChunk(){
+ string tag;
+ for ( int i=0;i<4;++i ){
+ tag+=char( ReadByte() );
+ }
+#ifdef DEBUG_B3D
+// cout<<"ReadChunk:"<<tag<<endl;
+#endif
+ unsigned sz=(unsigned)ReadInt();
+ _stack.push_back( _pos+sz );
+ return tag;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ExitChunk(){
+ _pos=_stack.back();
+ _stack.pop_back();
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned B3DImporter::ChunkSize(){
+ return _stack.back()-_pos;
+}
+// ------------------------------------------------------------------------------------------------
+
+template<class T>
+T *B3DImporter::to_array( const vector<T> &v ){
+ if ( !v.size() ) return 0;
+ T *p=new T[v.size()];
+ for ( size_t i=0;i<v.size();++i ){
+ p[i]=v[i];
+ }
+ return p;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadTEXS(){
+ while ( ChunkSize() ){
+ string name=ReadString();
+ /*int flags=*/ReadInt();
+ /*int blend=*/ReadInt();
+ /*aiVector2D pos=*/ReadVec2();
+ /*aiVector2D scale=*/ReadVec2();
+ /*float rot=*/ReadFloat();
+
+ _textures.push_back( name );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBRUS(){
+ int n_texs=ReadInt();
+ if ( n_texs<0 || n_texs>8 ){
+ Fail( "Bad texture count" );
+ }
+ while ( ChunkSize() ){
+ string name=ReadString();
+ aiVector3D color=ReadVec3();
+ float alpha=ReadFloat();
+ float shiny=ReadFloat();
+ /*int blend=**/ReadInt();
+ int fx=ReadInt();
+
+ MaterialHelper *mat=new MaterialHelper;
+ _materials.push_back( mat );
+
+ // Name
+ aiString ainame( name );
+ mat->AddProperty( &ainame,AI_MATKEY_NAME );
+
+ // Diffuse color
+ mat->AddProperty( &color,1,AI_MATKEY_COLOR_DIFFUSE );
+
+ // Opacity
+ mat->AddProperty( &alpha,1,AI_MATKEY_OPACITY );
+
+ // Specular color
+ aiColor3D speccolor( shiny,shiny,shiny );
+ mat->AddProperty( &speccolor,1,AI_MATKEY_COLOR_SPECULAR );
+
+ // Specular power
+ float specpow=shiny*128;
+ mat->AddProperty( &specpow,1,AI_MATKEY_SHININESS );
+
+ // Double sided
+ if ( fx & 0x10 ){
+ int i=1;
+ mat->AddProperty( &i,1,AI_MATKEY_TWOSIDED );
+ }
+
+ //Textures
+ for ( int i=0;i<n_texs;++i ){
+ int texid=ReadInt();
+ if ( texid<-1 || (texid>=0 && texid>=static_cast<int>(_textures.size())) ){
+ Fail( "Bad texture id" );
+ }
+ if ( i==0 && texid>=0 ){
+ aiString texname( _textures[texid] );
+ mat->AddProperty( &texname,AI_MATKEY_TEXTURE_DIFFUSE(0) );
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadVRTS(){
+ _vflags=ReadInt();
+ _tcsets=ReadInt();
+ _tcsize=ReadInt();
+ if ( _tcsets<0 || _tcsets>4 || _tcsize<0 || _tcsize>4 ){
+ Fail( "Bad texcoord data" );
+ }
+
+ int sz=12+(_vflags&1?12:0)+(_vflags&2?16:0)+(_tcsets*_tcsize*4);
+ int n_verts=ChunkSize()/sz;
+
+ int v0=_vertices.size();
+ _vertices.resize( v0+n_verts );
+
+ for ( int i=0;i<n_verts;++i ){
+ Vertex &v=_vertices[v0+i];
+
+ memset( v.bones,0,sizeof(v.bones) );
+ memset( v.weights,0,sizeof(v.weights) );
+
+ v.vertex=ReadVec3();
+
+ if ( _vflags & 1 ) v.normal=ReadVec3();
+
+ if ( _vflags & 2 ) ReadQuat(); //skip v 4bytes...
+
+ for ( int i=0;i<_tcsets;++i ){
+ float t[4]={0,0,0,0};
+ for ( int j=0;j<_tcsize;++j ){
+ t[j]=ReadFloat();
+ }
+ t[1]=1-t[1];
+ if ( !i ) v.texcoords=aiVector3D( t[0],t[1],t[2] );
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadTRIS( int v0 ){
+ int matid=ReadInt();
+ if ( matid==-1 ){
+ matid=0;
+ }else if ( matid<0 || matid>=(int)_materials.size() ){
+#ifdef DEBUG_B3D
+ cout<<"material id="<<matid<<endl;
+#endif
+ Fail( "Bad material id" );
+ }
+
+ aiMesh *mesh=new aiMesh;
+ _meshes.push_back( mesh );
+
+ mesh->mMaterialIndex=matid;
+ mesh->mNumFaces=0;
+ mesh->mPrimitiveTypes=aiPrimitiveType_TRIANGLE;
+
+ int n_tris=ChunkSize()/12;
+ aiFace *face=mesh->mFaces=new aiFace[n_tris];
+
+ for ( int i=0;i<n_tris;++i ){
+ int i0=ReadInt()+v0;
+ int i1=ReadInt()+v0;
+ int i2=ReadInt()+v0;
+ if ( i0<0 || i0>=(int)_vertices.size() || i1<0 || i1>=(int)_vertices.size() || i2<0 || i2>=(int)_vertices.size() ){
+#ifdef DEBUG_B3D
+ cout<<"Bad triangle index: i0="<<i0<<", i1="<<i1<<", i2="<<i2<<endl;
+#endif
+ Fail( "Bad triangle index" );
+ continue;
+ }
+ face->mNumIndices=3;
+ face->mIndices=new unsigned[3];
+ face->mIndices[0]=i0;
+ face->mIndices[1]=i1;
+ face->mIndices[2]=i2;
+ ++mesh->mNumFaces;
+ ++face;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadMESH(){
+ /*int matid=*/ReadInt();
+
+ int v0=_vertices.size();
+
+ while ( ChunkSize() ){
+ string t=ReadChunk();
+ if ( t=="VRTS" ){
+ ReadVRTS();
+ }else if ( t=="TRIS" ){
+ ReadTRIS( v0 );
+ }
+ ExitChunk();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBONE( int id ){
+ while ( ChunkSize() ){
+ int vertex=ReadInt();
+ float weight=ReadFloat();
+ if ( vertex<0 || vertex>=(int)_vertices.size() ){
+ Fail( "Bad vertex index" );
+ }
+
+ Vertex &v=_vertices[vertex];
+ int i;
+ for ( i=0;i<4;++i ){
+ if ( !v.weights[i] ){
+ v.bones[i]=id;
+ v.weights[i]=weight;
+ break;
+ }
+ }
+#ifdef DEBUG_B3D
+ if ( i==4 ){
+ cout<<"Too many bone weights"<<endl;
+ }
+#endif
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadKEYS( aiNodeAnim *nodeAnim ){
+ vector<aiVectorKey> trans,scale;
+ vector<aiQuatKey> rot;
+ int flags=ReadInt();
+ while ( ChunkSize() ){
+ int frame=ReadInt();
+ if ( flags & 1 ){
+ trans.push_back( aiVectorKey( frame,ReadVec3() ) );
+ }
+ if ( flags & 2 ){
+ scale.push_back( aiVectorKey( frame,ReadVec3() ) );
+ }
+ if ( flags & 4 ){
+ rot.push_back( aiQuatKey( frame,ReadQuat() ) );
+ }
+ }
+
+ if ( flags & 1 ){
+ nodeAnim->mNumPositionKeys=trans.size();
+ nodeAnim->mPositionKeys=to_array( trans );
+ }
+
+ if ( flags & 2 ){
+ nodeAnim->mNumScalingKeys=scale.size();
+ nodeAnim->mScalingKeys=to_array( scale );
+ }
+
+ if ( flags & 4 ){
+ nodeAnim->mNumRotationKeys=rot.size();
+ nodeAnim->mRotationKeys=to_array( rot );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadANIM(){
+ /*int flags=*/ReadInt();
+ int frames=ReadInt();
+ float fps=ReadFloat();
+
+ aiAnimation *anim=new aiAnimation;
+ _animations.push_back( anim );
+
+ anim->mDuration=frames;
+ anim->mTicksPerSecond=fps;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode *B3DImporter::ReadNODE( aiNode *parent ){
+
+ string name=ReadString();
+ aiVector3D t=ReadVec3();
+ aiVector3D s=ReadVec3();
+ aiQuaternion r=ReadQuat();
+
+ aiMatrix4x4 trans,scale,rot;
+
+ aiMatrix4x4::Translation( t,trans );
+ aiMatrix4x4::Scaling( s,scale );
+ rot=aiMatrix4x4( r.GetMatrix() );
+
+ aiMatrix4x4 tform=trans * rot * scale;
+
+ int nodeid=_nodes.size();
+
+ aiNode *node=new aiNode( name );
+ _nodes.push_back( node );
+
+ node->mParent=parent;
+ node->mTransformation=tform;
+
+ aiNodeAnim *nodeAnim=0;
+ vector<unsigned> meshes;
+ vector<aiNode*> children;
+
+ while ( ChunkSize() ){
+ string t=ReadChunk();
+ if ( t=="MESH" ){
+ int n=_meshes.size();
+ ReadMESH();
+ for ( int i=n;i<(int)_meshes.size();++i ){
+ meshes.push_back( i );
+ }
+ }else if ( t=="BONE" ){
+ ReadBONE( nodeid );
+ }else if ( t=="ANIM" ){
+ ReadANIM();
+ }else if ( t=="KEYS" ){
+ if ( !nodeAnim ){
+ nodeAnim=new aiNodeAnim;
+ _nodeAnims.push_back( nodeAnim );
+ nodeAnim->mNodeName=node->mName;
+ }
+ ReadKEYS( nodeAnim );
+ }else if ( t=="NODE" ){
+ aiNode *child=ReadNODE( node );
+ children.push_back( child );
+ }
+ ExitChunk();
+ }
+
+ node->mNumMeshes=meshes.size();
+ node->mMeshes=to_array( meshes );
+
+ node->mNumChildren=children.size();
+ node->mChildren=to_array( children );
+
+ return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+void B3DImporter::ReadBB3D( aiScene *scene ){
+
+ _textures.clear();
+ _materials.size();
+
+ _vertices.clear();
+ _meshes.clear();
+
+ _nodes.clear();
+ _nodeAnims.clear();
+ _animations.clear();
+
+ string t=ReadChunk();
+ if ( t=="BB3D" ){
+ int version=ReadInt();
+
+ if (!DefaultLogger::isNullLogger()) {
+ char dmp[128];
+ sprintf(dmp,"B3D file format version: %i",version);
+ DefaultLogger::get()->info(dmp);
+ }
+
+ while ( ChunkSize() ){
+ string t=ReadChunk();
+ if ( t=="TEXS" ){
+ ReadTEXS();
+ }else if ( t=="BRUS" ){
+ ReadBRUS();
+ }else if ( t=="NODE" ){
+ ReadNODE( 0 );
+ }
+ ExitChunk();
+ }
+ }
+ ExitChunk();
+
+ if ( !_nodes.size() ) Fail( "No nodes" );
+
+ if ( !_meshes.size() ) Fail( "No meshes" );
+
+ //Fix nodes/meshes/bones
+ for (size_t i=0;i<_nodes.size();++i ){
+ aiNode *node=_nodes[i];
+
+ for ( size_t j=0;j<node->mNumMeshes;++j ){
+ aiMesh *mesh=_meshes[node->mMeshes[j]];
+
+ int n_tris=mesh->mNumFaces;
+ int n_verts=mesh->mNumVertices=n_tris * 3;
+
+ aiVector3D *mv=mesh->mVertices=new aiVector3D[ n_verts ],*mn=0,*mc=0;
+ if ( _vflags & 1 ) mn=mesh->mNormals=new aiVector3D[ n_verts ];
+ if ( _tcsets ) mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ];
+
+ aiFace *face=mesh->mFaces;
+
+ vector< vector<aiVertexWeight> > vweights( _nodes.size() );
+
+ for ( int i=0;i<n_verts;i+=3 ){
+ for ( int j=0;j<3;++j ){
+ Vertex &v=_vertices[face->mIndices[j]];
+
+ *mv++=v.vertex;
+ if ( mn ) *mn++=v.normal;
+ if ( mc ) *mc++=v.texcoords;
+
+ face->mIndices[j]=i+j;
+
+ for ( int k=0;k<4;++k ){
+ if ( !v.weights[k] ) break;
+
+ int bone=v.bones[k];
+ float weight=v.weights[k];
+
+ vweights[bone].push_back( aiVertexWeight(i+j,weight) );
+ }
+ }
+ ++face;
+ }
+
+ vector<aiBone*> bones;
+ for (size_t i=0;i<vweights.size();++i ){
+ vector<aiVertexWeight> &weights=vweights[i];
+ if ( !weights.size() ) continue;
+
+ aiBone *bone=new aiBone;
+ bones.push_back( bone );
+
+ aiNode *bnode=_nodes[i];
+
+ bone->mName=bnode->mName;
+ bone->mNumWeights=weights.size();
+ bone->mWeights=to_array( weights );
+
+ aiMatrix4x4 mat=bnode->mTransformation;
+ while ( bnode->mParent ){
+ bnode=bnode->mParent;
+ mat=bnode->mTransformation * mat;
+ }
+ bone->mOffsetMatrix=mat.Inverse();
+ }
+ mesh->mNumBones=bones.size();
+ mesh->mBones=to_array( bones );
+ }
+ }
+
+ //nodes
+ scene->mRootNode=_nodes[0];
+
+ //material
+ if ( !_materials.size() ){
+ _materials.push_back( new MaterialHelper );
+ }
+ scene->mNumMaterials=_materials.size();
+ scene->mMaterials=to_array( _materials );
+
+ //meshes
+ scene->mNumMeshes=_meshes.size();
+ scene->mMeshes=to_array( _meshes );
+
+ //animations
+ if ( _animations.size()==1 && _nodeAnims.size() ){
+
+ aiAnimation *anim=_animations.back();
+ anim->mNumChannels=_nodeAnims.size();
+ anim->mChannels=to_array( _nodeAnims );
+
+ scene->mNumAnimations=_animations.size();
+ scene->mAnimations=to_array( _animations );
+ }
+
+ // convert to RH
+ MakeLeftHandedProcess makeleft;
+ makeleft.Execute( scene );
+
+ FlipWindingOrderProcess flip;
+ flip.Execute( scene );
+}
+
+#endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER
diff --git a/3rdparty/assimp/code/B3DImporter.h b/3rdparty/assimp/code/B3DImporter.h
new file mode 100644
index 000000000..302e33dd5
--- /dev/null
+++ b/3rdparty/assimp/code/B3DImporter.h
@@ -0,0 +1,126 @@
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 .b3d importer class. */
+
+#ifndef AI_B3DIMPORTER_H_INC
+#define AI_B3DIMPORTER_H_INC
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiMaterial.h"
+
+#include <string>
+#include <vector>
+
+namespace Assimp{
+
+class B3DImporter : public BaseImporter{
+public:
+
+ virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+
+protected:
+
+ virtual void GetExtensionList(std::set<std::string>& extensions);
+ virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+private:
+
+ int ReadByte();
+ int ReadInt();
+ float ReadFloat();
+ aiVector2D ReadVec2();
+ aiVector3D ReadVec3();
+ aiQuaternion ReadQuat();
+ std::string ReadString();
+ std::string ReadChunk();
+ void ExitChunk();
+ unsigned ChunkSize();
+
+ template<class T>
+ T *to_array( const std::vector<T> &v );
+
+ struct Vertex{
+ aiVector3D vertex;
+ aiVector3D normal;
+ aiVector3D texcoords;
+ unsigned char bones[4];
+ float weights[4];
+ };
+
+ void Oops();
+ void Fail( std::string str );
+
+ void ReadTEXS();
+ void ReadBRUS();
+
+ void ReadVRTS();
+ void ReadTRIS( int v0 );
+ void ReadMESH();
+ void ReadBONE( int id );
+ void ReadKEYS( aiNodeAnim *nodeAnim );
+ void ReadANIM();
+
+ aiNode *ReadNODE( aiNode *parent );
+
+ void ReadBB3D( aiScene *scene );
+
+ unsigned _pos;
+// unsigned _size;
+ std::vector<unsigned char> _buf;
+ std::vector<unsigned> _stack;
+
+ std::vector<std::string> _textures;
+ std::vector<aiMaterial*> _materials;
+
+ int _vflags,_tcsets,_tcsize;
+ std::vector<Vertex> _vertices;
+
+ std::vector<aiNode*> _nodes;
+ std::vector<aiMesh*> _meshes;
+ std::vector<aiNodeAnim*> _nodeAnims;
+ std::vector<aiAnimation*> _animations;
+};
+
+}
+
+#endif
diff --git a/3rdparty/assimp/code/BVHLoader.cpp b/3rdparty/assimp/code/BVHLoader.cpp
new file mode 100644
index 000000000..801714677
--- /dev/null
+++ b/3rdparty/assimp/code/BVHLoader.cpp
@@ -0,0 +1,505 @@
+/** Implementation of the BVH loader */
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
+
+#include "BVHLoader.h"
+#include "fast_atof.h"
+#include "SkeletonMeshBuilder.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BVHLoader::BVHLoader()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+BVHLoader::~BVHLoader()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
+{
+ // check file extension
+ const std::string extension = GetExtension(pFile);
+
+ if ( extension == "bvh")
+ return true;
+
+ if ((!extension.length() || cs) && pIOHandler) {
+ const char* tokens[] = {"HIERARCHY"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ mFileName = pFile;
+
+ // read file into memory
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open file " + pFile + ".");
+
+ size_t fileSize = file->FileSize();
+ if ( fileSize == 0)
+ throw DeadlyImportError( "File is too small.");
+
+ mBuffer.resize( fileSize);
+ file->Read( &mBuffer.front(), 1, fileSize);
+
+ // start reading
+ mReader = mBuffer.begin();
+ mLine = 1;
+ ReadStructure( pScene);
+
+ // build a dummy mesh for the skeleton so that we see something at least
+ SkeletonMeshBuilder meshBuilder( pScene);
+
+ // construct an animation from all the motion data we read
+ CreateAnimation( pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the file
+void BVHLoader::ReadStructure( aiScene* pScene)
+{
+ // first comes hierarchy
+ std::string header = GetNextToken();
+ if ( header != "HIERARCHY")
+ ThrowException( "Expected header string \"HIERARCHY\".");
+ ReadHierarchy( pScene);
+
+ // then comes the motion data
+ std::string motion = GetNextToken();
+ if ( motion != "MOTION")
+ ThrowException( "Expected beginning of motion data \"MOTION\".");
+ ReadMotion( pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the hierarchy
+void BVHLoader::ReadHierarchy( aiScene* pScene)
+{
+ std::string root = GetNextToken();
+ if ( root != "ROOT")
+ ThrowException( "Expected root node \"ROOT\".");
+
+ // Go read the hierarchy from here
+ pScene->mRootNode = ReadNode();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a node and recursively its childs and returns the created node;
+aiNode* BVHLoader::ReadNode()
+{
+ // first token is name
+ std::string nodeName = GetNextToken();
+ if ( nodeName.empty() || nodeName == "{")
+ ThrowException( boost::str( boost::format( "Expected node name, but found \"%s\".") % nodeName));
+
+ // then an opening brace should follow
+ std::string openBrace = GetNextToken();
+ if ( openBrace != "{")
+ ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace));
+
+ // Create a node
+ aiNode* node = new aiNode( nodeName);
+ std::vector<aiNode*> childNodes;
+
+ // and create an bone entry for it
+ mNodes.push_back( Node( node));
+ Node& internNode = mNodes.back();
+
+ // now read the node's contents
+ while ( 1)
+ {
+ std::string token = GetNextToken();
+
+ // node offset to parent node
+ if ( token == "OFFSET")
+ ReadNodeOffset( node);
+ else if ( token == "CHANNELS")
+ ReadNodeChannels( internNode);
+ else if ( token == "JOINT")
+ {
+ // child node follows
+ aiNode* child = ReadNode();
+ child->mParent = node;
+ childNodes.push_back( child);
+ }
+ else if ( token == "End")
+ {
+ // The real symbol is "End Site". Second part comes in a separate token
+ std::string siteToken = GetNextToken();
+ if ( siteToken != "Site")
+ ThrowException( boost::str( boost::format( "Expected \"End Site\" keyword, but found \"%s %s\".") % token % siteToken));
+
+ aiNode* child = ReadEndSite( nodeName);
+ child->mParent = node;
+ childNodes.push_back( child);
+ }
+ else if ( token == "}")
+ {
+ // we're done with that part of the hierarchy
+ break;
+ } else
+ {
+ // everything else is a parse error
+ ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token));
+ }
+ }
+
+ // add the child nodes if there are any
+ if ( childNodes.size() > 0)
+ {
+ node->mNumChildren = childNodes.size();
+ node->mChildren = new aiNode*[node->mNumChildren];
+ std::copy( childNodes.begin(), childNodes.end(), node->mChildren);
+ }
+
+ // and return the sub-hierarchy we built here
+ return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an end node and returns the created node.
+aiNode* BVHLoader::ReadEndSite( const std::string& pParentName)
+{
+ // check opening brace
+ std::string openBrace = GetNextToken();
+ if ( openBrace != "{")
+ ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace));
+
+ // Create a node
+ aiNode* node = new aiNode( "EndSite_" + pParentName);
+
+ // now read the node's contents. Only possible entry is "OFFSET"
+ while ( 1)
+ {
+ std::string token = GetNextToken();
+
+ // end node's offset
+ if ( token == "OFFSET")
+ {
+ ReadNodeOffset( node);
+ }
+ else if ( token == "}")
+ {
+ // we're done with the end node
+ break;
+ } else
+ {
+ // everything else is a parse error
+ ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token));
+ }
+ }
+
+ // and return the sub-hierarchy we built here
+ return node;
+}
+// ------------------------------------------------------------------------------------------------
+// Reads a node offset for the given node
+void BVHLoader::ReadNodeOffset( aiNode* pNode)
+{
+ // Offset consists of three floats to read
+ aiVector3D offset;
+ offset.x = GetNextTokenAsFloat();
+ offset.y = GetNextTokenAsFloat();
+ offset.z = GetNextTokenAsFloat();
+
+ // build a transformation matrix from it
+ pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, 0.0f, 1.0f, 0.0f, offset.y,
+ 0.0f, 0.0f, 1.0f, offset.z, 0.0f, 0.0f, 0.0f, 1.0f);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the animation channels for the given node
+void BVHLoader::ReadNodeChannels( BVHLoader::Node& pNode)
+{
+ // number of channels. Use the float reader because we're lazy
+ float numChannelsFloat = GetNextTokenAsFloat();
+ unsigned int numChannels = (unsigned int) numChannelsFloat;
+
+ for ( unsigned int a = 0; a < numChannels; a++)
+ {
+ std::string channelToken = GetNextToken();
+
+ if ( channelToken == "Xposition")
+ pNode.mChannels.push_back( Channel_PositionX);
+ else if ( channelToken == "Yposition")
+ pNode.mChannels.push_back( Channel_PositionY);
+ else if ( channelToken == "Zposition")
+ pNode.mChannels.push_back( Channel_PositionZ);
+ else if ( channelToken == "Xrotation")
+ pNode.mChannels.push_back( Channel_RotationX);
+ else if ( channelToken == "Yrotation")
+ pNode.mChannels.push_back( Channel_RotationY);
+ else if ( channelToken == "Zrotation")
+ pNode.mChannels.push_back( Channel_RotationZ);
+ else
+ ThrowException( boost::str( boost::format( "Invalid channel specifier \"%s\".") % channelToken));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the motion data
+void BVHLoader::ReadMotion( aiScene* /* pScene */)
+{
+ // Read number of frames
+ std::string tokenFrames = GetNextToken();
+ if ( tokenFrames != "Frames:")
+ ThrowException( boost::str( boost::format( "Expected frame count \"Frames:\", but found \"%s\".") % tokenFrames));
+
+ float numFramesFloat = GetNextTokenAsFloat();
+ mAnimNumFrames = (unsigned int) numFramesFloat;
+
+ // Read frame duration
+ std::string tokenDuration1 = GetNextToken();
+ std::string tokenDuration2 = GetNextToken();
+ if ( tokenDuration1 != "Frame" || tokenDuration2 != "Time:")
+ ThrowException( boost::str( boost::format( "Expected frame duration \"Frame Time:\", but found \"%s %s\".") % tokenDuration1 % tokenDuration2));
+
+ mAnimTickDuration = GetNextTokenAsFloat();
+
+ // resize value vectors for each node
+ for ( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
+ it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames);
+
+ // now read all the data and store it in the corresponding node's value vector
+ for ( unsigned int frame = 0; frame < mAnimNumFrames; ++frame)
+ {
+ // on each line read the values for all nodes
+ for ( std::vector<Node>::iterator it = mNodes.begin(); it != mNodes.end(); ++it)
+ {
+ // get as many values as the node has channels
+ for ( unsigned int c = 0; c < it->mChannels.size(); ++c)
+ it->mChannelValues.push_back( GetNextTokenAsFloat());
+ }
+
+ // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Retrieves the next token
+std::string BVHLoader::GetNextToken()
+{
+ // skip any preceeding whitespace
+ while ( mReader != mBuffer.end())
+ {
+ if ( !isspace( *mReader))
+ break;
+
+ // count lines
+ if ( *mReader == '\n')
+ mLine++;
+
+ ++mReader;
+ }
+
+ // collect all chars till the next whitespace. BVH is easy in respect to that.
+ std::string token;
+ while ( mReader != mBuffer.end())
+ {
+ if ( isspace( *mReader))
+ break;
+
+ token.push_back( *mReader);
+ ++mReader;
+
+ // little extra logic to make sure braces are counted correctly
+ if ( token == "{" || token == "}")
+ break;
+ }
+
+ // empty token means end of file, which is just fine
+ return token;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the next token as a float
+float BVHLoader::GetNextTokenAsFloat()
+{
+ std::string token = GetNextToken();
+ if ( token.empty())
+ ThrowException( "Unexpected end of file while trying to read a float");
+
+ // check if the float is valid by testing if the atof() function consumed every char of the token
+ const char* ctoken = token.c_str();
+ float result = 0.0f;
+ ctoken = fast_atof_move( ctoken, result);
+
+ if ( ctoken != token.c_str() + token.length())
+ ThrowException( boost::str( boost::format( "Expected a floating point number, but found \"%s\".") % token));
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+void BVHLoader::ThrowException( const std::string& pError)
+{
+ throw DeadlyImportError( boost::str( boost::format( "%s:%d - %s") % mFileName % mLine % pError));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs an animation for the motion data and stores it in the given scene
+void BVHLoader::CreateAnimation( aiScene* pScene)
+{
+ // create the animation
+ pScene->mNumAnimations = 1;
+ pScene->mAnimations = new aiAnimation*[1];
+ aiAnimation* anim = new aiAnimation;
+ pScene->mAnimations[0] = anim;
+
+ // put down the basic parameters
+ anim->mName.Set( "Motion");
+ anim->mTicksPerSecond = 1.0 / double( mAnimTickDuration);
+ anim->mDuration = double( mAnimNumFrames - 1);
+
+ // now generate the tracks for all nodes
+ anim->mNumChannels = mNodes.size();
+ anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
+
+ // FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown
+ for (unsigned int i = 0; i < anim->mNumChannels;++i)
+ anim->mChannels[i] = NULL;
+
+ for ( unsigned int a = 0; a < anim->mNumChannels; a++)
+ {
+ const Node& node = mNodes[a];
+ const std::string nodeName = std::string( node.mNode->mName.data );
+ aiNodeAnim* nodeAnim = new aiNodeAnim;
+ anim->mChannels[a] = nodeAnim;
+ nodeAnim->mNodeName.Set( nodeName);
+
+ // translational part, if given
+ if ( node.mChannels.size() == 6)
+ {
+ nodeAnim->mNumPositionKeys = mAnimNumFrames;
+ nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames];
+ aiVectorKey* poskey = nodeAnim->mPositionKeys;
+ for ( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
+ {
+ poskey->mTime = double( fr);
+
+ // Now compute all translations in the right order
+ for ( unsigned int channel = 0; channel < 3; ++channel)
+ {
+ switch( node.mChannels[channel])
+ {
+ case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
+ case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
+ case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break;
+ default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName );
+ }
+ }
+ ++poskey;
+ }
+ } else
+ {
+ // if no translation part is given, put a default sequence
+ aiVector3D nodePos( node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4);
+ nodeAnim->mNumPositionKeys = 1;
+ nodeAnim->mPositionKeys = new aiVectorKey[1];
+ nodeAnim->mPositionKeys[0].mTime = 0.0;
+ nodeAnim->mPositionKeys[0].mValue = nodePos;
+ }
+
+ // rotation part. Always present. First find value offsets
+ {
+ unsigned int rotOffset = 0;
+ if ( node.mChannels.size() == 6)
+ {
+ // Offset all further calculations
+ rotOffset = 3;
+ }
+
+ // Then create the number of rotation keys
+ nodeAnim->mNumRotationKeys = mAnimNumFrames;
+ nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames];
+ aiQuatKey* rotkey = nodeAnim->mRotationKeys;
+ for ( unsigned int fr = 0; fr < mAnimNumFrames; ++fr)
+ {
+ aiMatrix4x4 temp;
+ aiMatrix3x3 rotMatrix;
+
+ for ( unsigned int channel = 0; channel < 3; ++channel)
+ {
+ // translate ZXY euler angels into a quaternion
+ const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f;
+
+ // Compute rotation transformations in the right order
+ switch (node.mChannels[rotOffset+channel])
+ {
+ case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
+ case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
+ case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break;
+ default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName );
+ }
+ }
+
+ rotkey->mTime = double( fr);
+ rotkey->mValue = aiQuaternion( rotMatrix);
+ ++rotkey;
+ }
+ }
+
+ // scaling part. Always just a default track
+ {
+ nodeAnim->mNumScalingKeys = 1;
+ nodeAnim->mScalingKeys = new aiVectorKey[1];
+ nodeAnim->mScalingKeys[0].mTime = 0.0;
+ nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f);
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER
diff --git a/3rdparty/assimp/code/BVHLoader.h b/3rdparty/assimp/code/BVHLoader.h
new file mode 100644
index 000000000..dce8f5f71
--- /dev/null
+++ b/3rdparty/assimp/code/BVHLoader.h
@@ -0,0 +1,173 @@
+/** Defines the BHV motion capturing loader class */
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BVHLoader.h
+ * @brief Biovision BVH import
+ */
+
+#ifndef AI_BVHLOADER_H_INC
+#define AI_BVHLOADER_H_INC
+
+#include "BaseImporter.h"
+
+namespace Assimp
+{
+
+// --------------------------------------------------------------------------------
+/** Loader class to read Motion Capturing data from a .bvh file.
+ *
+ * This format only contains a hierarchy of joints and a series of keyframes for
+ * the hierarchy. It contains no actual mesh data, but we generate a dummy mesh
+ * inside the loader just to be able to see something.
+*/
+class BVHLoader : public BaseImporter
+{
+ friend class Importer;
+
+ /** Possible animation channels for which the motion data holds the values */
+ enum ChannelType
+ {
+ Channel_PositionX,
+ Channel_PositionY,
+ Channel_PositionZ,
+ Channel_RotationX,
+ Channel_RotationY,
+ Channel_RotationZ
+ };
+
+ /** Collected list of node. Will be bones of the dummy mesh some day, addressed by their array index */
+ struct Node
+ {
+ const aiNode* mNode;
+ std::vector<ChannelType> mChannels;
+ std::vector<float> mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames
+
+ Node() { }
+ Node( const aiNode* pNode) : mNode( pNode) { }
+ };
+
+protected:
+ /** Constructor to be privately used by Importer */
+ BVHLoader();
+
+ /** Destructor, private as well */
+ ~BVHLoader();
+
+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 cs) const;
+
+protected:
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions)
+ {
+ extensions.insert("bvh");
+ }
+
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+protected:
+ /** Reads the file */
+ void ReadStructure( aiScene* pScene);
+
+ /** Reads the hierarchy */
+ void ReadHierarchy( aiScene* pScene);
+
+ /** Reads a node and recursively its childs and returns the created node. */
+ aiNode* ReadNode();
+
+ /** Reads an end node and returns the created node. */
+ aiNode* ReadEndSite( const std::string& pParentName);
+
+ /** Reads a node offset for the given node */
+ void ReadNodeOffset( aiNode* pNode);
+
+ /** Reads the animation channels into the given node */
+ void ReadNodeChannels( BVHLoader::Node& pNode);
+
+ /** Reads the motion data */
+ void ReadMotion( aiScene* pScene);
+
+ /** Retrieves the next token */
+ std::string GetNextToken();
+
+ /** Reads the next token as a float */
+ float GetNextTokenAsFloat();
+
+ /** Aborts the file reading with an exception */
+ void ThrowException( const std::string& pError);
+
+ /** Constructs an animation for the motion data and stores it in the given scene */
+ void CreateAnimation( aiScene* pScene);
+
+protected:
+ /** Filename, for a verbose error message */
+ std::string mFileName;
+
+ /** Buffer to hold the loaded file */
+ std::vector<char> mBuffer;
+
+ /** Next char to read from the buffer */
+ std::vector<char>::const_iterator mReader;
+
+ /** Current line, for error messages */
+ unsigned int mLine;
+
+ /** Collected list of nodes. Will be bones of the dummy mesh some day, addressed by their array index.
+ * Also contain the motion data for the node's channels
+ */
+ std::vector<Node> mNodes;
+
+ /** basic Animation parameters */
+ float mAnimTickDuration;
+ unsigned int mAnimNumFrames;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_BVHLOADER_H_INC
diff --git a/3rdparty/assimp/code/BaseImporter.cpp b/3rdparty/assimp/code/BaseImporter.cpp
new file mode 100644
index 000000000..cd2de111a
--- /dev/null
+++ b/3rdparty/assimp/code/BaseImporter.cpp
@@ -0,0 +1,527 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BaseImporter.cpp
+ * @brief Implementation of BaseImporter
+ */
+
+#include "AssimpPCH.h"
+#include "BaseImporter.h"
+#include "FileSystemFilter.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BaseImporter::BaseImporter()
+: progress()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+BaseImporter::~BaseImporter()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file and returns the imported data.
+aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler)
+{
+ progress = pImp->GetProgressHandler();
+ ai_assert(progress);
+
+ // Gather configuration properties for this run
+ SetupProperties( pImp );
+
+ // Construct a file system filter to improve our success ratio at reading external files
+ FileSystemFilter filter(pFile,pIOHandler);
+
+ // create a scene object to hold the data
+ ScopeGuard<aiScene> sc(new aiScene());
+
+ // dispatch importing
+ try
+ {
+ InternReadFile( pFile, sc, &filter);
+
+ } catch( const std::exception& err ) {
+ // extract error description
+ mErrorText = err.what();
+ DefaultLogger::get()->error(mErrorText);
+ return NULL;
+ }
+
+ // return what we gathered from the import.
+ sc.dismiss();
+ return sc;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BaseImporter::SetupProperties(const Importer* /* pImp */)
+{
+ // the default implementation does nothing
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler,
+ const std::string& pFile,
+ const char** tokens,
+ unsigned int numTokens,
+ unsigned int searchBytes /* = 200 */)
+{
+ ai_assert(NULL != tokens && 0 != numTokens && 0 != searchBytes);
+ if (!pIOHandler)
+ return false;
+
+ boost::scoped_ptr<IOStream> pStream (pIOHandler->Open(pFile));
+ if (pStream.get() ) {
+ // read 200 characters from the file
+ boost::scoped_array<char> _buffer (new char[searchBytes+1 /* for the '\0' */]);
+ char* buffer = _buffer.get();
+
+ const unsigned int read = pStream->Read(buffer,1,searchBytes);
+ if (!read)
+ return false;
+
+ for (unsigned int i = 0; i < read;++i)
+ buffer[i] = ::tolower(buffer[i]);
+
+ // It is not a proper handling of unicode files here ...
+ // ehm ... but it works in most cases.
+ char* cur = buffer,*cur2 = buffer,*end = &buffer[read];
+ while (cur != end) {
+ if (*cur)
+ *cur2++ = *cur;
+ ++cur;
+ }
+ *cur2 = '\0';
+
+ for (unsigned int i = 0; i < numTokens;++i) {
+ ai_assert(NULL != tokens[i]);
+
+ if (::strstr(buffer,tokens[i])) {
+ DefaultLogger::get()->debug(std::string("Found positive match for header keyword: ") + tokens[i]);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Simple check for file extension
+/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile,
+ const char* ext0,
+ const char* ext1,
+ const char* ext2)
+{
+ std::string::size_type pos = pFile.find_last_of('.');
+
+ // no file extension - can't read
+ if ( pos == std::string::npos)
+ return false;
+
+ const char* ext_real = & pFile[ pos+1 ];
+ if ( !ASSIMP_stricmp(ext_real,ext0) )
+ return true;
+
+ // check for other, optional, file extensions
+ if (ext1 && !ASSIMP_stricmp(ext_real,ext1))
+ return true;
+
+ if (ext2 && !ASSIMP_stricmp(ext_real,ext2))
+ return true;
+
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get file extension from path
+/*static*/ std::string BaseImporter::GetExtension (const std::string& pFile)
+{
+ std::string::size_type pos = pFile.find_last_of('.');
+
+ // no file extension at all
+ if ( pos == std::string::npos)
+ return "";
+
+ std::string ret = pFile.substr(pos+1);
+ std::transform(ret.begin(),ret.end(),ret.begin(),::tolower); // thanks to Andy Maloney for the hint
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check for magic bytes at the beginning of the file.
+/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile,
+ const void* _magic, unsigned int num, unsigned int offset, unsigned int size)
+{
+ ai_assert(size <= 16 && _magic);
+
+ if (!pIOHandler) {
+ return false;
+ }
+ union {
+ const char* magic;
+ const uint16_t* magic_u16;
+ const uint32_t* magic_u32;
+ };
+ magic = reinterpret_cast<const char*>(_magic);
+ boost::scoped_ptr<IOStream> pStream (pIOHandler->Open(pFile));
+ if (pStream.get() ) {
+
+ // skip to offset
+ pStream->Seek(offset,aiOrigin_SET);
+
+ // read 'size' characters from the file
+ union {
+ char data[16];
+ uint16_t data_u16[8];
+ uint32_t data_u32[4];
+ };
+ if (size != pStream->Read(data,1,size)) {
+ return false;
+ }
+
+ for (unsigned int i = 0; i < num; ++i) {
+ // also check against big endian versions of tokens with size 2,4
+ // that's just for convinience, the chance that we cause conflicts
+ // is quite low and it can save some lines and prevent nasty bugs
+ if (2 == size) {
+ uint16_t rev = *magic_u16;
+ ByteSwap::Swap(&rev);
+ if (data_u16[0] == *magic_u16 || data_u16[0] == rev) {
+ return true;
+ }
+ }
+ else if (4 == size) {
+ uint32_t rev = *magic_u32;
+ ByteSwap::Swap(&rev);
+ if (data_u32[0] == *magic_u32 || data_u32[0] == rev) {
+ return true;
+ }
+ }
+ else {
+ // any length ... just compare
+ if (!memcmp(magic,data,size)) {
+ return true;
+ }
+ }
+ magic += size;
+ }
+ }
+ return false;
+}
+
+#include "../contrib/ConvertUTF/ConvertUTF.h"
+
+// ------------------------------------------------------------------------------------------------
+void ReportResult(ConversionResult res)
+{
+ if (res == sourceExhausted) {
+ DefaultLogger::get()->error("Source ends with incomplete character sequence, transformation to UTF-8 fails");
+ }
+ else if (res == sourceIllegal) {
+ DefaultLogger::get()->error("Source contains illegal character sequence, transformation to UTF-8 fails");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert to UTF8 data
+void BaseImporter::ConvertToUTF8(std::vector<char>& data)
+{
+ ConversionResult result;
+ if (data.size() < 8) {
+ throw DeadlyImportError("File is too small");
+ }
+
+ // UTF 8 with BOM
+ if ((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) {
+ DefaultLogger::get()->debug("Found UTF-8 BOM ...");
+
+ std::copy(data.begin()+3,data.end(),data.begin());
+ data.resize(data.size()-3);
+ return;
+ }
+
+ // UTF 32 BE with BOM
+ if (*((uint32_t*)&data.front()) == 0xFFFE0000) {
+
+ // swap the endianess ..
+ for (uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) {
+ AI_SWAP4P(p);
+ }
+ }
+
+ // UTF 32 LE with BOM
+ if (*((uint32_t*)&data.front()) == 0x0000FFFE) {
+ DefaultLogger::get()->debug("Found UTF-32 BOM ...");
+
+ const uint32_t* sstart = (uint32_t*)&data.front()+1, *send = (uint32_t*)&data.back()+1;
+ char* dstart,*dend;
+ std::vector<char> output;
+ do {
+ output.resize(output.size()?output.size()*3/2:data.size()/2);
+ dstart = &output.front(),dend = &output.back()+1;
+
+ result = ConvertUTF32toUTF8((const UTF32**)&sstart,(const UTF32*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion);
+ } while (result == targetExhausted);
+
+ ReportResult(result);
+
+ // copy to output buffer.
+ const size_t outlen = (size_t)(dstart-&output.front());
+ data.assign(output.begin(),output.begin()+outlen);
+ return;
+ }
+
+ // UTF 16 BE with BOM
+ if (*((uint16_t*)&data.front()) == 0xFFFE) {
+
+ // swap the endianess ..
+ for (uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) {
+ ByteSwap::Swap2(p);
+ }
+ }
+
+ // UTF 16 LE with BOM
+ if (*((uint16_t*)&data.front()) == 0xFEFF) {
+ DefaultLogger::get()->debug("Found UTF-16 BOM ...");
+
+ const uint16_t* sstart = (uint16_t*)&data.front()+1, *send = (uint16_t*)&data.back()+1;
+ char* dstart,*dend;
+ std::vector<char> output;
+ do {
+ output.resize(output.size()?output.size()*3/2:data.size()*3/4);
+ dstart = &output.front(),dend = &output.back()+1;
+
+ result = ConvertUTF16toUTF8((const UTF16**)&sstart,(const UTF16*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion);
+ } while (result == targetExhausted);
+
+ ReportResult(result);
+
+ // copy to output buffer.
+ const size_t outlen = (size_t)(dstart-&output.front());
+ data.assign(output.begin(),output.begin()+outlen);
+ return;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BaseImporter::TextFileToBuffer(IOStream* stream,
+ std::vector<char>& data)
+{
+ ai_assert(NULL != stream);
+
+ const size_t fileSize = stream->FileSize();
+ if (!fileSize) {
+ throw DeadlyImportError("File is empty");
+ }
+
+ data.reserve(fileSize+1);
+ data.resize(fileSize);
+ if (fileSize != stream->Read( &data[0], 1, fileSize)) {
+ throw DeadlyImportError("File read error");
+ }
+
+ ConvertToUTF8(data);
+
+ // append a binary zero to simplify string parsing
+ data.push_back(0);
+}
+
+// ------------------------------------------------------------------------------------------------
+namespace Assimp
+{
+ // Represents an import request
+ struct LoadRequest
+ {
+ LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id)
+ : file(_file), flags(_flags), refCnt(1),scene(NULL), loaded(false), id(_id)
+ {
+ if (_map)
+ map = *_map;
+ }
+
+ const std::string file;
+ unsigned int flags;
+ unsigned int refCnt;
+ aiScene* scene;
+ bool loaded;
+ BatchLoader::PropertyMap map;
+ unsigned int id;
+
+ bool operator== (const std::string& f) {
+ return file == f;
+ }
+ };
+}
+
+// ------------------------------------------------------------------------------------------------
+// BatchLoader::pimpl data structure
+struct Assimp::BatchData
+{
+ BatchData()
+ : next_id(0xffff)
+ {}
+
+ // IO system to be used for all imports
+ IOSystem* pIOSystem;
+
+ // Importer used to load all meshes
+ Importer* pImporter;
+
+ // List of all imports
+ std::list<LoadRequest> requests;
+
+ // Base path
+ std::string pathBase;
+
+ // Id for next item
+ unsigned int next_id;
+};
+
+// ------------------------------------------------------------------------------------------------
+BatchLoader::BatchLoader(IOSystem* pIO)
+{
+ ai_assert(NULL != pIO);
+
+ data = new BatchData();
+ data->pIOSystem = pIO;
+
+ data->pImporter = new Importer();
+ data->pImporter->SetIOHandler(data->pIOSystem);
+}
+
+// ------------------------------------------------------------------------------------------------
+BatchLoader::~BatchLoader()
+{
+ // delete all scenes wthat have not been polled by the user
+ for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) {
+
+ delete (*it).scene;
+ }
+ data->pImporter->SetIOHandler(NULL); /* get pointer back into our posession */
+ delete data->pImporter;
+ delete data;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+unsigned int BatchLoader::AddLoadRequest (const std::string& file,
+ unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/)
+{
+ ai_assert(!file.empty());
+
+ // check whether we have this loading request already
+ std::list<LoadRequest>::iterator it;
+ for (it = data->requests.begin();it != data->requests.end(); ++it) {
+
+ // Call IOSystem's path comparison function here
+ if (data->pIOSystem->ComparePaths((*it).file,file)) {
+
+ if (map) {
+ if (!((*it).map == *map))
+ continue;
+ }
+ else if (!(*it).map.empty())
+ continue;
+
+ (*it).refCnt++;
+ return (*it).id;
+ }
+ }
+
+ // no, we don't have it. So add it to the queue ...
+ data->requests.push_back(LoadRequest(file,steps,map,data->next_id));
+ return data->next_id++;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiScene* BatchLoader::GetImport (unsigned int which)
+{
+ for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) {
+
+ if ((*it).id == which && (*it).loaded) {
+
+ aiScene* sc = (*it).scene;
+ if (!(--(*it).refCnt)) {
+ data->requests.erase(it);
+ }
+ return sc;
+ }
+ }
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BatchLoader::LoadAll()
+{
+ // no threaded implementation for the moment
+ for (std::list<LoadRequest>::iterator it = data->requests.begin();it != data->requests.end(); ++it) {
+ // force validation in debug builds
+ unsigned int pp = (*it).flags;
+#ifdef _DEBUG
+ pp |= aiProcess_ValidateDataStructure;
+#endif
+ // setup config properties if necessary
+ data->pImporter->pimpl->mFloatProperties = (*it).map.floats;
+ data->pImporter->pimpl->mIntProperties = (*it).map.ints;
+ data->pImporter->pimpl->mStringProperties = (*it).map.strings;
+
+ if (!DefaultLogger::isNullLogger())
+ {
+ DefaultLogger::get()->info("%%% BEGIN EXTERNAL FILE %%%");
+ DefaultLogger::get()->info("File: " + (*it).file);
+ }
+ data->pImporter->ReadFile((*it).file,pp);
+ (*it).scene = const_cast<aiScene*>(data->pImporter->GetOrphanedScene());
+ (*it).loaded = true;
+
+ DefaultLogger::get()->info("%%% END EXTERNAL FILE %%%");
+ }
+}
+
+
+
+
diff --git a/3rdparty/assimp/code/BaseImporter.h b/3rdparty/assimp/code/BaseImporter.h
new file mode 100644
index 000000000..9da05713a
--- /dev/null
+++ b/3rdparty/assimp/code/BaseImporter.h
@@ -0,0 +1,499 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 <string>
+#include <map>
+#include <vector>
+#include "./../include/aiTypes.h"
+
+struct aiScene;
+
+namespace Assimp {
+
+class IOSystem;
+class Importer;
+class BaseImporter;
+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]))
+
+// ---------------------------------------------------------------------------
+template <typename T>
+struct ScopeGuard
+{
+ ScopeGuard(T* obj) : obj(obj), mdismiss() {}
+ ~ScopeGuard () throw() {
+ if (!mdismiss) {
+ delete obj;
+ }
+ obj = NULL;
+ }
+
+ T* dismiss() {
+ mdismiss=true;
+ return obj;
+ }
+
+ operator T*() {
+ return obj;
+ }
+
+ T* operator -> () {
+ return obj;
+ }
+
+private:
+ T* obj;
+ bool mdismiss;
+};
+
+//! @cond never
+// ---------------------------------------------------------------------------
+/** @brief Internal PIMPL implementation for Assimp::Importer
+ *
+ * Using this idiom here allows us to drop the dependency from
+ * std::vector and std::map in the public headers. Furthermore we are dropping
+ * any STL interface problems caused by mismatching STL settings. All
+ * size calculation are now done by us, not the app heap. */
+class ASSIMP_API ImporterPimpl
+{
+public:
+
+ // Data type to store the key hash
+ typedef unsigned int KeyType;
+
+ // typedefs for our three configuration maps.
+ // We don't need more, so there is no need for a generic solution
+ typedef std::map<KeyType, int> IntPropertyMap;
+ typedef std::map<KeyType, float> FloatPropertyMap;
+ typedef std::map<KeyType, std::string> StringPropertyMap;
+
+public:
+
+ /** IO handler to use for all file accesses. */
+ IOSystem* mIOHandler;
+ bool mIsDefaultHandler;
+
+ /** Progress handler for feedback. */
+ ProgressHandler* mProgressHandler;
+ bool mIsDefaultProgressHandler;
+
+ /** Format-specific importer worker objects - one for each format we can read.*/
+ std::vector<BaseImporter*> mImporter;
+
+ /** Post processing steps we can apply at the imported data. */
+ std::vector<BaseProcess*> mPostProcessingSteps;
+
+ /** The imported data, if ReadFile() was successful, NULL otherwise. */
+ aiScene* mScene;
+
+ /** The error description, if there was one. */
+ std::string mErrorString;
+
+ /** List of integer properties */
+ IntPropertyMap mIntProperties;
+
+ /** List of floating-point properties */
+ FloatPropertyMap mFloatProperties;
+
+ /** List of string properties */
+ StringPropertyMap mStringProperties;
+
+ /** Used for testing - extra verbose mode causes the ValidateDataStructure-Step
+ * to be executed before and after every single postprocess step */
+ bool bExtraVerbose;
+
+ /** Used by post-process steps to share data */
+ SharedPostProcessInfo* mPPShared;
+};
+//! @endcond
+
+// ---------------------------------------------------------------------------
+/** 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;
+
+protected:
+
+ /** 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 occured.
+ * @return A description of the last error that occured. An empty
+ * string if there was no error.
+ */
+ const std::string& GetErrorText() const {
+ return mErrorText;
+ }
+
+ // -------------------------------------------------------------------
+ /** 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
+ );
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * Implementations are expected to insert() all file extensions
+ * handled by them into the extension set. A loader capable of
+ * reading certain files with the extension BLA would place the
+ * string bla (lower-case!) in the output set.
+ * @param extensions Output set. */
+ virtual void GetExtensionList(
+ std::set<std::string>& extensions
+ ) = 0;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+
+ // -------------------------------------------------------------------
+ /** @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 convinence, 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);
+
+ // -------------------------------------------------------------------
+ /** 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. */
+ static void TextFileToBuffer(
+ IOStream* stream,
+ std::vector<char>& data);
+
+protected:
+
+ /** Error description in case there was one. */
+ std::string mErrorText;
+
+ /** Currently set progress handler */
+ ProgressHandler* progress;
+};
+
+struct BatchData;
+
+// ---------------------------------------------------------------------------
+/** FOR IMPORTER PLUGINS ONLY: A helper class for the pleasure of importers
+ * which need to load many extern meshes recursively.
+ *
+ * The class uses several threads to load these meshes (or at least it
+ * could, this has not yet been implemented at the moment).
+ *
+ * @note The class may not be used by more than one thread*/
+class ASSIMP_API BatchLoader
+{
+ // friend of Importer
+
+public:
+
+ //! @cond never
+ // -------------------------------------------------------------------
+ /** Wraps a full list of configuration properties for an importer.
+ * Properties can be set using SetGenericProperty */
+ struct PropertyMap
+ {
+ ImporterPimpl::IntPropertyMap ints;
+ ImporterPimpl::FloatPropertyMap floats;
+ ImporterPimpl::StringPropertyMap strings;
+
+ bool operator == (const PropertyMap& prop) const {
+ // fixme: really isocpp? gcc complains
+ return ints == prop.ints && floats == prop.floats && strings == prop.strings;
+ }
+
+ bool empty () const {
+ return ints.empty() && floats.empty() && strings.empty();
+ }
+ };
+ //! @endcond
+
+public:
+
+
+ // -------------------------------------------------------------------
+ /** Construct a batch loader from a given IO system to be used
+ * to acess external files */
+ BatchLoader(IOSystem* pIO);
+ ~BatchLoader();
+
+
+ // -------------------------------------------------------------------
+ /** Add a new file to the list of files to be loaded.
+ * @param file File to be loaded
+ * @param steps Post-processing steps to be executed on the file
+ * @param map Optional configuration properties
+ * @return 'Load request channel' - an unique ID that can later
+ * be used to access the imported file data.
+ * @see GetImport */
+ unsigned int AddLoadRequest (
+ const std::string& file,
+ unsigned int steps = 0,
+ const PropertyMap* map = NULL
+ );
+
+
+ // -------------------------------------------------------------------
+ /** Get an imported scene.
+ * This polls the import from the internal request list.
+ * If an import is requested several times, this function
+ * can be called several times, too.
+ *
+ * @param which LRWC returned by AddLoadRequest().
+ * @return NULL if there is no scene with this file name
+ * in the queue of the scene hasn't been loaded yet. */
+ aiScene* GetImport(
+ unsigned int which
+ );
+
+
+ // -------------------------------------------------------------------
+ /** Waits until all scenes have been loaded. This returns
+ * immediately if no scenes are queued.*/
+ void LoadAll();
+
+private:
+
+ // No need to have that in the public API ...
+ BatchData* data;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_BASEIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/BaseProcess.cpp b/3rdparty/assimp/code/BaseProcess.cpp
new file mode 100644
index 000000000..c5970fba1
--- /dev/null
+++ b/3rdparty/assimp/code/BaseProcess.cpp
@@ -0,0 +1,96 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of BaseProcess */
+
+#include "AssimpPCH.h"
+#include "BaseImporter.h"
+#include "BaseProcess.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BaseProcess::BaseProcess()
+: shared()
+, progress()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+BaseProcess::~BaseProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+void BaseProcess::ExecuteOnScene( Importer* pImp)
+{
+ ai_assert(NULL != pImp && NULL != pImp->pimpl->mScene);
+
+ progress = pImp->GetProgressHandler();
+ ai_assert(progress);
+
+ SetupProperties( pImp );
+
+ // catch exceptions thrown inside the PostProcess-Step
+ try
+ {
+ Execute(pImp->pimpl->mScene);
+
+ } catch( const std::exception& err ) {
+
+ // extract error description
+ pImp->pimpl->mErrorString = err.what();
+ DefaultLogger::get()->error(pImp->pimpl->mErrorString);
+
+ // and kill the partially imported data
+ delete pImp->pimpl->mScene;
+ pImp->pimpl->mScene = NULL;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BaseProcess::SetupProperties(const Importer* /* pImp */)
+{
+ // the default implementation does nothing
+}
diff --git a/3rdparty/assimp/code/BaseProcess.h b/3rdparty/assimp/code/BaseProcess.h
new file mode 100644
index 000000000..cd8bda0cf
--- /dev/null
+++ b/3rdparty/assimp/code/BaseProcess.h
@@ -0,0 +1,289 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Base class of all import post processing steps */
+#ifndef INCLUDED_AI_BASEPROCESS_H
+#define INCLUDED_AI_BASEPROCESS_H
+
+#include <map>
+
+#include "../include/aiTypes.h"
+#include "GenericProperty.h"
+
+struct aiScene;
+
+namespace Assimp {
+
+class Importer;
+
+// ---------------------------------------------------------------------------
+/** Helper class to allow post-processing steps to interact with each other.
+ *
+ * The class maintains a simple property list that can be used by pp-steps
+ * to provide additional information to other steps. This is primarily
+ * intended for cross-step optimizations.
+ */
+class ASSIMP_API SharedPostProcessInfo
+{
+public:
+
+ struct Base
+ {
+ virtual ~Base()
+ {}
+ };
+
+ //! Represents data that is allocated on the heap, thus needs to be deleted
+ template <typename T>
+ struct THeapData : public Base
+ {
+ THeapData(T* in)
+ : data (in)
+ {}
+
+ ~THeapData()
+ {
+ delete data;
+ }
+ T* data;
+ };
+
+ //! Represents static, by-value data not allocated on the heap
+ template <typename T>
+ struct TStaticData : public Base
+ {
+ TStaticData(T in)
+ : data (in)
+ {}
+
+ ~TStaticData()
+ {}
+
+ T data;
+ };
+
+ // some typedefs for cleaner code
+ typedef unsigned int KeyType;
+ typedef std::map<KeyType, Base*> PropertyMap;
+
+public:
+
+ //! Destructor
+ ~SharedPostProcessInfo()
+ {
+ Clean();
+ }
+
+ //! Remove all stored properties from the table
+ void Clean()
+ {
+ // invoke the virtual destructor for all stored properties
+ for (PropertyMap::iterator it = pmap.begin(), end = pmap.end();
+ it != end; ++it)
+ {
+ delete (*it).second;
+ }
+ pmap.clear();
+ }
+
+ //! Add a heap property to the list
+ template <typename T>
+ void AddProperty( const char* name, T* in ){
+ AddProperty(name,(Base*)new THeapData<T>(in));
+ }
+
+ //! Add a static by-value property to the list
+ template <typename T>
+ void AddProperty( const char* name, T in ){
+ AddProperty(name,(Base*)new TStaticData<T>(in));
+ }
+
+
+ //! Get a heap property
+ template <typename T>
+ bool GetProperty( const char* name, T*& out ) const
+ {
+ THeapData<T>* t = (THeapData<T>*)GetPropertyInternal(name);
+ if (!t)
+ {
+ out = NULL;
+ return false;
+ }
+ out = t->data;
+ return true;
+ }
+
+ //! Get a static, by-value property
+ template <typename T>
+ bool GetProperty( const char* name, T& out ) const
+ {
+ TStaticData<T>* t = (TStaticData<T>*)GetPropertyInternal(name);
+ if (!t)return false;
+ out = t->data;
+ return true;
+ }
+
+ //! Remove a property of a specific type
+ void RemoveProperty( const char* name) {
+ SetGenericPropertyPtr<Base>(pmap,name,NULL);
+ }
+
+private:
+
+ void AddProperty( const char* name, Base* data) {
+ SetGenericPropertyPtr<Base>(pmap,name,data);
+ }
+
+ Base* GetPropertyInternal( const char* name) const {
+ return GetGenericProperty<Base*>(pmap,name,NULL);
+ }
+
+private:
+
+ //! Map of all stored properties
+ PropertyMap pmap;
+};
+
+#if 0
+
+// ---------------------------------------------------------------------------
+/** @brief Represents a dependency table for a postprocessing steps.
+ *
+ * For future use.
+ */
+ struct PPDependencyTable
+ {
+ unsigned int execute_me_before_these;
+ unsigned int execute_me_after_these;
+ unsigned int only_if_these_are_not_specified;
+ unsigned int mutually_exclusive_with;
+ };
+
+#endif
+
+
+#define AI_SPP_SPATIAL_SORT "$Spat"
+
+// ---------------------------------------------------------------------------
+/** The BaseProcess defines a common interface for all post processing steps.
+ * A post processing step is run after a successful import if the caller
+ * specified the corresponding flag when calling ReadFile().
+ * Enum #aiPostProcessSteps defines which flags are available.
+ * After a successful import the Importer iterates over its internal array
+ * of processes and calls IsActive() on each process to evaluate if the step
+ * should be executed. If the function returns true, the class' Execute()
+ * function is called subsequently.
+ */
+class ASSIMP_API BaseProcess
+{
+ friend class Importer;
+
+public:
+
+ /** Constructor to be privately used by Importer */
+ BaseProcess();
+
+ /** Destructor, private as well */
+ virtual ~BaseProcess();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag.
+ * @param pFlags The processing flags the importer was called with. A
+ * bitwise combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields,
+ * false if not.
+ */
+ virtual bool IsActive( unsigned int pFlags) const = 0;
+
+ // -------------------------------------------------------------------
+ /** Executes the post processing step on the given imported data.
+ * The function deletes the scene if the postprocess step fails (
+ * the object pointer will be set to NULL).
+ * @param pImp Importer instance (pImp->mScene must be valid)
+ */
+ void ExecuteOnScene( Importer* pImp);
+
+ // -------------------------------------------------------------------
+ /** Called prior to ExecuteOnScene().
+ * The function is a request to the process to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ virtual void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ /** Executes the post processing step on the given imported data.
+ * A process should throw an ImportErrorException* if it fails.
+ * This method must be implemented by deriving classes.
+ * @param pScene The imported data to work at.
+ */
+ virtual void Execute( aiScene* pScene) = 0;
+
+
+ // -------------------------------------------------------------------
+ /** Assign a new SharedPostProcessInfo to the step. This object
+ * allows multiple postprocess steps to share data.
+ * @param sh May be NULL
+ */
+ inline void SetSharedData(SharedPostProcessInfo* sh) {
+ shared = sh;
+ }
+
+ // -------------------------------------------------------------------
+ /** Get the shared data that is assigned to the step.
+ */
+ inline SharedPostProcessInfo* GetSharedData() {
+ return shared;
+ }
+
+protected:
+
+ /** See the doc of #SharedPostProcessInfo for more details */
+ SharedPostProcessInfo* shared;
+
+ /** Currently active progress handler */
+ ProgressHandler* progress;
+};
+
+
+} // end of namespace Assimp
+
+#endif // AI_BASEPROCESS_H_INC
diff --git a/3rdparty/assimp/code/BlenderDNA.cpp b/3rdparty/assimp/code/BlenderDNA.cpp
new file mode 100644
index 000000000..5206ab130
--- /dev/null
+++ b/3rdparty/assimp/code/BlenderDNA.cpp
@@ -0,0 +1,372 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BlenderDNA.cpp
+ * @brief Implementation of the Blender `DNA`, that is its own
+ * serialized set of data structures.
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+#include "BlenderDNA.h"
+#include "StreamReader.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+using namespace Assimp::Formatter;
+
+#define for_each BOOST_FOREACH
+bool match4(StreamReaderAny& stream, const char* string) {
+ char tmp[] = {
+ (stream).GetI1(),
+ (stream).GetI1(),
+ (stream).GetI1(),
+ (stream).GetI1()
+ };
+ return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]);
+}
+
+struct Type {
+ size_t size;
+ std::string name;
+};
+
+// ------------------------------------------------------------------------------------------------
+void DNAParser :: Parse ()
+{
+ StreamReaderAny& stream = *db.reader.get();
+ DNA& dna = db.dna;
+
+ if (!match4(stream,"SDNA")) {
+ throw DeadlyImportError("BlenderDNA: Expected SDNA chunk");
+ }
+
+ // name dictionary
+ if (!match4(stream,"NAME")) {
+ throw DeadlyImportError("BlenderDNA: Expected NAME field");
+ }
+
+ std::vector<std::string> names (stream.GetI4());
+ for_each(std::string& s, names) {
+ while (char c = stream.GetI1()) {
+ s += c;
+ }
+ }
+
+ // type dictionary
+ for (;stream.GetCurrentPos() & 0x3; stream.GetI1()) {};
+ if (!match4(stream,"TYPE")) {
+ throw DeadlyImportError("BlenderDNA: Expected TYPE field");
+ }
+
+ std::vector<Type> types (stream.GetI4());
+ for_each(Type& s, types) {
+ while (char c = stream.GetI1()) {
+ s.name += c;
+ }
+ }
+
+ // type length dictionary
+ for (;stream.GetCurrentPos() & 0x3; stream.GetI1()) {};
+ if (!match4(stream,"TLEN")) {
+ throw DeadlyImportError("BlenderDNA: Expected TLEN field");
+ }
+
+ for_each(Type& s, types) {
+ s.size = stream.GetI2();
+ }
+
+ // structures dictionary
+ for (;stream.GetCurrentPos() & 0x3; stream.GetI1()) {};
+ if (!match4(stream,"STRC")) {
+ throw DeadlyImportError("BlenderDNA: Expected STRC field");
+ }
+
+ size_t end = stream.GetI4(), fields = 0;
+
+ dna.structures.reserve(end);
+ for (size_t i = 0; i != end; ++i) {
+
+ uint16_t n = stream.GetI2();
+ if (n >= types.size()) {
+ throw DeadlyImportError((format(),
+ "BlenderDNA: Invalid type index in structure name" ,n,
+ " (there are only ", types.size(), " entries)"
+ ));
+ }
+
+ // maintain separate indexes
+ dna.indices[types[n].name] = dna.structures.size();
+
+ dna.structures.push_back(Structure());
+ Structure& s = dna.structures.back();
+ s.name = types[n].name;
+ //s.index = dna.structures.size()-1;
+
+ n = stream.GetI2();
+ s.fields.reserve(n);
+
+ size_t offset = 0;
+ for (size_t m = 0; m < n; ++m, ++fields) {
+
+ uint16_t j = stream.GetI2();
+ if (j >= types.size()) {
+ throw DeadlyImportError((format(),
+ "BlenderDNA: Invalid type index in structure field ", j,
+ " (there are only ", types.size(), " entries)"
+ ));
+ }
+ s.fields.push_back(Field());
+ Field& f = s.fields.back();
+ f.offset = offset;
+
+ f.type = types[j].name;
+ f.size = types[j].size;
+
+ j = stream.GetI2();
+ if (j >= names.size()) {
+ throw DeadlyImportError((format(),
+ "BlenderDNA: Invalid name index in structure field ", j,
+ " (there are only ", names.size(), " entries)"
+ ));
+ }
+
+ f.name = names[j];
+ f.flags = 0u;
+
+ // pointers always specify the size of the pointee instead of their own.
+ // The pointer asterisk remains a property of the lookup name.
+ if (f.name[0] == '*') {
+ f.size = db.i64bit ? 8 : 4;
+ f.flags |= FieldFlag_Pointer;
+ }
+
+ // arrays, however, specify the size of a single element so we
+ // need to parse the (possibly multi-dimensional) array declaration
+ // in order to obtain the actual size of the array in the file.
+ // Also we need to alter the lookup name to include no array
+ // brackets anymore or size fixup won't work (if our size does
+ // not match the size read from the DNA).
+ if (*f.name.rbegin() == ']') {
+ const std::string::size_type rb = f.name.find('[');
+ if (rb == std::string::npos) {
+ throw DeadlyImportError((format(),
+ "BlenderDNA: Encountered invalid array declaration ",
+ f.name
+ ));
+ }
+
+ f.flags |= FieldFlag_Array;
+ DNA::ExtractArraySize(f.name,f.array_sizes);
+ f.name = f.name.substr(0,rb);
+
+ f.size *= f.array_sizes[0] * f.array_sizes[1];
+ }
+
+ // maintain separate indexes
+ s.indices[f.name] = s.fields.size()-1;
+ offset += f.size;
+ }
+ s.size = offset;
+ }
+
+ DefaultLogger::get()->debug((format(),"BlenderDNA: Got ",dna.structures.size(),
+ " structures with totally ",fields," fields"));
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+ dna.DumpToFile();
+#endif
+
+ dna.AddPrimitiveStructures();
+ dna.RegisterConverters();
+}
+
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+
+#include <fstream>
+// ------------------------------------------------------------------------------------------------
+void DNA :: DumpToFile()
+{
+ // we dont't bother using the VFS here for this is only for debugging.
+ // (and all your bases are belong to us).
+
+ std::ofstream f("dna.txt");
+ if (f.fail()) {
+ DefaultLogger::get()->error("Could not dump dna to dna.txt");
+ return;
+ }
+ f << "Field format: type name offset size" << "\n";
+ f << "Structure format: name size" << "\n";
+
+ for_each(const Structure& s, structures) {
+ f << s.name << " " << s.size << "\n\n";
+ for_each(const Field& ff, s.fields) {
+ f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << std::endl;
+ }
+ f << std::endl;
+ }
+ DefaultLogger::get()->info("BlenderDNA: Dumped dna to dna.txt");
+}
+#endif
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void DNA :: ExtractArraySize(
+ const std::string& out,
+ size_t array_sizes[2]
+)
+{
+ array_sizes[0] = array_sizes[1] = 1;
+ std::string::size_type pos = out.find('[');
+ if (pos++ == std::string::npos) {
+ return;
+ }
+ array_sizes[0] = strtol10(&out[pos]);
+
+ pos = out.find('[',pos);
+ if (pos++ == std::string::npos) {
+ return;
+ }
+ array_sizes[1] = strtol10(&out[pos]);
+}
+
+// ------------------------------------------------------------------------------------------------
+boost::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure(
+ const Structure& structure,
+ const FileDatabase& db
+) const
+{
+ std::map<std::string, FactoryPair >::const_iterator it = converters.find(structure.name);
+ if (it == converters.end()) {
+ return boost::shared_ptr< ElemBase >();
+ }
+
+ boost::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))();
+ (structure.*((*it).second.second))(ret,db);
+
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+DNA::FactoryPair DNA :: GetBlobToStructureConverter(
+ const Structure& structure,
+ const FileDatabase& /* db */
+) const
+{
+ std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name);
+ return it == converters.end() ? FactoryPair() : (*it).second;
+}
+
+// basing on http://www.blender.org/development/architecture/notes-on-sdna/
+// ------------------------------------------------------------------------------------------------
+void DNA :: AddPrimitiveStructures()
+{
+ // NOTE: these are just dummies. Their presence enforces
+ // Structure::Convert<target_type> to be called on these
+ // empty structures. These converters are special
+ // overloads which scan the name of the structure and
+ // perform the required data type conversion if one
+ // of these special names is found in the structure
+ // in question.
+
+ indices["int"] = structures.size();
+ structures.push_back( Structure() );
+ structures.back().name = "int";
+ structures.back().size = 4;
+
+ indices["short"] = structures.size();
+ structures.push_back( Structure() );
+ structures.back().name = "short";
+ structures.back().size = 2;
+
+
+ indices["char"] = structures.size();
+ structures.push_back( Structure() );
+ structures.back().name = "char";
+ structures.back().size = 1;
+
+
+ indices["float"] = structures.size();
+ structures.push_back( Structure() );
+ structures.back().name = "float";
+ structures.back().size = 4;
+
+
+ indices["double"] = structures.size();
+ structures.push_back( Structure() );
+ structures.back().name = "double";
+ structures.back().size = 8;
+
+ // no long, seemingly.
+}
+
+// ------------------------------------------------------------------------------------------------
+void SectionParser :: Next()
+{
+ stream.SetCurrentPos(current.start + current.size);
+
+ const char tmp[] = {
+ stream.GetI1(),
+ stream.GetI1(),
+ stream.GetI1(),
+ stream.GetI1()
+ };
+ current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1);
+
+ current.size = stream.GetI4();
+ current.address.val = ptr64 ? stream.GetU8() : stream.GetU4();
+
+ current.dna_index = stream.GetI4();
+ current.num = stream.GetI4();
+
+ current.start = stream.GetCurrentPos();
+ if (stream.GetRemainingSizeToLimit() < current.size) {
+ throw DeadlyImportError("BLEND: invalid size of file block");
+ }
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+ DefaultLogger::get()->debug(current.id);
+#endif
+}
+
+
+
+#endif
diff --git a/3rdparty/assimp/code/BlenderDNA.h b/3rdparty/assimp/code/BlenderDNA.h
new file mode 100644
index 000000000..8bf237da4
--- /dev/null
+++ b/3rdparty/assimp/code/BlenderDNA.h
@@ -0,0 +1,798 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BlenderDNA.h
+ * @brief Blender `DNA` (file format specification embedded in
+ * blend file itself) loader.
+ */
+#ifndef INCLUDED_AI_BLEND_DNA_H
+#define INCLUDED_AI_BLEND_DNA_H
+
+#include "BaseImporter.h"
+#include "TinyFormatter.h"
+
+// enable verbose log output. really verbose, so be careful.
+#ifdef _DEBUG
+# define ASSIMP_BUILD_BLENDER_DEBUG
+#endif
+
+// #define ASSIMP_BUILD_BLENDER_NO_STATS
+
+namespace Assimp {
+ template <bool,bool> class StreamReader;
+ typedef StreamReader<true,true> StreamReaderAny;
+
+ namespace Blender {
+ class FileDatabase;
+ struct FileBlockHead;
+
+ template <template <typename> class TOUT>
+ class ObjectCache;
+
+// -------------------------------------------------------------------------------
+/** Exception class used by the blender loader to selectively catch exceptions
+ * thrown in its own code (DeadlyImportErrors thrown in general utility
+ * functions are untouched then). If such an exception is not caught by
+ * the loader itself, it will still be caught by Assimp due to its
+ * ancestry. */
+// -------------------------------------------------------------------------------
+struct Error : DeadlyImportError
+{
+ Error (const std::string& s)
+ : DeadlyImportError(s)
+ {}
+};
+
+// -------------------------------------------------------------------------------
+/** The only purpose of this structure is to feed a virtual dtor into its
+ * descendents. It serves as base class for all data structure fields. */
+// -------------------------------------------------------------------------------
+struct ElemBase
+{
+ virtual ~ElemBase() {}
+
+ /** Type name of the element. The type
+ * string points is the `c_str` of the `name` attribute of the
+ * corresponding `Structure`, that is, it is only valid as long
+ * as the DNA is not modified. The dna_type is only set if the
+ * data type is not static, i.e. a boost::shared_ptr<ElemBase>
+ * in the scene description would have its type resolved
+ * at runtime, so this member is always set. */
+ const char* dna_type;
+};
+
+
+// -------------------------------------------------------------------------------
+/** Represents a generic pointer to a memory location, which can be either 32
+ * or 64 bits. These pointers are loaded from the BLEND file and finally
+ * fixed to point to the real, converted representation of the objects
+ * they used to point to.*/
+// -------------------------------------------------------------------------------
+struct Pointer
+{
+ Pointer() : val() {}
+ uint64_t val;
+};
+
+// -------------------------------------------------------------------------------
+/** Represents a generic offset within a BLEND file */
+// -------------------------------------------------------------------------------
+struct FileOffset
+{
+ FileOffset() : val() {}
+ uint64_t val;
+};
+
+// -------------------------------------------------------------------------------
+/** Dummy derivate of std::vector to be able to use it in templates simultaenously
+ * with boost::shared_ptr, which takes only one template argument
+ * while std::vector takes three. Also we need to provide some special member
+ * functions of shared_ptr */
+// -------------------------------------------------------------------------------
+template <typename T>
+class vector : public std::vector<T>
+{
+public:
+ using std::vector<T>::resize;
+ using std::vector<T>::empty;
+
+ void reset() {
+ resize(0);
+ }
+
+ operator bool () const {
+ return !empty();
+ }
+};
+
+// -------------------------------------------------------------------------------
+/** Mixed flags for use in #Field */
+// -------------------------------------------------------------------------------
+enum FieldFlags
+{
+ FieldFlag_Pointer = 0x1,
+ FieldFlag_Array = 0x2
+};
+
+// -------------------------------------------------------------------------------
+/** Represents a single member of a data structure in a BLEND file */
+// -------------------------------------------------------------------------------
+struct Field
+{
+ std::string name;
+ std::string type;
+
+ size_t size;
+ size_t offset;
+
+ /** Size of each array dimension. For flat arrays,
+ * the second dimension is set to 1. */
+ size_t array_sizes[2];
+
+ /** Any of the #FieldFlags enumerated values */
+ unsigned int flags;
+};
+
+// -------------------------------------------------------------------------------
+/** Range of possible behaviours for fields absend in the input file. Some are
+ * mission critical so we need them, while others can silently be default
+ * initialized and no animations are harmed. */
+// -------------------------------------------------------------------------------
+enum ErrorPolicy
+{
+ /** Substitute default value and ignore */
+ ErrorPolicy_Igno,
+ /** Substitute default value and write to log */
+ ErrorPolicy_Warn,
+ /** Substitute a massive error message and crash the whole matrix. Its time for another zion */
+ ErrorPolicy_Fail
+};
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+# define ErrorPolicy_Igno ErrorPolicy_Warn
+#endif
+
+// -------------------------------------------------------------------------------
+/** Represents a data structure in a BLEND file. A Structure defines n fields
+ * and their locatios and encodings the input stream. Usually, every
+ * Structure instance pertains to one equally-named data structure in the
+ * BlenderScene.h header. This class defines various utilities to map a
+ * binary `blob` read from the file to such a structure instance with
+ * meaningful contents. */
+// -------------------------------------------------------------------------------
+class Structure
+{
+ template <template <typename> class> friend class ObjectCache;
+
+public:
+
+ Structure()
+ : cache_idx(-1)
+ {}
+
+public:
+
+ // publicly accessible members
+ std::string name;
+ vector< Field > fields;
+ std::map<std::string, size_t> indices;
+
+ size_t size;
+
+public:
+
+ // --------------------------------------------------------
+ /** Access a field of the structure by its canonical name. The pointer version
+ * returns NULL on failure while the reference version raises an import error. */
+ inline const Field& operator [] (const std::string& ss) const;
+ inline const Field* Get (const std::string& ss) const;
+
+ // --------------------------------------------------------
+ /** Access a field of the structure by its index */
+ inline const Field& operator [] (const size_t i) const;
+
+ // --------------------------------------------------------
+ inline bool operator== (const Structure& other) const {
+ return name == other.name; // name is meant to be an unique identifier
+ }
+
+ // --------------------------------------------------------
+ inline bool operator!= (const Structure& other) const {
+ return name != other.name;
+ }
+
+public:
+
+ // --------------------------------------------------------
+ /** Try to read an instance of the structure from the stream
+ * and attempt to convert to `T`. This is done by
+ * an appropriate specialization. If none is available,
+ * a compiler complain is the result.
+ * @param dest Destination value to be written
+ * @param db File database, including input stream. */
+ template <typename T> inline void Convert (T& dest,
+ const FileDatabase& db) const;
+
+
+
+ // --------------------------------------------------------
+ // generic converter
+ template <typename T>
+ void Convert(boost::shared_ptr<ElemBase> in,const FileDatabase& db) const;
+
+ // --------------------------------------------------------
+ // generic allocator
+ template <typename T> boost::shared_ptr<ElemBase> Allocate() const;
+
+
+
+ // --------------------------------------------------------
+ // field parsing for 1d arrays
+ template <int error_policy, typename T, size_t M>
+ void ReadFieldArray(T (& out)[M], const char* name,
+ const FileDatabase& db) const;
+
+ // --------------------------------------------------------
+ // field parsing for 2d arrays
+ template <int error_policy, typename T, size_t M, size_t N>
+ void ReadFieldArray2(T (& out)[M][N], const char* name,
+ const FileDatabase& db) const;
+
+ // --------------------------------------------------------
+ // field parsing for pointer or dynamic array types
+ // (boost::shared_ptr or boost::shared_array)
+ template <int error_policy, template <typename> class TOUT, typename T>
+ void ReadFieldPtr(TOUT<T>& out, const char* name,
+ const FileDatabase& db) const;
+
+ // --------------------------------------------------------
+ // field parsing for static arrays of pointer or dynamic
+ // array types (boost::shared_ptr[] or boost::shared_array[])
+ template <int error_policy, template <typename> class TOUT, typename T, size_t N>
+ void ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
+ const FileDatabase& db) const;
+
+ // --------------------------------------------------------
+ // field parsing for `normal` values
+ template <int error_policy, typename T>
+ void ReadField(T& out, const char* name,
+ const FileDatabase& db) const;
+
+private:
+
+ // --------------------------------------------------------
+ template <template <typename> class TOUT, typename T>
+ void ResolvePointer(TOUT<T>& out, const Pointer & ptrval,
+ const FileDatabase& db, const Field& f) const;
+
+ // --------------------------------------------------------
+ template <template <typename> class TOUT, typename T>
+ void ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval,
+ const FileDatabase& db, const Field& f) const;
+
+ // --------------------------------------------------------
+ void ResolvePointer( boost::shared_ptr< FileOffset >& out, const Pointer & ptrval,
+ const FileDatabase& db, const Field& f) const;
+
+ // --------------------------------------------------------
+ inline const FileBlockHead* LocateFileBlockForAddress(
+ const Pointer & ptrval,
+ const FileDatabase& db) const;
+
+private:
+
+ // ------------------------------------------------------------------------------
+ template <typename T> T* _allocate(boost::shared_ptr<T>& out, size_t& s) const {
+ out = boost::shared_ptr<T>(new T());
+ s = 1;
+ return out.get();
+ }
+
+ template <typename T> T* _allocate(vector<T>& out, size_t& s) const {
+ out.resize(s);
+ return s ? &out.front() : NULL;
+ }
+
+ // --------------------------------------------------------
+ template <int error_policy>
+ struct _defaultInitializer {
+
+ template <typename T, unsigned int N>
+ void operator ()(T (& out)[N], const char* = NULL) {
+ for (unsigned int i = 0; i < N; ++i) {
+ out[i] = T();
+ }
+ }
+
+ template <typename T, unsigned int N, unsigned int M>
+ void operator ()(T (& out)[N][M], const char* = NULL) {
+ for (unsigned int i = 0; i < N; ++i) {
+ for (unsigned int j = 0; j < M; ++j) {
+ out[i][j] = T();
+ }
+ }
+ }
+
+ template <typename T>
+ void operator ()(T& out, const char* = NULL) {
+ out = T();
+ }
+ };
+
+private:
+
+ mutable size_t cache_idx;
+};
+
+// --------------------------------------------------------
+template <> struct Structure :: _defaultInitializer<ErrorPolicy_Warn> {
+
+ template <typename T>
+ void operator ()(T& out, const char* reason = "<add reason>") {
+ DefaultLogger::get()->warn(reason);
+
+ // ... and let the show go on
+ _defaultInitializer<0 /*ErrorPolicy_Igno*/>()(out);
+ }
+};
+
+template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
+
+ template <typename T>
+ void operator ()(T& /*out*/,const char* = "") {
+ // obviously, it is crucial that _DefaultInitializer is used
+ // only from within a catch clause.
+ throw;
+ }
+};
+
+// -------------------------------------------------------------------------------------------------------
+template <> inline void Structure :: ResolvePointer<boost::shared_ptr,ElemBase>(boost::shared_ptr<ElemBase>& out,
+ const Pointer & ptrval,
+ const FileDatabase& db,
+ const Field& f
+ ) const;
+
+
+// -------------------------------------------------------------------------------
+/** Represents the full data structure information for a single BLEND file.
+ * This data is extracted from the DNA1 chunk in the file.
+ * #DNAParser does the reading and represents currently the only place where
+ * DNA is altered.*/
+// -------------------------------------------------------------------------------
+class DNA
+{
+public:
+
+ typedef void (Structure::*ConvertProcPtr) (
+ boost::shared_ptr<ElemBase> in,
+ const FileDatabase&
+ ) const;
+
+ typedef boost::shared_ptr<ElemBase> (
+ Structure::*AllocProcPtr) () const;
+
+ typedef std::pair< AllocProcPtr, ConvertProcPtr > FactoryPair;
+
+public:
+
+ std::map<std::string, FactoryPair > converters;
+ vector<Structure > structures;
+ std::map<std::string, size_t> indices;
+
+public:
+
+ // --------------------------------------------------------
+ /** Access a structure by its canonical name, the pointer version returns NULL on failure
+ * while the reference version raises an error. */
+ inline const Structure& operator [] (const std::string& ss) const;
+ inline const Structure* Get (const std::string& ss) const;
+
+ // --------------------------------------------------------
+ /** Access a structure by its index */
+ inline const Structure& operator [] (const size_t i) const;
+
+public:
+
+ // --------------------------------------------------------
+ /** Add structure definitions for all the primitive types,
+ * i.e. integer, short, char, float */
+ void AddPrimitiveStructures();
+
+ // --------------------------------------------------------
+ /** Fill the @c converters member with converters for all
+ * known data types. The implementation of this method is
+ * in BlenderScene.cpp and is machine-generated.
+ * Converters are used to quickly handle objects whose
+ * exact data type is a runtime-property and not yet
+ * known at compile time (consier Object::data).*/
+ void RegisterConverters();
+
+
+ // --------------------------------------------------------
+ /** Take an input blob from the stream, interpret it according to
+ * a its structure name and convert it to the intermediate
+ * representation.
+ * @param structure Destination structure definition
+ * @param db File database.
+ * @return A null pointer if no appropriate converter is available.*/
+ boost::shared_ptr< ElemBase > ConvertBlobToStructure(
+ const Structure& structure,
+ const FileDatabase& db
+ ) const;
+
+ // --------------------------------------------------------
+ /** Find a suitable conversion function for a given Structure.
+ * Such a converter function takes a blob from the input
+ * stream, reads as much as it needs, and builds up a
+ * complete object in intermediate representation.
+ * @param structure Destination structure definition
+ * @param db File database.
+ * @return A null pointer in .first if no appropriate converter is available.*/
+ FactoryPair GetBlobToStructureConverter(
+ const Structure& structure,
+ const FileDatabase& db
+ ) const;
+
+
+#ifdef ASSIMP_BUILD_BLENDER_DEBUG
+ // --------------------------------------------------------
+ /** Dump the DNA to a text file. This is for debugging purposes.
+ * The output file is `dna.txt` in the current working folder*/
+ void DumpToFile();
+#endif
+
+ // --------------------------------------------------------
+ /** Extract array dimensions from a C array declaration, such
+ * as `...[4][6]`. Returned string would be `...[][]`.
+ * @param out
+ * @param array_sizes Receive maximally two array dimensions,
+ * the second element is set to 1 if the array is flat.
+ * Both are set to 1 if the input is not an array.
+ * @throw DeadlyImportError if more than 2 dimensions are
+ * encountered. */
+ static void ExtractArraySize(
+ const std::string& out,
+ size_t array_sizes[2]
+ );
+};
+
+// special converters for primitive types
+template <> inline void Structure :: Convert<int> (int& dest,const FileDatabase& db) const;
+template <> inline void Structure :: Convert<short> (short& dest,const FileDatabase& db) const;
+template <> inline void Structure :: Convert<char> (char& dest,const FileDatabase& db) const;
+template <> inline void Structure :: Convert<float> (float& dest,const FileDatabase& db) const;
+template <> inline void Structure :: Convert<double> (double& dest,const FileDatabase& db) const;
+template <> inline void Structure :: Convert<Pointer> (Pointer& dest,const FileDatabase& db) const;
+
+// -------------------------------------------------------------------------------
+/** Describes a master file block header. Each master file sections holds n
+ * elements of a certain SDNA structure (or otherwise unspecified data). */
+// -------------------------------------------------------------------------------
+struct FileBlockHead
+{
+ // points right after the header of the file block
+ StreamReaderAny::pos start;
+
+ std::string id;
+ size_t size;
+
+ // original memory address of the data
+ Pointer address;
+
+ // index into DNA
+ unsigned int dna_index;
+
+ // number of structure instances to follow
+ size_t num;
+
+
+
+ // file blocks are sorted by address to quickly locate specific memory addresses
+ bool operator < (const FileBlockHead& o) const {
+ return address.val < o.address.val;
+ }
+
+ // for std::upper_bound
+ operator const Pointer& () const {
+ return address;
+ }
+};
+
+// for std::upper_bound
+inline bool operator< (const Pointer& a, const Pointer& b) {
+ return a.val < b.val;
+}
+
+// -------------------------------------------------------------------------------
+/** Utility to read all master file blocks in turn. */
+// -------------------------------------------------------------------------------
+class SectionParser
+{
+public:
+
+ // --------------------------------------------------------
+ /** @param stream Inout stream, must point to the
+ * first section in the file. Call Next() once
+ * to have it read.
+ * @param ptr64 Pointer size in file is 64 bits? */
+ SectionParser(StreamReaderAny& stream,bool ptr64)
+ : stream(stream)
+ , ptr64(ptr64)
+ {
+ current.size = current.start = 0;
+ }
+
+public:
+
+ // --------------------------------------------------------
+ const FileBlockHead& GetCurrent() const {
+ return current;
+ }
+
+
+public:
+
+ // --------------------------------------------------------
+ /** Advance to the next section.
+ * @throw DeadlyImportError if the last chunk was passed. */
+ void Next();
+
+public:
+
+ FileBlockHead current;
+ StreamReaderAny& stream;
+ bool ptr64;
+};
+
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+// -------------------------------------------------------------------------------
+/** Import statistics, i.e. number of file blocks read*/
+// -------------------------------------------------------------------------------
+class Statistics {
+
+public:
+
+ Statistics ()
+ : fields_read ()
+ , pointers_resolved ()
+ , cache_hits ()
+// , blocks_read ()
+ , cached_objects ()
+ {}
+
+public:
+
+ /** total number of fields we read */
+ unsigned int fields_read;
+
+ /** total number of resolved pointers */
+ unsigned int pointers_resolved;
+
+ /** number of pointers resolved from the cache */
+ unsigned int cache_hits;
+
+ /** number of blocks (from FileDatabase::entries)
+ we did actually read from. */
+ // unsigned int blocks_read;
+
+ /** objects in FileData::cache */
+ unsigned int cached_objects;
+};
+#endif
+
+// -------------------------------------------------------------------------------
+/** The object cache - all objects addressed by pointers are added here. This
+ * avoids circular references and avoids object duplication. */
+// -------------------------------------------------------------------------------
+template <template <typename> class TOUT>
+class ObjectCache
+{
+public:
+
+ typedef std::map< Pointer, TOUT<ElemBase> > StructureCache;
+
+public:
+
+ ObjectCache(const FileDatabase& db)
+ : db(db)
+ {
+ // currently there are only ~400 structure records per blend file.
+ // we read only a small part of them and don't cache objects
+ // which we don't need, so this should suffice.
+ caches.reserve(64);
+ }
+
+public:
+
+ // --------------------------------------------------------
+ /** Check whether a specific item is in the cache.
+ * @param s Data type of the item
+ * @param out Output pointer. Unchanged if the
+ * cache doens't know the item yet.
+ * @param ptr Item address to look for. */
+ template <typename T> void get (
+ const Structure& s,
+ TOUT<T>& out,
+ const Pointer& ptr) const;
+
+ // --------------------------------------------------------
+ /** Add an item to the cache after the item has
+ * been fully read. Do not insert anything that
+ * may be faulty or might cause the loading
+ * to abort.
+ * @param s Data type of the item
+ * @param out Item to insert into the cache
+ * @param ptr address (cache key) of the item. */
+ template <typename T> void set
+ (const Structure& s,
+ const TOUT<T>& out,
+ const Pointer& ptr);
+
+private:
+
+ mutable vector<StructureCache> caches;
+ const FileDatabase& db;
+};
+
+// -------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------
+template <> class ObjectCache<Blender::vector>
+{
+public:
+
+ ObjectCache(const FileDatabase&) {}
+
+ template <typename T> void get(const Structure&, vector<T>& /*t*/, const Pointer&) {}
+ template <typename T> void set(const Structure&, const vector<T>&, const Pointer&) {}
+};
+
+#ifdef _MSC_VER
+# pragma warning(disable:4355)
+#endif
+
+// -------------------------------------------------------------------------------
+/** Memory representation of a full BLEND file and all its dependencies. The
+ * output aiScene is constructed from an instance of this data structure. */
+// -------------------------------------------------------------------------------
+class FileDatabase
+{
+ template <template <typename> class TOUT> friend class ObjectCache;
+
+public:
+
+
+ FileDatabase()
+ : _cacheArrays(*this)
+ , _cache(*this)
+ , next_cache_idx()
+ {}
+
+public:
+
+ // publicly accessible fields
+ bool i64bit;
+ bool little;
+
+ DNA dna;
+ boost::shared_ptr< StreamReaderAny > reader;
+ vector< FileBlockHead > entries;
+
+public:
+
+ Statistics& stats() const {
+ return _stats;
+ }
+
+ // For all our templates to work on both shared_ptr's and vector's
+ // using the same code, a dummy cache for arrays is provided. Actually,
+ // arrays of objects are never cached because we can't easily
+ // ensure their proper destruction.
+ template <typename T>
+ ObjectCache<boost::shared_ptr>& cache(boost::shared_ptr<T>& /* in */) const {
+ return _cache;
+ }
+
+ template <typename T>
+ ObjectCache<vector>& cache(vector<T>& /*in*/) const {
+ return _cacheArrays;
+ }
+
+private:
+
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ mutable Statistics _stats;
+#endif
+
+ mutable ObjectCache<vector> _cacheArrays;
+ mutable ObjectCache<boost::shared_ptr> _cache;
+
+ mutable size_t next_cache_idx;
+};
+
+#ifdef _MSC_VER
+# pragma warning(default:4355)
+#endif
+
+// -------------------------------------------------------------------------------
+/** Factory to extract a #DNA from the DNA1 file block in a BLEND file. */
+// -------------------------------------------------------------------------------
+class DNAParser
+{
+
+public:
+
+ /** Bind the parser to a empty DNA and an input stream */
+ DNAParser(FileDatabase& db)
+ : db(db)
+ {}
+
+public:
+
+ // --------------------------------------------------------
+ /** Locate the DNA in the file and parse it. The input
+ * stream is expected to point to the beginning of the DN1
+ * chunk at the time this method is called and is
+ * undefined afterwards.
+ * @throw DeadlyImportError if the DNA cannot be read.
+ * @note The position of the stream pointer is undefined
+ * afterwards.*/
+ void Parse ();
+
+public:
+
+ /** Obtain a reference to the extracted DNA information */
+ const Blender::DNA& GetDNA() const {
+ return db.dna;
+ }
+
+private:
+
+ FileDatabase& db;
+};
+
+ } // end Blend
+} // end Assimp
+
+#include "BlenderDNA.inl"
+
+#endif
diff --git a/3rdparty/assimp/code/BlenderDNA.inl b/3rdparty/assimp/code/BlenderDNA.inl
new file mode 100644
index 000000000..c1be2546c
--- /dev/null
+++ b/3rdparty/assimp/code/BlenderDNA.inl
@@ -0,0 +1,706 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BlenderDNA.inl
+ * @brief Blender `DNA` (file format specification embedded in
+ * blend file itself) loader.
+ */
+#ifndef INCLUDED_AI_BLEND_DNA_INL
+#define INCLUDED_AI_BLEND_DNA_INL
+
+namespace Assimp {
+ namespace Blender {
+
+//--------------------------------------------------------------------------------
+const Field& Structure :: operator [] (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ if (it == indices.end()) {
+ throw Error((Formatter::format(),
+ "BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`"
+ ));
+ }
+
+ return fields[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Field* Structure :: Get (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ return it == indices.end() ? NULL : &fields[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Field& Structure :: operator [] (const size_t i) const
+{
+ if (i >= fields.size()) {
+ throw Error((Formatter::format(),
+ "BlendDNA: There is no field with index `",i,"` in structure `",name,"`"
+ ));
+ }
+
+ return fields[i];
+}
+
+//--------------------------------------------------------------------------------
+template <typename T> boost::shared_ptr<ElemBase> Structure :: Allocate() const
+{
+ return boost::shared_ptr<T>(new T());
+}
+
+//--------------------------------------------------------------------------------
+template <typename T> void Structure :: Convert(
+ boost::shared_ptr<ElemBase> in,
+ const FileDatabase& db) const
+{
+ Convert<T> (*static_cast<T*> ( in.get() ),db);
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, typename T, size_t M>
+void Structure :: ReadFieldArray(T (& out)[M], const char* name, const FileDatabase& db) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ try {
+ const Field& f = (*this)[name];
+ const Structure& s = db.dna[f.type];
+
+ // is the input actually an array?
+ if (!(f.flags & FieldFlag_Array)) {
+ throw Error((Formatter::format(),"Field `",name,"` of structure `",
+ this->name,"` ought to be an array of size ",M
+ ));
+ }
+
+ db.reader->IncPtr(f.offset);
+
+ // size conversions are always allowed, regardless of error_policy
+ unsigned int i = 0;
+ for (; i < std::min(f.array_sizes[0],M); ++i) {
+ s.Convert(out[i],db);
+ }
+ for (; i < M; ++i) {
+ _defaultInitializer<ErrorPolicy_Igno>()(out[i]);
+ }
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, typename T, size_t M, size_t N>
+void Structure :: ReadFieldArray2(T (& out)[M][N], const char* name, const FileDatabase& db) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ try {
+ const Field& f = (*this)[name];
+ const Structure& s = db.dna[f.type];
+
+ // is the input actually an array?
+ if (!(f.flags & FieldFlag_Array)) {
+ throw Error((Formatter::format(),"Field `",name,"` of structure `",
+ this->name,"` ought to be an array of size ",M,"*",N
+ ));
+ }
+
+ db.reader->IncPtr(f.offset);
+
+ // size conversions are always allowed, regardless of error_policy
+ unsigned int i = 0;
+ for (; i < std::min(f.array_sizes[0],M); ++i) {
+ unsigned int j = 0;
+ for (; j < std::min(f.array_sizes[1],N); ++j) {
+ s.Convert(out[i][j],db);
+ }
+ for (; j < N; ++j) {
+ _defaultInitializer<ErrorPolicy_Igno>()(out[i][j]);
+ }
+ }
+ for (; i < M; ++i) {
+ _defaultInitializer<ErrorPolicy_Igno>()(out[i]);
+ }
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, template <typename> class TOUT, typename T>
+void Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabase& db) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ Pointer ptrval;
+ const Field* f;
+ try {
+ f = &(*this)[name];
+
+ // sanity check, should never happen if the genblenddna script is right
+ if (!(f->flags & FieldFlag_Pointer)) {
+ throw Error((Formatter::format(),"Field `",name,"` of structure `",
+ this->name,"` ought to be a pointer"));
+ }
+
+ db.reader->IncPtr(f->offset);
+ Convert(ptrval,db);
+ // actually it is meaningless on which Structure the Convert is called
+ // because the `Pointer` argument triggers a special implementation.
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+
+ out.reset();
+ return;
+ }
+
+ // resolve the pointer and load the corresponding structure
+ ResolvePointer(out,ptrval,db,*f);
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, template <typename> class TOUT, typename T, size_t N>
+void Structure :: ReadFieldPtr(TOUT<T> (&out)[N], const char* name,
+ const FileDatabase& db) const
+{
+ // XXX see if we can reduce this to call to the 'normal' ReadFieldPtr
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ Pointer ptrval[N];
+ const Field* f;
+ try {
+ f = &(*this)[name];
+
+ // sanity check, should never happen if the genblenddna script is right
+ if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) {
+ throw Error((Formatter::format(),"Field `",name,"` of structure `",
+ this->name,"` ought to be a pointer AND an array"));
+ }
+
+ db.reader->IncPtr(f->offset);
+
+ size_t i = 0;
+ for (; i < std::min(f->array_sizes[0],N); ++i) {
+ Convert(ptrval[i],db);
+ }
+ for (; i < N; ++i) {
+ _defaultInitializer<ErrorPolicy_Igno>()(ptrval[i]);
+ }
+
+ // actually it is meaningless on which Structure the Convert is called
+ // because the `Pointer` argument triggers a special implementation.
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ for (size_t i = 0; i < N; ++i) {
+ out[i].reset();
+ }
+ return;
+ }
+ for (size_t i = 0; i < N; ++i) {
+ // resolve the pointer and load the corresponding structure
+ ResolvePointer(out[i],ptrval[i],db,*f);
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, typename T>
+void Structure :: ReadField(T& out, const char* name, const FileDatabase& db) const
+{
+ const StreamReaderAny::pos old = db.reader->GetCurrentPos();
+ try {
+ const Field& f = (*this)[name];
+ // find the structure definition pertaining to this field
+ const Structure& s = db.dna[f.type];
+
+ db.reader->IncPtr(f.offset);
+ s.Convert(out,db);
+ }
+ catch (const Error& e) {
+ _defaultInitializer<error_policy>()(out,e.what());
+ }
+
+ // and recover the previous stream position
+ db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().fields_read;
+#endif
+}
+
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT, typename T>
+void Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const FileDatabase& db, const Field& f) const
+{
+ out.reset();
+ if (!ptrval.val) {
+ return;
+ }
+ const Structure& s = db.dna[f.type];
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+
+ // also determine the target type from the block header
+ // and check if it matches the type which we expect.
+ const Structure& ss = db.dna[block->dna_index];
+ if (ss != s) {
+ throw Error((Formatter::format(),"Expected target to be of type `",s.name,
+ "` but seemingly it is a `",ss.name,"` instead"
+ ));
+ }
+
+ // try to retrieve the object from the cache
+ db.cache(out).get(s,out,ptrval);
+ if (out) {
+ return;
+ }
+
+ // seek to this location, but save the previous stream pointer.
+ const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
+ db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
+ // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
+ // I really ought to improve StreamReader to work with 64 bit indices exclusively.
+
+ // continue conversion after allocating the required storage
+ size_t num = block->size / ss.size;
+ T* o = _allocate(out,num);
+
+ // cache the object before we convert it to avoid cyclic recursion.
+ db.cache(out).set(s,out,ptrval);
+
+ for (size_t i = 0; i < num; ++i,++o) {
+ s.Convert(*o,db);
+ }
+
+ db.reader->SetCurrentPos(pold);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ if (out) {
+ ++db.stats().pointers_resolved;
+ }
+#endif
+}
+
+//--------------------------------------------------------------------------------
+inline void Structure :: ResolvePointer( boost::shared_ptr< FileOffset >& out, const Pointer & ptrval, const FileDatabase& db, const Field& /* f */ ) const
+{
+ // Currently used exclusively by PackedFile::data to represent
+ // a simple offset into the mapped BLEND file.
+ out.reset();
+ if (!ptrval.val) {
+ return;
+ }
+
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+
+ out = boost::shared_ptr< FileOffset > (new FileOffset());
+ out->val = block->start+ static_cast<size_t>((ptrval.val - block->address.val) );
+}
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT, typename T>
+void Structure :: ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval, const FileDatabase& db, const Field& f) const
+{
+ // This is a function overload, not a template specialization. According to
+ // the partial ordering rules, it should be selected by the compiler
+ // for array-of-pointer inputs, i.e. Object::mats.
+
+ out.reset();
+ if (!ptrval.val) {
+ return;
+ }
+
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+ const size_t num = block->size / (db.i64bit?8:4);
+
+ // keep the old stream position
+ const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
+ db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
+
+ // allocate raw storage for the array
+ out.resize(num);
+ for (size_t i = 0; i< num; ++i) {
+ Pointer val;
+ Convert(val,db);
+
+ // and resolve the pointees
+ ResolvePointer(out[i],val,db,f);
+ }
+
+ db.reader->SetCurrentPos(pold);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: ResolvePointer<boost::shared_ptr,ElemBase>(boost::shared_ptr<ElemBase>& out,
+ const Pointer & ptrval,
+ const FileDatabase& db,
+ const Field& /* f */
+) const
+{
+ // Special case when the data type needs to be determined at runtime.
+ // Less secure than in the `strongly-typed` case.
+
+ out.reset();
+ if (!ptrval.val) {
+ return;
+ }
+
+ // find the file block the pointer is pointing to
+ const FileBlockHead* block = LocateFileBlockForAddress(ptrval,db);
+
+ // determine the target type from the block header
+ const Structure& s = db.dna[block->dna_index];
+
+ // try to retrieve the object from the cache
+ db.cache(out).get(s,out,ptrval);
+ if (out) {
+ return;
+ }
+
+ // seek to this location, but save the previous stream pointer.
+ const StreamReaderAny::pos pold = db.reader->GetCurrentPos();
+ db.reader->SetCurrentPos(block->start+ static_cast<size_t>((ptrval.val - block->address.val) ));
+ // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems.
+ // I really ought to improve StreamReader to work with 64 bit indices exclusively.
+
+ // continue conversion after allocating the required storage
+ DNA::FactoryPair builders = db.dna.GetBlobToStructureConverter(s,db);
+ if (!builders.first) {
+ // this might happen if DNA::RegisterConverters hasn't been called so far
+ // or if the target type is not contained in `our` DNA.
+ out.reset();
+ DefaultLogger::get()->warn((Formatter::format(),
+ "Failed to find a converter for the `",s.name,"` structure"
+ ));
+ return;
+ }
+
+ // allocate the object hull
+ out = (s.*builders.first)();
+
+ // cache the object immediately to prevent infinite recursion in a
+ // circular list with a single element (i.e. a self-referencing element).
+ db.cache(out).set(s,out,ptrval);
+
+ // and do the actual conversion
+ (s.*builders.second)(out,db);
+ db.reader->SetCurrentPos(pold);
+
+ // store a pointer to the name string of the actual type
+ // in the object itself. This allows the conversion code
+ // to perform additional type checking.
+ out->dna_type = s.name.c_str();
+
+
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().pointers_resolved;
+#endif
+}
+
+//--------------------------------------------------------------------------------
+const FileBlockHead* Structure :: LocateFileBlockForAddress(const Pointer & ptrval, const FileDatabase& db) const
+{
+ // the file blocks appear in list sorted by
+ // with ascending base addresses so we can run a
+ // binary search to locate the pointee quickly.
+
+ // NOTE: Blender seems to distinguish between side-by-side
+ // data (stored in the same data block) and far pointers,
+ // which are only used for structures starting with an ID.
+ // We don't need to make this distinction, our algorithm
+ // works regardless where the data is stored.
+ vector<FileBlockHead>::const_iterator it = std::lower_bound(db.entries.begin(),db.entries.end(),ptrval);
+ if (it == db.entries.end()) {
+ // this is crucial, pointers may not be invalid.
+ // this is either a corrupted file or an attempted attack.
+ throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
+ std::hex,ptrval.val,", no file block falls into this address range"
+ ));
+ }
+ if (ptrval.val >= (*it).address.val + (*it).size) {
+ throw DeadlyImportError((Formatter::format(),"Failure resolving pointer 0x",
+ std::hex,ptrval.val,", nearest file block starting at 0x",
+ (*it).address.val," ends at 0x",
+ (*it).address.val + (*it).size
+ ));
+ }
+ return &*it;
+}
+
+// ------------------------------------------------------------------------------------------------
+// NOTE: The MSVC debugger keeps showing up this annoying `a cast to a smaller data type has
+// caused a loss of data`-warning. Avoid this warning by a masking with an appropriate bitmask.
+
+template <typename T> struct signless;
+template <> struct signless<char> {typedef unsigned char type;};
+template <> struct signless<short> {typedef unsigned short type;};
+template <> struct signless<int> {typedef unsigned int type;};
+
+template <typename T>
+struct static_cast_silent {
+ template <typename V>
+ T operator()(V in) {
+ return static_cast<T>(in & static_cast<typename signless<T>::type>(-1));
+ }
+};
+
+template <> struct static_cast_silent<float> {
+ template <typename V> float operator()(V in) {
+ return static_cast<float> (in);
+ }
+};
+
+template <> struct static_cast_silent<double> {
+ template <typename V> double operator()(V in) {
+ return static_cast<double>(in);
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+template <typename T> inline void ConvertDispatcher(T& out, const Structure& in,const FileDatabase& db)
+{
+ if (in.name == "int") {
+ out = static_cast_silent<T>()(db.reader->GetU4());
+ }
+ else if (in.name == "short") {
+ out = static_cast_silent<T>()(db.reader->GetU2());
+ }
+ else if (in.name == "char") {
+ out = static_cast_silent<T>()(db.reader->GetU1());
+ }
+ else if (in.name == "float") {
+ out = static_cast<T>(db.reader->GetF4());
+ }
+ else if (in.name == "double") {
+ out = static_cast<T>(db.reader->GetF8());
+ }
+ else {
+ throw DeadlyImportError("Unknown source for conversion to primitive data type: "+in.name);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<int> (int& dest,const FileDatabase& db) const
+{
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<short> (short& dest,const FileDatabase& db) const
+{
+ // automatic rescaling from short to float and vice versa (seems to be used by normals)
+ if (name == "float") {
+ dest = static_cast<short>(db.reader->GetF4() * 32767.f);
+ //db.reader->IncPtr(-4);
+ return;
+ }
+ else if (name == "double") {
+ dest = static_cast<short>(db.reader->GetF8() * 32767.);
+ //db.reader->IncPtr(-8);
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<char> (char& dest,const FileDatabase& db) const
+{
+ // automatic rescaling from char to float and vice versa (seems useful for RGB colors)
+ if (name == "float") {
+ dest = static_cast<char>(db.reader->GetF4() * 255.f);
+ return;
+ }
+ else if (name == "double") {
+ dest = static_cast<char>(db.reader->GetF8() * 255.f);
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<float> (float& dest,const FileDatabase& db) const
+{
+ // automatic rescaling from char to float and vice versa (seems useful for RGB colors)
+ if (name == "char") {
+ dest = db.reader->GetI1() / 255.f;
+ return;
+ }
+ // automatic rescaling from short to float and vice versa (used by normals)
+ else if (name == "short") {
+ dest = db.reader->GetI2() / 32767.f;
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<double> (double& dest,const FileDatabase& db) const
+{
+ if (name == "char") {
+ dest = db.reader->GetI1() / 255.;
+ return;
+ }
+ else if (name == "short") {
+ dest = db.reader->GetI2() / 32767.;
+ return;
+ }
+ ConvertDispatcher(dest,*this,db);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <> inline void Structure :: Convert<Pointer> (Pointer& dest,const FileDatabase& db) const
+{
+ if (db.i64bit) {
+ dest.val = db.reader->GetU8();
+ //db.reader->IncPtr(-8);
+ return;
+ }
+ dest.val = db.reader->GetU4();
+ //db.reader->IncPtr(-4);
+}
+
+//--------------------------------------------------------------------------------
+const Structure& DNA :: operator [] (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ if (it == indices.end()) {
+ throw Error((Formatter::format(),
+ "BlendDNA: Did not find a structure named `",ss,"`"
+ ));
+ }
+
+ return structures[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Structure* DNA :: Get (const std::string& ss) const
+{
+ std::map<std::string, size_t>::const_iterator it = indices.find(ss);
+ return it == indices.end() ? NULL : &structures[(*it).second];
+}
+
+//--------------------------------------------------------------------------------
+const Structure& DNA :: operator [] (const size_t i) const
+{
+ if (i >= structures.size()) {
+ throw Error((Formatter::format(),
+ "BlendDNA: There is no structure with index `",i,"`"
+ ));
+ }
+
+ return structures[i];
+}
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: get (
+ const Structure& s,
+ TOUT<T>& out,
+ const Pointer& ptr
+) const {
+
+ if (s.cache_idx == static_cast<size_t>(-1)) {
+ s.cache_idx = db.next_cache_idx++;
+ caches.resize(db.next_cache_idx);
+ return;
+ }
+
+ typename StructureCache::const_iterator it = caches[s.cache_idx].find(ptr);
+ if (it != caches[s.cache_idx].end()) {
+ out = boost::static_pointer_cast<T>( (*it).second );
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().cache_hits;
+#endif
+ }
+ // otherwise, out remains untouched
+}
+
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT> template <typename T> void ObjectCache<TOUT> :: set (
+ const Structure& s,
+ const TOUT<T>& out,
+ const Pointer& ptr
+) {
+ if (s.cache_idx == static_cast<size_t>(-1)) {
+ s.cache_idx = db.next_cache_idx++;
+ caches.resize(db.next_cache_idx);
+ }
+ caches[s.cache_idx][ptr] = boost::static_pointer_cast<ElemBase>( out );
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ ++db.stats().cached_objects;
+#endif
+}
+
+}}
+#endif
diff --git a/3rdparty/assimp/code/BlenderIntermediate.h b/3rdparty/assimp/code/BlenderIntermediate.h
new file mode 100644
index 000000000..e8c969e9e
--- /dev/null
+++ b/3rdparty/assimp/code/BlenderIntermediate.h
@@ -0,0 +1,183 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BlenderIntermediate.h
+ * @brief Internal utility structures for the BlenderLoader. It also serves
+ * as master include file for the whole (internal) Blender subsystem.
+ */
+#ifndef INCLUDED_AI_BLEND_INTERMEDIATE_H
+#define INCLUDED_AI_BLEND_INTERMEDIATE_H
+
+#include "BlenderLoader.h"
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include "BlenderSceneGen.h"
+
+#define for_each(x,y) BOOST_FOREACH(x,y)
+
+namespace Assimp {
+namespace Blender {
+
+ // --------------------------------------------------------------------
+ /** Mini smart-array to avoid pulling in even more boost stuff. usable with vector and deque */
+ // --------------------------------------------------------------------
+ template <template <typename,typename> class TCLASS, typename T>
+ struct TempArray {
+ typedef TCLASS< T*,std::allocator<T*> > mywrap;
+
+ TempArray() {
+ }
+
+ ~TempArray () {
+ for_each(T* elem, arr) {
+ delete elem;
+ }
+ }
+
+ void dismiss() {
+ arr.clear();
+ }
+
+ mywrap* operator -> () {
+ return &arr;
+ }
+
+ operator mywrap& () {
+ return arr;
+ }
+
+ operator const mywrap& () const {
+ return arr;
+ }
+
+ mywrap& get () {
+ return arr;
+ }
+
+ const mywrap& get () const {
+ return arr;
+ }
+
+ T* operator[] (size_t idx) const {
+ return arr[idx];
+ }
+
+ T*& operator[] (size_t idx) {
+ return arr[idx];
+ }
+
+ private:
+ // no copy semantics
+ void operator= (const TempArray&) {
+ }
+
+ TempArray(const TempArray& arr) {
+ }
+
+ private:
+ mywrap arr;
+ };
+
+#ifdef _MSC_VER
+# pragma warning(disable:4351)
+#endif
+ // --------------------------------------------------------------------
+ /** ConversionData acts as intermediate storage location for
+ * the various ConvertXXX routines in BlenderImporter.*/
+ // --------------------------------------------------------------------
+ struct ConversionData
+ {
+ ConversionData(const FileDatabase& db)
+ : sentinel_cnt()
+ , next_texture()
+ , db(db)
+ {}
+
+ std::set<const Object*> objects;
+
+ TempArray <std::vector, aiMesh> meshes;
+ TempArray <std::vector, aiCamera> cameras;
+ TempArray <std::vector, aiLight> lights;
+ TempArray <std::vector, aiMaterial> materials;
+ TempArray <std::vector, aiTexture> textures;
+
+ // set of all materials referenced by at least one mesh in the scene
+ std::deque< boost::shared_ptr< Material > > materials_raw;
+
+ // counter to name sentinel textures inserted as substitutes for procedural textures.
+ unsigned int sentinel_cnt;
+
+ // next texture ID for each texture type, respectively
+ unsigned int next_texture[aiTextureType_UNKNOWN+1];
+
+ // original file data
+ const FileDatabase& db;
+ };
+#ifdef _MSC_VER
+# pragma warning(default:4351)
+#endif
+
+// ------------------------------------------------------------------------------------------------
+inline const char* GetTextureTypeDisplayString(Tex::Type t)
+{
+ switch (t) {
+ case Tex::Type_CLOUDS : return "Clouds";
+ case Tex::Type_WOOD : return "Wood";
+ case Tex::Type_MARBLE : return "Marble";
+ case Tex::Type_MAGIC : return "Magic";
+ case Tex::Type_BLEND : return "Blend";
+ case Tex::Type_STUCCI : return "Stucci";
+ case Tex::Type_NOISE : return "Noise";
+ case Tex::Type_PLUGIN : return "Plugin";
+ case Tex::Type_MUSGRAVE : return "Musgrave";
+ case Tex::Type_VORONOI : return "Voronoi";
+ case Tex::Type_DISTNOISE : return "DistortedNoise";
+ case Tex::Type_ENVMAP : return "EnvMap";
+ case Tex::Type_IMAGE : return "Image";
+ default:
+ break;
+ }
+ return "<Unknown>";
+}
+
+} // ! Blender
+} // ! Assimp
+
+#endif // ! INCLUDED_AI_BLEND_INTERMEDIATE_H
diff --git a/3rdparty/assimp/code/BlenderLoader.cpp b/3rdparty/assimp/code/BlenderLoader.cpp
new file mode 100644
index 000000000..47f3e58d3
--- /dev/null
+++ b/3rdparty/assimp/code/BlenderLoader.cpp
@@ -0,0 +1,1003 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BlenderLoader.cpp
+ * @brief Implementation of the Blender3D importer class.
+ */
+#include "AssimpPCH.h"
+
+//#define ASSIMP_BUILD_NO_COMPRESSED_BLEND
+// Uncomment this to disable support for (gzip)compressed .BLEND files
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderIntermediate.h"
+#include "BlenderModifier.h"
+
+#include "StreamReader.h"
+#include "TinyFormatter.h"
+#include "MemoryIOWrapper.h"
+
+// zlib is needed for compressed blend files
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
+# ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+# include <zlib.h>
+# else
+# include "../contrib/zlib/zlib.h"
+# endif
+#endif
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+using namespace Assimp::Formatter;
+
+static const aiLoaderDesc blenderDesc = {
+ "Blender 3D Importer \nhttp://www.blender3d.org",
+ "Alexander Gessler <alexander.gessler@gmx.net>",
+ "",
+ "",
+ aiLoaderFlags_SupportBinaryFlavour | aiLoaderFlags_Experimental,
+ 0,
+ 0,
+ 2,
+ 50
+};
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BlenderImporter::BlenderImporter()
+: modifier_cache(new BlenderModifierShowcase())
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+BlenderImporter::~BlenderImporter()
+{
+ delete modifier_cache;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool BlenderImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string& extension = GetExtension(pFile);
+ if (extension == "blend") {
+ return true;
+ }
+
+ else if ((!extension.length() || checkSig) && pIOHandler) {
+ // note: this won't catch compressed files
+ const char* tokens[] = {"BLENDER"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// List all extensions handled by this loader
+void BlenderImporter::GetExtensionList(std::set<std::string>& app)
+{
+ app.insert("blend");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader registry entry
+const aiLoaderDesc& BlenderImporter::GetInfo () const
+{
+ return blenderDesc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void BlenderImporter::SetupProperties(const Importer* /* pImp */)
+{
+ // nothing to be done for the moment
+}
+
+struct free_it
+{
+ free_it(void* free) : free(free) {}
+ ~free_it() {
+ ::free(this->free);
+ }
+
+ void* free;
+};
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void BlenderImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
+ Bytef* dest = NULL;
+ free_it free_it_really(dest);
+#endif
+
+ FileDatabase file;
+ boost::shared_ptr<IOStream> stream(pIOHandler->Open(pFile,"rb"));
+ if (!stream) {
+ ThrowException("Could not open file for reading");
+ }
+
+ char magic[8] = {0};
+ stream->Read(magic,7,1);
+ if (strcmp(magic,"BLENDER")) {
+ // Check for presence of the gzip header. If yes, assume it is a
+ // compressed blend file and try uncompressing it, else fail. This is to
+ // avoid uncompressing random files which our loader might end up with.
+#ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND
+ ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?");
+#else
+
+ if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) {
+ ThrowException("BLENDER magic bytes are missing, couldn't find GZIP header either");
+ }
+
+ LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file");
+ if (magic[2] != 8) {
+ ThrowException("Unsupported GZIP compression method");
+ }
+
+ // http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
+ stream->Seek(0L,aiOrigin_SET);
+ boost::shared_ptr<StreamReaderLE> reader = boost::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
+
+ // build a zlib stream
+ z_stream zstream;
+ zstream.opaque = Z_NULL;
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.data_type = Z_BINARY;
+
+ // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib
+ inflateInit2(&zstream, 16+MAX_WBITS);
+
+ zstream.next_in = reinterpret_cast<Bytef*>( reader->GetPtr() );
+ zstream.avail_in = reader->GetRemainingSize();
+
+ size_t total = 0l;
+
+ // and decompress the data .... do 1k chunks in the hope that we won't kill the stack
+#define MYBLOCK 1024
+ Bytef block[MYBLOCK];
+ int ret;
+ do {
+ zstream.avail_out = MYBLOCK;
+ zstream.next_out = block;
+ ret = inflate(&zstream, Z_NO_FLUSH);
+
+ if (ret != Z_STREAM_END && ret != Z_OK) {
+ ThrowException("Failure decompressing this file using gzip, seemingly it is NOT a compressed .BLEND file");
+ }
+ const size_t have = MYBLOCK - zstream.avail_out;
+ total += have;
+ dest = reinterpret_cast<Bytef*>( realloc(dest,total) );
+ memcpy(dest + total - have,block,have);
+ }
+ while (ret != Z_STREAM_END);
+
+ // terminate zlib
+ inflateEnd(&zstream);
+
+ // replace the input stream with a memory stream
+ stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t*>(dest),total));
+
+ // .. and retry
+ stream->Read(magic,7,1);
+ if (strcmp(magic,"BLENDER")) {
+ ThrowException("Found no BLENDER magic word in decompressed GZIP file");
+ }
+#endif
+ }
+
+ file.i64bit = (stream->Read(magic,1,1),magic[0]=='-');
+ file.little = (stream->Read(magic,1,1),magic[0]=='v');
+
+ stream->Read(magic,3,1);
+ magic[3] = '\0';
+
+ LogInfo((format(),"Blender version is ",magic[0],".",magic+1,
+ " (64bit: ",file.i64bit?"true":"false",
+ ", little endian: ",file.little?"true":"false",")"
+ ));
+
+ ParseBlendFile(file,stream);
+
+ Scene scene;
+ ExtractScene(scene,file);
+
+ ConvertBlendFile(pScene,scene,file);
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ParseBlendFile(FileDatabase& out, boost::shared_ptr<IOStream> stream)
+{
+ out.reader = boost::shared_ptr<StreamReaderAny>(new StreamReaderAny(stream,out.little));
+
+ DNAParser dna_reader(out);
+ const DNA* dna = NULL;
+
+ out.entries.reserve(128); { // even small BLEND files tend to consist of many file blocks
+ SectionParser parser(*out.reader.get(),out.i64bit);
+
+ // first parse the file in search for the DNA and insert all other sections into the database
+ while ((parser.Next(),1)) {
+ const FileBlockHead& head = parser.GetCurrent();
+
+ if (head.id == "ENDB") {
+ break; // only valid end of the file
+ }
+ else if (head.id == "DNA1") {
+ dna_reader.Parse();
+ dna = &dna_reader.GetDNA();
+ continue;
+ }
+
+ out.entries.push_back(head);
+ }
+ }
+ if (!dna) {
+ ThrowException("SDNA not found");
+ }
+
+ std::sort(out.entries.begin(),out.entries.end());
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ExtractScene(Scene& out, const FileDatabase& file)
+{
+ const FileBlockHead* block = NULL;
+ std::map<std::string,size_t>::const_iterator it = file.dna.indices.find("Scene");
+ if (it == file.dna.indices.end()) {
+ ThrowException("There is no `Scene` structure record");
+ }
+
+ const Structure& ss = file.dna.structures[(*it).second];
+
+ // we need a scene somewhere to start with.
+ for_each(const FileBlockHead& bl,file.entries) {
+ if (bl.id == "SC") {
+ block = &bl;
+ break;
+ }
+ }
+
+ if (!block) {
+ ThrowException("There is not a single `Scene` record to load");
+ }
+
+ file.reader->SetCurrentPos(block->start);
+ ss.Convert(out,file);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+ DefaultLogger::get()->info((format(),
+ "(Stats) Fields read: " ,file.stats().fields_read,
+ ", pointers resolved: " ,file.stats().pointers_resolved,
+ ", cache hits: " ,file.stats().cache_hits,
+ ", cached objects: " ,file.stats().cached_objects
+ ));
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ConvertBlendFile(aiScene* out, const Scene& in,const FileDatabase& file)
+{
+ ConversionData conv(file);
+
+ // FIXME it must be possible to take the hierarchy directly from
+ // the file. This is terrible. Here, we're first looking for
+ // all objects which don't have parent objects at all -
+ std::deque<const Object*> no_parents;
+ for (boost::shared_ptr<Base> cur = boost::static_pointer_cast<Base> ( in.base.first ); cur; cur = cur->next) {
+ if (cur->object) {
+ if (!cur->object->parent) {
+ no_parents.push_back(cur->object.get());
+ }
+ else conv.objects.insert(cur->object.get());
+ }
+ }
+ for (boost::shared_ptr<Base> cur = in.basact; cur; cur = cur->next) {
+ if (cur->object) {
+ if (cur->object->parent) {
+ conv.objects.insert(cur->object.get());
+ }
+ }
+ }
+
+ if (no_parents.empty()) {
+ ThrowException("Expected at least one object with no parent");
+ }
+
+ aiNode* root = out->mRootNode = new aiNode("<BlenderRoot>");
+
+ root->mNumChildren = static_cast<unsigned int>(no_parents.size());
+ root->mChildren = new aiNode*[root->mNumChildren]();
+ for (unsigned int i = 0; i < root->mNumChildren; ++i) {
+ root->mChildren[i] = ConvertNode(in, no_parents[i], conv);
+ root->mChildren[i]->mParent = root;
+ }
+
+ BuildMaterials(conv);
+
+ if (conv.meshes->size()) {
+ out->mMeshes = new aiMesh*[out->mNumMeshes = static_cast<unsigned int>( conv.meshes->size() )];
+ std::copy(conv.meshes->begin(),conv.meshes->end(),out->mMeshes);
+ conv.meshes.dismiss();
+ }
+
+ if (conv.lights->size()) {
+ out->mLights = new aiLight*[out->mNumLights = static_cast<unsigned int>( conv.lights->size() )];
+ std::copy(conv.lights->begin(),conv.lights->end(),out->mLights);
+ conv.lights.dismiss();
+ }
+
+ if (conv.cameras->size()) {
+ out->mCameras = new aiCamera*[out->mNumCameras = static_cast<unsigned int>( conv.cameras->size() )];
+ std::copy(conv.cameras->begin(),conv.cameras->end(),out->mCameras);
+ conv.cameras.dismiss();
+ }
+
+ if (conv.materials->size()) {
+ out->mMaterials = new aiMaterial*[out->mNumMaterials = static_cast<unsigned int>( conv.materials->size() )];
+ std::copy(conv.materials->begin(),conv.materials->end(),out->mMaterials);
+ conv.materials.dismiss();
+ }
+
+ if (conv.textures->size()) {
+ out->mTextures = new aiTexture*[out->mNumTextures = static_cast<unsigned int>( conv.textures->size() )];
+ std::copy(conv.textures->begin(),conv.textures->end(),out->mTextures);
+ conv.textures.dismiss();
+ }
+
+ // acknowledge that the scene might come out incomplete
+ // by Assimps definition of `complete`: blender scenes
+ // can consist of thousands of cameras or lights with
+ // not a single mesh between them.
+ if (!out->mNumMeshes) {
+ out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ResolveImage(MaterialHelper* out, const Material* /* mat */, const MTex* /*tex*/, const Image* img, ConversionData& conv_data)
+{
+ // mat; tex; conv_data;
+ aiString name;
+
+ // check if the file contents are bundled with the BLEND file
+ if (img->packedfile) {
+ name.data[0] = '*';
+ name.length = 1+ ASSIMP_itoa10(name.data+1,MAXLEN-1,conv_data.textures->size());
+
+ conv_data.textures->push_back(new aiTexture());
+ aiTexture* tex = conv_data.textures->back();
+
+ // usually 'img->name' will be the original file name of the embedded textures,
+ // so we can extract the file extension from it.
+ const size_t nlen = strlen( img->name );
+ const char* s = img->name+nlen, *e = s;
+
+ while (s >= img->name && *s != '.')--s;
+
+ tex->achFormatHint[0] = s+1>e ? '\0' : s[1];
+ tex->achFormatHint[1] = s+2>e ? '\0' : s[2];
+ tex->achFormatHint[2] = s+3>e ? '\0' : s[3];
+ tex->achFormatHint[3] = '\0';
+
+ // tex->mHeight = 0;
+ tex->mWidth = img->packedfile->size;
+ uint8_t* ch = new uint8_t[tex->mWidth];
+
+ conv_data.db.reader->SetCurrentPos(static_cast<size_t>( img->packedfile->data->val));
+ conv_data.db.reader->CopyAndAdvance(ch,tex->mWidth);
+
+ tex->pcData = reinterpret_cast<aiTexel*>(ch);
+
+ LogInfo("Reading embedded texture, original file was "+std::string(img->name));
+ }
+ else {
+ name = aiString( img->name );
+ }
+ out->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(
+ conv_data.next_texture[aiTextureType_DIFFUSE]++)
+ );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::AddSentinelTexture(MaterialHelper* out, const Material* /*mat*/, const MTex* tex, ConversionData& conv_data)
+{
+ // mat; tex; conv_data;
+
+ aiString name;
+ name.length = sprintf(name.data, "Procedural,num=%i,type=%s",conv_data.sentinel_cnt++,
+ GetTextureTypeDisplayString(tex->tex->type)
+ );
+ out->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(
+ conv_data.next_texture[aiTextureType_DIFFUSE]++)
+ );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ResolveTexture(MaterialHelper* out, const Material* mat, const MTex* tex, ConversionData& conv_data)
+{
+ const Tex* rtex = tex->tex.get();
+ if (!rtex || !rtex->type) {
+ return;
+ }
+
+ // We can't support most of the texture types because the're mostly procedural.
+ // These are substituted by a dummy texture.
+ const char* dispnam = "";
+ switch( rtex->type )
+ {
+ // these are listed in blender's UI
+ case Tex::Type_CLOUDS :
+ case Tex::Type_WOOD :
+ case Tex::Type_MARBLE :
+ case Tex::Type_MAGIC :
+ case Tex::Type_BLEND :
+ case Tex::Type_STUCCI :
+ case Tex::Type_NOISE :
+ case Tex::Type_PLUGIN :
+ case Tex::Type_MUSGRAVE :
+ case Tex::Type_VORONOI :
+ case Tex::Type_DISTNOISE :
+ case Tex::Type_ENVMAP :
+
+ // these do no appear in the UI, why?
+ case Tex::Type_POINTDENSITY :
+ case Tex::Type_VOXELDATA :
+
+ LogWarn(std::string("Encountered a texture with an unsupported type: ")+dispnam);
+ AddSentinelTexture(out, mat, tex, conv_data);
+ break;
+
+ case Tex::Type_IMAGE :
+ if (!rtex->ima) {
+ LogError("A texture claims to be an Image, but no image reference is given");
+ break;
+ }
+ ResolveImage(out, mat, tex, rtex->ima.get(),conv_data);
+ break;
+
+ default:
+ ai_assert(false);
+ };
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::BuildMaterials(ConversionData& conv_data)
+{
+ conv_data.materials->reserve(conv_data.materials_raw.size());
+
+ // add a default material if necessary
+ unsigned int index = static_cast<unsigned int>( -1 );
+ for_each( aiMesh* mesh, conv_data.meshes.get() ) {
+ if (mesh->mMaterialIndex == static_cast<unsigned int>( -1 )) {
+
+ if (index == static_cast<unsigned int>( -1 )) {
+
+ // ok, we need to add a dedicated default material for some poor material-less meshes
+ boost::shared_ptr<Material> p(new Material());
+ strcpy( p->id.name+2, AI_DEFAULT_MATERIAL_NAME );
+
+ p->r = p->g = p->b = 0.6f;
+ p->specr = p->specg = p->specb = 0.6f;
+ p->ambir = p->ambig = p->ambib = 0.0f;
+ p->mirr = p->mirg = p->mirb = 0.0f;
+ p->emit = 0.f;
+ p->alpha = 0.f;
+
+ // XXX add more / or add default c'tor to Material
+
+ index = static_cast<unsigned int>( conv_data.materials_raw.size() );
+ conv_data.materials_raw.push_back(p);
+
+ LogInfo("Adding default material ...");
+ }
+ mesh->mMaterialIndex = index;
+ }
+ }
+
+ for_each(boost::shared_ptr<Material> mat, conv_data.materials_raw) {
+
+ // reset per material global counters
+ for (size_t i = 0; i < sizeof(conv_data.next_texture)/sizeof(conv_data.next_texture[0]);++i) {
+ conv_data.next_texture[i] = 0 ;
+ }
+
+ MaterialHelper* mout = new MaterialHelper();
+ conv_data.materials->push_back(mout);
+
+ // set material name
+ aiString name = aiString(mat->id.name+2); // skip over the name prefix 'MA'
+ mout->AddProperty(&name,AI_MATKEY_NAME);
+
+
+ // basic material colors
+ aiColor3D col(mat->r,mat->g,mat->b);
+ if (mat->r || mat->g || mat->b ) {
+
+ // Usually, zero diffuse color means no diffuse color at all in the equation - seemingly.
+ // So we ommit this member to express this intent.
+ mout->AddProperty(&col,1,AI_MATKEY_COLOR_DIFFUSE);
+ }
+
+ col = aiColor3D(mat->specr,mat->specg,mat->specb);
+ mout->AddProperty(&col,1,AI_MATKEY_COLOR_SPECULAR);
+
+ col = aiColor3D(mat->ambir,mat->ambig,mat->ambib);
+ mout->AddProperty(&col,1,AI_MATKEY_COLOR_AMBIENT);
+
+ col = aiColor3D(mat->mirr,mat->mirg,mat->mirb);
+ mout->AddProperty(&col,1,AI_MATKEY_COLOR_REFLECTIVE);
+
+ for (size_t i = 0; i < sizeof(mat->mtex) / sizeof(mat->mtex[0]); ++i) {
+ if (!mat->mtex[i]) {
+ continue;
+ }
+
+ ResolveTexture(mout,mat.get(),mat->mtex[i].get(),conv_data);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::CheckActualType(const ElemBase* dt, const char* check)
+{
+ ai_assert(dt);
+ if (strcmp(dt->dna_type,check)) {
+ ThrowException((format(),
+ "Expected object at ",std::hex,dt," to be of type `",check,
+ "`, but it claims to be a `",dt->dna_type,"`instead"
+ ));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::NotSupportedObjectType(const Object* obj, const char* type)
+{
+ LogWarn((format(), "Object `",obj->id.name,"` - type is unsupported: `",type, "`, skipping" ));
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::ConvertMesh(const Scene& /* in */, const Object* /* obj */, const Mesh* mesh,
+ ConversionData& conv_data, TempArray<std::vector,aiMesh>& temp
+ )
+{
+ typedef std::pair<const int,size_t> MyPair;
+ if (!mesh->totface || !mesh->totvert) {
+ return;
+ }
+
+ // some sanity checks
+ if (static_cast<size_t> ( mesh->totface ) > mesh->mface.size() ){
+ ThrowException("Number of faces is larger than the corresponding array");
+ }
+
+ if (static_cast<size_t> ( mesh->totvert ) > mesh->mvert.size()) {
+ ThrowException("Number of vertices is larger than the corresponding array");
+ }
+
+ // collect per-submesh numbers
+ std::map<int,size_t> per_mat;
+ for (int i = 0; i < mesh->totface; ++i) {
+
+ const MFace& mf = mesh->mface[i];
+ per_mat[ mf.mat_nr ]++;
+ }
+
+ // ... and allocate the corresponding meshes
+ const size_t old = temp->size();
+ temp->reserve(temp->size() + per_mat.size());
+
+ std::map<size_t,size_t> mat_num_to_mesh_idx;
+ for_each(MyPair& it, per_mat) {
+
+ mat_num_to_mesh_idx[it.first] = temp->size();
+ temp->push_back(new aiMesh());
+
+ aiMesh* out = temp->back();
+ out->mVertices = new aiVector3D[it.second*4];
+ out->mNormals = new aiVector3D[it.second*4];
+
+ //out->mNumFaces = 0
+ //out->mNumVertices = 0
+ out->mFaces = new aiFace[it.second]();
+
+ // all submeshes created from this mesh are named equally. this allows
+ // curious users to recover the original adjacency.
+ out->mName = aiString(mesh->id.name+2);
+ // skip over the name prefix 'ME'
+
+ // resolve the material reference and add this material to the set of
+ // output materials. The (temporary) material index is the index
+ // of the material entry within the list of resolved materials.
+ if (mesh->mat) {
+
+ if (static_cast<size_t> ( it.first ) >= mesh->mat.size() ) {
+ ThrowException("Material index is out of range");
+ }
+
+ boost::shared_ptr<Material> mat = mesh->mat[it.first];
+ const std::deque< boost::shared_ptr<Material> >::iterator has = std::find(
+ conv_data.materials_raw.begin(),
+ conv_data.materials_raw.end(),mat
+ );
+
+ if (has != conv_data.materials_raw.end()) {
+ out->mMaterialIndex = static_cast<unsigned int>( std::distance(conv_data.materials_raw.begin(),has));
+ }
+ else {
+ out->mMaterialIndex = static_cast<unsigned int>( conv_data.materials_raw.size() );
+ conv_data.materials_raw.push_back(mat);
+ }
+ }
+ else out->mMaterialIndex = static_cast<unsigned int>( -1 );
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+
+ const MFace& mf = mesh->mface[i];
+
+ aiMesh* const out = temp[ mat_num_to_mesh_idx[ mf.mat_nr ] ];
+ aiFace& f = out->mFaces[out->mNumFaces++];
+
+ f.mIndices = new unsigned int[ f.mNumIndices = mf.v4?4:3 ];
+ aiVector3D* vo = out->mVertices + out->mNumVertices;
+ aiVector3D* vn = out->mNormals + out->mNumVertices;
+
+ // XXX we can't fold this easily, because we are restricted
+ // to the member names from the BLEND file (v1,v2,v3,v4)
+ // which are assigned by the genblenddna.py script and
+ // cannot be changed without breaking the entire
+ // import process.
+
+ if (mf.v1 >= mesh->totvert) {
+ ThrowException("Vertex index v1 out of range");
+ }
+ const MVert* v = &mesh->mvert[mf.v1];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[0] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ // if (f.mNumIndices >= 2) {
+ if (mf.v2 >= mesh->totvert) {
+ ThrowException("Vertex index v2 out of range");
+ }
+ v = &mesh->mvert[mf.v2];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[1] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ if (mf.v3 >= mesh->totvert) {
+ ThrowException("Vertex index v3 out of range");
+ }
+ // if (f.mNumIndices >= 3) {
+ v = &mesh->mvert[mf.v3];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[2] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ if (mf.v4 >= mesh->totvert) {
+ ThrowException("Vertex index v4 out of range");
+ }
+ // if (f.mNumIndices >= 4) {
+ if (mf.v4) {
+ v = &mesh->mvert[mf.v4];
+ vo->x = v->co[0];
+ vo->y = v->co[1];
+ vo->z = v->co[2];
+ vn->x = v->no[0];
+ vn->y = v->no[1];
+ vn->z = v->no[2];
+ f.mIndices[3] = out->mNumVertices++;
+ ++vo;
+ ++vn;
+
+ out->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ }
+ else out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+
+ // }
+ // }
+ // }
+ }
+
+ // collect texture coordinates, they're stored in a separate per-face buffer
+ if (mesh->mtface) {
+ if (mesh->totface > static_cast<int> ( mesh->mtface.size())) {
+ ThrowException("Number of UV faces is larger than the corresponding UV face array (#1)");
+ }
+ for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
+ ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
+
+ (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
+ (*it)->mNumFaces = (*it)->mNumVertices = 0;
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+ const MTFace* v = &mesh->mtface[i];
+
+ aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
+ const aiFace& f = out->mFaces[out->mNumFaces++];
+
+ aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
+ for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) {
+ vo->x = v->uv[i][0];
+ vo->y = v->uv[i][1];
+ }
+ }
+ }
+
+ // collect texture coordinates, old-style (marked as deprecated in current blender sources)
+ if (mesh->tface) {
+ if (mesh->totface > static_cast<int> ( mesh->mtface.size())) {
+ ThrowException("Number of faces is larger than the corresponding UV face array (#2)");
+ }
+ for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
+ ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
+
+ (*it)->mTextureCoords[0] = new aiVector3D[(*it)->mNumVertices];
+ (*it)->mNumFaces = (*it)->mNumVertices = 0;
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+ const TFace* v = &mesh->tface[i];
+
+ aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
+ const aiFace& f = out->mFaces[out->mNumFaces++];
+
+ aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
+ for (unsigned int i = 0; i < f.mNumIndices; ++i,++vo,++out->mNumVertices) {
+ vo->x = v->uv[i][0];
+ vo->y = v->uv[i][1];
+ }
+ }
+ }
+
+ // collect vertex colors, stored separately as well
+ if (mesh->mcol) {
+ if (mesh->totface > static_cast<int> ( (mesh->mcol.size()/4)) ) {
+ ThrowException("Number of faces is larger than the corresponding color face array");
+ }
+ for (std::vector<aiMesh*>::iterator it = temp->begin()+old; it != temp->end(); ++it) {
+ ai_assert((*it)->mNumVertices && (*it)->mNumFaces);
+
+ (*it)->mColors[0] = new aiColor4D[(*it)->mNumVertices];
+ (*it)->mNumFaces = (*it)->mNumVertices = 0;
+ }
+
+ for (int i = 0; i < mesh->totface; ++i) {
+
+ aiMesh* const out = temp[ mat_num_to_mesh_idx[ mesh->mface[i].mat_nr ] ];
+ const aiFace& f = out->mFaces[out->mNumFaces++];
+
+ aiColor4D* vo = &out->mColors[0][out->mNumVertices];
+ for (unsigned int n = 0; n < f.mNumIndices; ++n, ++vo,++out->mNumVertices) {
+ const MCol* col = &mesh->mcol[(i<<2)+n];
+
+ vo->r = col->r;
+ vo->g = col->g;
+ vo->b = col->b;
+ vo->a = col->a;
+ }
+ for (unsigned int n = f.mNumIndices; n < 4; ++n) {};
+ }
+ }
+
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiCamera* BlenderImporter::ConvertCamera(const Scene& /* in */, const Object* /* obj */, const Camera* /* mesh */, ConversionData& /* conv_data */)
+{
+ ScopeGuard<aiCamera> out(new aiCamera());
+
+ return NULL ; //out.dismiss();
+}
+
+// ------------------------------------------------------------------------------------------------
+aiLight* BlenderImporter::ConvertLight(const Scene& /* in */, const Object* /* obj */, const Lamp* /* mesh */, ConversionData& /* conv_data */)
+{
+ ScopeGuard<aiLight> out(new aiLight());
+
+ return NULL ; //out.dismiss();
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, ConversionData& conv_data)
+{
+ std::deque<const Object*> children;
+ for (std::set<const Object*>::iterator it = conv_data.objects.begin(); it != conv_data.objects.end() ;) {
+ const Object* object = *it;
+ if (object->parent.get() == obj) {
+ children.push_back(object);
+
+ conv_data.objects.erase(it++);
+ continue;
+ }
+ ++it;
+ }
+
+ ScopeGuard<aiNode> node(new aiNode(obj->id.name+2)); // skip over the name prefix 'OB'
+ if (obj->data) {
+ switch (obj->type)
+ {
+ case Object :: Type_EMPTY:
+ break; // do nothing
+
+
+ // supported object types
+ case Object :: Type_MESH: {
+ const size_t old = conv_data.meshes->size();
+
+ CheckActualType(obj->data.get(),"Mesh");
+ ConvertMesh(in,obj,static_cast<const Mesh*>(obj->data.get()),conv_data,conv_data.meshes);
+
+ if (conv_data.meshes->size() > old) {
+ node->mMeshes = new unsigned int[node->mNumMeshes = static_cast<unsigned int>(conv_data.meshes->size()-old)];
+ for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+ node->mMeshes[i] = i + old;
+ }
+ }}
+ break;
+ case Object :: Type_LAMP: {
+ CheckActualType(obj->data.get(),"Lamp");
+ aiLight* mesh = ConvertLight(in,obj,static_cast<const Lamp*>(
+ obj->data.get()),conv_data);
+
+ if (mesh) {
+ conv_data.lights->push_back(mesh);
+ }}
+ break;
+ case Object :: Type_CAMERA: {
+ CheckActualType(obj->data.get(),"Camera");
+ aiCamera* mesh = ConvertCamera(in,obj,static_cast<const Camera*>(
+ obj->data.get()),conv_data);
+
+ if (mesh) {
+ conv_data.cameras->push_back(mesh);
+ }}
+ break;
+
+
+ // unsupported object types / log, but do not break
+ case Object :: Type_CURVE:
+ NotSupportedObjectType(obj,"Curve");
+ break;
+ case Object :: Type_SURF:
+ NotSupportedObjectType(obj,"Surface");
+ break;
+ case Object :: Type_FONT:
+ NotSupportedObjectType(obj,"Font");
+ break;
+ case Object :: Type_MBALL:
+ NotSupportedObjectType(obj,"MetaBall");
+ break;
+ case Object :: Type_WAVE:
+ NotSupportedObjectType(obj,"Wave");
+ break;
+ case Object :: Type_LATTICE:
+ NotSupportedObjectType(obj,"Lattice");
+ break;
+
+ // invalid or unknown type
+ default:
+ break;
+ }
+ }
+
+ for (unsigned int x = 0; x < 4; ++x) {
+ for (unsigned int y = 0; y < 4; ++y) {
+ node->mTransformation[y][x] = obj->parentinv[x][y];
+ }
+ }
+
+ aiMatrix4x4 m;
+ for (unsigned int x = 0; x < 4; ++x) {
+ for (unsigned int y = 0; y < 4; ++y) {
+ m[y][x] = obj->obmat[x][y];
+ }
+ }
+
+ node->mTransformation = m*node->mTransformation;
+
+ if (children.size()) {
+ node->mNumChildren = static_cast<unsigned int>(children.size());
+ aiNode** nd = node->mChildren = new aiNode*[node->mNumChildren]();
+ for_each (const Object* nobj,children) {
+ *nd = ConvertNode(in,nobj,conv_data);
+ (*nd++)->mParent = node;
+ }
+ }
+
+ // apply modifiers
+ modifier_cache->ApplyModifiers(*node,conv_data,in,*obj);
+
+ return node.dismiss();
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void BlenderImporter::ThrowException(const std::string& msg)
+{
+ throw DeadlyImportError("BLEND: "+msg);
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void BlenderImporter::LogWarn(const Formatter::format& message) {
+ DefaultLogger::get()->warn(std::string("BLEND: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void BlenderImporter::LogError(const Formatter::format& message) {
+ DefaultLogger::get()->error(std::string("BLEND: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void BlenderImporter::LogInfo(const Formatter::format& message) {
+ DefaultLogger::get()->info(std::string("BLEND: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void BlenderImporter::LogDebug(const Formatter::format& message) {
+ DefaultLogger::get()->debug(std::string("BLEND: ")+=message);
+}
+
+#endif
diff --git a/3rdparty/assimp/code/BlenderLoader.h b/3rdparty/assimp/code/BlenderLoader.h
new file mode 100644
index 000000000..0439b9abb
--- /dev/null
+++ b/3rdparty/assimp/code/BlenderLoader.h
@@ -0,0 +1,261 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BlenderLoader.h
+ * @brief Declaration of the Blender 3D (*.blend) importer class.
+ */
+#ifndef INCLUDED_AI_BLEND_LOADER_H
+#define INCLUDED_AI_BLEND_LOADER_H
+
+#include "BaseImporter.h"
+namespace Assimp {
+
+ // TinyFormatter.h
+ namespace Formatter {
+ template <typename T,typename TR, typename A> class basic_formatter;
+ typedef class basic_formatter< char, std::char_traits<char>, std::allocator<char> > format;
+ }
+
+ // BlenderDNA.h
+ namespace Blender {
+ class FileDatabase;
+ struct ElemBase;
+ }
+
+ // BlenderScene.h
+ namespace Blender {
+ struct Scene;
+ struct Object;
+ struct Mesh;
+ struct Camera;
+ struct Lamp;
+ struct MTex;
+ struct Image;
+ struct Material;
+ }
+
+ // BlenderIntermediate.h
+ namespace Blender {
+ struct ConversionData;
+ template <template <typename,typename> class TCLASS, typename T> struct TempArray;
+ }
+
+ // BlenderModifier.h
+ namespace Blender {
+ class BlenderModifierShowcase;
+ class BlenderModifier;
+ }
+
+enum aiLoaderFlags
+{
+ aiLoaderFlags_SupportAsciiFlavour = 0x1,
+ aiLoaderFlags_SupportBinaryFlavour = 0x2,
+ aiLoaderFlags_SupportCompressedFlavour = 0x4,
+
+ aiLoaderFlags_LimitedSupport = 0x8,
+
+ aiLoaderFlags_Experimental = 0x10,
+ aiLoaderFlags_Testing = 0x20,
+ aiLoaderFlags_Production = 0x40,
+};
+
+struct aiLoaderDesc
+{
+ const char* mName;
+ const char* mAuthor;
+ const char* mMaintainer;
+ const char* mComments;
+ unsigned int mFlags;
+
+ unsigned int mMinMajor;
+ unsigned int mMinMinor;
+ unsigned int mMaxMajor;
+ unsigned int mMaxMinor;
+};
+
+
+// -------------------------------------------------------------------------------------------
+/** Load blenders official binary format. The actual file structure (the `DNA` how they
+ * call it is outsourced to BlenderDNA.cpp/BlenderDNA.h. This class only performs the
+ * conversion from intermediate format to aiScene. */
+// -------------------------------------------------------------------------------------------
+class BlenderImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+
+ /** Constructor to be privately used by Importer */
+ BlenderImporter();
+
+ /** Destructor, private as well */
+ ~BlenderImporter();
+
+public:
+
+ // --------------------
+ bool CanRead( const std::string& pFile,
+ IOSystem* pIOHandler,
+ bool checkSig
+ ) const;
+
+protected:
+
+ // --------------------
+ const aiLoaderDesc& GetInfo () const;
+
+ // --------------------
+ void GetExtensionList(std::set<std::string>& app);
+
+ // --------------------
+ void SetupProperties(const Importer* pImp);
+
+ // --------------------
+ void InternReadFile( const std::string& pFile,
+ aiScene* pScene,
+ IOSystem* pIOHandler
+ );
+
+ // --------------------
+ void ParseBlendFile(Blender::FileDatabase& out,
+ boost::shared_ptr<IOStream> stream
+ );
+
+ // --------------------
+ void ExtractScene(Blender::Scene& out,
+ const Blender::FileDatabase& file
+ );
+
+ // --------------------
+ void ConvertBlendFile(aiScene* out,
+ const Blender::Scene& in,
+ const Blender::FileDatabase& file
+ );
+
+private:
+
+ // --------------------
+ aiNode* ConvertNode(const Blender::Scene& in,
+ const Blender::Object* obj,
+ Blender::ConversionData& conv_info
+ );
+
+ // --------------------
+ void ConvertMesh(const Blender::Scene& in,
+ const Blender::Object* obj,
+ const Blender::Mesh* mesh,
+ Blender::ConversionData& conv_data,
+ Blender::TempArray<std::vector,aiMesh>& temp
+ );
+
+ // --------------------
+ aiLight* ConvertLight(const Blender::Scene& in,
+ const Blender::Object* obj,
+ const Blender::Lamp* mesh,
+ Blender::ConversionData& conv_data
+ );
+
+ // --------------------
+ aiCamera* ConvertCamera(const Blender::Scene& in,
+ const Blender::Object* obj,
+ const Blender::Camera* mesh,
+ Blender::ConversionData& conv_data
+ );
+
+ // --------------------
+ void BuildMaterials(
+ Blender::ConversionData& conv_data
+ ) ;
+
+ // --------------------
+ void ResolveTexture(
+ MaterialHelper* out,
+ const Blender::Material* mat,
+ const Blender::MTex* tex,
+ Blender::ConversionData& conv_data
+ );
+
+ // --------------------
+ void ResolveImage(
+ MaterialHelper* out,
+ const Blender::Material* mat,
+ const Blender::MTex* tex,
+ const Blender::Image* img,
+ Blender::ConversionData& conv_data
+ );
+
+ void AddSentinelTexture(
+ MaterialHelper* out,
+ const Blender::Material* mat,
+ const Blender::MTex* tex,
+ Blender::ConversionData& conv_data
+ );
+
+private: // static stuff, mostly logging and error reporting.
+
+ // --------------------
+ static void CheckActualType(const Blender::ElemBase* dt,
+ const char* check
+ );
+
+ // --------------------
+ static void NotSupportedObjectType(const Blender::Object* obj,
+ const char* type
+ );
+
+ // -------------------------------------------------------------------
+ /** Prepend 'BLEND: ' and throw msg.*/
+ static void ThrowException(const std::string& msg);
+
+ // -------------------------------------------------------------------
+ /** @defgroup blog Prepend 'BLEND: ' and write @c message to log.*/
+ static void LogWarn (const Formatter::format& message); //! @ingroup blog
+ static void LogError (const Formatter::format& message); //! @ingroup blog
+ static void LogInfo (const Formatter::format& message); //! @ingroup blog
+ static void LogDebug (const Formatter::format& message); //! @ingroup blog
+
+private:
+
+ Blender::BlenderModifierShowcase* modifier_cache;
+
+}; // !class BlenderImporter
+
+} // end of namespace Assimp
+#endif // AI_UNREALIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/BlenderModifier.cpp b/3rdparty/assimp/code/BlenderModifier.cpp
new file mode 100644
index 000000000..eb3795a84
--- /dev/null
+++ b/3rdparty/assimp/code/BlenderModifier.cpp
@@ -0,0 +1,311 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BlenderModifier.cpp
+ * @brief Implementation of some blender modifiers (i.e subdivision, mirror).
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+#include "BlenderModifier.h"
+#include "SceneCombiner.h"
+#include "Subdivision.h"
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+template <typename T> BlenderModifier* god() {
+ return new T();
+}
+
+// add all available modifiers here
+typedef BlenderModifier* (*fpCreateModifier)();
+static const fpCreateModifier creators[] = {
+ &god<BlenderModifier_Mirror>,
+ &god<BlenderModifier_Subdivision>,
+
+ NULL // sentinel
+};
+
+// ------------------------------------------------------------------------------------------------
+// just testing out some new macros to simplify logging
+#define ASSIMP_LOG_WARN_F(string,...)\
+ DefaultLogger::get()->warn((Formatter::format(string),__VA_ARGS__))
+
+#define ASSIMP_LOG_ERROR_F(string,...)\
+ DefaultLogger::get()->error((Formatter::format(string),__VA_ARGS__))
+
+#define ASSIMP_LOG_DEBUG_F(string,...)\
+ DefaultLogger::get()->debug((Formatter::format(string),__VA_ARGS__))
+
+#define ASSIMP_LOG_INFO_F(string,...)\
+ DefaultLogger::get()->info((Formatter::format(string),__VA_ARGS__))
+
+
+#define ASSIMP_LOG_WARN(string)\
+ DefaultLogger::get()->warn(string)
+
+#define ASSIMP_LOG_ERROR(string)\
+ DefaultLogger::get()->error(string)
+
+#define ASSIMP_LOG_DEBUG(string)\
+ DefaultLogger::get()->debug(string)
+
+#define ASSIMP_LOG_INFO(string)\
+ DefaultLogger::get()->info(string)
+
+
+// ------------------------------------------------------------------------------------------------
+struct SharedModifierData : ElemBase
+{
+ ModifierData modifier;
+};
+
+// ------------------------------------------------------------------------------------------------
+void BlenderModifierShowcase::ApplyModifiers(aiNode& out, ConversionData& conv_data, const Scene& in, const Object& orig_object )
+{
+ size_t cnt = 0u, ful = 0u;
+
+ // NOTE: this cast is potentially unsafe by design, so we need to perform type checks before
+ // we're allowed to dereference the pointers without risking to crash. We might still be
+ // invoking UB btw - we're assuming that the ModifierData member of the respective modifier
+ // structures is at offset sizeof(vftable) with no padding.
+ const SharedModifierData* cur = boost::static_pointer_cast<const SharedModifierData> ( orig_object.modifiers.first.get() );
+ for (; cur; cur = boost::static_pointer_cast<const SharedModifierData> ( cur->modifier.next.get() ), ++ful) {
+ ai_assert(cur->dna_type);
+
+ const Structure* s = conv_data.db.dna.Get( cur->dna_type );
+ if (!s) {
+ ASSIMP_LOG_WARN_F("BlendModifier: could not resolve DNA name: ",cur->dna_type);
+ continue;
+ }
+
+ // this is a common trait of all XXXMirrorData structures in BlenderDNA
+ const Field* f = s->Get("modifier");
+ if (!f || f->offset != 0) {
+ ASSIMP_LOG_WARN("BlendModifier: expected a `modifier` member at offset 0");
+ continue;
+ }
+
+ s = conv_data.db.dna.Get( f->type );
+ if (!s || s->name != "ModifierData") {
+ ASSIMP_LOG_WARN("BlendModifier: expected a ModifierData structure as first member");
+ continue;
+ }
+
+ // now, we can be sure that we should be fine to dereference *cur* as
+ // ModifierData (with the above note).
+ const ModifierData& dat = cur->modifier;
+
+ const fpCreateModifier* curgod = creators;
+ std::vector< BlenderModifier* >::iterator curmod = cached_modifiers->begin(), endmod = cached_modifiers->end();
+
+ for (;*curgod;++curgod,++curmod) { // allocate modifiers on the fly
+ if (curmod == endmod) {
+ cached_modifiers->push_back((*curgod)());
+
+ endmod = cached_modifiers->end();
+ curmod = endmod-1;
+ }
+
+ BlenderModifier* const modifier = *curmod;
+ if (modifier->IsActive(dat)) {
+ modifier->DoIt(out,conv_data,*boost::static_pointer_cast<const ElemBase>(cur),in,orig_object);
+ cnt++;
+
+ curgod = NULL;
+ break;
+ }
+ }
+ if (curgod) {
+ ASSIMP_LOG_WARN_F("Couldn't find a handler for modifier: ",dat.name);
+ }
+ }
+
+ // Even though we managed to resolve some or all of the modifiers on this
+ // object, we still can't say whether our modifier implementations were
+ // able to fully do their job.
+ if (ful) {
+ ASSIMP_LOG_DEBUG_F("BlendModifier: found handlers for ",cnt," of ",ful," modifiers on `",orig_object.id.name,
+ "`, check log messages above for errors");
+ }
+}
+
+
+
+// ------------------------------------------------------------------------------------------------
+bool BlenderModifier_Mirror :: IsActive (const ModifierData& modin)
+{
+ return modin.type == ModifierData::eModifierType_Mirror;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderModifier_Mirror :: DoIt(aiNode& out, ConversionData& conv_data, const ElemBase& orig_modifier,
+ const Scene& /* in */,
+ const Object& orig_object )
+{
+ // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers()
+ const MirrorModifierData& mir = static_cast<const MirrorModifierData&>(orig_modifier);
+ ai_assert(mir.modifier.type == ModifierData::eModifierType_Mirror);
+
+ // XXX not entirely correct, mirroring on two axes results in 4 distinct objects in blender ...
+
+ // take all input meshes and clone them
+ for (unsigned int i = 0; i < out.mNumMeshes; ++i) {
+ aiMesh* mesh;
+ SceneCombiner::Copy(&mesh,conv_data.meshes[out.mMeshes[i]]);
+
+ const float xs = mir.flag & MirrorModifierData::Flags_AXIS_X ? -1.f : 1.f;
+ const float ys = mir.flag & MirrorModifierData::Flags_AXIS_Y ? -1.f : 1.f;
+ const float zs = mir.flag & MirrorModifierData::Flags_AXIS_Z ? -1.f : 1.f;
+
+ if (mir.mirror_ob) {
+ const aiVector3D center( mir.mirror_ob->obmat[3][0],mir.mirror_ob->obmat[3][1],mir.mirror_ob->obmat[3][2] );
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mVertices[i];
+
+ v.x = center.x + xs*(center.x - v.x);
+ v.y = center.y + ys*(center.y - v.y);
+ v.z = center.z + zs*(center.z - v.z);
+ }
+ }
+ else {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mVertices[i];
+ v.x *= xs;v.y *= ys;v.z *= zs;
+ }
+ }
+
+ if (mesh->mNormals) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mNormals[i];
+ v.x *= xs;v.y *= ys;v.z *= zs;
+ }
+ }
+
+ if (mesh->mTangents) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mTangents[i];
+ v.x *= xs;v.y *= ys;v.z *= zs;
+ }
+ }
+
+ if (mesh->mBitangents) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mBitangents[i];
+ v.x *= xs;v.y *= ys;v.z *= zs;
+ }
+ }
+
+ const float us = mir.flag & MirrorModifierData::Flags_MIRROR_U ? -1.f : 1.f;
+ const float vs = mir.flag & MirrorModifierData::Flags_MIRROR_V ? -1.f : 1.f;
+
+ for (unsigned int n = 0; mesh->HasTextureCoords(n); ++n) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ aiVector3D& v = mesh->mTextureCoords[n][i];
+ v.x *= us;v.y *= vs;
+ }
+ }
+
+ conv_data.meshes->push_back(mesh);
+ }
+ unsigned int* nind = new unsigned int[out.mNumMeshes*2];
+
+ std::copy(out.mMeshes,out.mMeshes+out.mNumMeshes,nind);
+ std::transform(out.mMeshes,out.mMeshes+out.mNumMeshes,nind+out.mNumMeshes,
+ std::bind1st(std::plus< unsigned int >(),out.mNumMeshes));
+
+ delete[] out.mMeshes;
+ out.mMeshes = nind;
+ out.mNumMeshes *= 2;
+
+ ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Mirror` modifier to `",
+ orig_object.id.name,"`");
+}
+
+
+
+
+// ------------------------------------------------------------------------------------------------
+bool BlenderModifier_Subdivision :: IsActive (const ModifierData& modin)
+{
+ return modin.type == ModifierData::eModifierType_Subsurf;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderModifier_Subdivision :: DoIt(aiNode& out, ConversionData& conv_data, const ElemBase& orig_modifier,
+ const Scene& /* in */,
+ const Object& orig_object )
+{
+ // hijacking the ABI, see the big note in BlenderModifierShowcase::ApplyModifiers()
+ const SubsurfModifierData& mir = static_cast<const SubsurfModifierData&>(orig_modifier);
+ ai_assert(mir.modifier.type == ModifierData::eModifierType_Subsurf);
+
+ Subdivider::Algorithm algo;
+ switch (mir.subdivType)
+ {
+ case SubsurfModifierData::TYPE_CatmullClarke:
+ algo = Subdivider::CATMULL_CLARKE;
+ break;
+
+ case SubsurfModifierData::TYPE_Simple:
+ ASSIMP_LOG_WARN("BlendModifier: The `SIMPLE` subdivision algorithm is not currently implemented, using Catmull-Clarke");
+ algo = Subdivider::CATMULL_CLARKE;
+ break;
+
+ default:
+ ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: ",mir.subdivType);
+ return;
+ };
+
+ boost::scoped_ptr<Subdivider> subd(Subdivider::Create(algo));
+ ai_assert(subd);
+
+ aiMesh** const meshes = &conv_data.meshes[conv_data.meshes->size() - out.mNumMeshes];
+ boost::scoped_array<aiMesh*> tempmeshes(new aiMesh*[out.mNumMeshes]());
+
+ subd->Subdivide(meshes,out.mNumMeshes,tempmeshes.get(),std::max( mir.renderLevels, mir.levels ),true);
+ std::copy(tempmeshes.get(),tempmeshes.get()+out.mNumMeshes,meshes);
+
+ ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Subdivision` modifier to `",
+ orig_object.id.name,"`");
+}
+
+#endif
diff --git a/3rdparty/assimp/code/BlenderModifier.h b/3rdparty/assimp/code/BlenderModifier.h
new file mode 100644
index 000000000..43e503fbc
--- /dev/null
+++ b/3rdparty/assimp/code/BlenderModifier.h
@@ -0,0 +1,155 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BlenderModifier.h
+ * @brief Declare dedicated helper classes to simulate some blender modifiers (i.e. mirror)
+ */
+#ifndef INCLUDED_AI_BLEND_MODIFIER_H
+#define INCLUDED_AI_BLEND_MODIFIER_H
+
+#include "BlenderIntermediate.h"
+#include "TinyFormatter.h"
+namespace Assimp {
+ namespace Blender {
+
+// -------------------------------------------------------------------------------------------
+/** Dummy base class for all blender modifiers. Modifiers are reused between imports, so
+ * they should be stateless and not try to cache model data. */
+// -------------------------------------------------------------------------------------------
+class BlenderModifier
+{
+public:
+
+ virtual ~BlenderModifier() {
+ }
+
+public:
+
+ // --------------------
+ /** Check if *this* modifier is active, given a ModifierData& block.*/
+ virtual bool IsActive( const ModifierData& /* modin */) {
+ return false;
+ }
+
+ // --------------------
+ /** Apply the modifier to a given output node. The original data used
+ * to construct the node is given as well. Not called unless IsActive()
+ * was called and gave positive response. */
+ virtual void DoIt(aiNode& /* out */,
+ ConversionData& /* conv_data */,
+ const ElemBase& orig_modifier,
+ const Scene& /* in */,
+ const Object& /* orig_object */
+ ) {
+ DefaultLogger::get()->warn((Formatter::format("This modifier is not supported, skipping: "),orig_modifier.dna_type));
+ return;
+ }
+};
+
+
+// -------------------------------------------------------------------------------------------
+/** Manage all known modifiers and instance and apply them if necessary */
+// -------------------------------------------------------------------------------------------
+class BlenderModifierShowcase
+{
+public:
+
+ // --------------------
+ /** Apply all requested modifiers provided we support them. */
+ void ApplyModifiers(aiNode& out,
+ ConversionData& conv_data,
+ const Scene& in,
+ const Object& orig_object
+ );
+
+private:
+
+ TempArray< std::vector,BlenderModifier > cached_modifiers;
+};
+
+
+
+
+
+// MODIFIERS
+
+
+
+// -------------------------------------------------------------------------------------------
+/** Mirror modifier. Status: implemented. */
+// -------------------------------------------------------------------------------------------
+class BlenderModifier_Mirror : public BlenderModifier
+{
+public:
+
+ // --------------------
+ virtual bool IsActive( const ModifierData& modin);
+
+ // --------------------
+ virtual void DoIt(aiNode& out,
+ ConversionData& conv_data,
+ const ElemBase& orig_modifier,
+ const Scene& in,
+ const Object& orig_object
+ ) ;
+};
+
+// -------------------------------------------------------------------------------------------
+/** Subdivision modifier. Status: dummy. */
+// -------------------------------------------------------------------------------------------
+class BlenderModifier_Subdivision : public BlenderModifier
+{
+public:
+
+ // --------------------
+ virtual bool IsActive( const ModifierData& modin);
+
+ // --------------------
+ virtual void DoIt(aiNode& out,
+ ConversionData& conv_data,
+ const ElemBase& orig_modifier,
+ const Scene& in,
+ const Object& orig_object
+ ) ;
+};
+
+
+}}
+#endif // !INCLUDED_AI_BLEND_MODIFIER_H
diff --git a/3rdparty/assimp/code/BlenderScene.cpp b/3rdparty/assimp/code/BlenderScene.cpp
new file mode 100644
index 000000000..cfb97d2e7
--- /dev/null
+++ b/3rdparty/assimp/code/BlenderScene.cpp
@@ -0,0 +1,596 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BlenderScene.cpp
+ * @brief MACHINE GENERATED BY ./scripts/BlenderImporter/genblenddna.py
+ */
+#include "AssimpPCH.h"
+#ifndef AI_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include "BlenderSceneGen.h"
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Object> (
+ Object& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Fail>((int&)dest.type,"type",db);
+ ReadFieldArray2<ErrorPolicy_Warn>(dest.obmat,"obmat",db);
+ ReadFieldArray2<ErrorPolicy_Warn>(dest.parentinv,"parentinv",db);
+ ReadFieldArray<ErrorPolicy_Warn>(dest.parsubstr,"parsubstr",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.parent,"*parent",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.track,"*track",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy,"*proxy",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_from,"*proxy_from",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.proxy_group,"*proxy_group",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.dup_group,"*dup_group",db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.data,"*data",db);
+ ReadField<ErrorPolicy_Igno>(dest.modifiers,"modifiers",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Group> (
+ Group& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Igno>(dest.layer,"layer",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.gobject,"*gobject",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MTex> (
+ MTex& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Igno>((int&)dest.blendtype,"blendtype",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.object,"*object",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.tex,"*tex",db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.uvname,"uvname",db);
+ ReadField<ErrorPolicy_Igno>((int&)dest.projx,"projx",db);
+ ReadField<ErrorPolicy_Igno>((int&)dest.projy,"projy",db);
+ ReadField<ErrorPolicy_Igno>((int&)dest.projz,"projz",db);
+ ReadField<ErrorPolicy_Igno>(dest.mapping,"mapping",db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.ofs,"ofs",db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.size,"size",db);
+ ReadField<ErrorPolicy_Igno>(dest.rot,"rot",db);
+ ReadField<ErrorPolicy_Igno>(dest.texflag,"texflag",db);
+ ReadField<ErrorPolicy_Igno>(dest.colormodel,"colormodel",db);
+ ReadField<ErrorPolicy_Igno>(dest.pmapto,"pmapto",db);
+ ReadField<ErrorPolicy_Igno>(dest.pmaptoneg,"pmaptoneg",db);
+ ReadField<ErrorPolicy_Warn>(dest.r,"r",db);
+ ReadField<ErrorPolicy_Warn>(dest.g,"g",db);
+ ReadField<ErrorPolicy_Warn>(dest.b,"b",db);
+ ReadField<ErrorPolicy_Warn>(dest.k,"k",db);
+ ReadField<ErrorPolicy_Igno>(dest.colspecfac,"colspecfac",db);
+ ReadField<ErrorPolicy_Igno>(dest.mirrfac,"mirrfac",db);
+ ReadField<ErrorPolicy_Igno>(dest.alphafac,"alphafac",db);
+ ReadField<ErrorPolicy_Igno>(dest.difffac,"difffac",db);
+ ReadField<ErrorPolicy_Igno>(dest.specfac,"specfac",db);
+ ReadField<ErrorPolicy_Igno>(dest.emitfac,"emitfac",db);
+ ReadField<ErrorPolicy_Igno>(dest.hardfac,"hardfac",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<TFace> (
+ TFace& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldArray2<ErrorPolicy_Fail>(dest.uv,"uv",db);
+ ReadFieldArray<ErrorPolicy_Fail>(dest.col,"col",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Igno>(dest.mode,"mode",db);
+ ReadField<ErrorPolicy_Igno>(dest.tile,"tile",db);
+ ReadField<ErrorPolicy_Igno>(dest.unwrap,"unwrap",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<SubsurfModifierData> (
+ SubsurfModifierData& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.modifier,"modifier",db);
+ ReadField<ErrorPolicy_Igno>(dest.subdivType,"subdivType",db);
+ ReadField<ErrorPolicy_Igno>(dest.levels,"levels",db);
+ ReadField<ErrorPolicy_Igno>(dest.renderLevels,"renderLevels",db);
+ ReadField<ErrorPolicy_Igno>(dest.flags,"flags",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MFace> (
+ MFace& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.v1,"v1",db);
+ ReadField<ErrorPolicy_Fail>(dest.v2,"v2",db);
+ ReadField<ErrorPolicy_Fail>(dest.v3,"v3",db);
+ ReadField<ErrorPolicy_Fail>(dest.v4,"v4",db);
+ ReadField<ErrorPolicy_Fail>(dest.mat_nr,"mat_nr",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Lamp> (
+ Lamp& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Fail>((int&)dest.type,"type",db);
+ ReadField<ErrorPolicy_Igno>(dest.flags,"flags",db);
+ ReadField<ErrorPolicy_Igno>(dest.colormodel,"colormodel",db);
+ ReadField<ErrorPolicy_Igno>(dest.totex,"totex",db);
+ ReadField<ErrorPolicy_Warn>(dest.r,"r",db);
+ ReadField<ErrorPolicy_Warn>(dest.g,"g",db);
+ 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_Igno>(dest.spotsize,"spotsize",db);
+ ReadField<ErrorPolicy_Igno>(dest.spotblend,"spotblend",db);
+ ReadField<ErrorPolicy_Igno>(dest.att1,"att1",db);
+ ReadField<ErrorPolicy_Igno>(dest.att2,"att2",db);
+ ReadField<ErrorPolicy_Igno>((int&)dest.falloff_type,"falloff_type",db);
+ ReadField<ErrorPolicy_Igno>(dest.sun_brightness,"sun_brightness",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MDeformWeight> (
+ MDeformWeight& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.def_nr,"def_nr",db);
+ ReadField<ErrorPolicy_Fail>(dest.weight,"weight",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<PackedFile> (
+ PackedFile& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Warn>(dest.size,"size",db);
+ ReadField<ErrorPolicy_Warn>(dest.seek,"seek",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.data,"*data",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Base> (
+ Base& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.prev,"*prev",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.next,"*next",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.object,"*object",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MTFace> (
+ MTFace& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldArray2<ErrorPolicy_Fail>(dest.uv,"uv",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Igno>(dest.mode,"mode",db);
+ ReadField<ErrorPolicy_Igno>(dest.tile,"tile",db);
+ ReadField<ErrorPolicy_Igno>(dest.unwrap,"unwrap",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Material> (
+ Material& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Warn>(dest.r,"r",db);
+ ReadField<ErrorPolicy_Warn>(dest.g,"g",db);
+ ReadField<ErrorPolicy_Warn>(dest.b,"b",db);
+ ReadField<ErrorPolicy_Warn>(dest.specr,"specr",db);
+ ReadField<ErrorPolicy_Warn>(dest.specg,"specg",db);
+ ReadField<ErrorPolicy_Warn>(dest.specb,"specb",db);
+ ReadField<ErrorPolicy_Warn>(dest.ambir,"ambir",db);
+ ReadField<ErrorPolicy_Warn>(dest.ambig,"ambig",db);
+ ReadField<ErrorPolicy_Warn>(dest.ambib,"ambib",db);
+ ReadField<ErrorPolicy_Igno>(dest.mirr,"mirr",db);
+ ReadField<ErrorPolicy_Igno>(dest.mirg,"mirg",db);
+ ReadField<ErrorPolicy_Igno>(dest.mirb,"mirb",db);
+ ReadField<ErrorPolicy_Warn>(dest.emit,"emit",db);
+ ReadField<ErrorPolicy_Warn>(dest.alpha,"alpha",db);
+ ReadField<ErrorPolicy_Igno>(dest.ref,"ref",db);
+ ReadField<ErrorPolicy_Igno>(dest.translucency,"translucency",db);
+ ReadField<ErrorPolicy_Igno>(dest.roughness,"roughness",db);
+ ReadField<ErrorPolicy_Igno>(dest.darkness,"darkness",db);
+ ReadField<ErrorPolicy_Igno>(dest.refrac,"refrac",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.group,"*group",db);
+ ReadField<ErrorPolicy_Warn>(dest.diff_shader,"diff_shader",db);
+ ReadField<ErrorPolicy_Warn>(dest.spec_shader,"spec_shader",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mtex,"*mtex",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Mesh> (
+ Mesh& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Fail>(dest.totface,"totface",db);
+ ReadField<ErrorPolicy_Fail>(dest.totedge,"totedge",db);
+ ReadField<ErrorPolicy_Fail>(dest.totvert,"totvert",db);
+ ReadField<ErrorPolicy_Igno>(dest.subdiv,"subdiv",db);
+ ReadField<ErrorPolicy_Igno>(dest.subdivr,"subdivr",db);
+ ReadField<ErrorPolicy_Igno>(dest.subsurftype,"subsurftype",db);
+ ReadField<ErrorPolicy_Igno>(dest.smoothresh,"smoothresh",db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.mface,"*mface",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mtface,"*mtface",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.tface,"*tface",db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.mvert,"*mvert",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.medge,"*medge",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.dvert,"*dvert",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mcol,"*mcol",db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.mat,"**mat",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MDeformVert> (
+ MDeformVert& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.dw,"*dw",db);
+ ReadField<ErrorPolicy_Igno>(dest.totweight,"totweight",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<World> (
+ World& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MVert> (
+ MVert& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldArray<ErrorPolicy_Fail>(dest.co,"co",db);
+ ReadFieldArray<ErrorPolicy_Fail>(dest.no,"no",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Warn>(dest.mat_nr,"mat_nr",db);
+ ReadField<ErrorPolicy_Igno>(dest.bweight,"bweight",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MEdge> (
+ MEdge& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.v1,"v1",db);
+ ReadField<ErrorPolicy_Fail>(dest.v2,"v2",db);
+ ReadField<ErrorPolicy_Igno>(dest.crease,"crease",db);
+ ReadField<ErrorPolicy_Igno>(dest.bweight,"bweight",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<GroupObject> (
+ GroupObject& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.prev,"*prev",db);
+ ReadFieldPtr<ErrorPolicy_Fail>(dest.next,"*next",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.ob,"*ob",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<ListBase> (
+ ListBase& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.first,"*first",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.last,"*last",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<ModifierData> (
+ ModifierData& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.next,"*next",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.prev,"*prev",db);
+ ReadField<ErrorPolicy_Igno>(dest.type,"type",db);
+ ReadField<ErrorPolicy_Igno>(dest.mode,"mode",db);
+ ReadFieldArray<ErrorPolicy_Igno>(dest.name,"name",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<ID> (
+ ID& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadFieldArray<ErrorPolicy_Warn>(dest.name,"name",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MCol> (
+ MCol& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.r,"r",db);
+ ReadField<ErrorPolicy_Fail>(dest.g,"g",db);
+ ReadField<ErrorPolicy_Fail>(dest.b,"b",db);
+ ReadField<ErrorPolicy_Fail>(dest.a,"a",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Image> (
+ Image& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadFieldArray<ErrorPolicy_Warn>(dest.name,"name",db);
+ ReadField<ErrorPolicy_Igno>(dest.ok,"ok",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Igno>(dest.source,"source",db);
+ ReadField<ErrorPolicy_Igno>(dest.type,"type",db);
+ ReadField<ErrorPolicy_Igno>(dest.pad,"pad",db);
+ ReadField<ErrorPolicy_Igno>(dest.pad1,"pad1",db);
+ ReadField<ErrorPolicy_Igno>(dest.lastframe,"lastframe",db);
+ ReadField<ErrorPolicy_Igno>(dest.tpageflag,"tpageflag",db);
+ ReadField<ErrorPolicy_Igno>(dest.totbind,"totbind",db);
+ ReadField<ErrorPolicy_Igno>(dest.xrep,"xrep",db);
+ ReadField<ErrorPolicy_Igno>(dest.yrep,"yrep",db);
+ ReadField<ErrorPolicy_Igno>(dest.twsta,"twsta",db);
+ ReadField<ErrorPolicy_Igno>(dest.twend,"twend",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.packedfile,"*packedfile",db);
+ ReadField<ErrorPolicy_Igno>(dest.lastupdate,"lastupdate",db);
+ ReadField<ErrorPolicy_Igno>(dest.lastused,"lastused",db);
+ ReadField<ErrorPolicy_Igno>(dest.animspeed,"animspeed",db);
+ ReadField<ErrorPolicy_Igno>(dest.gen_x,"gen_x",db);
+ ReadField<ErrorPolicy_Igno>(dest.gen_y,"gen_y",db);
+ ReadField<ErrorPolicy_Igno>(dest.gen_type,"gen_type",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Scene> (
+ Scene& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.camera,"*camera",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.world,"*world",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.basact,"*basact",db);
+ ReadField<ErrorPolicy_Igno>(dest.base,"base",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Library> (
+ Library& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadFieldArray<ErrorPolicy_Warn>(dest.name,"name",db);
+ ReadFieldArray<ErrorPolicy_Fail>(dest.filename,"filename",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.parent,"*parent",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Tex> (
+ Tex& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>((int&)dest.type,"type",db);
+ ReadFieldPtr<ErrorPolicy_Warn>(dest.ima,"*ima",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<Camera> (
+ Camera& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.id,"id",db);
+ ReadField<ErrorPolicy_Warn>((int&)dest.type,"type",db);
+ ReadField<ErrorPolicy_Warn>((int&)dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Warn>(dest.angle,"angle",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+template <> void Structure :: Convert<MirrorModifierData> (
+ MirrorModifierData& dest,
+ const FileDatabase& db
+ ) const
+{
+
+ ReadField<ErrorPolicy_Fail>(dest.modifier,"modifier",db);
+ ReadField<ErrorPolicy_Igno>(dest.axis,"axis",db);
+ ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+ ReadField<ErrorPolicy_Igno>(dest.tolerance,"tolerance",db);
+ ReadFieldPtr<ErrorPolicy_Igno>(dest.mirror_ob,"*mirror_ob",db);
+
+ db.reader->IncPtr(size);
+}
+
+//--------------------------------------------------------------------------------
+void DNA::RegisterConverters() {
+
+ converters["Object"] = DNA::FactoryPair( &Structure::Allocate<Object>, &Structure::Convert<Object> );
+ converters["Group"] = DNA::FactoryPair( &Structure::Allocate<Group>, &Structure::Convert<Group> );
+ converters["MTex"] = DNA::FactoryPair( &Structure::Allocate<MTex>, &Structure::Convert<MTex> );
+ converters["TFace"] = DNA::FactoryPair( &Structure::Allocate<TFace>, &Structure::Convert<TFace> );
+ converters["SubsurfModifierData"] = DNA::FactoryPair( &Structure::Allocate<SubsurfModifierData>, &Structure::Convert<SubsurfModifierData> );
+ converters["MFace"] = DNA::FactoryPair( &Structure::Allocate<MFace>, &Structure::Convert<MFace> );
+ converters["Lamp"] = DNA::FactoryPair( &Structure::Allocate<Lamp>, &Structure::Convert<Lamp> );
+ converters["MDeformWeight"] = DNA::FactoryPair( &Structure::Allocate<MDeformWeight>, &Structure::Convert<MDeformWeight> );
+ converters["PackedFile"] = DNA::FactoryPair( &Structure::Allocate<PackedFile>, &Structure::Convert<PackedFile> );
+ converters["Base"] = DNA::FactoryPair( &Structure::Allocate<Base>, &Structure::Convert<Base> );
+ converters["MTFace"] = DNA::FactoryPair( &Structure::Allocate<MTFace>, &Structure::Convert<MTFace> );
+ converters["Material"] = DNA::FactoryPair( &Structure::Allocate<Material>, &Structure::Convert<Material> );
+ converters["Mesh"] = DNA::FactoryPair( &Structure::Allocate<Mesh>, &Structure::Convert<Mesh> );
+ converters["MDeformVert"] = DNA::FactoryPair( &Structure::Allocate<MDeformVert>, &Structure::Convert<MDeformVert> );
+ converters["World"] = DNA::FactoryPair( &Structure::Allocate<World>, &Structure::Convert<World> );
+ converters["MVert"] = DNA::FactoryPair( &Structure::Allocate<MVert>, &Structure::Convert<MVert> );
+ converters["MEdge"] = DNA::FactoryPair( &Structure::Allocate<MEdge>, &Structure::Convert<MEdge> );
+ converters["GroupObject"] = DNA::FactoryPair( &Structure::Allocate<GroupObject>, &Structure::Convert<GroupObject> );
+ converters["ListBase"] = DNA::FactoryPair( &Structure::Allocate<ListBase>, &Structure::Convert<ListBase> );
+ converters["ModifierData"] = DNA::FactoryPair( &Structure::Allocate<ModifierData>, &Structure::Convert<ModifierData> );
+ converters["ID"] = DNA::FactoryPair( &Structure::Allocate<ID>, &Structure::Convert<ID> );
+ converters["MCol"] = DNA::FactoryPair( &Structure::Allocate<MCol>, &Structure::Convert<MCol> );
+ converters["Image"] = DNA::FactoryPair( &Structure::Allocate<Image>, &Structure::Convert<Image> );
+ converters["Scene"] = DNA::FactoryPair( &Structure::Allocate<Scene>, &Structure::Convert<Scene> );
+ converters["Library"] = DNA::FactoryPair( &Structure::Allocate<Library>, &Structure::Convert<Library> );
+ converters["Tex"] = DNA::FactoryPair( &Structure::Allocate<Tex>, &Structure::Convert<Tex> );
+ converters["Camera"] = DNA::FactoryPair( &Structure::Allocate<Camera>, &Structure::Convert<Camera> );
+ converters["MirrorModifierData"] = DNA::FactoryPair( &Structure::Allocate<MirrorModifierData>, &Structure::Convert<MirrorModifierData> );
+}
+
+
+#endif
diff --git a/3rdparty/assimp/code/BlenderScene.h b/3rdparty/assimp/code/BlenderScene.h
new file mode 100644
index 000000000..58d6adddb
--- /dev/null
+++ b/3rdparty/assimp/code/BlenderScene.h
@@ -0,0 +1,683 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BlenderScene.h
+ * @brief Intermediate representation of a BLEND scene.
+ */
+#ifndef INCLUDED_AI_BLEND_SCENE_H
+#define INCLUDED_AI_BLEND_SCENE_H
+
+namespace Assimp {
+ namespace Blender {
+
+// Minor parts of this file are extracts from blender data structures,
+// declared in the ./source/blender/makesdna directory.
+// Stuff that is not used by Assimp is commented.
+
+
+// NOTE
+// this file serves as input data to the `./scripts/genblenddna.py`
+// script. This script generates the actual binding code to read a
+// blender file with a possibly different DNA into our structures.
+// Only `struct` declarations are considered and the following
+// rules must be obeyed in order for the script to work properly:
+//
+// * C++ style comments only
+//
+// * Structures may include the primitive types char, int, short,
+// float, double. Signedness specifiers are not allowed on
+// integers. Enum types are allowed, but they must have been
+// defined in this header.
+//
+// * Structures may aggregate other structures, unless not defined
+// in this header.
+//
+// * Pointers to other structures or primitive types are allowed.
+// No references or double pointers or arrays of pointers.
+// A pointer to a T is written as boost::shared_ptr, while a
+// pointer to an array of elements is written as boost::
+// shared_array.
+//
+// * Arrays can have maximally two-dimensions. Any non-pointer
+// type can form them.
+//
+// * Multiple fields can be declare in a single line (i.e `int a,b;`)
+// provided they are neither pointers nor arrays.
+//
+// * One of WARN, FAIL can be appended to the declaration (
+// prior to the semiolon to specifiy the error handling policy if
+// this field is missing in the input DNA). If none of those
+// is specified the default policy is to subtitute a default
+// value for the field.
+//
+
+#define WARN // warn if field is missing, substitute default value
+#define FAIL // fail the import if the field does not exist
+
+struct Object;
+struct MTex;
+
+#define AI_BLEND_MESH_MAX_VERTS 2000000000L
+
+// -------------------------------------------------------------------------------
+struct ID : ElemBase {
+
+ char name[24] WARN;
+ short flag;
+};
+
+// -------------------------------------------------------------------------------
+struct ListBase : ElemBase {
+
+ boost::shared_ptr<ElemBase> first;
+ boost::shared_ptr<ElemBase> last;
+};
+
+
+// -------------------------------------------------------------------------------
+struct PackedFile : ElemBase {
+ int size WARN;
+ int seek WARN;
+ boost::shared_ptr< FileOffset > data WARN;
+};
+
+// -------------------------------------------------------------------------------
+struct GroupObject : ElemBase {
+
+ boost::shared_ptr<GroupObject> prev,next FAIL;
+ boost::shared_ptr<Object> ob;
+};
+
+// -------------------------------------------------------------------------------
+struct Group : ElemBase {
+ ID id FAIL;
+ int layer;
+
+ boost::shared_ptr<GroupObject> gobject;
+};
+
+// -------------------------------------------------------------------------------
+struct World : ElemBase {
+ ID id FAIL;
+
+};
+
+// -------------------------------------------------------------------------------
+struct MVert : ElemBase {
+ float co[3] FAIL;
+ float no[3] FAIL;
+ char flag;
+ int mat_nr WARN;
+ int bweight;
+};
+
+// -------------------------------------------------------------------------------
+struct MEdge : ElemBase {
+ int v1, v2 FAIL;
+ char crease, bweight;
+ short flag;
+};
+
+// -------------------------------------------------------------------------------
+struct MCol : ElemBase {
+ char r,g,b,a FAIL;
+};
+
+// -------------------------------------------------------------------------------
+struct MFace : ElemBase {
+ int v1,v2,v3,v4 FAIL;
+ int mat_nr FAIL;
+ char flag;
+};
+
+// -------------------------------------------------------------------------------
+struct TFace : ElemBase {
+ float uv[4][2] FAIL;
+ int col[4] FAIL;
+ char flag;
+ short mode;
+ short tile;
+ short unwrap;
+};
+
+// -------------------------------------------------------------------------------
+struct MTFace : ElemBase {
+
+ float uv[4][2] FAIL;
+ char flag;
+ short mode;
+ short tile;
+ short unwrap;
+
+ // boost::shared_ptr<Image> tpage;
+};
+
+// -------------------------------------------------------------------------------
+struct MDeformWeight : ElemBase {
+ int def_nr FAIL;
+ float weight FAIL;
+};
+
+// -------------------------------------------------------------------------------
+struct MDeformVert : ElemBase {
+
+ vector<MDeformWeight> dw WARN;
+ int totweight;
+};
+
+// -------------------------------------------------------------------------------
+struct Material : ElemBase {
+ ID id FAIL;
+
+ float r,g,b WARN;
+ float specr,specg,specb WARN;
+ float ambir,ambig,ambib WARN;
+ float mirr,mirg,mirb;
+ float emit WARN;
+ float alpha WARN;
+ float ref;
+ float translucency;
+ float roughness;
+ float darkness;
+ float refrac;
+
+ boost::shared_ptr<Group> group;
+
+ short diff_shader WARN;
+ short spec_shader WARN;
+
+ boost::shared_ptr<MTex> mtex[18];
+};
+
+// -------------------------------------------------------------------------------
+struct Mesh : ElemBase {
+ ID id FAIL;
+
+ int totface FAIL;
+ int totedge FAIL;
+ int totvert FAIL;
+
+ short subdiv;
+ short subdivr;
+ short subsurftype;
+ short smoothresh;
+
+ vector<MFace> mface FAIL;
+ vector<MTFace> mtface;
+ vector<TFace> tface;
+ vector<MVert> mvert FAIL;
+ vector<MEdge> medge WARN;
+ vector<MDeformVert> dvert;
+ vector<MCol> mcol;
+
+ vector< boost::shared_ptr<Material> > mat FAIL;
+};
+
+// -------------------------------------------------------------------------------
+struct Library : ElemBase {
+ ID id FAIL;
+
+ char name[240] WARN;
+ char filename[240] FAIL;
+ boost::shared_ptr<Library> parent WARN;
+};
+
+// -------------------------------------------------------------------------------
+struct Camera : ElemBase {
+ enum Type {
+ Type_PERSP = 0
+ ,Type_ORTHO = 1
+ };
+
+ ID id FAIL;
+
+ // struct AnimData *adt;
+
+ Type type,flag WARN;
+ float angle WARN;
+ //float passepartalpha, angle;
+ //float clipsta, clipend;
+ //float lens, ortho_scale, drawsize;
+ //float shiftx, shifty;
+
+ //float YF_dofdist, YF_aperture;
+ //short YF_bkhtype, YF_bkhbias;
+ //float YF_bkhrot;
+};
+
+
+// -------------------------------------------------------------------------------
+struct Lamp : ElemBase {
+
+ enum FalloffType {
+ FalloffType_Constant = 0x0
+ ,FalloffType_InvLinear = 0x1
+ ,FalloffType_InvSquare = 0x2
+ //,FalloffType_Curve = 0x3
+ //,FalloffType_Sliders = 0x4
+ };
+
+ enum Type {
+ Type_Local = 0x0
+ ,Type_Sun = 0x1
+ ,Type_Spot = 0x2
+ ,Type_Hemi = 0x3
+ ,Type_Area = 0x4
+ //,Type_YFPhoton = 0x5
+ };
+
+ ID id FAIL;
+ //AnimData *adt;
+
+ Type type FAIL;
+ short flags;
+
+ //int mode;
+
+ short colormodel, totex;
+ float r,g,b,k WARN;
+ //float shdwr, shdwg, shdwb;
+
+ float energy, dist, spotsize, spotblend;
+ //float haint;
+
+ float att1, att2;
+ //struct CurveMapping *curfalloff;
+ FalloffType falloff_type;
+
+ //float clipsta, clipend, shadspotsize;
+ //float bias, soft, compressthresh;
+ //short bufsize, samp, buffers, filtertype;
+ //char bufflag, buftype;
+
+ //short ray_samp, ray_sampy, ray_sampz;
+ //short ray_samp_type;
+ //short area_shape;
+ //float area_size, area_sizey, area_sizez;
+ //float adapt_thresh;
+ //short ray_samp_method;
+
+ //short texact, shadhalostep;
+
+ //short sun_effect_type;
+ //short skyblendtype;
+ //float horizon_brightness;
+ //float spread;
+ float sun_brightness;
+ //float sun_size;
+ //float backscattered_light;
+ //float sun_intensity;
+ //float atm_turbidity;
+ //float atm_inscattering_factor;
+ //float atm_extinction_factor;
+ //float atm_distance_factor;
+ //float skyblendfac;
+ //float sky_exposure;
+ //short sky_colorspace;
+
+ // int YF_numphotons, YF_numsearch;
+ // short YF_phdepth, YF_useqmc, YF_bufsize, YF_pad;
+ // float YF_causticblur, YF_ltradius;
+
+ // float YF_glowint, YF_glowofs;
+ // short YF_glowtype, YF_pad2;
+
+ //struct Ipo *ipo;
+ //struct MTex *mtex[18];
+ // short pr_texture;
+
+ //struct PreviewImage *preview;
+};
+
+// -------------------------------------------------------------------------------
+struct ModifierData : ElemBase {
+ enum ModifierType {
+ eModifierType_None = 0,
+ eModifierType_Subsurf,
+ eModifierType_Lattice,
+ eModifierType_Curve,
+ eModifierType_Build,
+ eModifierType_Mirror,
+ eModifierType_Decimate,
+ eModifierType_Wave,
+ eModifierType_Armature,
+ eModifierType_Hook,
+ eModifierType_Softbody,
+ eModifierType_Boolean,
+ eModifierType_Array,
+ eModifierType_EdgeSplit,
+ eModifierType_Displace,
+ eModifierType_UVProject,
+ eModifierType_Smooth,
+ eModifierType_Cast,
+ eModifierType_MeshDeform,
+ eModifierType_ParticleSystem,
+ eModifierType_ParticleInstance,
+ eModifierType_Explode,
+ eModifierType_Cloth,
+ eModifierType_Collision,
+ eModifierType_Bevel,
+ eModifierType_Shrinkwrap,
+ eModifierType_Fluidsim,
+ eModifierType_Mask,
+ eModifierType_SimpleDeform,
+ eModifierType_Multires,
+ eModifierType_Surface,
+ eModifierType_Smoke,
+ eModifierType_ShapeKey
+ };
+
+ boost::shared_ptr<ElemBase> next WARN;
+ boost::shared_ptr<ElemBase> prev WARN;
+
+ int type, mode;
+ char name[32];
+};
+
+// -------------------------------------------------------------------------------
+struct SubsurfModifierData : ElemBase {
+
+ enum Type {
+
+ TYPE_CatmullClarke = 0x0,
+ TYPE_Simple = 0x1
+ };
+
+ enum Flags {
+ // some ommitted
+ FLAGS_SubsurfUV =1<<3
+ };
+
+ ModifierData modifier FAIL;
+ short subdivType WARN;
+ short levels FAIL;
+ short renderLevels ;
+ short flags;
+};
+
+// -------------------------------------------------------------------------------
+struct MirrorModifierData : ElemBase {
+
+ enum Flags {
+ Flags_CLIPPING =1<<0,
+ Flags_MIRROR_U =1<<1,
+ Flags_MIRROR_V =1<<2,
+ Flags_AXIS_X =1<<3,
+ Flags_AXIS_Y =1<<4,
+ Flags_AXIS_Z =1<<5,
+ Flags_VGROUP =1<<6
+ };
+
+ ModifierData modifier FAIL;
+
+ short axis, flag;
+ float tolerance;
+ boost::shared_ptr<Object> mirror_ob;
+};
+
+// -------------------------------------------------------------------------------
+struct Object : ElemBase {
+ ID id FAIL;
+
+ enum Type {
+ Type_EMPTY = 0
+ ,Type_MESH = 1
+ ,Type_CURVE = 2
+ ,Type_SURF = 3
+ ,Type_FONT = 4
+ ,Type_MBALL = 5
+
+ ,Type_LAMP = 10
+ ,Type_CAMERA = 11
+
+ ,Type_WAVE = 21
+ ,Type_LATTICE = 22
+ };
+
+ Type type FAIL;
+ float obmat[4][4] WARN;
+ float parentinv[4][4] WARN;
+ char parsubstr[32] WARN;
+
+ boost::shared_ptr<Object> parent WARN;
+ boost::shared_ptr<Object> track WARN;
+
+ boost::shared_ptr<Object> proxy,proxy_from,proxy_group WARN;
+ boost::shared_ptr<Group> dup_group WARN;
+ boost::shared_ptr<ElemBase> data FAIL;
+
+ ListBase modifiers;
+};
+
+
+// -------------------------------------------------------------------------------
+struct Base : ElemBase {
+ boost::shared_ptr<Base> prev WARN;
+ boost::shared_ptr<Base> next WARN;
+ boost::shared_ptr<Object> object WARN;
+};
+
+// -------------------------------------------------------------------------------
+struct Scene : ElemBase {
+ ID id FAIL;
+
+ boost::shared_ptr<Object> camera WARN;
+ boost::shared_ptr<World> world WARN;
+ boost::shared_ptr<Base> basact WARN;
+
+ ListBase base;
+};
+
+
+// -------------------------------------------------------------------------------
+struct Image : ElemBase {
+ ID id FAIL;
+
+ char name[240] WARN;
+
+ //struct anim *anim;
+
+ short ok, flag;
+ short source, type, pad, pad1;
+ int lastframe;
+
+ short tpageflag, totbind;
+ short xrep, yrep;
+ short twsta, twend;
+ //unsigned int bindcode;
+ //unsigned int *repbind;
+
+ boost::shared_ptr<PackedFile> packedfile;
+ //struct PreviewImage * preview;
+
+ float lastupdate;
+ int lastused;
+ short animspeed;
+
+ short gen_x, gen_y, gen_type;
+};
+
+// -------------------------------------------------------------------------------
+struct Tex : ElemBase {
+
+ // actually, the only texture type we support is Type_IMAGE
+ enum Type {
+ Type_CLOUDS = 1
+ ,Type_WOOD = 2
+ ,Type_MARBLE = 3
+ ,Type_MAGIC = 4
+ ,Type_BLEND = 5
+ ,Type_STUCCI = 6
+ ,Type_NOISE = 7
+ ,Type_IMAGE = 8
+ ,Type_PLUGIN = 9
+ ,Type_ENVMAP = 10
+ ,Type_MUSGRAVE = 11
+ ,Type_VORONOI = 12
+ ,Type_DISTNOISE = 13
+ ,Type_POINTDENSITY = 14
+ ,Type_VOXELDATA = 15
+ };
+
+ ID id FAIL;
+ // AnimData *adt;
+
+ //float noisesize, turbul;
+ //float bright, contrast, rfac, gfac, bfac;
+ //float filtersize;
+
+ //float mg_H, mg_lacunarity, mg_octaves, mg_offset, mg_gain;
+ //float dist_amount, ns_outscale;
+
+ //float vn_w1;
+ //float vn_w2;
+ //float vn_w3;
+ //float vn_w4;
+ //float vn_mexp;
+ //short vn_distm, vn_coltype;
+
+ //short noisedepth, noisetype;
+ //short noisebasis, noisebasis2;
+
+ //short imaflag, flag;
+ Type type FAIL;
+ //short stype;
+
+ //float cropxmin, cropymin, cropxmax, cropymax;
+ //int texfilter;
+ //int afmax;
+ //short xrepeat, yrepeat;
+ //short extend;
+
+ //short fie_ima;
+ //int len;
+ //int frames, offset, sfra;
+
+ //float checkerdist, nabla;
+ //float norfac;
+
+ //ImageUser iuser;
+
+ //bNodeTree *nodetree;
+ //Ipo *ipo;
+ boost::shared_ptr<Image> ima WARN;
+ //PluginTex *plugin;
+ //ColorBand *coba;
+ //EnvMap *env;
+ //PreviewImage * preview;
+ //PointDensity *pd;
+ //VoxelData *vd;
+
+ //char use_nodes;
+};
+
+// -------------------------------------------------------------------------------
+struct MTex : ElemBase {
+
+ enum Projection {
+ Proj_N = 0
+ ,Proj_X = 1
+ ,Proj_Y = 2
+ ,Proj_Z = 3
+ };
+
+ enum Flag {
+ Flag_RGBTOINT = 0x1
+ ,Flag_STENCIL = 0x2
+ ,Flag_NEGATIVE = 0x4
+ ,Flag_ALPHAMIX = 0x8
+ ,Flag_VIEWSPACE = 0x10
+ };
+
+ enum BlendType {
+ BlendType_BLEND = 0
+ ,BlendType_MUL = 1
+ ,BlendType_ADD = 2
+ ,BlendType_SUB = 3
+ ,BlendType_DIV = 4
+ ,BlendType_DARK = 5
+ ,BlendType_DIFF = 6
+ ,BlendType_LIGHT = 7
+ ,BlendType_SCREEN = 8
+ ,BlendType_OVERLAY = 9
+ ,BlendType_BLEND_HUE = 10
+ ,BlendType_BLEND_SAT = 11
+ ,BlendType_BLEND_VAL = 12
+ ,BlendType_BLEND_COLOR = 13
+ };
+
+ // short texco, mapto, maptoneg;
+
+ BlendType blendtype;
+ boost::shared_ptr<Object> object;
+ boost::shared_ptr<Tex> tex;
+ char uvname[32];
+
+ Projection projx,projy,projz;
+ char mapping;
+ float ofs[3], size[3], rot;
+
+ int texflag;
+ short colormodel, pmapto, pmaptoneg;
+ //short normapspace, which_output;
+ //char brush_map_mode;
+ float r,g,b,k WARN;
+ //float def_var, rt;
+
+ //float colfac, varfac;
+
+ //float norfac, dispfac, warpfac;
+ float colspecfac, mirrfac, alphafac;
+ float difffac, specfac, emitfac, hardfac;
+ //float raymirrfac, translfac, ambfac;
+ //float colemitfac, colreflfac, coltransfac;
+ //float densfac, scatterfac, reflfac;
+
+ //float timefac, lengthfac, clumpfac;
+ //float kinkfac, roughfac, padensfac;
+ //float lifefac, sizefac, ivelfac, pvelfac;
+ //float shadowfac;
+ //float zenupfac, zendownfac, blendfac;
+};
+
+
+ }
+}
+#endif
diff --git a/3rdparty/assimp/code/BlenderSceneGen.h b/3rdparty/assimp/code/BlenderSceneGen.h
new file mode 100644
index 000000000..0a7096e5f
--- /dev/null
+++ b/3rdparty/assimp/code/BlenderSceneGen.h
@@ -0,0 +1,223 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 BlenderSceneGen.h
+ * @brief MACHINE GENERATED BY ./scripts/BlenderImporter/genblenddna.py
+ */
+#ifndef INCLUDED_AI_BLEND_SCENEGEN_H
+#define INCLUDED_AI_BLEND_SCENEGEN_H
+
+namespace Assimp {
+ namespace Blender {
+
+
+template <> void Structure :: Convert<Object> (
+ Object& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Group> (
+ Group& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MTex> (
+ MTex& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<TFace> (
+ TFace& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<SubsurfModifierData> (
+ SubsurfModifierData& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MFace> (
+ MFace& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Lamp> (
+ Lamp& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MDeformWeight> (
+ MDeformWeight& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<PackedFile> (
+ PackedFile& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Base> (
+ Base& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MTFace> (
+ MTFace& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Material> (
+ Material& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Mesh> (
+ Mesh& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MDeformVert> (
+ MDeformVert& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<World> (
+ World& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MVert> (
+ MVert& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MEdge> (
+ MEdge& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<GroupObject> (
+ GroupObject& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<ListBase> (
+ ListBase& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<ModifierData> (
+ ModifierData& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<ID> (
+ ID& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MCol> (
+ MCol& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Image> (
+ Image& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Scene> (
+ Scene& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Library> (
+ Library& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Tex> (
+ Tex& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<Camera> (
+ Camera& dest,
+ const FileDatabase& db
+ ) const
+;
+
+template <> void Structure :: Convert<MirrorModifierData> (
+ MirrorModifierData& dest,
+ const FileDatabase& db
+ ) const
+;
+
+
+ }
+}
+
+#endif
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/LICENSE_1_0.txt b/3rdparty/assimp/code/BoostWorkaround/boost/LICENSE_1_0.txt
new file mode 100644
index 000000000..7925d62e6
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/LICENSE_1_0.txt
@@ -0,0 +1,24 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/foreach.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/foreach.hpp
new file mode 100644
index 000000000..dfb05bdf5
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/foreach.hpp
@@ -0,0 +1,93 @@
+
+#ifndef BOOST_FOREACH
+
+///////////////////////////////////////////////////////////////////////////////
+// A stripped down version of FOREACH for
+// illustration purposes. NOT FOR GENERAL USE.
+// For a complete implementation, see BOOST_FOREACH at
+// http://boost-sandbox.sourceforge.net/vault/index.php?directory=eric_niebler
+//
+// Copyright 2004 Eric Niebler.
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+// Adapted to Assimp November 29th, 2008 (Alexander Gessler).
+// Added code to handle both const and non-const iterators, simplified some
+// parts.
+///////////////////////////////////////////////////////////////////////////////
+
+namespace boost {
+namespace foreach_detail {
+
+///////////////////////////////////////////////////////////////////////////////
+// auto_any
+
+struct auto_any_base
+{
+ operator bool() const { return false; }
+};
+
+template<typename T>
+struct auto_any : auto_any_base
+{
+ auto_any(T const& t) : item(t) {}
+ mutable T item;
+};
+
+template<typename T>
+T& auto_any_cast(auto_any_base const& any)
+{
+ return static_cast<auto_any<T> const&>(any).item;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FOREACH helper function
+
+template<typename T>
+auto_any<typename T::const_iterator> begin(T const& t)
+{
+ return t.begin();
+}
+
+template<typename T>
+auto_any<typename T::const_iterator> end(T const& t)
+{
+ return t.end();
+}
+
+// iterator
+template<typename T>
+bool done(auto_any_base const& cur, auto_any_base const& end, T&)
+{
+ typedef typename T::iterator iter_type;
+ return auto_any_cast<iter_type>(cur) == auto_any_cast<iter_type>(end);
+}
+
+template<typename T>
+void next(auto_any_base const& cur, T&)
+{
+ ++auto_any_cast<typename T::iterator>(cur);
+}
+
+template<typename T>
+typename T::reference deref(auto_any_base const& cur, T&)
+{
+ return *auto_any_cast<typename T::iterator>(cur);
+}
+
+} // end foreach_detail
+
+///////////////////////////////////////////////////////////////////////////////
+// FOREACH
+
+#define BOOST_FOREACH(item, container) \
+ if (boost::foreach_detail::auto_any_base const& b = boost::foreach_detail::begin(container)) {} else \
+ if (boost::foreach_detail::auto_any_base const& e = boost::foreach_detail::end(container)) {} else \
+ for (;!boost::foreach_detail::done(b,e,container); boost::foreach_detail::next(b,container)) \
+ if (bool ugly_and_unique_break = false) {} else \
+ for (item = boost::foreach_detail::deref(b,container); !ugly_and_unique_break; ugly_and_unique_break = true)
+
+} // end boost
+
+#endif
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/format.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/format.hpp
new file mode 100644
index 000000000..b7d956b29
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/format.hpp
@@ -0,0 +1,81 @@
+
+
+
+/* DEPRECATED! - use code/TinyFormatter.h instead.
+ *
+ *
+ * */
+
+#ifndef AI_BOOST_FORMAT_DUMMY_INCLUDED
+#define AI_BOOST_FORMAT_DUMMY_INCLUDED
+
+#if (!defined BOOST_FORMAT_HPP) || (defined ASSIMP_FORCE_NOBOOST)
+
+#include <string>
+#include <vector>
+
+namespace boost
+{
+
+
+ class format
+ {
+ public:
+ format (const std::string& _d)
+ : d(_d)
+ {
+ }
+
+ template <typename T>
+ format& operator % (T in)
+ {
+ // XXX add replacement for boost::lexical_cast?
+
+ std::stringstream ss;
+ ss << in; // note: ss cannot be an rvalue, or the global operator << (const char*) is not called for T == const char*.
+ chunks.push_back( ss.str());
+ return *this;
+ }
+
+
+ operator std::string () const {
+ std::string res; // pray for NRVO to kick in
+
+ size_t start = 0, last = 0;
+
+ std::vector<std::string>::const_iterator chunkin = chunks.begin();
+
+ for ( start = d.find('%');start != std::string::npos; start = d.find('%',last)) {
+ res += d.substr(last,start-last);
+ last = start+2;
+ if (d[start+1] == '%') {
+ res += "%";
+ continue;
+ }
+
+ if (chunkin == chunks.end()) {
+ break;
+ }
+
+ res += *chunkin++;
+ }
+ res += d.substr(last);
+ return res;
+ }
+
+ private:
+ std::string d;
+ std::vector<std::string> chunks;
+ };
+
+ inline std::string str(const std::string& s) {
+ return s;
+ }
+}
+
+
+#else
+# error "format.h was already included"
+#endif //
+#endif // !! AI_BOOST_FORMAT_DUMMY_INCLUDED
+
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/lexical_cast.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/lexical_cast.hpp
new file mode 100644
index 000000000..6c3f78198
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/lexical_cast.hpp
@@ -0,0 +1,23 @@
+/// A quick replacement for boost::lexical_cast for all the Boost haters out there
+
+#ifndef __AI_BOOST_WORKAROUND_LEXICAL_CAST
+#define __AI_BOOST_WORKAROUND_LEXICAL_CAST
+
+namespace boost
+{
+
+ /// A quick replacement for boost::lexical_cast - should work for all types a stringstream can handle
+ template <typename TargetType, typename SourceType>
+ TargetType lexical_cast( const SourceType& source)
+ {
+ std::stringstream stream;
+ TargetType result;
+
+ stream << source;
+ stream >> result;
+ return result;
+ }
+
+} // namespace boost
+
+#endif // __AI_BOOST_WORKAROUND_LEXICAL_CAST
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/math/common_factor_rt.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/math/common_factor_rt.hpp
new file mode 100644
index 000000000..8b07f69b4
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/math/common_factor_rt.hpp
@@ -0,0 +1,37 @@
+
+
+#ifndef BOOST_MATH_COMMON_FACTOR_RT_HPP
+#define BOOST_MATH_COMMON_FACTOR_RT_HPP
+
+
+namespace boost {
+namespace math {
+
+// TODO: use binary GCD for unsigned integers ....
+template < typename IntegerType >
+IntegerType gcd( IntegerType a, IntegerType b )
+{
+ const IntegerType zero = (IntegerType)0;
+ while ( true )
+ {
+ if ( a == zero )
+ return b;
+ b %= a;
+
+ if ( b == zero )
+ return a;
+ a %= b;
+ }
+}
+
+template < typename IntegerType >
+IntegerType lcm( IntegerType a, IntegerType b )
+{
+ const IntegerType t = gcd (a,b);
+ if (!t)return t;
+ return a / t * b;
+}
+
+}}
+
+#endif
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/pointer_cast.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/pointer_cast.hpp
new file mode 100644
index 000000000..ce188c13c
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/pointer_cast.hpp
@@ -0,0 +1,45 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// (C) Copyright Ion Gaztanaga 2005.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#ifndef BOOST_POINTER_CAST_HPP
+#define BOOST_POINTER_CAST_HPP
+
+namespace boost {
+
+//static_pointer_cast overload for raw pointers
+template<class T, class U>
+inline T* static_pointer_cast(U *ptr)
+{
+ return static_cast<T*>(ptr);
+}
+
+//dynamic_pointer_cast overload for raw pointers
+template<class T, class U>
+inline T* dynamic_pointer_cast(U *ptr)
+{
+ return dynamic_cast<T*>(ptr);
+}
+
+//const_pointer_cast overload for raw pointers
+template<class T, class U>
+inline T* const_pointer_cast(U *ptr)
+{
+ return const_cast<T*>(ptr);
+}
+
+//reinterpret_pointer_cast overload for raw pointers
+template<class T, class U>
+inline T* reinterpret_pointer_cast(U *ptr)
+{
+ return reinterpret_cast<T*>(ptr);
+}
+
+} // namespace boost
+
+#endif //BOOST_POINTER_CAST_HPP
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/scoped_array.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/scoped_array.hpp
new file mode 100644
index 000000000..cd7398193
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/scoped_array.hpp
@@ -0,0 +1,79 @@
+
+#ifndef __AI_BOOST_SCOPED_ARRAY_INCLUDED
+#define __AI_BOOST_SCOPED_ARRAY_INCLUDED
+
+#ifndef BOOST_SCOPED_ARRAY_HPP_INCLUDED
+
+namespace boost {
+
+// small replacement for boost::scoped_array
+template <class T>
+class scoped_array
+{
+public:
+
+ // provide a default construtctor
+ scoped_array()
+ : ptr(0)
+ {
+ }
+
+ // construction from an existing heap object of type T
+ scoped_array(T* _ptr)
+ : ptr(_ptr)
+ {
+ }
+
+ // automatic destruction of the wrapped object at the
+ // end of our lifetime
+ ~scoped_array()
+ {
+ delete[] ptr;
+ }
+
+ inline T* get()
+ {
+ return ptr;
+ }
+
+ inline T* operator-> ()
+ {
+ return ptr;
+ }
+
+ inline void reset (T* t = 0)
+ {
+ delete[] ptr;
+ ptr = t;
+ }
+
+ T & operator[](std::ptrdiff_t i) const
+ {
+ return ptr[i];
+ }
+
+ void swap(scoped_array & b)
+ {
+ std::swap(ptr, b.ptr);
+ }
+
+private:
+
+ // encapsulated object pointer
+ T* ptr;
+
+};
+
+template<class T>
+inline void swap(scoped_array<T> & a, scoped_array<T> & b)
+{
+ a.swap(b);
+}
+
+} // end of namespace boost
+
+#else
+# error "scoped_array.h was already included"
+#endif
+#endif // __AI_BOOST_SCOPED_ARRAY_INCLUDED
+
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/scoped_ptr.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/scoped_ptr.hpp
new file mode 100644
index 000000000..84021a829
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/scoped_ptr.hpp
@@ -0,0 +1,79 @@
+
+#ifndef __AI_BOOST_SCOPED_PTR_INCLUDED
+#define __AI_BOOST_SCOPED_PTR_INCLUDED
+
+#ifndef BOOST_SCOPED_PTR_HPP_INCLUDED
+
+namespace boost {
+
+// small replacement for boost::scoped_ptr
+template <class T>
+class scoped_ptr
+{
+public:
+
+ // provide a default construtctor
+ scoped_ptr()
+ : ptr(0)
+ {
+ }
+
+ // construction from an existing heap object of type T
+ scoped_ptr(T* _ptr)
+ : ptr(_ptr)
+ {
+ }
+
+ // automatic destruction of the wrapped object at the
+ // end of our lifetime
+ ~scoped_ptr()
+ {
+ delete ptr;
+ }
+
+ inline T* get() const
+ {
+ return ptr;
+ }
+
+ inline operator T*()
+ {
+ return ptr;
+ }
+
+ inline T* operator-> ()
+ {
+ return ptr;
+ }
+
+ inline void reset (T* t = 0)
+ {
+ delete ptr;
+ ptr = t;
+ }
+
+ void swap(scoped_ptr & b)
+ {
+ std::swap(ptr, b.ptr);
+ }
+
+private:
+
+ // encapsulated object pointer
+ T* ptr;
+
+};
+
+template<class T>
+inline void swap(scoped_ptr<T> & a, scoped_ptr<T> & b)
+{
+ a.swap(b);
+}
+
+} // end of namespace boost
+
+#else
+# error "scoped_ptr.h was already included"
+#endif
+#endif // __AI_BOOST_SCOPED_PTR_INCLUDED
+
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/shared_array.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/shared_array.hpp
new file mode 100644
index 000000000..7f604ae08
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/shared_array.hpp
@@ -0,0 +1,228 @@
+
+#ifndef INCLUDED_AI_BOOST_SHARED_ARRAY
+#define INCLUDED_AI_BOOST_SHARED_ARRAY
+
+#ifndef BOOST_SCOPED_ARRAY_HPP_INCLUDED
+
+// ------------------------------
+// Internal stub
+namespace boost {
+ namespace array_detail {
+ class controller {
+ public:
+
+ controller()
+ : cnt(1)
+ {}
+
+ public:
+
+ template <typename T>
+ controller* decref(T* pt) {
+ if (--cnt <= 0) {
+ delete this;
+ delete[] pt;
+ }
+ return NULL;
+ }
+
+ controller* incref() {
+ ++cnt;
+ return this;
+ }
+
+ long get() const {
+ return cnt;
+ }
+
+ private:
+ long cnt;
+ };
+
+ struct empty {};
+
+ template <typename DEST, typename SRC>
+ struct is_convertible_stub {
+
+ struct yes {char s[1];};
+ struct no {char s[2];};
+
+ static yes foo(DEST*);
+ static no foo(...);
+
+ enum {result = (sizeof(foo((SRC*)0)) == sizeof(yes) ? 1 : 0)};
+ };
+
+ template <bool> struct enable_if {};
+ template <> struct enable_if<true> {
+ typedef empty result;
+ };
+
+ template <typename DEST, typename SRC>
+ struct is_convertible : public enable_if<is_convertible_stub<DEST,SRC>::result > {
+ };
+ }
+
+// ------------------------------
+// Small replacement for boost::shared_array, not threadsafe because no
+// atomic reference counter is in use.
+// ------------------------------
+template <class T>
+class shared_array
+{
+ template <typename TT> friend class shared_array;
+
+ template<class TT> friend bool operator== (const shared_array<TT>& a, const shared_array<TT>& b);
+ template<class TT> friend bool operator!= (const shared_array<TT>& a, const shared_array<TT>& b);
+ template<class TT> friend bool operator< (const shared_array<TT>& a, const shared_array<TT>& b);
+
+public:
+
+ typedef T element_type;
+
+public:
+
+ // provide a default constructor
+ shared_array()
+ : ptr()
+ , ctr(NULL)
+ {
+ }
+
+ // construction from an existing object of type T
+ explicit shared_array(T* ptr)
+ : ptr(ptr)
+ , ctr(ptr ? new array_detail::controller() : NULL)
+ {
+ }
+
+ shared_array(const shared_array& r)
+ : ptr(r.ptr)
+ , ctr(r.ctr ? r.ctr->incref() : NULL)
+ {
+ }
+
+ template <typename Y>
+ shared_array(const shared_array<Y>& r,typename detail::is_convertible<T,Y>::result = detail::empty())
+ : ptr(r.ptr)
+ , ctr(r.ctr ? r.ctr->incref() : NULL)
+ {
+ }
+
+ // automatic destruction of the wrapped object when all
+ // references are freed.
+ ~shared_array() {
+ if (ctr) {
+ ctr = ctr->decref(ptr);
+ }
+ }
+
+ shared_array& operator=(const shared_array& r) {
+ if (this == &r) {
+ return *this;
+ }
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = r.ptr;
+ ctr = ptr?r.ctr->incref():NULL;
+ return *this;
+ }
+
+ template <typename Y>
+ shared_array& operator=(const shared_array<Y>& r) {
+ if (this == &r) {
+ return *this;
+ }
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = r.ptr;
+ ctr = ptr?r.ctr->incref():NULL;
+ return *this;
+ }
+
+ // pointer access
+ inline operator T*() {
+ return ptr;
+ }
+
+ inline T* operator-> () const {
+ return ptr;
+ }
+
+ // standard semantics
+ inline T* get() {
+ return ptr;
+ }
+
+ T& operator[] (std::ptrdiff_t index) const {
+ return ptr[index];
+ }
+
+ inline const T* get() const {
+ return ptr;
+ }
+
+ inline operator bool () const {
+ return ptr != NULL;
+ }
+
+ inline bool unique() const {
+ return use_count() == 1;
+ }
+
+ inline long use_count() const {
+ return ctr->get();
+ }
+
+ inline void reset (T* t = 0) {
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = t;
+ ctr = ptr?new array_detail::controller():NULL;
+ }
+
+ void swap(shared_array & b) {
+ std::swap(ptr, b.ptr);
+ std::swap(ctr, b.ctr);
+ }
+
+
+private:
+
+ // encapsulated object pointer
+ T* ptr;
+
+ // control block
+ array_detail::controller* ctr;
+};
+
+template<class T>
+inline void swap(shared_array<T> & a, shared_array<T> & b)
+{
+ a.swap(b);
+}
+
+template<class T>
+bool operator== (const shared_array<T>& a, const shared_array<T>& b) {
+ return a.ptr == b.ptr;
+}
+template<class T>
+bool operator!= (const shared_array<T>& a, const shared_array<T>& b) {
+ return a.ptr != b.ptr;
+}
+
+template<class T>
+bool operator< (const shared_array<T>& a, const shared_array<T>& b) {
+ return a.ptr < b.ptr;
+}
+
+
+} // end of namespace boost
+
+#else
+# error "shared_array.h was already included"
+#endif
+#endif // INCLUDED_AI_BOOST_SHARED_ARRAY
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/shared_ptr.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/shared_ptr.hpp
new file mode 100644
index 000000000..f0dd44911
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/shared_ptr.hpp
@@ -0,0 +1,257 @@
+
+#ifndef INCLUDED_AI_BOOST_SHARED_PTR
+#define INCLUDED_AI_BOOST_SHARED_PTR
+
+#ifndef BOOST_SCOPED_PTR_HPP_INCLUDED
+
+// ------------------------------
+// Internal stub
+namespace boost {
+ namespace detail {
+ class controller {
+ public:
+
+ controller()
+ : cnt(1)
+ {}
+
+ public:
+
+ template <typename T>
+ controller* decref(T* pt) {
+ if (--cnt <= 0) {
+ delete this;
+ delete pt;
+ }
+ return NULL;
+ }
+
+ controller* incref() {
+ ++cnt;
+ return this;
+ }
+
+ long get() const {
+ return cnt;
+ }
+
+ private:
+ long cnt;
+ };
+
+ struct empty {};
+
+ template <typename DEST, typename SRC>
+ struct is_convertible_stub {
+
+ struct yes {char s[1];};
+ struct no {char s[2];};
+
+ static yes foo(DEST*);
+ static no foo(...);
+
+ enum {result = (sizeof(foo((SRC*)0)) == sizeof(yes) ? 1 : 0)};
+ };
+
+ template <bool> struct enable_if {};
+ template <> struct enable_if<true> {
+ typedef empty result;
+ };
+
+ template <typename DEST, typename SRC>
+ struct is_convertible : public enable_if<is_convertible_stub<DEST,SRC>::result > {
+ };
+ }
+
+// ------------------------------
+// Small replacement for boost::shared_ptr, not threadsafe because no
+// atomic reference counter is in use.
+// ------------------------------
+template <class T>
+class shared_ptr
+{
+ template <typename TT> friend class shared_ptr;
+
+ template<class TT, class U> friend shared_ptr<TT> static_pointer_cast (shared_ptr<U> ptr);
+ template<class TT, class U> friend shared_ptr<TT> dynamic_pointer_cast (shared_ptr<U> ptr);
+ template<class TT, class U> friend shared_ptr<TT> const_pointer_cast (shared_ptr<U> ptr);
+
+ template<class TT> friend bool operator== (const shared_ptr<TT>& a, const shared_ptr<TT>& b);
+ template<class TT> friend bool operator!= (const shared_ptr<TT>& a, const shared_ptr<TT>& b);
+ template<class TT> friend bool operator< (const shared_ptr<TT>& a, const shared_ptr<TT>& b);
+
+public:
+
+ typedef T element_type;
+
+public:
+
+ // provide a default constructor
+ shared_ptr()
+ : ptr()
+ , ctr(NULL)
+ {
+ }
+
+ // construction from an existing object of type T
+ explicit shared_ptr(T* ptr)
+ : ptr(ptr)
+ , ctr(ptr ? new detail::controller() : NULL)
+ {
+ }
+
+ shared_ptr(const shared_ptr& r)
+ : ptr(r.ptr)
+ , ctr(r.ctr ? r.ctr->incref() : NULL)
+ {
+ }
+
+ template <typename Y>
+ shared_ptr(const shared_ptr<Y>& r,typename detail::is_convertible<T,Y>::result = detail::empty())
+ : ptr(r.ptr)
+ , ctr(r.ctr ? r.ctr->incref() : NULL)
+ {
+ }
+
+ // automatic destruction of the wrapped object when all
+ // references are freed.
+ ~shared_ptr() {
+ if (ctr) {
+ ctr = ctr->decref(ptr);
+ }
+ }
+
+ shared_ptr& operator=(const shared_ptr& r) {
+ if (this == &r) {
+ return *this;
+ }
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = r.ptr;
+ ctr = ptr?r.ctr->incref():NULL;
+ return *this;
+ }
+
+ template <typename Y>
+ shared_ptr& operator=(const shared_ptr<Y>& r) {
+ if (this == &r) {
+ return *this;
+ }
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = r.ptr;
+ ctr = ptr?r.ctr->incref():NULL;
+ return *this;
+ }
+
+ // pointer access
+ inline operator T*() {
+ return ptr;
+ }
+
+ inline T* operator-> () const {
+ return ptr;
+ }
+
+ // standard semantics
+ inline T* get() {
+ return ptr;
+ }
+
+ inline const T* get() const {
+ return ptr;
+ }
+
+ inline operator bool () const {
+ return ptr != NULL;
+ }
+
+ inline bool unique() const {
+ return use_count() == 1;
+ }
+
+ inline long use_count() const {
+ return ctr->get();
+ }
+
+ inline void reset (T* t = 0) {
+ if (ctr) {
+ ctr->decref(ptr);
+ }
+ ptr = t;
+ ctr = ptr?new detail::controller():NULL;
+ }
+
+ void swap(shared_ptr & b) {
+ std::swap(ptr, b.ptr);
+ std::swap(ctr, b.ctr);
+ }
+
+private:
+
+
+ // for use by the various xxx_pointer_cast helper templates
+ explicit shared_ptr(T* ptr, detail::controller* ctr)
+ : ptr(ptr)
+ , ctr(ctr->incref())
+ {
+ }
+
+private:
+
+ // encapsulated object pointer
+ T* ptr;
+
+ // control block
+ detail::controller* ctr;
+};
+
+template<class T>
+inline void swap(shared_ptr<T> & a, shared_ptr<T> & b)
+{
+ a.swap(b);
+}
+
+template<class T>
+bool operator== (const shared_ptr<T>& a, const shared_ptr<T>& b) {
+ return a.ptr == b.ptr;
+}
+template<class T>
+bool operator!= (const shared_ptr<T>& a, const shared_ptr<T>& b) {
+ return a.ptr != b.ptr;
+}
+
+template<class T>
+bool operator< (const shared_ptr<T>& a, const shared_ptr<T>& b) {
+ return a.ptr < b.ptr;
+}
+
+
+template<class T, class U>
+inline shared_ptr<T> static_pointer_cast( shared_ptr<U> ptr)
+{
+ return shared_ptr<T>(static_cast<T*>(ptr.ptr),ptr.ctr);
+}
+
+template<class T, class U>
+inline shared_ptr<T> dynamic_pointer_cast( shared_ptr<U> ptr)
+{
+ return shared_ptr<T>(dynamic_cast<T*>(ptr.ptr),ptr.ctr);
+}
+
+template<class T, class U>
+inline shared_ptr<T> const_pointer_cast( shared_ptr<U> ptr)
+{
+ return shared_ptr<T>(const_cast<T*>(ptr.ptr),ptr.ctr);
+}
+
+
+
+} // end of namespace boost
+
+#else
+# error "shared_ptr.h was already included"
+#endif
+#endif // INCLUDED_AI_BOOST_SCOPED_PTR
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/static_assert.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/static_assert.hpp
new file mode 100644
index 000000000..4ee728bae
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/static_assert.hpp
@@ -0,0 +1,20 @@
+
+#ifndef AI_BOOST_STATIC_ASSERT_INCLUDED
+#define AI_BOOST_STATIC_ASSERT_INCLUDED
+
+#ifndef BOOST_STATIC_ASSERT
+
+namespace boost {
+ namespace detail {
+
+ template <bool b> class static_assertion_failure;
+ template <> class static_assertion_failure<true> {};
+ }
+}
+
+
+#define BOOST_STATIC_ASSERT(eval) \
+{boost::detail::static_assertion_failure<(eval)> assert_dummy; (void)assert_dummy;}
+
+#endif
+#endif // !! AI_BOOST_STATIC_ASSERT_INCLUDED
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/timer.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/timer.hpp
new file mode 100644
index 000000000..df93b25f6
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/timer.hpp
@@ -0,0 +1,72 @@
+// boost timer.hpp header file ---------------------------------------------//
+
+// Copyright Beman Dawes 1994-99. Distributed under the Boost
+// Software License, Version 1.0. (See accompanying file
+// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org/libs/timer for documentation.
+
+// Revision History
+// 01 Apr 01 Modified to use new <boost/limits.hpp> header. (JMaddock)
+// 12 Jan 01 Change to inline implementation to allow use without library
+// builds. See docs for more rationale. (Beman Dawes)
+// 25 Sep 99 elapsed_max() and elapsed_min() added (John Maddock)
+// 16 Jul 99 Second beta
+// 6 Jul 99 Initial boost version
+
+#ifndef BOOST_TIMER_HPP
+#define BOOST_TIMER_HPP
+
+//#include <boost/config.hpp>
+#include <ctime>
+//#include <boost/limits.hpp>
+
+# ifdef BOOST_NO_STDC_NAMESPACE
+ namespace std { using ::clock_t; using ::clock; }
+# endif
+
+
+namespace boost {
+
+// timer -------------------------------------------------------------------//
+
+// A timer object measures elapsed time.
+
+// It is recommended that implementations measure wall clock rather than CPU
+// time since the intended use is performance measurement on systems where
+// total elapsed time is more important than just process or CPU time.
+
+// Warnings: The maximum measurable elapsed time may well be only 596.5+ hours
+// due to implementation limitations. The accuracy of timings depends on the
+// accuracy of timing information provided by the underlying platform, and
+// this varies a great deal from platform to platform.
+
+class timer
+{
+ public:
+ timer() { _start_time = std::clock(); } // postcondition: elapsed()==0
+// timer( const timer& src ); // post: elapsed()==src.elapsed()
+// ~timer(){}
+// timer& operator=( const timer& src ); // post: elapsed()==src.elapsed()
+ void restart() { _start_time = std::clock(); } // post: elapsed()==0
+ double elapsed() const // return elapsed time in seconds
+ { return double(std::clock() - _start_time) / CLOCKS_PER_SEC; }
+
+ double elapsed_max() const // return estimated maximum value for elapsed()
+ // Portability warning: elapsed_max() may return too high a value on systems
+ // where std::clock_t overflows or resets at surprising values.
+ {
+ return (double((std::numeric_limits<std::clock_t>::max)())
+ - double(_start_time)) / double(CLOCKS_PER_SEC);
+ }
+
+ double elapsed_min() const // return minimum value for elapsed()
+ { return double(1)/double(CLOCKS_PER_SEC); }
+
+ private:
+ std::clock_t _start_time;
+}; // timer
+
+} // namespace boost
+
+#endif // BOOST_TIMER_HPP
diff --git a/3rdparty/assimp/code/BoostWorkaround/boost/tuple/tuple.hpp b/3rdparty/assimp/code/BoostWorkaround/boost/tuple/tuple.hpp
new file mode 100644
index 000000000..afd37bd79
--- /dev/null
+++ b/3rdparty/assimp/code/BoostWorkaround/boost/tuple/tuple.hpp
@@ -0,0 +1,285 @@
+// A very small replacement for boost::tuple
+// (c) Alexander Gessler, 2008 [alexander.gessler@gmx.net]
+
+#ifndef BOOST_TUPLE_INCLUDED
+#define BOOST_TUPLE_INCLUDED
+
+namespace boost {
+ namespace detail {
+
+ // Represents an empty tuple slot (up to 5 supported)
+ struct nulltype {};
+
+ // For readable error messages
+ struct tuple_component_idx_out_of_bounds;
+
+ // To share some code for the const/nonconst versions of the getters
+ template <bool b, typename T>
+ struct ConstIf {
+ typedef T t;
+ };
+
+ template <typename T>
+ struct ConstIf<true,T> {
+ typedef const T t;
+ };
+
+ // Predeclare some stuff
+ template <typename, unsigned, typename, bool, unsigned> struct value_getter;
+
+ // Helper to obtain the type of a tuple element
+ template <typename T, unsigned NIDX, typename TNEXT, unsigned N /*= 0*/>
+ struct type_getter {
+ typedef type_getter<typename TNEXT::type,NIDX+1,typename TNEXT::next_type,N> next_elem_getter;
+ typedef typename next_elem_getter::type type;
+ };
+
+ template <typename T, unsigned NIDX, typename TNEXT >
+ struct type_getter <T,NIDX,TNEXT,NIDX> {
+ typedef T type;
+ };
+
+ // Base class for all explicit specializations of list_elem
+ template <typename T, unsigned NIDX, typename TNEXT >
+ struct list_elem_base {
+
+ // Store template parameters
+ typedef TNEXT next_type;
+ typedef T type;
+
+ static const unsigned nidx = NIDX;
+ };
+
+ // Represents an element in the tuple component list
+ template <typename T, unsigned NIDX, typename TNEXT >
+ struct list_elem : list_elem_base<T,NIDX,TNEXT>{
+
+ // Real members
+ T me;
+ TNEXT next;
+
+ // Get the value of a specific tuple element
+ template <unsigned N>
+ typename type_getter<T,NIDX,TNEXT,N>::type& get () {
+ value_getter <T,NIDX,TNEXT,false,N> s;
+ return s(*this);
+ }
+
+ // Get the value of a specific tuple element
+ template <unsigned N>
+ const typename type_getter<T,NIDX,TNEXT,N>::type& get () const {
+ value_getter <T,NIDX,TNEXT,true,N> s;
+ return s(*this);
+ }
+
+ // Explicit cast
+ template <typename T2, typename TNEXT2 >
+ operator list_elem<T2,NIDX,TNEXT2> () const {
+ list_elem<T2,NIDX,TNEXT2> ret;
+ ret.me = (T2)me;
+ ret.next = next;
+ return ret;
+ }
+
+ // Recursively compare two elements (last element returns always true)
+ bool operator == (const list_elem& s) const {
+ return (me == s.me && next == s.next);
+ }
+ };
+
+ // Represents a non-used tuple element - the very last element processed
+ template <typename TNEXT, unsigned NIDX >
+ struct list_elem<nulltype,NIDX,TNEXT> : list_elem_base<nulltype,NIDX,TNEXT> {
+ template <unsigned N, bool IS_CONST = true> struct value_getter {
+ /* just dummy members to produce readable error messages */
+ tuple_component_idx_out_of_bounds operator () (typename ConstIf<IS_CONST,list_elem>::t& me);
+ };
+ template <unsigned N> struct type_getter {
+ /* just dummy members to produce readable error messages */
+ typedef tuple_component_idx_out_of_bounds type;
+ };
+
+ // dummy
+ list_elem& operator = (const list_elem& other) {
+ return *this;
+ }
+
+ // dummy
+ bool operator == (const list_elem& other) {
+ return true;
+ }
+ };
+
+ // Represents the absolute end of the list
+ typedef list_elem<nulltype,0,int> list_end;
+
+ // Helper obtain to query the value of a tuple element
+ // NOTE: This can't be a nested class as the compiler won't accept a full or
+ // partial specialization of a nested class of a non-specialized template
+ template <typename T, unsigned NIDX, typename TNEXT, bool IS_CONST, unsigned N>
+ struct value_getter {
+
+ // calling list_elem
+ typedef list_elem<T,NIDX,TNEXT> outer_elem;
+
+ // typedef for the getter for next element
+ typedef value_getter<typename TNEXT::type,NIDX+1,typename TNEXT::next_type,
+ IS_CONST, N> next_value_getter;
+
+ typename ConstIf<IS_CONST,typename type_getter<T,NIDX,TNEXT,N>::type>::t&
+ operator () (typename ConstIf<IS_CONST,outer_elem >::t& me) {
+
+ next_value_getter s;
+ return s(me.next);
+ }
+ };
+
+ template <typename T, unsigned NIDX, typename TNEXT, bool IS_CONST>
+ struct value_getter <T,NIDX,TNEXT,IS_CONST,NIDX> {
+ typedef list_elem<T,NIDX,TNEXT> outer_elem;
+
+ typename ConstIf<IS_CONST,T>::t& operator () (typename ConstIf<IS_CONST,outer_elem >::t& me) {
+ return me.me;
+ }
+ };
+ };
+
+ // A very minimal implementation for up to 5 elements
+ template <typename T0 = detail::nulltype,
+ typename T1 = detail::nulltype,
+ typename T2 = detail::nulltype,
+ typename T3 = detail::nulltype,
+ typename T4 = detail::nulltype>
+ class tuple {
+
+ template <typename T0b,
+ typename T1b,
+ typename T2b,
+ typename T3b,
+ typename T4b >
+ friend class tuple;
+
+ private:
+
+ typedef detail::list_elem<T0,0,
+ detail::list_elem<T1,1,
+ detail::list_elem<T2,2,
+ detail::list_elem<T3,3,
+ detail::list_elem<T4,4,
+ detail::list_end > > > > > very_long;
+
+ very_long m;
+
+ public:
+
+ // Get a specific tuple element
+ template <unsigned N>
+ typename detail::type_getter<T0,0,typename very_long::next_type, N>::type& get () {
+ return m.get<N>();
+ }
+
+ // ... and the const version
+ template <unsigned N>
+ typename const detail::type_getter<T0,0,typename very_long::next_type, N>::type& get () const {
+ return m.get<N>();
+ }
+
+
+ // comparison operators
+ bool operator== (const tuple& other) const {
+ return m == other.m;
+ }
+
+ // ... and the other way round
+ bool operator!= (const tuple& other) const {
+ return !(m == other.m);
+ }
+
+ // cast to another tuple - all single elements must be convertible
+ template < typename T0, typename T1,typename T2,
+ typename T3, typename T4>
+
+ operator tuple <T0,T1,T2,T3,T4> () const {
+ tuple <T0,T1,T2,T3,T4> s;
+ s.m = (tuple <T0,T1,T2,T3,T4>::very_long)m;
+ return s;
+ }
+ };
+
+ // Another way to access an element ...
+ template <unsigned N,typename T0,typename T1,typename T2,typename T3,typename T4>
+ inline typename tuple<T0,T1,T2,T3,T4>::very_long::type_getter<N>::type& get (
+ tuple<T0,T1,T2,T3,T4>& m) {
+ return m.get<N>();
+ }
+
+ // ... and the const version
+ template <unsigned N,typename T0,typename T1,typename T2,typename T3,typename T4>
+ inline const typename tuple<T0,T1,T2,T3,T4>::very_long::type_getter<N>::type& get (
+ const tuple<T0,T1,T2,T3,T4>& m) {
+ return m.get<N>();
+ }
+
+ // Constructs a tuple with 5 elements
+ template <typename T0,typename T1,typename T2,typename T3,typename T4>
+ inline tuple <T0,T1,T2,T3,T4> make_tuple (const T0& t0,
+ const T1& t1,const T2& t2,const T3& t3,const T4& t4) {
+
+ tuple <T0,T1,T2,T3,T4> t;
+ t.get<0>() = t0;
+ t.get<1>() = t1;
+ t.get<2>() = t2;
+ t.get<3>() = t3;
+ t.get<4>() = t4;
+ return t;
+ }
+
+ // Constructs a tuple with 4 elements
+ template <typename T0,typename T1,typename T2,typename T3>
+ inline tuple <T0,T1,T2,T3> make_tuple (const T0& t0,
+ const T1& t1,const T2& t2,const T3& t3) {
+ tuple <T0,T1,T2,T3> t;
+ t.get<0>() = t0;
+ t.get<1>() = t1;
+ t.get<2>() = t2;
+ t.get<3>() = t3;
+ return t;
+ }
+
+ // Constructs a tuple with 3 elements
+ template <typename T0,typename T1,typename T2>
+ inline tuple <T0,T1,T2> make_tuple (const T0& t0,
+ const T1& t1,const T2& t2) {
+ tuple <T0,T1,T2> t;
+ t.get<0>() = t0;
+ t.get<1>() = t1;
+ t.get<2>() = t2;
+ return t;
+ }
+
+ // Constructs a tuple with 2 elements (fucking idiot, use std::pair instead!)
+ template <typename T0,typename T1>
+ inline tuple <T0,T1> make_tuple (const T0& t0,
+ const T1& t1) {
+ tuple <T0,T1> t;
+ t.get<0>() = t0;
+ t.get<1>() = t1;
+ return t;
+ }
+
+ // Constructs a tuple with 1 elements (no comment ...)
+ template <typename T0>
+ inline tuple <T0> make_tuple (const T0& t0) {
+ tuple <T0> t;
+ t.get<0>() = t0;
+ return t;
+ }
+
+ // Constructs a tuple with 0 elements (ehm? Try http://www.promillerechner.net)
+ inline tuple <> make_tuple () {
+ tuple <> t;
+ return t;
+ }
+};
+
+#endif // !! BOOST_TUPLE_INCLUDED
diff --git a/3rdparty/assimp/code/ByteSwap.h b/3rdparty/assimp/code/ByteSwap.h
new file mode 100644
index 000000000..86ce9791e
--- /dev/null
+++ b/3rdparty/assimp/code/ByteSwap.h
@@ -0,0 +1,245 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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_BYTESWAP_H_INC
+#define AI_BYTESWAP_H_INC
+
+#include "../include/aiAssert.h"
+#include "../include/aiTypes.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;
+ }
+};
+
+} // Namespace Assimp
+
+
+// --------------------------------------------------------------------------------------
+// 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
+
+
+
+#endif //!! AI_BYTESWAP_H_INC
diff --git a/3rdparty/assimp/code/COBLoader.cpp b/3rdparty/assimp/code/COBLoader.cpp
new file mode 100644
index 000000000..9639fc6bd
--- /dev/null
+++ b/3rdparty/assimp/code/COBLoader.cpp
@@ -0,0 +1,1278 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 COBLoader.cpp
+ * @brief Implementation of the TrueSpace COB/SCN importer class.
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
+#include "COBLoader.h"
+#include "COBScene.h"
+
+#include "StreamReader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+#include "LineSplitter.h"
+#include "TinyFormatter.h"
+
+using namespace Assimp;
+using namespace Assimp::COB;
+using namespace Assimp::Formatter;
+
+#define for_each BOOST_FOREACH
+
+
+static const float units[] = {
+ 1000.f,
+ 100.f,
+ 1.f,
+ 0.001f,
+ 1.f/0.0254f,
+ 1.f/0.3048f,
+ 1.f/0.9144f,
+ 1.f/1609.344f
+};
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+COBImporter::COBImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+COBImporter::~COBImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool COBImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string& extension = GetExtension(pFile);
+ if (extension == "cob" || extension == "scn") {
+ return true;
+ }
+
+ else if ((!extension.length() || checkSig) && pIOHandler) {
+ const char* tokens[] = {"Caligary"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// List all extensions handled by this loader
+void COBImporter::GetExtensionList(std::set<std::string>& app)
+{
+ app.insert("cob");
+ app.insert("scn");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void COBImporter::SetupProperties(const Importer* /*pImp*/)
+{
+ // nothing to be done for the moment
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ void COBImporter::ThrowException(const std::string& msg)
+{
+ throw DeadlyImportError("COB: "+msg);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void COBImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ COB::Scene scene;
+ boost::scoped_ptr<StreamReaderLE> stream(new StreamReaderLE( pIOHandler->Open(pFile,"rb")) );
+
+ // check header
+ char head[32];
+ stream->CopyAndAdvance(head,32);
+ if (strncmp(head,"Caligari ",9)) {
+ ThrowException("Could not found magic id: `Caligari`");
+ }
+
+ DefaultLogger::get()->info("File format tag: "+std::string(head+9,6));
+ void (COBImporter::* load)(Scene&,StreamReaderLE*)= head[15]=='A'?&COBImporter::ReadAsciiFile:&COBImporter::ReadBinaryFile;
+ if (head[16]!='L') {
+ ThrowException("File is big-endian, which is not supported");
+ }
+
+ // load data into intermediate structures
+ (this->*load)(scene,stream.get());
+ if (scene.nodes.empty()) {
+ ThrowException("No nodes loaded");
+ }
+
+ // sort faces by material indices
+ for_each(boost::shared_ptr< Node >& n,scene.nodes) {
+ if (n->type == Node::TYPE_MESH) {
+ Mesh& mesh = (Mesh&)(*n.get());
+ for_each(Face& f,mesh.faces) {
+ mesh.temp_map[f.material].push_back(&f);
+ }
+ }
+ }
+
+ // count meshes
+ for_each(boost::shared_ptr< Node >& n,scene.nodes) {
+ if (n->type == Node::TYPE_MESH) {
+ Mesh& mesh = (Mesh&)(*n.get());
+ if (mesh.vertex_positions.size() && mesh.texture_coords.size()) {
+ pScene->mNumMeshes += mesh.temp_map.size();
+ }
+ }
+ }
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]();
+ pScene->mNumMeshes = 0;
+
+ // count lights and cameras
+ for_each(boost::shared_ptr< Node >& n,scene.nodes) {
+ if (n->type == Node::TYPE_LIGHT) {
+ ++pScene->mNumLights;
+ }
+ else if (n->type == Node::TYPE_CAMERA) {
+ ++pScene->mNumCameras;
+ }
+ }
+
+ if (pScene->mNumLights) {
+ pScene->mLights = new aiLight*[pScene->mNumLights]();
+ }
+ if (pScene->mNumCameras) {
+ pScene->mCameras = new aiCamera*[pScene->mNumCameras]();
+ }
+ pScene->mNumLights = pScene->mNumCameras = 0;
+
+ // resolve parents by their IDs and build the output graph
+ boost::scoped_ptr<Node> root(new Group());
+ for (size_t n = 0; n < scene.nodes.size(); ++n) {
+ const Node& nn = *scene.nodes[n].get();
+ if (nn.parent_id==0) {
+ root->temp_children.push_back(&nn);
+ }
+
+ for (size_t m = n; m < scene.nodes.size(); ++m) {
+ const Node& mm = *scene.nodes[m].get();
+ if (mm.parent_id == nn.id) {
+ nn.temp_children.push_back(&mm);
+ }
+ }
+ }
+
+ pScene->mRootNode = BuildNodes(*root.get(),scene,pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+void ConvertTexture(boost::shared_ptr< Texture > tex, MaterialHelper* out, aiTextureType type)
+{
+ const aiString path( tex->path );
+ out->AddProperty(&path,AI_MATKEY_TEXTURE(type,0));
+ out->AddProperty(&tex->transform,1,AI_MATKEY_UVTRANSFORM(type,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode* COBImporter::BuildNodes(const Node& root,const Scene& scin,aiScene* fill)
+{
+ aiNode* nd = new aiNode();
+ nd->mName.Set(root.name);
+ nd->mTransformation = root.transform;
+
+ // Note to everybody believing Voodoo is appropriate here:
+ // I know polymorphism, run as fast as you can ;-)
+ if (Node::TYPE_MESH == root.type) {
+ const Mesh& ndmesh = (const Mesh&)(root);
+ if (ndmesh.vertex_positions.size() && ndmesh.texture_coords.size()) {
+
+ typedef std::pair<unsigned int,Mesh::FaceRefList> Entry;
+ for_each(const Entry& reflist,ndmesh.temp_map) {
+ { // create mesh
+ size_t n = 0;
+ for_each(Face* f, reflist.second) {
+ n += f->indices.size();
+ }
+ if (!n) {
+ continue;
+ }
+ aiMesh* outmesh = fill->mMeshes[fill->mNumMeshes++] = new aiMesh();
+ ++nd->mNumMeshes;
+
+ outmesh->mVertices = new aiVector3D[n];
+ outmesh->mTextureCoords[0] = new aiVector3D[n];
+
+ outmesh->mFaces = new aiFace[reflist.second.size()]();
+ for_each(Face* f, reflist.second) {
+ if (f->indices.empty()) {
+ continue;
+ }
+
+ aiFace& fout = outmesh->mFaces[outmesh->mNumFaces++];
+ fout.mIndices = new unsigned int[f->indices.size()];
+
+ for_each(VertexIndex& v, f->indices) {
+ if (v.pos_idx >= ndmesh.vertex_positions.size()) {
+ ThrowException("Position index out of range");
+ }
+ if (v.uv_idx >= ndmesh.texture_coords.size()) {
+ ThrowException("UV index out of range");
+ }
+ outmesh->mVertices[outmesh->mNumVertices] = ndmesh.vertex_positions[ v.pos_idx ];
+ outmesh->mTextureCoords[0][outmesh->mNumVertices] = aiVector3D(
+ ndmesh.texture_coords[ v.uv_idx ].x,
+ ndmesh.texture_coords[ v.uv_idx ].y,
+ 0.f
+ );
+
+ fout.mIndices[fout.mNumIndices++] = outmesh->mNumVertices++;
+ }
+ }
+ outmesh->mMaterialIndex = fill->mNumMaterials;
+ }{ // create material
+ const Material* min = NULL;
+ for_each(const Material& m, scin.materials) {
+ if (m.parent_id == ndmesh.id && m.matnum == reflist.first) {
+ min = &m;
+ break;
+ }
+ }
+ boost::scoped_ptr<const Material> defmat;
+ if (!min) {
+ DefaultLogger::get()->debug(format()<<"Could not resolve material index "
+ <<reflist.first<<" - creating default material for this slot");
+
+ defmat.reset(min=new Material());
+ }
+
+ MaterialHelper* mat = new MaterialHelper();
+ fill->mMaterials[fill->mNumMaterials++] = mat;
+
+ const aiString s(format("#mat_")<<fill->mNumMeshes<<"_"<<min->matnum);
+ mat->AddProperty(&s,AI_MATKEY_NAME);
+
+ if (int tmp = ndmesh.draw_flags & Mesh::WIRED ? 1 : 0) {
+ mat->AddProperty(&tmp,1,AI_MATKEY_ENABLE_WIREFRAME);
+ }
+
+ { int shader;
+ switch(min->shader)
+ {
+ case Material::FLAT:
+ shader = aiShadingMode_Gouraud;
+ break;
+
+ case Material::PHONG:
+ shader = aiShadingMode_Phong;
+ break;
+
+ case Material::METAL:
+ shader = aiShadingMode_CookTorrance;
+ break;
+
+ default:
+ ai_assert(false); // shouldn't be here
+ }
+ mat->AddProperty(&shader,1,AI_MATKEY_SHADING_MODEL);
+ if (shader != aiShadingMode_Gouraud) {
+ mat->AddProperty(&min->exp,1,AI_MATKEY_SHININESS);
+ }
+ }
+
+ mat->AddProperty(&min->ior,1,AI_MATKEY_REFRACTI);
+ mat->AddProperty(&min->rgb,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ aiColor3D c = aiColor3D(min->rgb)*min->ks;
+ mat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR);
+
+ c = aiColor3D(min->rgb)*min->ka;
+ mat->AddProperty(&c,1,AI_MATKEY_COLOR_AMBIENT);
+
+ // convert textures if some exist.
+ if (min->tex_color) {
+ ConvertTexture(min->tex_color,mat,aiTextureType_DIFFUSE);
+ }
+ if (min->tex_env) {
+ ConvertTexture(min->tex_env ,mat,aiTextureType_UNKNOWN);
+ }
+ if (min->tex_bump) {
+ ConvertTexture(min->tex_bump ,mat,aiTextureType_HEIGHT);
+ }
+ }
+ }
+ }
+ }
+ else if (Node::TYPE_LIGHT == root.type) {
+ const Light& ndlight = (const Light&)(root);
+ aiLight* outlight = fill->mLights[fill->mNumLights++] = new aiLight();
+
+ outlight->mName.Set(ndlight.name);
+ outlight->mColorDiffuse = outlight->mColorAmbient = outlight->mColorSpecular = ndlight.color;
+
+ outlight->mAngleOuterCone = AI_DEG_TO_RAD(ndlight.angle);
+ outlight->mAngleInnerCone = AI_DEG_TO_RAD(ndlight.inner_angle);
+
+ // XXX
+ outlight->mType = ndlight.ltype==Light::SPOT ? aiLightSource_SPOT : aiLightSource_DIRECTIONAL;
+ }
+ else if (Node::TYPE_CAMERA == root.type) {
+ const Camera& ndcam = (const Camera&)(root);
+ aiCamera* outcam = fill->mCameras[fill->mNumCameras++] = new aiCamera();
+
+ outcam->mName.Set(ndcam.name);
+ }
+
+ // add meshes
+ if (nd->mNumMeshes) { // mMeshes must be NULL if count is 0
+ nd->mMeshes = new unsigned int[nd->mNumMeshes];
+ for (unsigned int i = 0; i < nd->mNumMeshes;++i) {
+ nd->mMeshes[i] = fill->mNumMeshes-i-1;
+ }
+ }
+
+ // add children recursively
+ nd->mChildren = new aiNode*[root.temp_children.size()]();
+ for_each(const Node* n, root.temp_children) {
+ (nd->mChildren[nd->mNumChildren++] = BuildNodes(*n,scin,fill))->mParent = nd;
+ }
+
+ return nd;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read an ASCII file into the given scene data structure
+void COBImporter::ReadAsciiFile(Scene& out, StreamReaderLE* stream)
+{
+ ChunkInfo ci;
+ for (LineSplitter splitter(*stream);splitter;++splitter) {
+
+ // add all chunks to be recognized here. /else ../ ommitted intentionally.
+ if (splitter.match_start("PolH ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadPolH_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("BitM ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadBitM_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Mat1 ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadMat1_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Grou ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadGrou_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Lght ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadLght_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Came ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadCame_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Bone ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadBone_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Chan ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadChan_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("Unit ")) {
+ ReadChunkInfo_Ascii(ci,splitter);
+ ReadUnit_Ascii(out,splitter,ci);
+ }
+ if (splitter.match_start("END ")) {
+ // we don't need this, but I guess there is a reason this
+ // chunk has been implemented into COB for.
+ return;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadChunkInfo_Ascii(ChunkInfo& out, const LineSplitter& splitter)
+{
+ const char* all_tokens[8];
+ splitter.get_tokens(all_tokens);
+
+ out.version = (all_tokens[1][1]-'0')*100+(all_tokens[1][3]-'0')*10+(all_tokens[1][4]-'0');
+ out.id = strtol10(all_tokens[3]);
+ out.parent_id = strtol10(all_tokens[5]);
+ out.size = strtol10s(all_tokens[7]);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::UnsupportedChunk_Ascii(LineSplitter& splitter, const ChunkInfo& nfo, const char* name)
+{
+ const std::string error = format("Encountered unsupported chunk: ") << name <<
+ " [version: "<<nfo.version<<", size: "<<nfo.size<<"]";
+
+ // we can recover if the chunk size was specified.
+ if (nfo.size != static_cast<unsigned int>(-1)) {
+ DefaultLogger::get()->error(error);
+
+ // (HACK) - our current position in the stream is the beginning of the
+ // head line of the next chunk. That's fine, but the caller is going
+ // to call ++ on `splitter`, which we need to swallow to avoid
+ // missing the next line.
+ splitter.get_stream().IncPtr(nfo.size);
+ splitter.swallow_next_increment();
+ }
+ else ThrowException(error);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogWarn_Ascii(const LineSplitter& splitter, const format& message) {
+ LogWarn_Ascii(message << " [at line "<< splitter.get_index()<<"]");
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogError_Ascii(const LineSplitter& splitter, const format& message) {
+ LogError_Ascii(message << " [at line "<< splitter.get_index()<<"]");
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogInfo_Ascii(const LineSplitter& splitter, const format& message) {
+ LogInfo_Ascii(message << " [at line "<< splitter.get_index()<<"]");
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogDebug_Ascii(const LineSplitter& splitter, const format& message) {
+ LogDebug_Ascii(message << " [at line "<< splitter.get_index()<<"]");
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogWarn_Ascii(const Formatter::format& message) {
+ DefaultLogger::get()->warn(std::string("COB: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogError_Ascii(const Formatter::format& message) {
+ DefaultLogger::get()->error(std::string("COB: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogInfo_Ascii(const Formatter::format& message) {
+ DefaultLogger::get()->info(std::string("COB: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::LogDebug_Ascii(const Formatter::format& message) {
+ DefaultLogger::get()->debug(std::string("COB: ")+=message);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBasicNodeInfo_Ascii(Node& msh, LineSplitter& splitter, const ChunkInfo& /*nfo*/)
+{
+ for (;splitter;++splitter) {
+ if (splitter.match_start("Name")) {
+ msh.name = std::string(splitter[1]);
+
+ // make nice names by merging the dupe count
+ std::replace(msh.name.begin(),msh.name.end(),
+ ',','_');
+ }
+ else if (splitter.match_start("Transform")) {
+ for (unsigned int y = 0; y < 4 && ++splitter; ++y) {
+ const char* s = splitter->c_str();
+ for (unsigned int x = 0; x < 4; ++x) {
+ SkipSpaces(&s);
+ msh.transform[y][x] = fast_atof(&s);
+ }
+ }
+ // we need the transform chunk, so we won't return until we have it.
+ return;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+void COBImporter::ReadFloat3Tuple_Ascii(T& fill, const char** in)
+{
+ const char* rgb = *in;
+ for (unsigned int i = 0; i < 3; ++i) {
+ SkipSpaces(&rgb);
+ if (*rgb == ',')++rgb;
+ SkipSpaces(&rgb);
+
+ fill[i] = fast_atof(&rgb);
+ }
+ *in = rgb;
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadMat1_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Mat1");
+ }
+
+ ++splitter;
+ if (!splitter.match_start("mat# ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `mat#` line in `Mat1` chunk "<<nfo.id);
+ return;
+ }
+
+ out.materials.push_back(Material());
+ Material& mat = out.materials.back();
+ mat = nfo;
+
+ mat.matnum = strtol10(splitter[1]);
+ ++splitter;
+
+ if (!splitter.match_start("shader: ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `mat#` line in `Mat1` chunk "<<nfo.id);
+ return;
+ }
+ std::string shader = std::string(splitter[1]);
+ shader = shader.substr(0,shader.find_first_of(" \t"));
+
+ if (shader == "metal") {
+ mat.shader = Material::METAL;
+ }
+ else if (shader == "phong") {
+ mat.shader = Material::PHONG;
+ }
+ else if (shader != "flat") {
+ LogWarn_Ascii(splitter,format()<<
+ "Unknown value for `shader` in `Mat1` chunk "<<nfo.id);
+ }
+
+ ++splitter;
+ if (!splitter.match_start("rgb ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `rgb` line in `Mat1` chunk "<<nfo.id);
+ }
+
+ const char* rgb = splitter[1];
+ ReadFloat3Tuple_Ascii(mat.rgb,&rgb);
+
+ ++splitter;
+ if (!splitter.match_start("alpha ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `alpha` line in `Mat1` chunk "<<nfo.id);
+ }
+
+ const char* tokens[10];
+ splitter.get_tokens(tokens);
+
+ mat.alpha = fast_atof( tokens[1] );
+ mat.ka = fast_atof( tokens[3] );
+ mat.ks = fast_atof( tokens[5] );
+ mat.exp = fast_atof( tokens[7] );
+ mat.ior = fast_atof( tokens[9] );
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadUnit_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 1) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Unit");
+ }
+ ++splitter;
+ if (!splitter.match_start("Units ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `Units` line in `Unit` chunk "<<nfo.id);
+ return;
+ }
+
+ // parent chunks preceede their childs, so we should have the
+ // corresponding chunk already.
+ for_each(boost::shared_ptr< Node >& nd, out.nodes) {
+ if (nd->id == nfo.parent_id) {
+ const unsigned int t=strtol10(splitter[1]);
+
+ nd->unit_scale = t>=sizeof(units)/sizeof(units[0])?(
+ LogWarn_Ascii(splitter,format()<<t<<" is not a valid value for `Units` attribute in `Unit chunk` "<<nfo.id)
+ ,1.f):units[t];
+ return;
+ }
+ }
+ LogWarn_Ascii(splitter,format()<<"`Unit` chunk "<<nfo.id<<" is a child of "
+ <<nfo.parent_id<<" which does not exist");
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadChan_Ascii(Scene& /*out*/, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Chan");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadLght_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Lght");
+ }
+
+ out.nodes.push_back(boost::shared_ptr<Light>(new Light()));
+ Light& msh = (Light&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
+
+ if (splitter.match_start("Infinite ")) {
+ msh.ltype = Light::INFINITE;
+ }
+ else if (splitter.match_start("Local ")) {
+ msh.ltype = Light::LOCAL;
+ }
+ else if (splitter.match_start("Spot ")) {
+ msh.ltype = Light::SPOT;
+ }
+ else {
+ LogWarn_Ascii(splitter,format()<<
+ "Unknown kind of light source in `Lght` chunk "<<nfo.id<<" : "<<*splitter);
+ msh.ltype = Light::SPOT;
+ }
+
+ ++splitter;
+ if (!splitter.match_start("color ")) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `color` line in `Lght` chunk "<<nfo.id);
+ }
+
+ const char* rgb = splitter[1];
+ ReadFloat3Tuple_Ascii(msh.color ,&rgb);
+
+ SkipSpaces(&rgb);
+ if (strncmp(rgb,"cone angle",10)) {
+ LogWarn_Ascii(splitter,format()<<
+ "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)) {
+ LogWarn_Ascii(splitter,format()<<
+ "Expected `inner angle` entity in `color` line in `Lght` chunk "<<nfo.id);
+ }
+ SkipSpaces(rgb+11,&rgb);
+ msh.inner_angle = fast_atof(&rgb);
+
+ // skip the rest for we can't handle this kind of physically-based lighting information.
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadCame_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 2) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Came");
+ }
+
+ out.nodes.push_back(boost::shared_ptr<Camera>(new Camera()));
+ Camera& msh = (Camera&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
+
+ // skip the next line, we don't know this differenciation between a
+ // standard camera and a panoramic camera.
+ ++splitter;
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBone_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 5) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Bone");
+ }
+
+ out.nodes.push_back(boost::shared_ptr<Bone>(new Bone()));
+ Bone& msh = (Bone&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
+
+ // TODO
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadGrou_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 1) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"Grou");
+ }
+
+ out.nodes.push_back(boost::shared_ptr<Group>(new Group()));
+ Group& msh = (Group&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadPolH_Ascii(Scene& out, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"PolH");
+ }
+
+ out.nodes.push_back(boost::shared_ptr<Mesh>(new Mesh()));
+ Mesh& msh = (Mesh&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Ascii(msh,++splitter,nfo);
+
+ // the chunk has a fixed order of components, but some are not interesting of us so
+ // we're just looking for keywords in arbitrary order. The end of the chunk is
+ // either the last `Face` or the `DrawFlags` attribute, depending on the format ver.
+ for (;splitter;++splitter) {
+ if (splitter.match_start("World Vertices")) {
+ const unsigned int cnt = strtol10(splitter[2]);
+ msh.vertex_positions.resize(cnt);
+
+ for (unsigned int cur = 0;cur < cnt && ++splitter;++cur) {
+ const char* s = splitter->c_str();
+
+ aiVector3D& v = msh.vertex_positions[cur];
+
+ SkipSpaces(&s);
+ v.x = fast_atof(&s);
+ SkipSpaces(&s);
+ v.y = fast_atof(&s);
+ SkipSpaces(&s);
+ v.z = fast_atof(&s);
+ }
+ }
+ else if (splitter.match_start("Texture Vertices")) {
+ const unsigned int cnt = strtol10(splitter[2]);
+ msh.texture_coords.resize(cnt);
+
+ for (unsigned int cur = 0;cur < cnt && ++splitter;++cur) {
+ const char* s = splitter->c_str();
+
+ aiVector2D& v = msh.texture_coords[cur];
+
+ SkipSpaces(&s);
+ v.x = fast_atof(&s);
+ SkipSpaces(&s);
+ v.y = fast_atof(&s);
+ }
+ }
+ else if (splitter.match_start("Faces")) {
+ const unsigned int cnt = strtol10(splitter[1]);
+ msh.faces.reserve(cnt);
+
+ for (unsigned int cur = 0; cur < cnt && ++splitter ;++cur) {
+ if (splitter.match_start("Hole")) {
+ LogWarn_Ascii(splitter,"Skipping unsupported `Hole` line");
+ continue;
+ }
+
+ if (!splitter.match_start("Face")) {
+ ThrowException("Expected Face line");
+ }
+
+ msh.faces.push_back(Face());
+ Face& face = msh.faces.back();
+
+ face.indices.resize(strtol10(splitter[2]));
+ face.flags = strtol10(splitter[4]);
+ face.material = strtol10(splitter[6]);
+
+ const char* s = (++splitter)->c_str();
+ for (size_t i = 0; i < face.indices.size(); ++i) {
+ if (!SkipSpaces(&s)) {
+ ThrowException("Expected EOL token in Face entry");
+ }
+ if ('<' != *s++) {
+ ThrowException("Expected < token in Face entry");
+ }
+ face.indices[i].pos_idx = strtol10(s,&s);
+ if (',' != *s++) {
+ ThrowException("Expected , token in Face entry");
+ }
+ face.indices[i].uv_idx = strtol10(s,&s);
+ if ('>' != *s++) {
+ ThrowException("Expected < token in Face entry");
+ }
+ }
+ }
+ if (nfo.version <= 4) {
+ break;
+ }
+ }
+ else if (splitter.match_start("DrawFlags")) {
+ msh.draw_flags = strtol10(splitter[1]);
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBitM_Ascii(Scene& /*out*/, LineSplitter& splitter, const ChunkInfo& nfo)
+{
+ if (nfo.version > 1) {
+ return UnsupportedChunk_Ascii(splitter,nfo,"BitM");
+ }
+/*
+ "\nThumbNailHdrSize %ld"
+ "\nThumbHeader: %02hx 02hx %02hx "
+ "\nColorBufSize %ld"
+ "\nColorBufZipSize %ld"
+ "\nZippedThumbnail: %02hx 02hx %02hx "
+*/
+
+ const unsigned int head = strtol10((++splitter)[1]);
+ if (head != sizeof(Bitmap::BitmapHeader)) {
+ LogWarn_Ascii(splitter,"Unexpected ThumbNailHdrSize, skipping this chunk");
+ return;
+ }
+
+ /*union {
+ Bitmap::BitmapHeader data;
+ char opaq[sizeof Bitmap::BitmapHeader()];
+ };*/
+// ReadHexOctets(opaq,head,(++splitter)[1]);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadString_Binary(std::string& out, StreamReaderLE& reader)
+{
+ out.resize( reader.GetI2());
+ for_each(char& c,out) {
+ c = reader.GetI1();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBasicNodeInfo_Binary(Node& msh, StreamReaderLE& reader, const ChunkInfo& /*nfo*/)
+{
+ const unsigned int dupes = reader.GetI2();
+ ReadString_Binary(msh.name,reader);
+
+ msh.name = format(msh.name)<<'_'<<dupes;
+
+ // skip local axes for the moment
+ reader.IncPtr(48);
+
+ msh.transform = aiMatrix4x4();
+ for (unsigned int y = 0; y < 3; ++y) {
+ for (unsigned int x =0; x < 4; ++x) {
+ msh.transform[y][x] = reader.GetF4();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::UnsupportedChunk_Binary( StreamReaderLE& reader, const ChunkInfo& nfo, const char* name)
+{
+ const std::string error = format("Encountered unsupported chunk: ") << name <<
+ " [version: "<<nfo.version<<", size: "<<nfo.size<<"]";
+
+ // we can recover if the chunk size was specified.
+ if (nfo.size != static_cast<unsigned int>(-1)) {
+ DefaultLogger::get()->error(error);
+ reader.IncPtr(nfo.size);
+ }
+ else ThrowException(error);
+}
+
+// ------------------------------------------------------------------------------------------------
+// tiny utility guard to aid me at staying within chunk boundaries.
+class chunk_guard {
+
+public:
+
+ chunk_guard(const COB::ChunkInfo& nfo, StreamReaderLE& reader)
+ : nfo(nfo)
+ , reader(reader)
+ , cur(reader.GetCurrentPos())
+ {
+ }
+
+ ~chunk_guard() {
+ // don't do anything if the size is not given
+ if (nfo.size != static_cast<unsigned int>(-1)) {
+ reader.IncPtr(static_cast<int>(nfo.size)-reader.GetCurrentPos()+cur);
+ }
+ }
+
+private:
+
+ const COB::ChunkInfo& nfo;
+ StreamReaderLE& reader;
+ long cur;
+};
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBinaryFile(Scene& out, StreamReaderLE* reader)
+{
+ while (1) {
+ std::string type;
+ type += reader -> GetI1()
+ ,type += reader -> GetI1()
+ ,type += reader -> GetI1()
+ ,type += reader -> GetI1()
+ ;
+
+ ChunkInfo nfo;
+ nfo.version = reader -> GetI2()*10;
+ nfo.version += reader -> GetI2();
+
+ nfo.id = reader->GetI4();
+ nfo.parent_id = reader->GetI4();
+ nfo.size = reader->GetI4();
+
+ if (type == "PolH") {
+ ReadPolH_Binary(out,*reader,nfo);
+ }
+ else if (type == "BitM") {
+ ReadBitM_Binary(out,*reader,nfo);
+ }
+ else if (type == "Grou") {
+ ReadGrou_Binary(out,*reader,nfo);
+ }
+ else if (type == "Lght") {
+ ReadLght_Binary(out,*reader,nfo);
+ }
+ else if (type == "Came") {
+ ReadCame_Binary(out,*reader,nfo);
+ }
+ else if (type == "Mat1") {
+ ReadMat1_Binary(out,*reader,nfo);
+ }
+ /* else if (type == "Bone") {
+ ReadBone_Binary(out,*reader,nfo);
+ }
+ else if (type == "Chan") {
+ ReadChan_Binary(out,*reader,nfo);
+ }*/
+ else if (type == "Unit") {
+ ReadUnit_Binary(out,*reader,nfo);
+ }
+ else if (type == "OLay") {
+ // ignore layer index silently.
+ if (nfo.size != static_cast<unsigned int>(-1) ) {
+ reader->IncPtr(nfo.size);
+ }
+ else return UnsupportedChunk_Binary(*reader,nfo,type.c_str());
+ }
+ else if (type == "END ") {
+ return;
+ }
+ else UnsupportedChunk_Binary(*reader,nfo,type.c_str());
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadPolH_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Binary(reader,nfo,"PolH");
+ }
+ const chunk_guard cn(nfo,reader);
+
+ out.nodes.push_back(boost::shared_ptr<Mesh>(new Mesh()));
+ Mesh& msh = (Mesh&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Binary(msh,reader,nfo);
+
+ msh.vertex_positions.resize(reader.GetI4());
+ for_each(aiVector3D& v,msh.vertex_positions) {
+ v.x = reader.GetF4();
+ v.y = reader.GetF4();
+ v.z = reader.GetF4();
+ }
+
+ msh.texture_coords.resize(reader.GetI4());
+ for_each(aiVector2D& v,msh.texture_coords) {
+ v.x = reader.GetF4();
+ v.y = reader.GetF4();
+ }
+
+ const size_t numfuck = reader.GetI4();
+ msh.faces.reserve(numfuck);
+ for (size_t i = 0; i < numfuck; ++i) {
+ // XXX backface culling flag is 0x10 in flags
+
+ // hole?
+ bool hole;
+ if ((hole = (reader.GetI1() & 0x08) != 0)) {
+ // XXX Basically this should just work fine - then triangulator
+ // should output properly triangulated data even for polygons
+ // with holes. Test data specific to COB is needed to confirm it.
+ if (msh.faces.empty()) {
+ ThrowException(format("A hole is the first entity in the `PolH` chunk with id ") << nfo.id);
+ }
+ }
+ else msh.faces.push_back(Face());
+ Face& f = msh.faces.back();
+
+ const size_t num = reader.GetI2();
+ f.indices.reserve(f.indices.size() + num);
+
+ if (!hole) {
+ f.material = reader.GetI2();
+ f.flags = 0;
+ }
+
+ for (size_t x = 0; x < num; ++x) {
+ f.indices.push_back(VertexIndex());
+
+ VertexIndex& v = f.indices.back();
+ v.pos_idx = reader.GetI4();
+ v.uv_idx = reader.GetI4();
+ }
+
+ if (hole) {
+ std::reverse(f.indices.rbegin(),f.indices.rbegin()+num);
+ }
+ }
+ if (nfo.version>4) {
+ msh.draw_flags = reader.GetI4();
+ }
+ nfo.version>5 && nfo.version<8 ? reader.GetI4() : 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadBitM_Binary(COB::Scene& /*out*/, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 1) {
+ return UnsupportedChunk_Binary(reader,nfo,"BitM");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ const uint32_t len = reader.GetI4();
+ reader.IncPtr(len);
+
+ reader.GetI4();
+ reader.IncPtr(reader.GetI4());
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadMat1_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 8) {
+ return UnsupportedChunk_Binary(reader,nfo,"Mat1");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ out.materials.push_back(Material());
+ Material& mat = out.materials.back();
+ mat = nfo;
+
+ mat.matnum = reader.GetI2();
+ switch(reader.GetI1()) {
+ case 'f':
+ mat.type = Material::FLAT;
+ break;
+ case 'p':
+ mat.type = Material::PHONG;
+ break;
+ case 'm':
+ mat.type = Material::METAL;
+ break;
+ default:
+ LogError_Ascii(format("Unrecognized shader type in `Mat1` chunk with id ")<<nfo.id);
+ mat.type = Material::FLAT;
+ }
+
+ switch(reader.GetI1()) {
+ case 'f':
+ mat.autofacet = Material::FACETED;
+ break;
+ case 'a':
+ mat.autofacet = Material::AUTOFACETED;
+ break;
+ case 's':
+ mat.autofacet = Material::SMOOTH;
+ break;
+ default:
+ LogError_Ascii(format("Unrecognized faceting mode in `Mat1` chunk with id ")<<nfo.id);
+ mat.autofacet = Material::FACETED;
+ }
+ mat.autofacet_angle = static_cast<float>(reader.GetI1());
+
+ mat.rgb.r = reader.GetF4();
+ mat.rgb.g = reader.GetF4();
+ mat.rgb.b = reader.GetF4();
+
+ mat.alpha = reader.GetF4();
+ mat.ka = reader.GetF4();
+ mat.ks = reader.GetF4();
+ mat.exp = reader.GetF4();
+ mat.ior = reader.GetF4();
+
+ char id[2];
+ id[0] = reader.GetI1(),id[1] = reader.GetI1();
+
+ if (id[0] == 'e' && id[1] == ':') {
+ mat.tex_env.reset(new Texture());
+
+ reader.GetI1();
+ ReadString_Binary(mat.tex_env->path,reader);
+
+ // advance to next texture-id
+ id[0] = reader.GetI1(),id[1] = reader.GetI1();
+ }
+
+ if (id[0] == 't' && id[1] == ':') {
+ mat.tex_color.reset(new Texture());
+
+ reader.GetI1();
+ ReadString_Binary(mat.tex_color->path,reader);
+
+ mat.tex_color->transform.mTranslation.x = reader.GetF4();
+ mat.tex_color->transform.mTranslation.y = reader.GetF4();
+
+ mat.tex_color->transform.mScaling.x = reader.GetF4();
+ mat.tex_color->transform.mScaling.y = reader.GetF4();
+
+ // advance to next texture-id
+ id[0] = reader.GetI1(),id[1] = reader.GetI1();
+ }
+
+ if (id[0] == 'b' && id[1] == ':') {
+ mat.tex_bump.reset(new Texture());
+
+ reader.GetI1();
+ ReadString_Binary(mat.tex_bump->path,reader);
+
+ mat.tex_bump->transform.mTranslation.x = reader.GetF4();
+ mat.tex_bump->transform.mTranslation.y = reader.GetF4();
+
+ mat.tex_bump->transform.mScaling.x = reader.GetF4();
+ mat.tex_bump->transform.mScaling.y = reader.GetF4();
+
+ // skip amplitude for I don't know its purpose.
+ reader.GetF4();
+ }
+ reader.IncPtr(-2);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadCame_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 2) {
+ return UnsupportedChunk_Binary(reader,nfo,"Came");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ out.nodes.push_back(boost::shared_ptr<Camera>(new Camera()));
+ Camera& msh = (Camera&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Binary(msh,reader,nfo);
+
+ // the rest is not interesting for us, so we skip over it.
+ if (nfo.version > 1) {
+ if (reader.GetI2()==512) {
+ reader.IncPtr(42);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadLght_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 2) {
+ return UnsupportedChunk_Binary(reader,nfo,"Lght");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ out.nodes.push_back(boost::shared_ptr<Light>(new Light()));
+ Light& msh = (Light&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Binary(msh,reader,nfo);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadGrou_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 2) {
+ return UnsupportedChunk_Binary(reader,nfo,"Grou");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ out.nodes.push_back(boost::shared_ptr<Group>(new Group()));
+ Group& msh = (Group&)(*out.nodes.back().get());
+ msh = nfo;
+
+ ReadBasicNodeInfo_Binary(msh,reader,nfo);
+}
+
+// ------------------------------------------------------------------------------------------------
+void COBImporter::ReadUnit_Binary(COB::Scene& out, StreamReaderLE& reader, const ChunkInfo& nfo)
+{
+ if (nfo.version > 1) {
+ return UnsupportedChunk_Binary(reader,nfo,"Unit");
+ }
+
+ const chunk_guard cn(nfo,reader);
+
+ // parent chunks preceede their childs, so we should have the
+ // corresponding chunk already.
+ for_each(boost::shared_ptr< Node >& nd, out.nodes) {
+ if (nd->id == nfo.parent_id) {
+ const unsigned int t=reader.GetI2();
+ nd->unit_scale = t>=sizeof(units)/sizeof(units[0])?(
+ LogWarn_Ascii(format()<<t<<" is not a valid value for `Units` attribute in `Unit chunk` "<<nfo.id)
+ ,1.f):units[t];
+
+ return;
+ }
+ }
+ LogWarn_Ascii(format()<<"`Unit` chunk "<<nfo.id<<" is a child of "
+ <<nfo.parent_id<<" which does not exist");
+}
+
+
+#endif
diff --git a/3rdparty/assimp/code/COBLoader.h b/3rdparty/assimp/code/COBLoader.h
new file mode 100644
index 000000000..976fc6081
--- /dev/null
+++ b/3rdparty/assimp/code/COBLoader.h
@@ -0,0 +1,175 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 COBLoader.h
+ * @brief Declaration of the TrueSpace (*.cob,*.scn) importer class.
+ */
+#ifndef INCLUDED_AI_COB_LOADER_H
+#define INCLUDED_AI_COB_LOADER_H
+
+#include "BaseImporter.h"
+namespace Assimp {
+ class LineSplitter;
+
+ // TinyFormatter.h
+ namespace Formatter {
+ template <typename T,typename TR, typename A> class basic_formatter;
+ typedef class basic_formatter< char, std::char_traits<char>, std::allocator<char> > format;
+ }
+
+ // COBScene.h
+ namespace COB {
+ struct ChunkInfo;
+ struct Node;
+ struct Scene;
+ }
+
+// -------------------------------------------------------------------------------------------
+/** Importer class to load TrueSpace files (cob,scn) up to v6.
+ *
+ * Currently relatively limited, loads only ASCII files and needs more test coverage. */
+// -------------------------------------------------------------------------------------------
+class COBImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+
+ /** Constructor to be privately used by Importer */
+ COBImporter();
+
+ /** Destructor, private as well */
+ ~COBImporter();
+
+public:
+
+ // --------------------
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // --------------------
+ void GetExtensionList(std::set<std::string>& app);
+
+ // --------------------
+ void SetupProperties(const Importer* pImp);
+
+ // --------------------
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+ // -------------------------------------------------------------------
+ /** Prepend 'COB: ' and throw msg.*/
+ static void ThrowException(const std::string& msg);
+
+ // -------------------------------------------------------------------
+ /** @brief Read from an ascii scene/object file
+ * @param out Receives output data.
+ * @param stream Stream to read from. */
+ void ReadAsciiFile(COB::Scene& out, StreamReaderLE* stream);
+
+ // -------------------------------------------------------------------
+ /** @brief Read from a binary scene/object file
+ * @param out Receives output data.
+ * @param stream Stream to read from. */
+ void ReadBinaryFile(COB::Scene& out, StreamReaderLE* stream);
+
+
+private:
+
+ // Conversion to Assimp output format
+
+ aiNode* BuildNodes(const COB::Node& root,const COB::Scene& scin,aiScene* fill);
+
+private:
+
+ // ASCII file support
+
+ void UnsupportedChunk_Ascii(LineSplitter& splitter, const COB::ChunkInfo& nfo, const char* name);
+ void ReadChunkInfo_Ascii(COB::ChunkInfo& out, const LineSplitter& splitter);
+ void ReadBasicNodeInfo_Ascii(COB::Node& msh, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ template <typename T> void ReadFloat3Tuple_Ascii(T& fill, const char** in);
+
+ void ReadPolH_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadBitM_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadMat1_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadGrou_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadBone_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadCame_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadLght_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadUnit_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+ void ReadChan_Ascii(COB::Scene& out, LineSplitter& splitter, const COB::ChunkInfo& nfo);
+
+
+ // ASCII file logging stuff to add proper line numbers to messages
+
+ static void LogWarn_Ascii (const LineSplitter& splitter, const Formatter::format& message);
+ static void LogError_Ascii(const LineSplitter& splitter, const Formatter::format& message);
+ static void LogInfo_Ascii (const LineSplitter& splitter, const Formatter::format& message);
+ static void LogDebug_Ascii(const LineSplitter& splitter, const Formatter::format& message);
+
+ static void LogWarn_Ascii (const Formatter::format& message);
+ static void LogError_Ascii (const Formatter::format& message);
+ static void LogInfo_Ascii (const Formatter::format& message);
+ static void LogDebug_Ascii (const Formatter::format& message);
+
+
+ // Binary file support
+
+ void UnsupportedChunk_Binary(StreamReaderLE& reader, const COB::ChunkInfo& nfo, const char* name);
+ void ReadString_Binary(std::string& out, StreamReaderLE& reader);
+ void ReadBasicNodeInfo_Binary(COB::Node& msh, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+
+ void ReadPolH_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadBitM_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadMat1_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadCame_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadLght_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadGrou_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+ void ReadUnit_Binary(COB::Scene& out, StreamReaderLE& reader, const COB::ChunkInfo& nfo);
+
+
+}; // !class COBImporter
+
+} // end of namespace Assimp
+#endif // AI_UNREALIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/COBScene.h b/3rdparty/assimp/code/COBScene.h
new file mode 100644
index 000000000..145cb76c9
--- /dev/null
+++ b/3rdparty/assimp/code/COBScene.h
@@ -0,0 +1,271 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 COBScene.h
+* @brief Utilities for the COB importer.
+*/
+#ifndef INCLUDED_AI_COB_SCENE_H
+#define INCLUDED_AI_COB_SCENE_H
+
+#include <boost/shared_ptr.hpp>
+#include "BaseImporter.h"
+
+namespace Assimp {
+ namespace COB {
+
+// ------------------
+/** Represents a single vertex index in a face */
+struct VertexIndex
+{
+ // intentionally uninitialized
+ unsigned int pos_idx,uv_idx;
+};
+
+// ------------------
+/** COB Face data structure */
+struct Face
+{
+ // intentionally uninitialized
+ unsigned int material, flags;
+ std::vector<VertexIndex> indices;
+};
+
+// ------------------
+/** COB chunk header information */
+struct ChunkInfo
+{
+ enum {NO_SIZE=0xffffffff};
+
+ ChunkInfo ()
+ : id (0)
+ , parent_id (0)
+ , version (0)
+ , size (NO_SIZE)
+ {}
+
+ // Id of this chunk, unique within file
+ unsigned int id;
+
+ // and the corresponding parent
+ unsigned int parent_id;
+
+ // version. v1.23 becomes 123
+ unsigned int version;
+
+ // chunk size in bytes, only relevant for binary files
+ // NO_SIZE is also valid.
+ unsigned int size;
+};
+
+// ------------------
+/** A node in the scenegraph */
+struct Node : public ChunkInfo
+{
+ enum Type {
+ TYPE_MESH,TYPE_GROUP,TYPE_LIGHT,TYPE_CAMERA,TYPE_BONE
+ };
+
+ virtual ~Node() {}
+ Node(Type type) : type(type), unit_scale(1.f){}
+
+ Type type;
+
+ // used during resolving
+ typedef std::deque<const Node*> ChildList;
+ mutable ChildList temp_children;
+
+ // unique name
+ std::string name;
+
+ // local mesh transformation
+ aiMatrix4x4 transform;
+
+ // scaling for this node to get to the metric system
+ float unit_scale;
+};
+
+// ------------------
+/** COB Mesh data structure */
+struct Mesh : public Node
+{
+ using ChunkInfo::operator=;
+ enum DrawFlags {
+ SOLID = 0x1,
+ TRANS = 0x2,
+ WIRED = 0x4,
+ BBOX = 0x8,
+ HIDE = 0x10
+ };
+
+ Mesh()
+ : Node(TYPE_MESH)
+ , draw_flags(SOLID)
+ {}
+
+ // vertex elements
+ std::vector<aiVector2D> texture_coords;
+ std::vector<aiVector3D> vertex_positions;
+
+ // face data
+ std::vector<Face> faces;
+
+ // misc. drawing flags
+ unsigned int draw_flags;
+
+ // used during resolving
+ typedef std::deque<Face*> FaceRefList;
+ typedef std::map< unsigned int,FaceRefList > TempMap;
+ TempMap temp_map;
+};
+
+// ------------------
+/** COB Group data structure */
+struct Group : public Node
+{
+ using ChunkInfo::operator=;
+ Group() : Node(TYPE_GROUP) {}
+};
+
+// ------------------
+/** COB Bone data structure */
+struct Bone : public Node
+{
+ using ChunkInfo::operator=;
+ Bone() : Node(TYPE_BONE) {}
+};
+
+// ------------------
+/** COB Light data structure */
+struct Light : public Node
+{
+ enum LightType {
+ SPOT,LOCAL,INFINITE
+ };
+
+ using ChunkInfo::operator=;
+ Light() : Node(TYPE_LIGHT),angle(),inner_angle(),ltype(SPOT) {}
+
+ aiColor3D color;
+ float angle,inner_angle;
+
+ LightType ltype;
+};
+
+// ------------------
+/** COB Camera data structure */
+struct Camera : public Node
+{
+ using ChunkInfo::operator=;
+ Camera() : Node(TYPE_CAMERA) {}
+};
+
+// ------------------
+/** COB Texture data structure */
+struct Texture
+{
+ std::string path;
+ aiUVTransform transform;
+};
+
+// ------------------
+/** COB Material data structure */
+struct Material : ChunkInfo
+{
+ using ChunkInfo::operator=;
+ enum Shader {
+ FLAT,PHONG,METAL
+ };
+
+ enum AutoFacet {
+ FACETED,AUTOFACETED,SMOOTH
+ };
+
+ Material() : alpha(),exp(),ior(),ka(),ks(1.f),
+ matnum(0xffffffff),
+ shader(FLAT),autofacet(FACETED),
+ autofacet_angle()
+ {}
+
+ std::string type;
+
+ aiColor3D rgb;
+ float alpha, exp, ior,ka,ks;
+
+ unsigned int matnum;
+ Shader shader;
+
+ AutoFacet autofacet;
+ float autofacet_angle;
+
+ boost::shared_ptr<Texture> tex_env,tex_bump,tex_color;
+};
+
+// ------------------
+/** Embedded bitmap, for instance for the thumbnail image */
+struct Bitmap : ChunkInfo
+{
+ Bitmap() : orig_size() {}
+ struct BitmapHeader
+ {
+ };
+
+ BitmapHeader head;
+ size_t orig_size;
+ std::vector<char> buff_zipped;
+};
+
+typedef std::deque< boost::shared_ptr<Node> > NodeList;
+typedef std::vector< Material > MaterialList;
+
+// ------------------
+/** Represents a master COB scene, even if we loaded just a single COB file */
+struct Scene
+{
+ NodeList nodes;
+ MaterialList materials;
+
+ // becomes *0 later
+ Bitmap thumbnail;
+};
+
+ } // end COB
+} // end Assimp
+
+#endif
diff --git a/3rdparty/assimp/code/CSMLoader.cpp b/3rdparty/assimp/code/CSMLoader.cpp
new file mode 100644
index 000000000..5e91fea35
--- /dev/null
+++ b/3rdparty/assimp/code/CSMLoader.cpp
@@ -0,0 +1,281 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2009, ASSIMP Development 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 Development 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 CSMLoader.cpp
+ * Implementation of the CSM importer class.
+ */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER
+
+#include "CSMLoader.h"
+#include "SkeletonMeshBuilder.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+CSMImporter::CSMImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+CSMImporter::~CSMImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ // check file extension
+ const std::string extension = GetExtension(pFile);
+
+ if ( extension == "csm")
+ return true;
+
+ if ((checkSig || !extension.length()) && pIOHandler) {
+ const char* tokens[] = {"$Filename"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a string of all file extensions supported
+void CSMImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("csm");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void CSMImporter::SetupProperties(const Importer* /*pImp*/)
+{
+ // nothing to be done for the moment
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void CSMImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open CSM file " + pFile + ".");
+ }
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+ const char* buffer = &mBuffer2[0];
+
+ aiAnimation* anim = new aiAnimation();
+ int first = 0, last = 0x00ffffff;
+
+ // now process the file and look out for '$' sections
+ while (1) {
+ SkipSpaces(&buffer);
+ if ('\0' == *buffer)
+ break;
+
+ if ('$' == *buffer) {
+ ++buffer;
+ if (TokenMatchI(buffer,"firstframe",10)) {
+ SkipSpaces(&buffer);
+ first = strtol10s(buffer,&buffer);
+ }
+ else if (TokenMatchI(buffer,"lastframe",9)) {
+ SkipSpaces(&buffer);
+ last = strtol10s(buffer,&buffer);
+ }
+ else if (TokenMatchI(buffer,"rate",4)) {
+ SkipSpaces(&buffer);
+ float d;
+ buffer = fast_atof_move(buffer,d);
+ anim->mTicksPerSecond = d;
+ }
+ else if (TokenMatchI(buffer,"order",5)) {
+ std::vector< aiNodeAnim* > anims_temp;
+ anims_temp.reserve(30);
+ while (1) {
+ SkipSpaces(&buffer);
+ if (IsLineEnd(*buffer) && SkipSpacesAndLineEnd(&buffer) && *buffer == '$')
+ break; // next section
+
+ // Construct a new node animation channel and setup its name
+ anims_temp.push_back(new aiNodeAnim());
+ aiNodeAnim* nda = anims_temp.back();
+
+ char* ot = nda->mNodeName.data;
+ while (!IsSpaceOrNewLine(*buffer))
+ *ot++ = *buffer++;
+
+ *ot = '\0';
+ nda->mNodeName.length = (size_t)(ot-nda->mNodeName.data);
+ }
+
+ anim->mNumChannels = anims_temp.size();
+ if (!anim->mNumChannels)
+ throw DeadlyImportError("CSM: Empty $order section");
+
+ // copy over to the output animation
+ anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
+ ::memcpy(anim->mChannels,&anims_temp[0],sizeof(aiNodeAnim*)*anim->mNumChannels);
+ }
+ else if (TokenMatchI(buffer,"points",6)) {
+ if (!anim->mNumChannels)
+ throw DeadlyImportError("CSM: \'$order\' section is required to appear prior to \'$points\'");
+
+ // If we know how many frames we'll read, we can preallocate some storage
+ unsigned int alloc = 100;
+ if (last != 0x00ffffff)
+ {
+ alloc = last-first;
+ alloc += alloc>>2u; // + 25%
+ for (unsigned int i = 0; i < anim->mNumChannels;++i)
+ anim->mChannels[i]->mPositionKeys = new aiVectorKey[alloc];
+ }
+
+ unsigned int filled = 0;
+
+ // Now read all point data.
+ while (1) {
+ SkipSpaces(&buffer);
+ if (IsLineEnd(*buffer) && (!SkipSpacesAndLineEnd(&buffer) || *buffer == '$')) {
+ break; // next section
+ }
+
+ // read frame
+ const int frame = ::strtol10(buffer,&buffer);
+ last = std::max(frame,last);
+ first = std::min(frame,last);
+ for (unsigned int i = 0; i < anim->mNumChannels;++i) {
+
+ aiNodeAnim* s = anim->mChannels[i];
+ if (s->mNumPositionKeys == alloc) { /* need to reallocate? */
+
+ aiVectorKey* old = s->mPositionKeys;
+ s->mPositionKeys = new aiVectorKey[s->mNumPositionKeys = alloc*2];
+ ::memcpy(s->mPositionKeys,old,sizeof(aiVectorKey)*alloc);
+ delete[] old;
+ }
+
+ // read x,y,z
+ if (!SkipSpacesAndLineEnd(&buffer))
+ throw DeadlyImportError("CSM: Unexpected EOF occured reading sample x coord");
+
+ if (TokenMatchI(buffer, "DROPOUT", 7)) {
+ // seems this is invalid marker data; at least the doc says it's possible
+ DefaultLogger::get()->warn("CSM: Encountered invalid marker data (DROPOUT)");
+ }
+ else {
+ aiVectorKey* sub = s->mPositionKeys + s->mNumPositionKeys;
+ sub->mTime = (double)frame;
+ buffer = fast_atof_move(buffer, (float&)sub->mValue.x);
+
+ if (!SkipSpacesAndLineEnd(&buffer))
+ throw DeadlyImportError("CSM: Unexpected EOF occured reading sample y coord");
+ buffer = fast_atof_move(buffer, (float&)sub->mValue.y);
+
+ if (!SkipSpacesAndLineEnd(&buffer))
+ throw DeadlyImportError("CSM: Unexpected EOF occured reading sample z coord");
+ buffer = fast_atof_move(buffer, (float&)sub->mValue.z);
+
+ ++s->mNumPositionKeys;
+ }
+ }
+
+ // update allocation granularity
+ if (filled == alloc)
+ alloc *= 2;
+
+ ++filled;
+ }
+ // all channels must be complete in order to continue safely.
+ for (unsigned int i = 0; i < anim->mNumChannels;++i) {
+
+ if (!anim->mChannels[i]->mNumPositionKeys)
+ throw DeadlyImportError("CSM: Invalid marker track");
+ }
+ }
+ }
+ else {
+ // advance to the next line
+ SkipLine(&buffer);
+ }
+ }
+
+ // Setup a proper animation duration
+ anim->mDuration = last - std::min( first, 0 );
+
+ // build a dummy root node with the tiny markers as children
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("$CSM_DummyRoot");
+
+ pScene->mRootNode->mNumChildren = anim->mNumChannels;
+ pScene->mRootNode->mChildren = new aiNode* [anim->mNumChannels];
+
+ for (unsigned int i = 0; i < anim->mNumChannels;++i) {
+ aiNodeAnim* na = anim->mChannels[i];
+
+ aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode();
+ nd->mName = anim->mChannels[i]->mNodeName;
+ nd->mParent = pScene->mRootNode;
+
+ aiMatrix4x4::Translation(na->mPositionKeys[0].mValue, nd->mTransformation);
+ }
+
+ // Store the one and only animation in the scene
+ pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations=1];
+ pScene->mAnimations[0] = anim;
+ anim->mName.Set("$CSM_MasterAnim");
+
+ // mark the scene as incomplete and run SkeletonMeshBuilder on it
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ SkeletonMeshBuilder maker(pScene,pScene->mRootNode,true);
+}
+
+#endif // !! ASSIMP_BUILD_NO_CSM_IMPORTER
diff --git a/3rdparty/assimp/code/CSMLoader.h b/3rdparty/assimp/code/CSMLoader.h
new file mode 100644
index 000000000..f3d1c458d
--- /dev/null
+++ b/3rdparty/assimp/code/CSMLoader.h
@@ -0,0 +1,88 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 CSMLoader.h
+ * Declaration of the CharacterStudio Motion importer class.
+ */
+#ifndef INCLUDED_AI_CSM_LOADER_H
+#define INCLUDED_AI_CSM_LOADER_H
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Importer class to load MOCAPs in CharacterStudio Motion format.
+ *
+ * A very rudimentary loader for the moment. No support for the hierarchy,
+ * every marker is returned as child of root.
+ *
+ * Link to file format specification:
+ * <max_8_dvd>\samples\Motion\Docs\CSM.rtf
+*/
+class CSMImporter : public BaseImporter
+{
+ friend class Importer;
+protected:
+ /** Constructor to be privately used by Importer */
+ CSMImporter();
+
+ /** Destructor, private as well */
+ ~CSMImporter();
+
+public:
+ // -------------------------------------------------------------------
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+}; // end of class CSMImporter
+} // end of namespace Assimp
+#endif // AI_AC3DIMPORTER_H_INC
+
diff --git a/3rdparty/assimp/code/CalcTangentsProcess.cpp b/3rdparty/assimp/code/CalcTangentsProcess.cpp
new file mode 100644
index 000000000..83ec00a54
--- /dev/null
+++ b/3rdparty/assimp/code/CalcTangentsProcess.cpp
@@ -0,0 +1,283 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the post processing step to calculate
+ * tangents and bitangents for all imported meshes
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "CalcTangentsProcess.h"
+#include "ProcessHelper.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+CalcTangentsProcess::CalcTangentsProcess()
+{
+ this->configMaxAngle = AI_DEG_TO_RAD(45.f);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+CalcTangentsProcess::~CalcTangentsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool CalcTangentsProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_CalcTangentSpace) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void CalcTangentsProcess::SetupProperties(const Importer* pImp)
+{
+ // get the current value of the property
+ this->configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f);
+ this->configMaxAngle = std::max(std::min(this->configMaxAngle,45.0f),0.0f);
+ this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void CalcTangentsProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("CalcTangentsProcess begin");
+
+ bool bHas = false;
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ if (ProcessMesh( pScene->mMeshes[a],a))bHas = true;
+
+ if (bHas)DefaultLogger::get()->debug("CalcTangentsProcess finished. Tangents have been calculated");
+ else DefaultLogger::get()->debug("CalcTangentsProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Calculates tangents and bitangents for the given mesh
+bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
+{
+ // we assume that the mesh is still in the verbose vertex format where each face has its own set
+ // of vertices and no vertices are shared between faces. Sadly I don't know any quick test to
+ // assert() it here.
+ //assert( must be verbose, dammit);
+
+ if (pMesh->mTangents) // thisimplies that mBitangents is also there
+ return false;
+
+ // If the mesh consists of lines and/or points but not of
+ // triangles or higher-order polygons the normal vectors
+ // are undefined.
+ if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON)))
+ {
+ DefaultLogger::get()->info("Tangents are undefined for line and point meshes");
+ return false;
+ }
+
+ // what we can check, though, is if the mesh has normals and texture coord. That's a requirement
+ if ( pMesh->mNormals == NULL || pMesh->mTextureCoords[0] == NULL)
+ {
+ DefaultLogger::get()->error("Unable to compute tangents: UV0 and normals must be there ");
+ return false;
+ }
+ const float angleEpsilon = 0.9999f;
+
+ std::vector<bool> vertexDone( pMesh->mNumVertices, false);
+ const float qnan = get_qnan();
+
+ // create space for the tangents and bitangents
+ pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
+ pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
+
+ const aiVector3D* meshPos = pMesh->mVertices;
+ const aiVector3D* meshNorm = pMesh->mNormals;
+ const aiVector3D* meshTex = pMesh->mTextureCoords[0];
+ aiVector3D* meshTang = pMesh->mTangents;
+ aiVector3D* meshBitang = pMesh->mBitangents;
+
+ // calculate the tangent and bitangent for every face
+ for ( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+ {
+ const aiFace& face = pMesh->mFaces[a];
+ if (face.mNumIndices < 3)
+ {
+ // There are less than three indices, thus the tangent vector
+ // is not defined. We are finished with these vertices now,
+ // their tangent vectors are set to qnan.
+ for (unsigned int i = 0; i < face.mNumIndices;++i)
+ {
+ register unsigned int idx = face.mIndices[i];
+ vertexDone [idx] = true;
+ meshTang [idx] = qnan;
+ meshBitang [idx] = qnan;
+ }
+
+ continue;
+ }
+
+ // triangle or polygon... we always use only the first three indices. A polygon
+ // is supposed to be planar anyways....
+ // FIXME: (thom) create correct calculation for multi-vertex polygons maybe?
+ const unsigned int p0 = face.mIndices[0], p1 = face.mIndices[1], p2 = face.mIndices[2];
+
+ // position differences p1->p2 and p1->p3
+ aiVector3D v = meshPos[p1] - meshPos[p0], w = meshPos[p2] - meshPos[p0];
+
+ // texture offset p1->p2 and p1->p3
+ float sx = meshTex[p1].x - meshTex[p0].x, sy = meshTex[p1].y - meshTex[p0].y;
+ float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y;
+ float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f;
+
+ // tangent points in the direction where to positive X axis of the texture coords would point in model space
+ // bitangents points along the positive Y axis of the texture coords, respectively
+ aiVector3D tangent, bitangent;
+ tangent.x = (w.x * sy - v.x * ty) * dirCorrection;
+ tangent.y = (w.y * sy - v.y * ty) * dirCorrection;
+ tangent.z = (w.z * sy - v.z * ty) * dirCorrection;
+ bitangent.x = (w.x * sx - v.x * tx) * dirCorrection;
+ bitangent.y = (w.y * sx - v.y * tx) * dirCorrection;
+ bitangent.z = (w.z * sx - v.z * tx) * dirCorrection;
+
+ // store for every vertex of that face
+ for ( unsigned int b = 0; b < face.mNumIndices; b++)
+ {
+ unsigned int p = face.mIndices[b];
+
+ // project tangent and bitangent into the plane formed by the vertex' normal
+ aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
+ aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
+ localTangent.Normalize(); localBitangent.Normalize();
+
+ // and write it into the mesh.
+ meshTang[p] = localTangent;
+ meshBitang[p] = localBitangent;
+ }
+ }
+
+
+ // create a helper to quickly find locally close vertices among the vertex array
+ // FIX: check whether we can reuse the SpatialSort of a previous step
+ SpatialSort* vertexFinder = NULL;
+ SpatialSort _vertexFinder;
+ float posEpsilon;
+ if (shared)
+ {
+ std::vector<std::pair<SpatialSort,float> >* avf;
+ shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
+ if (avf)
+ {
+ std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
+ vertexFinder = &blubb.first;
+ posEpsilon = blubb.second;;
+ }
+ }
+ if (!vertexFinder)
+ {
+ _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
+ vertexFinder = &_vertexFinder;
+ posEpsilon = ComputePositionEpsilon(pMesh);
+ }
+ std::vector<unsigned int> verticesFound;
+
+ const float fLimit = cosf(this->configMaxAngle);
+ std::vector<unsigned int> closeVertices;
+
+ // in the second pass we now smooth out all tangents and bitangents at the same local position
+ // if they are not too far off.
+ for ( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+ {
+ if ( vertexDone[a])
+ continue;
+
+ const aiVector3D& origPos = pMesh->mVertices[a];
+ const aiVector3D& origNorm = pMesh->mNormals[a];
+ const aiVector3D& origTang = pMesh->mTangents[a];
+ const aiVector3D& origBitang = pMesh->mBitangents[a];
+ closeVertices.clear();
+
+ // find all vertices close to that position
+ vertexFinder->FindPositions( origPos, posEpsilon, verticesFound);
+
+ closeVertices.reserve (verticesFound.size()+5);
+ closeVertices.push_back( a);
+
+ // look among them for other vertices sharing the same normal and a close-enough tangent/bitangent
+ for ( unsigned int b = 0; b < verticesFound.size(); b++)
+ {
+ unsigned int idx = verticesFound[b];
+ if ( vertexDone[idx])
+ continue;
+ if ( meshNorm[idx] * origNorm < angleEpsilon)
+ continue;
+ if ( meshTang[idx] * origTang < fLimit)
+ continue;
+ if ( meshBitang[idx] * origBitang < fLimit)
+ continue;
+
+ // it's similar enough -> add it to the smoothing group
+ closeVertices.push_back( idx);
+ vertexDone[idx] = true;
+ }
+
+ // smooth the tangents and bitangents of all vertices that were found to be close enough
+ aiVector3D smoothTangent( 0, 0, 0), smoothBitangent( 0, 0, 0);
+ for ( unsigned int b = 0; b < closeVertices.size(); ++b)
+ {
+ smoothTangent += meshTang[ closeVertices[b] ];
+ smoothBitangent += meshBitang[ closeVertices[b] ];
+ }
+ smoothTangent.Normalize();
+ smoothBitangent.Normalize();
+
+ // and write it back into all affected tangents
+ for ( unsigned int b = 0; b < closeVertices.size(); ++b)
+ {
+ meshTang[ closeVertices[b] ] = smoothTangent;
+ meshBitang[ closeVertices[b] ] = smoothBitangent;
+ }
+ }
+ return true;
+}
diff --git a/3rdparty/assimp/code/CalcTangentsProcess.h b/3rdparty/assimp/code/CalcTangentsProcess.h
new file mode 100644
index 000000000..189710ff8
--- /dev/null
+++ b/3rdparty/assimp/code/CalcTangentsProcess.h
@@ -0,0 +1,118 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to calculate tangents and
+ bitangents on all imported meshes.*/
+#ifndef AI_CALCTANGENTSPROCESS_H_INC
+#define AI_CALCTANGENTSPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The CalcTangentsProcess calculates the tangent and bitangent for any vertex
+ * of all meshes. It is expected to be run before the JoinVerticesProcess runs
+ * because the joining of vertices also considers tangents and bitangents for
+ * uniqueness.
+ */
+class ASSIMP_API CalcTangentsProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ CalcTangentsProcess();
+
+ /** Destructor, private as well */
+ ~CalcTangentsProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag.
+ * @param pFlags The processing flags the importer was called with.
+ * A bitwise combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields,
+ * false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ExecuteOnScene().
+ * The function is a request to the process to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+
+ // setter for configMaxAngle
+ inline void SetMaxSmoothAngle(float f)
+ {
+ configMaxAngle =f;
+ }
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Calculates tangents and bitangents for a specific mesh.
+ * @param pMesh The mesh to process.
+ * @param meshIndex Index of the mesh
+ */
+ bool ProcessMesh( aiMesh* pMesh, unsigned int meshIndex);
+
+ // -------------------------------------------------------------------
+ /** Executes the post processing step on the given imported data.
+ * @param pScene The imported data to work at.
+ */
+ void Execute( aiScene* pScene);
+
+private:
+
+ /** Configuration option: maximum smoothing angle, in radians*/
+ float configMaxAngle;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CALCTANGENTSPROCESS_H_INC
diff --git a/3rdparty/assimp/code/ColladaHelper.h b/3rdparty/assimp/code/ColladaHelper.h
new file mode 100644
index 000000000..ddf149903
--- /dev/null
+++ b/3rdparty/assimp/code/ColladaHelper.h
@@ -0,0 +1,601 @@
+/** Helper structures for the Collada loader */
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 AI_COLLADAHELPER_H_INC
+#define AI_COLLADAHELPER_H_INC
+
+namespace Assimp {
+namespace Collada {
+
+/** Collada file versions which evolved during the years ... */
+enum FormatVersion
+{
+ FV_1_5_n,
+ FV_1_4_n,
+ FV_1_3_n
+};
+
+
+/** Transformation types that can be applied to a node */
+enum TransformType
+{
+ TF_LOOKAT,
+ TF_ROTATE,
+ TF_TRANSLATE,
+ TF_SCALE,
+ TF_SKEW,
+ TF_MATRIX
+};
+
+/** Different types of input data to a vertex or face */
+enum InputType
+{
+ IT_Invalid,
+ IT_Vertex, // special type for per-index data referring to the <vertices> element carrying the per-vertex data.
+ IT_Position,
+ IT_Normal,
+ IT_Texcoord,
+ IT_Color,
+ IT_Tangent,
+ IT_Bitangent
+};
+
+/** Contains all data for one of the different transformation types */
+struct Transform
+{
+ std::string mID; ///< SID of the transform step, by which anim channels address their target node
+ TransformType mType;
+ float f[16]; ///< Interpretation of data depends on the type of the transformation
+};
+
+/** A collada camera. */
+struct Camera
+{
+ Camera()
+ : mOrtho (false)
+ , mHorFov (10e10f)
+ , mVerFov (10e10f)
+ , mAspect (10e10f)
+ , mZNear (0.1f)
+ , mZFar (1000.f)
+ {}
+
+ // Name of camera
+ std::string mName;
+
+ // True if it is an orthografic camera
+ bool mOrtho;
+
+ //! Horizontal field of view in degrees
+ float mHorFov;
+
+ //! Vertical field of view in degrees
+ float mVerFov;
+
+ //! Screen aspect
+ float mAspect;
+
+ //! Near& far z
+ float mZNear, mZFar;
+};
+
+#define aiLightSource_AMBIENT 0xdeaddead
+
+/** A collada light source. */
+struct Light
+{
+ Light()
+ : mAttConstant (1.f)
+ , mAttLinear (0.f)
+ , mAttQuadratic (0.f)
+ , mFalloffAngle (180.f)
+ , mFalloffExponent (0.f)
+ , mPenumbraAngle (10e10f)
+ , mOuterAngle (10e10f)
+ , mIntensity (1.f)
+ {}
+
+ //! Type of the light source aiLightSourceType + ambient
+ unsigned int mType;
+
+ //! Color of the light
+ aiColor3D mColor;
+
+ //! Light attenuation
+ float mAttConstant,mAttLinear,mAttQuadratic;
+
+ //! Spot light falloff
+ float mFalloffAngle;
+ float mFalloffExponent;
+
+ // -----------------------------------------------------
+ // FCOLLADA extension from here
+
+ //! ... related stuff from maja and max extensions
+ float mPenumbraAngle;
+ float mOuterAngle;
+
+ //! Common light intensity
+ float mIntensity;
+};
+
+/** Short vertex index description */
+struct InputSemanticMapEntry
+{
+ InputSemanticMapEntry()
+ : mSet (0)
+ {}
+
+ //! Index of set, optional
+ unsigned int mSet;
+
+ //! Name of referenced vertex input
+ InputType mType;
+};
+
+/** Table to map from effect to vertex input semantics */
+struct SemanticMappingTable
+{
+ //! Name of material
+ std::string mMatName;
+
+ //! List of semantic map commands, grouped by effect semantic name
+ std::map<std::string, InputSemanticMapEntry> mMap;
+
+ //! For std::find
+ bool operator == (const std::string& s) const {
+ return s == mMatName;
+ }
+};
+
+/** A reference to a mesh inside a node, including materials assigned to the various subgroups.
+ * The ID refers to either a mesh or a controller which specifies the mesh
+ */
+struct MeshInstance
+{
+ ///< ID of the mesh or controller to be instanced
+ std::string mMeshOrController;
+
+ ///< Map of materials by the subgroup ID they're applied to
+ std::map<std::string, SemanticMappingTable> mMaterials;
+};
+
+/** A reference to a camera inside a node*/
+struct CameraInstance
+{
+ ///< ID of the camera
+ std::string mCamera;
+};
+
+/** A reference to a light inside a node*/
+struct LightInstance
+{
+ ///< ID of the camera
+ std::string mLight;
+};
+
+/** A reference to a node inside a node*/
+struct NodeInstance
+{
+ ///< ID of the node
+ std::string mNode;
+};
+
+/** A node in a scene hierarchy */
+struct Node
+{
+ std::string mName;
+ std::string mID;
+ std::string mSID;
+ Node* mParent;
+ std::vector<Node*> mChildren;
+
+ /** Operations in order to calculate the resulting transformation to parent. */
+ std::vector<Transform> mTransforms;
+
+ /** Meshes at this node */
+ std::vector<MeshInstance> mMeshes;
+
+ /** Lights at this node */
+ std::vector<LightInstance> mLights;
+
+ /** Cameras at this node */
+ std::vector<CameraInstance> mCameras;
+
+ /** Node instances at this node */
+ std::vector<NodeInstance> mNodeInstances;
+
+ /** Rootnodes: Name of primary camera, if any */
+ std::string mPrimaryCamera;
+
+ //! Constructor. Begin with a zero parent
+ Node() {
+ mParent = NULL;
+ }
+
+ //! Destructor: delete all children subsequently
+ ~Node() {
+ for ( std::vector<Node*>::iterator it = mChildren.begin(); it != mChildren.end(); ++it)
+ delete *it;
+ }
+};
+
+/** Data source array: either floats or strings */
+struct Data
+{
+ bool mIsStringArray;
+ std::vector<float> mValues;
+ std::vector<std::string> mStrings;
+};
+
+/** Accessor to a data array */
+struct Accessor
+{
+ size_t mCount; // in number of objects
+ size_t mSize; // size of an object, in elements (floats or strings, mostly 1)
+ size_t mOffset; // in number of values
+ size_t mStride; // Stride in number of values
+ std::vector<std::string> mParams; // names of the data streams in the accessors. Empty string tells to ignore.
+ size_t mSubOffset[4]; // Suboffset inside the object for the common 4 elements. For a vector, thats XYZ, for a color RGBA and so on.
+ // For example, SubOffset[0] denotes which of the values inside the object is the vector X component.
+ std::string mSource; // URL of the source array
+ mutable const Data* mData; // Pointer to the source array, if resolved. NULL else
+
+ Accessor()
+ {
+ mCount = 0; mSize = 0; mOffset = 0; mStride = 0; mData = NULL;
+ mSubOffset[0] = mSubOffset[1] = mSubOffset[2] = mSubOffset[3] = 0;
+ }
+};
+
+/** A single face in a mesh */
+struct Face
+{
+ std::vector<size_t> mIndices;
+};
+
+/** An input channel for mesh data, referring to a single accessor */
+struct InputChannel
+{
+ InputType mType; // Type of the data
+ size_t mIndex; // Optional index, if multiple sets of the same data type are given
+ size_t mOffset; // Index offset in the indices array of per-face indices. Don't ask, can't explain that any better.
+ std::string mAccessor; // ID of the accessor where to read the actual values from.
+ mutable const Accessor* mResolved; // Pointer to the accessor, if resolved. NULL else
+
+ InputChannel() { mType = IT_Invalid; mIndex = 0; mOffset = 0; mResolved = NULL; }
+};
+
+/** Subset of a mesh with a certain material */
+struct SubMesh
+{
+ std::string mMaterial; ///< subgroup identifier
+ size_t mNumFaces; ///< number of faces in this submesh
+};
+
+/** Contains data for a single mesh */
+struct Mesh
+{
+ Mesh()
+ {
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
+ mNumUVComponents[i] = 2;
+ }
+
+ // just to check if there's some sophisticated addressing involved...
+ // which we don't support, and therefore should warn about.
+ std::string mVertexID;
+
+ // Vertex data addressed by vertex indices
+ std::vector<InputChannel> mPerVertexData;
+
+ // actual mesh data, assembled on encounter of a <p> element. Verbose format, not indexed
+ std::vector<aiVector3D> mPositions;
+ std::vector<aiVector3D> mNormals;
+ std::vector<aiVector3D> mTangents;
+ std::vector<aiVector3D> mBitangents;
+ std::vector<aiVector3D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+ unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+
+ // Faces. Stored are only the number of vertices for each face.
+ // 1 == point, 2 == line, 3 == triangle, 4+ == poly
+ std::vector<size_t> mFaceSize;
+
+ // Position indices for all faces in the sequence given in mFaceSize -
+ // necessary for bone weight assignment
+ std::vector<size_t> mFacePosIndices;
+
+ // Submeshes in this mesh, each with a given material
+ std::vector<SubMesh> mSubMeshes;
+};
+
+/** Which type of primitives the ReadPrimitives() function is going to read */
+enum PrimitiveType
+{
+ Prim_Invalid,
+ Prim_Lines,
+ Prim_LineStrip,
+ Prim_Triangles,
+ Prim_TriStrips,
+ Prim_TriFans,
+ Prim_Polylist,
+ Prim_Polygon
+};
+
+/** A skeleton controller to deform a mesh with the use of joints */
+struct Controller
+{
+ // the URL of the mesh deformed by the controller.
+ std::string mMeshId;
+
+ // accessor URL of the joint names
+ std::string mJointNameSource;
+
+ ///< The bind shape matrix, as array of floats. I'm not sure what this matrix actually describes, but it can't be ignored in all cases
+ float mBindShapeMatrix[16];
+
+ // accessor URL of the joint inverse bind matrices
+ std::string mJointOffsetMatrixSource;
+
+ // input channel: joint names.
+ InputChannel mWeightInputJoints;
+ // input channel: joint weights
+ InputChannel mWeightInputWeights;
+
+ // Number of weights per vertex.
+ std::vector<size_t> mWeightCounts;
+
+ // JointIndex-WeightIndex pairs for all vertices
+ std::vector< std::pair<size_t, size_t> > mWeights;
+};
+
+/** A collada material. Pretty much the only member is a reference to an effect. */
+struct Material
+{
+ std::string mEffect;
+};
+
+/** Type of the effect param */
+enum ParamType
+{
+ Param_Sampler,
+ Param_Surface
+};
+
+/** A param for an effect. Might be of several types, but they all just refer to each other, so I summarize them */
+struct EffectParam
+{
+ ParamType mType;
+ std::string mReference; // to which other thing the param is referring to.
+};
+
+/** Shading type supported by the standard effect spec of Collada */
+enum ShadeType
+{
+ Shade_Invalid,
+ Shade_Constant,
+ Shade_Lambert,
+ Shade_Phong,
+ Shade_Blinn
+};
+
+/** Represents a texture sampler in collada */
+struct Sampler
+{
+ Sampler()
+ : mWrapU (true)
+ , mWrapV (true)
+ , mMirrorU ()
+ , mMirrorV ()
+ , mOp (aiTextureOp_Multiply)
+ , mUVId (0xffffffff)
+ , mWeighting (1.f)
+ , mMixWithPrevious (1.f)
+ {}
+
+ /** Name of image reference
+ */
+ std::string mName;
+
+ /** Wrap U?
+ */
+ bool mWrapU;
+
+ /** Wrap V?
+ */
+ bool mWrapV;
+
+ /** Mirror U?
+ */
+ bool mMirrorU;
+
+ /** Mirror V?
+ */
+ bool mMirrorV;
+
+ /** Blend mode
+ */
+ aiTextureOp mOp;
+
+ /** UV transformation
+ */
+ aiUVTransform mTransform;
+
+ /** Name of source UV channel
+ */
+ std::string mUVChannel;
+
+ /** Resolved UV channel index or 0xffffffff if not known
+ */
+ unsigned int mUVId;
+
+ // OKINO/MAX3D extensions from here
+ // -------------------------------------------------------
+
+ /** Weighting factor
+ */
+ float mWeighting;
+
+ /** Mixing factor from OKINO
+ */
+ float mMixWithPrevious;
+};
+
+/** A collada effect. Can contain about anything according to the Collada spec,
+ but we limit our version to a reasonable subset. */
+struct Effect
+{
+ // Shading mode
+ ShadeType mShadeType;
+
+ // Colors
+ aiColor4D mEmissive, mAmbient, mDiffuse, mSpecular,
+ mTransparent, mReflective;
+
+ // Textures
+ Sampler mTexEmissive, mTexAmbient, mTexDiffuse, mTexSpecular,
+ mTexTransparent, mTexBump, mTexReflective;
+
+ // Scalar factory
+ float mShininess, mRefractIndex, mReflectivity;
+ float mTransparency;
+
+ // local params referring to each other by their SID
+ typedef std::map<std::string, Collada::EffectParam> ParamLibrary;
+ ParamLibrary mParams;
+
+ // MAX3D extensions
+ // ---------------------------------------------------------
+ // Double-sided?
+ bool mDoubleSided, mWireframe, mFaceted;
+
+ Effect()
+ : mShadeType (Shade_Phong)
+ , mEmissive ( 0, 0, 0, 1)
+ , mAmbient ( 0.1f, 0.1f, 0.1f, 1)
+ , mDiffuse ( 0.6f, 0.6f, 0.6f, 1)
+ , mSpecular ( 0.4f, 0.4f, 0.4f, 1)
+ , mTransparent ( 0, 0, 0, 1)
+ , mShininess (10.0f)
+ , mRefractIndex (1.f)
+ , mReflectivity (1.f)
+ , mTransparency (0.f)
+ , mDoubleSided (false)
+ , mWireframe (false)
+ , mFaceted (false)
+ {
+ }
+};
+
+/** An image, meaning texture */
+struct Image
+{
+ std::string mFileName;
+
+ /** If image file name is zero, embedded image data
+ */
+ std::vector<uint8_t> mImageData;
+
+ /** If image file name is zero, file format of
+ * embedded image data.
+ */
+ std::string mEmbeddedFormat;
+
+};
+
+/** An animation channel. */
+struct AnimationChannel
+{
+ /** URL of the data to animate. Could be about anything, but we support only the
+ * "NodeID/TransformID.SubElement" notation
+ */
+ std::string mTarget;
+
+ /** Source URL of the time values. Collada calls them "input". Meh. */
+ std::string mSourceTimes;
+ /** Source URL of the value values. Collada calls them "output". */
+ std::string mSourceValues;
+};
+
+/** An animation. Container for 0-x animation channels or 0-x animations */
+struct Animation
+{
+ /** Anim name */
+ std::string mName;
+
+ /** the animation channels, if any */
+ std::vector<AnimationChannel> mChannels;
+
+ /** the sub-animations, if any */
+ std::vector<Animation*> mSubAnims;
+
+ /** Destructor */
+ ~Animation()
+ {
+ for ( std::vector<Animation*>::iterator it = mSubAnims.begin(); it != mSubAnims.end(); ++it)
+ delete *it;
+ }
+};
+
+/** Description of a collada animation channel which has been determined to affect the current node */
+struct ChannelEntry
+{
+ const Collada::AnimationChannel* mChannel; ///> the source channel
+ std::string mTransformId; // the ID of the transformation step of the node which is influenced
+ size_t mTransformIndex; // Index into the node's transform chain to apply the channel to
+ size_t mSubElement; // starting index inside the transform data
+
+ // resolved data references
+ const Collada::Accessor* mTimeAccessor; ///> Collada accessor to the time values
+ const Collada::Data* mTimeData; ///> Source data array for the time values
+ const Collada::Accessor* mValueAccessor; ///> Collada accessor to the key value values
+ const Collada::Data* mValueData; ///> Source datat array for the key value values
+
+ ChannelEntry() { mChannel = NULL; mSubElement = 0; }
+};
+
+} // end of namespace Collada
+} // end of namespace Assimp
+
+#endif // AI_COLLADAHELPER_H_INC
diff --git a/3rdparty/assimp/code/ColladaLoader.cpp b/3rdparty/assimp/code/ColladaLoader.cpp
new file mode 100644
index 000000000..c3f0a7874
--- /dev/null
+++ b/3rdparty/assimp/code/ColladaLoader.cpp
@@ -0,0 +1,1487 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the Collada loader */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_DAE_IMPORTER
+
+#include "../include/aiAnim.h"
+#include "ColladaLoader.h"
+#include "ColladaParser.h"
+
+#include "fast_atof.h"
+#include "ParsingUtils.h"
+#include "SkeletonMeshBuilder.h"
+
+#include "time.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ColladaLoader::ColladaLoader()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ColladaLoader::~ColladaLoader()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ // check file extension
+ std::string extension = GetExtension(pFile);
+
+ if ( extension == "dae")
+ return true;
+
+ // XML - too generic, we need to open the file and search for typical keywords
+ if ( extension == "xml" || !extension.length() || checkSig) {
+ /* If CanRead() is called in order to check whether we
+ * support a specific file extension in general pIOHandler
+ * might be NULL and it's our duty to return true here.
+ */
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"collada"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get file extension list
+void ColladaLoader::GetExtensionList( std::set<std::string>& extensions )
+{
+ extensions.insert("dae");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ mFileName = pFile;
+
+ // clean all member arrays - just for safety, it should work even if we did not
+ mMeshIndexByID.clear();
+ mMaterialIndexByName.clear();
+ mMeshes.clear();
+ newMats.clear();
+ mLights.clear();
+ mCameras.clear();
+ mTextures.clear();
+
+ // parse the input file
+ ColladaParser parser( pIOHandler, pFile);
+
+ if ( !parser.mRootNode)
+ throw DeadlyImportError( "Collada: File came out empty. Something is wrong here.");
+
+ // reserve some storage to avoid unnecessary reallocs
+ newMats.reserve(parser.mMaterialLibrary.size()*2);
+ mMeshes.reserve(parser.mMeshLibrary.size()*2);
+
+ mCameras.reserve(parser.mCameraLibrary.size());
+ mLights.reserve(parser.mLightLibrary.size());
+
+ // create the materials first, for the meshes to find
+ BuildMaterials( parser, pScene);
+
+ // build the node hierarchy from it
+ pScene->mRootNode = BuildHierarchy( parser, parser.mRootNode);
+
+ // ... then fill the materials with the now adjusted settings
+ FillMaterials(parser, pScene);
+
+ // Convert to Y_UP, if different orientation
+ if ( parser.mUpDirection == ColladaParser::UP_X)
+ pScene->mRootNode->mTransformation *= aiMatrix4x4(
+ 0, -1, 0, 0,
+ 1, 0, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+ else if ( parser.mUpDirection == ColladaParser::UP_Z)
+ pScene->mRootNode->mTransformation *= aiMatrix4x4(
+ 1, 0, 0, 0,
+ 0, 0, 1, 0,
+ 0, -1, 0, 0,
+ 0, 0, 0, 1);
+
+ // store all meshes
+ StoreSceneMeshes( pScene);
+
+ // store all materials
+ StoreSceneMaterials( pScene);
+
+ // store all lights
+ StoreSceneLights( pScene);
+
+ // store all cameras
+ StoreSceneCameras( pScene);
+
+ // store all animations
+ StoreAnimations( pScene, parser);
+
+
+ // If no meshes have been loaded, it's probably just an animated skeleton.
+ if (!pScene->mNumMeshes) {
+
+ SkeletonMeshBuilder hero(pScene);
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively constructs a scene node for the given parser node and returns it.
+aiNode* ColladaLoader::BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode)
+{
+ // create a node for it
+ aiNode* node = new aiNode();
+
+ // find a name for the new node. It's more complicated than you might think
+ node->mName.Set( FindNameForNode( pNode));
+
+ // calculate the transformation matrix for it
+ node->mTransformation = pParser.CalculateResultTransform( pNode->mTransforms);
+
+ // now resolve node instances
+ std::vector<const Collada::Node*> instances;
+ ResolveNodeInstances(pParser,pNode,instances);
+
+ // add children. first the *real* ones
+ node->mNumChildren = pNode->mChildren.size()+instances.size();
+ node->mChildren = new aiNode*[node->mNumChildren];
+
+ for ( size_t a = 0; a < pNode->mChildren.size(); a++)
+ {
+ node->mChildren[a] = BuildHierarchy( pParser, pNode->mChildren[a]);
+ node->mChildren[a]->mParent = node;
+ }
+
+ // ... and finally the resolved node instances
+ for ( size_t a = 0; a < instances.size(); a++)
+ {
+ node->mChildren[pNode->mChildren.size() + a] = BuildHierarchy( pParser, instances[a]);
+ node->mChildren[pNode->mChildren.size() + a]->mParent = node;
+ }
+
+ // construct meshes
+ BuildMeshesForNode( pParser, pNode, node);
+
+ // construct cameras
+ BuildCamerasForNode(pParser, pNode, node);
+
+ // construct lights
+ BuildLightsForNode(pParser, pNode, node);
+ return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Resolve node instances
+void ColladaLoader::ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode,
+ std::vector<const Collada::Node*>& resolved)
+{
+ // reserve enough storage
+ resolved.reserve(pNode->mNodeInstances.size());
+
+ // ... and iterate through all nodes to be instanced as children of pNode
+ for (std::vector<Collada::NodeInstance>::const_iterator it = pNode->mNodeInstances.begin(),
+ end = pNode->mNodeInstances.end(); it != end; ++it)
+ {
+ // find the corresponding node in the library
+ const ColladaParser::NodeLibrary::const_iterator itt = pParser.mNodeLibrary.find((*it).mNode);
+ Collada::Node* nd = itt == pParser.mNodeLibrary.end() ? NULL : (*itt).second;
+
+ // FIX for http://sourceforge.net/tracker/?func=detail&aid=3054873&group_id=226462&atid=1067632
+ // need to check for both name and ID to catch all. To avoid breaking valid files,
+ // the workaround is only enabled when the first attempt to resolve the node has failed.
+ if (!nd) {
+ nd = const_cast<Collada::Node*>(FindNode(pParser.mRootNode,(*it).mNode));
+ }
+ if (!nd)
+ DefaultLogger::get()->error("Collada: Unable to resolve reference to instanced node " + (*it).mNode);
+
+ else {
+ // attach this node to the list of children
+ resolved.push_back(nd);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Resolve UV channels
+void ColladaLoader::ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler,
+ const Collada::SemanticMappingTable& table)
+{
+ std::map<std::string, Collada::InputSemanticMapEntry>::const_iterator it = table.mMap.find(sampler.mUVChannel);
+ if (it != table.mMap.end()) {
+ if (it->second.mType != Collada::IT_Texcoord)
+ DefaultLogger::get()->error("Collada: Unexpected effect input mapping");
+
+ sampler.mUVId = it->second.mSet;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Builds lights for the given node and references them
+void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
+{
+ BOOST_FOREACH( const Collada::LightInstance& lid, pNode->mLights)
+ {
+ // find the referred light
+ ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find( lid.mLight);
+ if ( srcLightIt == pParser.mLightLibrary.end())
+ {
+ DefaultLogger::get()->warn("Collada: Unable to find light for ID \"" + lid.mLight + "\". Skipping.");
+ continue;
+ }
+ const Collada::Light* srcLight = &srcLightIt->second;
+ if (srcLight->mType == aiLightSource_AMBIENT) {
+ DefaultLogger::get()->error("Collada: Skipping ambient light for the moment");
+ continue;
+ }
+
+ // now fill our ai data structure
+ aiLight* out = new aiLight();
+ out->mName = pTarget->mName;
+ out->mType = (aiLightSourceType)srcLight->mType;
+
+ // collada lights point in -Z by default, rest is specified in node transform
+ out->mDirection = aiVector3D(0.f,0.f,-1.f);
+
+ out->mAttenuationConstant = srcLight->mAttConstant;
+ out->mAttenuationLinear = srcLight->mAttLinear;
+ out->mAttenuationQuadratic = srcLight->mAttQuadratic;
+
+ // collada doesn't differenciate between these color types
+ out->mColorDiffuse = out->mColorSpecular = out->mColorAmbient = srcLight->mColor*srcLight->mIntensity;
+
+ // convert falloff angle and falloff exponent in our representation, if given
+ if (out->mType == aiLightSource_SPOT) {
+
+ out->mAngleInnerCone = AI_DEG_TO_RAD( srcLight->mFalloffAngle );
+
+ // ... some extension magic. FUCKING COLLADA.
+ if (srcLight->mOuterAngle == 10e10f)
+ {
+ // ... some deprecation magic. FUCKING FCOLLADA.
+ if (srcLight->mPenumbraAngle == 10e10f)
+ {
+ // Need to rely on falloff_exponent. I don't know how to interpret it, so I need to guess ....
+ // epsilon chosen to be 0.1
+ out->mAngleOuterCone = AI_DEG_TO_RAD (acos(pow(0.1f,1.f/srcLight->mFalloffExponent))+
+ srcLight->mFalloffAngle);
+ }
+ else {
+ out->mAngleOuterCone = out->mAngleInnerCone + AI_DEG_TO_RAD( srcLight->mPenumbraAngle );
+ if (out->mAngleOuterCone < out->mAngleInnerCone)
+ std::swap(out->mAngleInnerCone,out->mAngleOuterCone);
+ }
+ }
+ else out->mAngleOuterCone = AI_DEG_TO_RAD( srcLight->mOuterAngle );
+ }
+
+ // add to light list
+ mLights.push_back(out);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Builds cameras for the given node and references them
+void ColladaLoader::BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
+{
+ BOOST_FOREACH( const Collada::CameraInstance& cid, pNode->mCameras)
+ {
+ // find the referred light
+ ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find( cid.mCamera);
+ if ( srcCameraIt == pParser.mCameraLibrary.end())
+ {
+ DefaultLogger::get()->warn("Collada: Unable to find camera for ID \"" + cid.mCamera + "\". Skipping.");
+ continue;
+ }
+ const Collada::Camera* srcCamera = &srcCameraIt->second;
+
+ // orthographic cameras not yet supported in Assimp
+ if (srcCamera->mOrtho) {
+ DefaultLogger::get()->warn("Collada: Orthographic cameras are not supported.");
+ }
+
+ // now fill our ai data structure
+ aiCamera* out = new aiCamera();
+ out->mName = pTarget->mName;
+
+ // collada cameras point in -Z by default, rest is specified in node transform
+ out->mLookAt = aiVector3D(0.f,0.f,-1.f);
+
+ // near/far z is already ok
+ out->mClipPlaneFar = srcCamera->mZFar;
+ out->mClipPlaneNear = srcCamera->mZNear;
+
+ // ... but for the rest some values are optional
+ // and we need to compute the others in any combination. FUCKING COLLADA.
+ if (srcCamera->mAspect != 10e10f)
+ out->mAspect = srcCamera->mAspect;
+
+ if (srcCamera->mHorFov != 10e10f) {
+ out->mHorizontalFOV = srcCamera->mHorFov;
+
+ if (srcCamera->mVerFov != 10e10f && srcCamera->mAspect == 10e10f) {
+ out->mAspect = tan(AI_DEG_TO_RAD(srcCamera->mHorFov)) /
+ tan(AI_DEG_TO_RAD(srcCamera->mVerFov));
+ }
+ }
+ else if (srcCamera->mAspect != 10e10f && srcCamera->mVerFov != 10e10f) {
+ out->mHorizontalFOV = 2.0f * AI_RAD_TO_DEG(atan(srcCamera->mAspect *
+ tan(AI_DEG_TO_RAD(srcCamera->mVerFov) * 0.5f)));
+ }
+
+ // Collada uses degrees, we use radians
+ out->mHorizontalFOV = AI_DEG_TO_RAD(out->mHorizontalFOV);
+
+ // add to camera list
+ mCameras.push_back(out);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Builds meshes for the given node and references them
+void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget)
+{
+ // accumulated mesh references by this node
+ std::vector<size_t> newMeshRefs;
+ newMeshRefs.reserve(pNode->mMeshes.size());
+
+ // add a mesh for each subgroup in each collada mesh
+ BOOST_FOREACH( const Collada::MeshInstance& mid, pNode->mMeshes)
+ {
+ const Collada::Mesh* srcMesh = NULL;
+ const Collada::Controller* srcController = NULL;
+
+ // find the referred mesh
+ ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMeshOrController);
+ if ( srcMeshIt == pParser.mMeshLibrary.end())
+ {
+ // if not found in the mesh-library, it might also be a controller referring to a mesh
+ ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find( mid.mMeshOrController);
+ if ( srcContrIt != pParser.mControllerLibrary.end())
+ {
+ srcController = &srcContrIt->second;
+ srcMeshIt = pParser.mMeshLibrary.find( srcController->mMeshId);
+ if ( srcMeshIt != pParser.mMeshLibrary.end())
+ srcMesh = srcMeshIt->second;
+ }
+
+ if ( !srcMesh)
+ {
+ DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMeshOrController));
+ continue;
+ }
+ } else
+ {
+ // ID found in the mesh library -> direct reference to an unskinned mesh
+ srcMesh = srcMeshIt->second;
+ }
+
+ // build a mesh for each of its subgroups
+ size_t vertexStart = 0, faceStart = 0;
+ for ( size_t sm = 0; sm < srcMesh->mSubMeshes.size(); ++sm)
+ {
+ const Collada::SubMesh& submesh = srcMesh->mSubMeshes[sm];
+ if ( submesh.mNumFaces == 0)
+ continue;
+
+ // find material assigned to this submesh
+ std::map<std::string, Collada::SemanticMappingTable >::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial);
+
+ const Collada::SemanticMappingTable* table;
+ if ( meshMatIt != mid.mMaterials.end())
+ table = &meshMatIt->second;
+ else {
+ table = NULL;
+ DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup \"%s\" in geometry \"%s\".") % submesh.mMaterial % mid.mMeshOrController));
+ }
+ const std::string& meshMaterial = table ? table->mMatName : "";
+
+ // OK ... here the *real* fun starts ... we have the vertex-input-to-effect-semantic-table
+ // given. The only mapping stuff which we do actually support is the UV channel.
+ std::map<std::string, size_t>::const_iterator matIt = mMaterialIndexByName.find( meshMaterial);
+ unsigned int matIdx;
+ if ( matIt != mMaterialIndexByName.end())
+ matIdx = matIt->second;
+ else
+ matIdx = 0;
+
+ if (table && !table->mMap.empty() ) {
+ std::pair<Collada::Effect*, aiMaterial*>& mat = newMats[matIdx];
+
+ // Iterate through all texture channels assigned to the effect and
+ // check whether we have mapping information for it.
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexDiffuse, *table);
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexAmbient, *table);
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexSpecular, *table);
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexEmissive, *table);
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexTransparent,*table);
+ ApplyVertexToEffectSemanticMapping(mat.first->mTexBump, *table);
+ }
+
+ // built lookup index of the Mesh-Submesh-Material combination
+ ColladaMeshIndex index( mid.mMeshOrController, sm, meshMaterial);
+
+ // if we already have the mesh at the library, just add its index to the node's array
+ std::map<ColladaMeshIndex, size_t>::const_iterator dstMeshIt = mMeshIndexByID.find( index);
+ if ( dstMeshIt != mMeshIndexByID.end()) {
+ newMeshRefs.push_back( dstMeshIt->second);
+ }
+ else
+ {
+ // else we have to add the mesh to the collection and store its newly assigned index at the node
+ aiMesh* dstMesh = CreateMesh( pParser, srcMesh, submesh, srcController, vertexStart, faceStart);
+
+ // store the mesh, and store its new index in the node
+ newMeshRefs.push_back( mMeshes.size());
+ mMeshIndexByID[index] = mMeshes.size();
+ mMeshes.push_back( dstMesh);
+ vertexStart += dstMesh->mNumVertices; faceStart += submesh.mNumFaces;
+
+ // assign the material index
+ dstMesh->mMaterialIndex = matIdx;
+ }
+ }
+ }
+
+ // now place all mesh references we gathered in the target node
+ pTarget->mNumMeshes = newMeshRefs.size();
+ if ( newMeshRefs.size())
+ {
+ pTarget->mMeshes = new unsigned int[pTarget->mNumMeshes];
+ std::copy( newMeshRefs.begin(), newMeshRefs.end(), pTarget->mMeshes);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh
+aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh,
+ const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace)
+{
+ aiMesh* dstMesh = new aiMesh;
+
+ // count the vertices addressed by its faces
+ const size_t numVertices = std::accumulate( pSrcMesh->mFaceSize.begin() + pStartFace,
+ pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, 0);
+
+ // copy positions
+ dstMesh->mNumVertices = numVertices;
+ dstMesh->mVertices = new aiVector3D[numVertices];
+ std::copy( pSrcMesh->mPositions.begin() + pStartVertex, pSrcMesh->mPositions.begin() +
+ pStartVertex + numVertices, dstMesh->mVertices);
+
+ // normals, if given. HACK: (thom) Due to the fucking Collada spec we never
+ // know if we have the same number of normals as there are positions. So we
+ // also ignore any vertex attribute if it has a different count
+ if ( pSrcMesh->mNormals.size() >= pStartVertex + numVertices)
+ {
+ dstMesh->mNormals = new aiVector3D[numVertices];
+ std::copy( pSrcMesh->mNormals.begin() + pStartVertex, pSrcMesh->mNormals.begin() +
+ pStartVertex + numVertices, dstMesh->mNormals);
+ }
+
+ // tangents, if given.
+ if ( pSrcMesh->mTangents.size() >= pStartVertex + numVertices)
+ {
+ dstMesh->mTangents = new aiVector3D[numVertices];
+ std::copy( pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() +
+ pStartVertex + numVertices, dstMesh->mTangents);
+ }
+
+ // bitangents, if given.
+ if ( pSrcMesh->mBitangents.size() >= pStartVertex + numVertices)
+ {
+ dstMesh->mBitangents = new aiVector3D[numVertices];
+ std::copy( pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() +
+ pStartVertex + numVertices, dstMesh->mBitangents);
+ }
+
+ // same for texturecoords, as many as we have
+ // empty slots are not allowed, need to pack and adjust UV indexes accordingly
+ for ( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++)
+ {
+ if ( pSrcMesh->mTexCoords[a].size() >= pStartVertex + numVertices)
+ {
+ dstMesh->mTextureCoords[real] = new aiVector3D[numVertices];
+ for ( size_t b = 0; b < numVertices; ++b)
+ dstMesh->mTextureCoords[real][b] = pSrcMesh->mTexCoords[a][pStartVertex+b];
+
+ dstMesh->mNumUVComponents[real] = pSrcMesh->mNumUVComponents[a];
+ ++real;
+ }
+ }
+
+ // same for vertex colors, as many as we have. again the same packing to avoid empty slots
+ for ( size_t a = 0, real = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++)
+ {
+ if ( pSrcMesh->mColors[a].size() >= pStartVertex + numVertices)
+ {
+ dstMesh->mColors[real] = new aiColor4D[numVertices];
+ std::copy( pSrcMesh->mColors[a].begin() + pStartVertex, pSrcMesh->mColors[a].begin() + pStartVertex + numVertices,dstMesh->mColors[real]);
+ ++real;
+ }
+ }
+
+ // create faces. Due to the fact that each face uses unique vertices, we can simply count up on each vertex
+ size_t vertex = 0;
+ dstMesh->mNumFaces = pSubMesh.mNumFaces;
+ dstMesh->mFaces = new aiFace[dstMesh->mNumFaces];
+ for ( size_t a = 0; a < dstMesh->mNumFaces; ++a)
+ {
+ size_t s = pSrcMesh->mFaceSize[ pStartFace + a];
+ aiFace& face = dstMesh->mFaces[a];
+ face.mNumIndices = s;
+ face.mIndices = new unsigned int[s];
+ for ( size_t b = 0; b < s; ++b)
+ face.mIndices[b] = vertex++;
+ }
+
+ // create bones if given
+ if ( pSrcController)
+ {
+ // refuse if the vertex count does not match
+// if ( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices)
+// throw DeadlyImportError( "Joint Controller vertex count does not match mesh vertex count");
+
+ // resolve references - joint names
+ const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource);
+ const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource);
+ // joint offset matrices
+ const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource);
+ const Collada::Data& jointMatrices = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointMatrixAcc.mSource);
+ // joint vertex_weight name list - should refer to the same list as the joint names above. If not, report and reconsider
+ const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor);
+ if ( &weightNamesAcc != &jointNamesAcc)
+ throw DeadlyImportError( "Temporary implementational lazyness. If you read this, please report to the author.");
+ // vertex weights
+ const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor);
+ const Collada::Data& weights = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightsAcc.mSource);
+
+ if ( !jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray)
+ throw DeadlyImportError( "Data type mismatch while resolving mesh joints");
+ // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex
+ if ( pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1)
+ throw DeadlyImportError( "Unsupported vertex_weight adresssing scheme. Fucking collada spec.");
+
+ // create containers to collect the weights for each bone
+ size_t numBones = jointNames.mStrings.size();
+ std::vector<std::vector<aiVertexWeight> > dstBones( numBones);
+
+ // build a temporary array of pointers to the start of each vertex's weights
+ typedef std::vector< std::pair<size_t, size_t> > IndexPairVector;
+ std::vector<IndexPairVector::const_iterator> weightStartPerVertex( pSrcController->mWeightCounts.size());
+ IndexPairVector::const_iterator pit = pSrcController->mWeights.begin();
+ for ( size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a)
+ {
+ weightStartPerVertex[a] = pit;
+ pit += pSrcController->mWeightCounts[a];
+ }
+
+ // now for each vertex put the corresponding vertex weights into each bone's weight collection
+ for ( size_t a = pStartVertex; a < pStartVertex + numVertices; ++a)
+ {
+ // which position index was responsible for this vertex? that's also the index by which
+ // the controller assigns the vertex weights
+ size_t orgIndex = pSrcMesh->mFacePosIndices[a];
+ // find the vertex weights for this vertex
+ IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex];
+ size_t pairCount = pSrcController->mWeightCounts[orgIndex];
+
+ for ( size_t b = 0; b < pairCount; ++b, ++iit)
+ {
+ size_t jointIndex = iit->first;
+ size_t vertexIndex = iit->second;
+
+ float weight = ReadFloat( weightsAcc, weights, vertexIndex, 0);
+
+ // one day I gonna kill that XSI Collada exporter
+ if ( weight > 0.0f)
+ {
+ aiVertexWeight w;
+ w.mVertexId = a - pStartVertex;
+ w.mWeight = weight;
+ dstBones[jointIndex].push_back( w);
+ }
+ }
+ }
+
+ // count the number of bones which influence vertices of the current submesh
+ size_t numRemainingBones = 0;
+ for ( std::vector<std::vector<aiVertexWeight> >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it)
+ if ( it->size() > 0)
+ numRemainingBones++;
+
+ // create bone array and copy bone weights one by one
+ dstMesh->mNumBones = numRemainingBones;
+ dstMesh->mBones = new aiBone*[numRemainingBones];
+ size_t boneCount = 0;
+ for ( size_t a = 0; a < numBones; ++a)
+ {
+ // omit bones without weights
+ if ( dstBones[a].size() == 0)
+ continue;
+
+ // create bone with its weights
+ aiBone* bone = new aiBone;
+ bone->mName = ReadString( jointNamesAcc, jointNames, a);
+ bone->mOffsetMatrix.a1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 0);
+ bone->mOffsetMatrix.a2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 1);
+ bone->mOffsetMatrix.a3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 2);
+ bone->mOffsetMatrix.a4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 3);
+ bone->mOffsetMatrix.b1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 4);
+ bone->mOffsetMatrix.b2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 5);
+ bone->mOffsetMatrix.b3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 6);
+ bone->mOffsetMatrix.b4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 7);
+ bone->mOffsetMatrix.c1 = ReadFloat( jointMatrixAcc, jointMatrices, a, 8);
+ bone->mOffsetMatrix.c2 = ReadFloat( jointMatrixAcc, jointMatrices, a, 9);
+ bone->mOffsetMatrix.c3 = ReadFloat( jointMatrixAcc, jointMatrices, a, 10);
+ bone->mOffsetMatrix.c4 = ReadFloat( jointMatrixAcc, jointMatrices, a, 11);
+ bone->mNumWeights = dstBones[a].size();
+ bone->mWeights = new aiVertexWeight[bone->mNumWeights];
+ std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights);
+
+ // apply bind shape matrix to offset matrix
+ aiMatrix4x4 bindShapeMatrix;
+ bindShapeMatrix.a1 = pSrcController->mBindShapeMatrix[0];
+ bindShapeMatrix.a2 = pSrcController->mBindShapeMatrix[1];
+ bindShapeMatrix.a3 = pSrcController->mBindShapeMatrix[2];
+ bindShapeMatrix.a4 = pSrcController->mBindShapeMatrix[3];
+ bindShapeMatrix.b1 = pSrcController->mBindShapeMatrix[4];
+ bindShapeMatrix.b2 = pSrcController->mBindShapeMatrix[5];
+ bindShapeMatrix.b3 = pSrcController->mBindShapeMatrix[6];
+ bindShapeMatrix.b4 = pSrcController->mBindShapeMatrix[7];
+ bindShapeMatrix.c1 = pSrcController->mBindShapeMatrix[8];
+ bindShapeMatrix.c2 = pSrcController->mBindShapeMatrix[9];
+ bindShapeMatrix.c3 = pSrcController->mBindShapeMatrix[10];
+ bindShapeMatrix.c4 = pSrcController->mBindShapeMatrix[11];
+ bindShapeMatrix.d1 = pSrcController->mBindShapeMatrix[12];
+ bindShapeMatrix.d2 = pSrcController->mBindShapeMatrix[13];
+ bindShapeMatrix.d3 = pSrcController->mBindShapeMatrix[14];
+ bindShapeMatrix.d4 = pSrcController->mBindShapeMatrix[15];
+ bone->mOffsetMatrix *= bindShapeMatrix;
+
+ // HACK: (thom) Some exporters address the bone nodes by SID, others address them by ID or even name.
+ // Therefore I added a little name replacement here: I search for the bone's node by either name, ID or SID,
+ // and replace the bone's name by the node's name so that the user can use the standard
+ // find-by-name method to associate nodes with bones.
+ const Collada::Node* bnode = FindNode( pParser.mRootNode, bone->mName.data);
+ if ( !bnode)
+ bnode = FindNodeBySID( pParser.mRootNode, bone->mName.data);
+
+ // assign the name that we would have assigned for the source node
+ if ( bnode)
+ bone->mName.Set( FindNameForNode( bnode));
+ else
+ DefaultLogger::get()->warn( boost::str( boost::format( "ColladaLoader::CreateMesh(): could not find corresponding node for joint \"%s\".") % bone->mName.data));
+
+ // and insert bone
+ dstMesh->mBones[boneCount++] = bone;
+ }
+ }
+
+ return dstMesh;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all meshes in the given scene
+void ColladaLoader::StoreSceneMeshes( aiScene* pScene)
+{
+ pScene->mNumMeshes = mMeshes.size();
+ if ( mMeshes.size() > 0)
+ {
+ pScene->mMeshes = new aiMesh*[mMeshes.size()];
+ std::copy( mMeshes.begin(), mMeshes.end(), pScene->mMeshes);
+ mMeshes.clear();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all cameras in the given scene
+void ColladaLoader::StoreSceneCameras( aiScene* pScene)
+{
+ pScene->mNumCameras = mCameras.size();
+ if ( mCameras.size() > 0)
+ {
+ pScene->mCameras = new aiCamera*[mCameras.size()];
+ std::copy( mCameras.begin(), mCameras.end(), pScene->mCameras);
+ mCameras.clear();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all lights in the given scene
+void ColladaLoader::StoreSceneLights( aiScene* pScene)
+{
+ pScene->mNumLights = mLights.size();
+ if ( mLights.size() > 0)
+ {
+ pScene->mLights = new aiLight*[mLights.size()];
+ std::copy( mLights.begin(), mLights.end(), pScene->mLights);
+ mLights.clear();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all textures in the given scene
+void ColladaLoader::StoreSceneTextures( aiScene* pScene)
+{
+ pScene->mNumTextures = mTextures.size();
+ if ( mTextures.size() > 0)
+ {
+ pScene->mTextures = new aiTexture*[mTextures.size()];
+ std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures);
+ mTextures.clear();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all materials in the given scene
+void ColladaLoader::StoreSceneMaterials( aiScene* pScene)
+{
+ pScene->mNumMaterials = newMats.size();
+
+ if (newMats.size() > 0) {
+ pScene->mMaterials = new aiMaterial*[newMats.size()];
+ for (unsigned int i = 0; i < newMats.size();++i)
+ pScene->mMaterials[i] = newMats[i].second;
+
+ newMats.clear();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Stores all animations
+void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser)
+{
+ // recursivly collect all animations from the collada scene
+ StoreAnimations( pScene, pParser, &pParser.mAnims, "");
+
+ // catch special case: many animations with the same length, each affecting only a single node.
+ // we need to unite all those single-node-anims to a proper combined animation
+ for ( size_t a = 0; a < mAnims.size(); ++a)
+ {
+ aiAnimation* templateAnim = mAnims[a];
+ if ( templateAnim->mNumChannels == 1)
+ {
+ // search for other single-channel-anims with the same duration
+ std::vector<size_t> collectedAnimIndices;
+ for ( size_t b = a+1; b < mAnims.size(); ++b)
+ {
+ aiAnimation* other = mAnims[b];
+ if ( other->mNumChannels == 1 && other->mDuration == templateAnim->mDuration && other->mTicksPerSecond == templateAnim->mTicksPerSecond )
+ collectedAnimIndices.push_back( b);
+ }
+
+ // if there are other animations which fit the template anim, combine all channels into a single anim
+ if ( !collectedAnimIndices.empty() )
+ {
+ aiAnimation* combinedAnim = new aiAnimation();
+ combinedAnim->mName = aiString( std::string( "combinedAnim_") + char( '0' + a));
+ combinedAnim->mDuration = templateAnim->mDuration;
+ combinedAnim->mTicksPerSecond = templateAnim->mTicksPerSecond;
+ combinedAnim->mNumChannels = collectedAnimIndices.size() + 1;
+ combinedAnim->mChannels = new aiNodeAnim*[combinedAnim->mNumChannels];
+ // add the template anim as first channel by moving its aiNodeAnim to the combined animation
+ combinedAnim->mChannels[0] = templateAnim->mChannels[0];
+ templateAnim->mChannels[0] = NULL;
+ delete templateAnim;
+ // combined animation replaces template animation in the anim array
+ mAnims[a] = combinedAnim;
+
+ // move the memory of all other anims to the combined anim and erase them from the source anims
+ for ( size_t b = 0; b < collectedAnimIndices.size(); ++b)
+ {
+ aiAnimation* srcAnimation = mAnims[collectedAnimIndices[b]];
+ combinedAnim->mChannels[1 + b] = srcAnimation->mChannels[0];
+ srcAnimation->mChannels[0] = NULL;
+ delete srcAnimation;
+ }
+
+ // in a second go, delete all the single-channel-anims that we've stripped from their channels
+ // back to front to preserve indices - you know, removing an element from a vector moves all elements behind the removed one
+ while ( !collectedAnimIndices.empty() )
+ {
+ mAnims.erase( mAnims.begin() + collectedAnimIndices.back());
+ collectedAnimIndices.pop_back();
+ }
+ }
+ }
+ }
+
+ // now store all anims in the scene
+ if ( !mAnims.empty())
+ {
+ pScene->mNumAnimations = mAnims.size();
+ pScene->mAnimations = new aiAnimation*[mAnims.size()];
+ std::copy( mAnims.begin(), mAnims.end(), pScene->mAnimations);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs the animations for the given source anim
+void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string pPrefix)
+{
+ std::string animName = pPrefix.empty() ? pSrcAnim->mName : pPrefix + "_" + pSrcAnim->mName;
+
+ // create nested animations, if given
+ for ( std::vector<Collada::Animation*>::const_iterator it = pSrcAnim->mSubAnims.begin(); it != pSrcAnim->mSubAnims.end(); ++it)
+ StoreAnimations( pScene, pParser, *it, animName);
+
+ // create animation channels, if any
+ if ( !pSrcAnim->mChannels.empty())
+ CreateAnimation( pScene, pParser, pSrcAnim, animName);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs the animation for the given source anim
+void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName)
+{
+ // collect a list of animatable nodes
+ std::vector<const aiNode*> nodes;
+ CollectNodes( pScene->mRootNode, nodes);
+
+ std::vector<aiNodeAnim*> anims;
+ for ( std::vector<const aiNode*>::const_iterator nit = nodes.begin(); nit != nodes.end(); ++nit)
+ {
+ // find all the collada anim channels which refer to the current node
+ std::vector<Collada::ChannelEntry> entries;
+ std::string nodeName = (*nit)->mName.data;
+
+ // find the collada node corresponding to the aiNode
+ const Collada::Node* srcNode = FindNode( pParser.mRootNode, nodeName);
+// ai_assert( srcNode != NULL);
+ if ( !srcNode)
+ continue;
+
+ // now check all channels if they affect the current node
+ for ( std::vector<Collada::AnimationChannel>::const_iterator cit = pSrcAnim->mChannels.begin();
+ cit != pSrcAnim->mChannels.end(); ++cit)
+ {
+ const Collada::AnimationChannel& srcChannel = *cit;
+ Collada::ChannelEntry entry;
+
+ // we except the animation target to be of type "nodeName/transformID.subElement". Ignore all others
+ // find the slash that separates the node name - there should be only one
+ std::string::size_type slashPos = srcChannel.mTarget.find( '/');
+ if ( slashPos == std::string::npos)
+ continue;
+ if ( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos)
+ continue;
+ std::string targetID = srcChannel.mTarget.substr( 0, slashPos);
+ if ( targetID != srcNode->mID)
+ continue;
+
+ // find the dot that separates the transformID - there should be only one or zero
+ std::string::size_type dotPos = srcChannel.mTarget.find( '.');
+ if ( dotPos != std::string::npos)
+ {
+ if ( srcChannel.mTarget.find( '.', dotPos+1) != std::string::npos)
+ continue;
+
+ entry.mTransformId = srcChannel.mTarget.substr( slashPos+1, dotPos - slashPos - 1);
+
+ std::string subElement = srcChannel.mTarget.substr( dotPos+1);
+ if ( subElement == "ANGLE")
+ entry.mSubElement = 3; // last number in an Axis-Angle-Transform is the angle
+ else if ( subElement == "X")
+ entry.mSubElement = 0;
+ else if ( subElement == "Y")
+ entry.mSubElement = 1;
+ else if ( subElement == "Z")
+ entry.mSubElement = 2;
+ else
+ DefaultLogger::get()->warn( boost::str( boost::format( "Unknown anim subelement \"%s\". Ignoring") % subElement));
+ } else
+ {
+ // no subelement following, transformId is remaining string
+ entry.mTransformId = srcChannel.mTarget.substr( slashPos+1);
+ }
+
+ // determine which transform step is affected by this channel
+ entry.mTransformIndex = 0xffffffff;
+ for ( size_t a = 0; a < srcNode->mTransforms.size(); ++a)
+ if ( srcNode->mTransforms[a].mID == entry.mTransformId)
+ entry.mTransformIndex = a;
+
+ if ( entry.mTransformIndex == 0xffffffff)
+ continue;
+
+ entry.mChannel = &(*cit);
+ entries.push_back( entry);
+ }
+
+ // if there's no channel affecting the current node, we skip it
+ if ( entries.empty())
+ continue;
+
+ // resolve the data pointers for all anim channels. Find the minimum time while we're at it
+ float startTime = 1e20f, endTime = -1e20f;
+ for ( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
+ {
+ Collada::ChannelEntry& e = *it;
+ e.mTimeAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceTimes);
+ e.mTimeData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mTimeAccessor->mSource);
+ e.mValueAccessor = &pParser.ResolveLibraryReference( pParser.mAccessorLibrary, e.mChannel->mSourceValues);
+ e.mValueData = &pParser.ResolveLibraryReference( pParser.mDataLibrary, e.mValueAccessor->mSource);
+
+ // time count and value count must match
+ if ( e.mTimeAccessor->mCount != e.mValueAccessor->mCount)
+ throw DeadlyImportError( boost::str( boost::format( "Time count / value count mismatch in animation channel \"%s\".") % e.mChannel->mTarget));
+
+ // find bounding times
+ startTime = std::min( startTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, 0, 0));
+ endTime = std::max( endTime, ReadFloat( *e.mTimeAccessor, *e.mTimeData, e.mTimeAccessor->mCount-1, 0));
+ }
+
+ // create a local transformation chain of the node's transforms
+ std::vector<Collada::Transform> transforms = srcNode->mTransforms;
+
+ // now for every unique point in time, find or interpolate the key values for that time
+ // and apply them to the transform chain. Then the node's present transformation can be calculated.
+ float time = startTime;
+ std::vector<aiMatrix4x4> resultTrafos;
+ while ( 1)
+ {
+ for ( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
+ {
+ Collada::ChannelEntry& e = *it;
+
+ // find the keyframe behind the current point in time
+ size_t pos = 0;
+ float postTime = 0.f;
+ while ( 1)
+ {
+ if ( pos >= e.mTimeAccessor->mCount)
+ break;
+ postTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
+ if ( postTime >= time)
+ break;
+ ++pos;
+ }
+
+ pos = std::min( pos, e.mTimeAccessor->mCount-1);
+
+ // read values from there
+ float temp[16];
+ for ( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
+ temp[c] = ReadFloat( *e.mValueAccessor, *e.mValueData, pos, c);
+
+ // if not exactly at the key time, interpolate with previous value set
+ if ( postTime > time && pos > 0)
+ {
+ float preTime = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos-1, 0);
+ float factor = (time - postTime) / (preTime - postTime);
+
+ for ( size_t c = 0; c < e.mValueAccessor->mSize; ++c)
+ {
+ float v = ReadFloat( *e.mValueAccessor, *e.mValueData, pos-1, c);
+ temp[c] += (v - temp[c]) * factor;
+ }
+ }
+
+ // Apply values to current transformation
+ std::copy( temp, temp + e.mValueAccessor->mSize, transforms[e.mTransformIndex].f + e.mSubElement);
+ }
+
+ // Calculate resulting transformation
+ aiMatrix4x4 mat = pParser.CalculateResultTransform( transforms);
+
+ // out of lazyness: we store the time in matrix.d4
+ mat.d4 = time;
+ resultTrafos.push_back( mat);
+
+ // find next point in time to evaluate. That's the closest frame larger than the current in any channel
+ float nextTime = 1e20f;
+ for ( std::vector<Collada::ChannelEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
+ {
+ Collada::ChannelEntry& e = *it;
+
+ // find the next time value larger than the current
+ size_t pos = 0;
+ while ( pos < e.mTimeAccessor->mCount)
+ {
+ float t = ReadFloat( *e.mTimeAccessor, *e.mTimeData, pos, 0);
+ if ( t > time)
+ {
+ nextTime = std::min( nextTime, t);
+ break;
+ }
+ ++pos;
+ }
+ }
+
+ // no more keys on any channel after the current time -> we're done
+ if ( nextTime > 1e19)
+ break;
+
+ // else construct next keyframe at this following time point
+ time = nextTime;
+ }
+
+ // there should be some keyframes
+ ai_assert( resultTrafos.size() > 0);
+
+ // build an animation channel for the given node out of these trafo keys
+ aiNodeAnim* dstAnim = new aiNodeAnim;
+ dstAnim->mNodeName = nodeName;
+ dstAnim->mNumPositionKeys = resultTrafos.size();
+ dstAnim->mNumRotationKeys= resultTrafos.size();
+ dstAnim->mNumScalingKeys = resultTrafos.size();
+ dstAnim->mPositionKeys = new aiVectorKey[resultTrafos.size()];
+ dstAnim->mRotationKeys = new aiQuatKey[resultTrafos.size()];
+ dstAnim->mScalingKeys = new aiVectorKey[resultTrafos.size()];
+
+ for ( size_t a = 0; a < resultTrafos.size(); ++a)
+ {
+ const aiMatrix4x4& mat = resultTrafos[a];
+ double time = double( mat.d4); // remember? time is stored in mat.d4
+
+ dstAnim->mPositionKeys[a].mTime = time;
+ dstAnim->mRotationKeys[a].mTime = time;
+ dstAnim->mScalingKeys[a].mTime = time;
+ mat.Decompose( dstAnim->mScalingKeys[a].mValue, dstAnim->mRotationKeys[a].mValue, dstAnim->mPositionKeys[a].mValue);
+ }
+
+ anims.push_back( dstAnim);
+ }
+
+ if ( !anims.empty())
+ {
+ aiAnimation* anim = new aiAnimation;
+ anim->mName.Set( pName);
+ anim->mNumChannels = anims.size();
+ anim->mChannels = new aiNodeAnim*[anims.size()];
+ std::copy( anims.begin(), anims.end(), anim->mChannels);
+ anim->mDuration = 0.0f;
+ for ( size_t a = 0; a < anims.size(); ++a)
+ {
+ anim->mDuration = std::max( anim->mDuration, anims[a]->mPositionKeys[anims[a]->mNumPositionKeys-1].mTime);
+ anim->mDuration = std::max( anim->mDuration, anims[a]->mRotationKeys[anims[a]->mNumRotationKeys-1].mTime);
+ anim->mDuration = std::max( anim->mDuration, anims[a]->mScalingKeys[anims[a]->mNumScalingKeys-1].mTime);
+ }
+ anim->mTicksPerSecond = 1;
+ mAnims.push_back( anim);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add a texture to a material structure
+void ColladaLoader::AddTexture ( Assimp::MaterialHelper& mat, const ColladaParser& pParser,
+ const Collada::Effect& effect,
+ const Collada::Sampler& sampler,
+ aiTextureType type, unsigned int idx)
+{
+ // first of all, basic file name
+ const aiString name = FindFilenameForEffectTexture( pParser, effect, sampler.mName );
+ mat.AddProperty( &name, _AI_MATKEY_TEXTURE_BASE, type, idx );
+
+ // mapping mode
+ int map = aiTextureMapMode_Clamp;
+ if (sampler.mWrapU)
+ map = aiTextureMapMode_Wrap;
+ if (sampler.mWrapU && sampler.mMirrorU)
+ map = aiTextureMapMode_Mirror;
+
+ mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_U_BASE, type, idx);
+
+ map = aiTextureMapMode_Clamp;
+ if (sampler.mWrapV)
+ map = aiTextureMapMode_Wrap;
+ if (sampler.mWrapV && sampler.mMirrorV)
+ map = aiTextureMapMode_Mirror;
+
+ mat.AddProperty( &map, 1, _AI_MATKEY_MAPPINGMODE_V_BASE, type, idx);
+
+ // UV transformation
+ mat.AddProperty(&sampler.mTransform, 1,
+ _AI_MATKEY_UVTRANSFORM_BASE, type, idx);
+
+ // Blend mode
+ mat.AddProperty((int*)&sampler.mOp , 1,
+ _AI_MATKEY_TEXBLEND_BASE, type, idx);
+
+ // Blend factor
+ mat.AddProperty((float*)&sampler.mWeighting , 1,
+ _AI_MATKEY_TEXBLEND_BASE, type, idx);
+
+ // UV source index ... if we didn't resolve the mapping it is actually just
+ // a guess but it works in most cases. We search for the frst occurence of a
+ // number in the channel name. We assume it is the zero-based index into the
+ // UV channel array of all corresponding meshes. It could also be one-based
+ // for some exporters, but we won't care of it unless someone complains about.
+ if (sampler.mUVId != 0xffffffff)
+ map = sampler.mUVId;
+ else {
+ map = -1;
+ for (std::string::const_iterator it = sampler.mUVChannel.begin();it != sampler.mUVChannel.end(); ++it){
+ if (IsNumeric(*it)) {
+ map = strtol10(&(*it));
+ break;
+ }
+ }
+ if (-1 == map) {
+ DefaultLogger::get()->warn("Collada: unable to determine UV channel for texture");
+ map = 0;
+ }
+ }
+ mat.AddProperty(&map,1,_AI_MATKEY_UVWSRC_BASE,type,idx);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Fills materials from the collada material definitions
+void ColladaLoader::FillMaterials( const ColladaParser& pParser, aiScene* /*pScene*/)
+{
+ for (std::vector<std::pair<Collada::Effect*, aiMaterial*> >::iterator it = newMats.begin(),
+ end = newMats.end(); it != end; ++it)
+ {
+ MaterialHelper& mat = (MaterialHelper&)*it->second;
+ Collada::Effect& effect = *it->first;
+
+ // resolve shading mode
+ int shadeMode;
+ if (effect.mFaceted) /* fixme */
+ shadeMode = aiShadingMode_Flat;
+ else {
+ switch( effect.mShadeType)
+ {
+ case Collada::Shade_Constant:
+ shadeMode = aiShadingMode_NoShading;
+ break;
+ case Collada::Shade_Lambert:
+ shadeMode = aiShadingMode_Gouraud;
+ break;
+ case Collada::Shade_Blinn:
+ shadeMode = aiShadingMode_Blinn;
+ break;
+ case Collada::Shade_Phong:
+ shadeMode = aiShadingMode_Phong;
+ break;
+
+ default:
+ DefaultLogger::get()->warn("Collada: Unrecognized shading mode, using gouraud shading");
+ shadeMode = aiShadingMode_Gouraud;
+ break;
+ }
+ }
+ mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ // double-sided?
+ shadeMode = effect.mDoubleSided;
+ mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_TWOSIDED);
+
+ // wireframe?
+ shadeMode = effect.mWireframe;
+ mat.AddProperty<int>( &shadeMode, 1, AI_MATKEY_ENABLE_WIREFRAME);
+
+ // add material colors
+ mat.AddProperty( &effect.mAmbient, 1,AI_MATKEY_COLOR_AMBIENT);
+ mat.AddProperty( &effect.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat.AddProperty( &effect.mSpecular, 1,AI_MATKEY_COLOR_SPECULAR);
+ mat.AddProperty( &effect.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE);
+ mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT);
+ mat.AddProperty( &effect.mReflective, 1, AI_MATKEY_COLOR_REFLECTIVE);
+
+ // scalar properties
+ mat.AddProperty( &effect.mShininess, 1, AI_MATKEY_SHININESS);
+ mat.AddProperty( &effect.mReflectivity, 1, AI_MATKEY_REFLECTIVITY);
+ mat.AddProperty( &effect.mRefractIndex, 1, AI_MATKEY_REFRACTI);
+
+ // transparency, a very hard one. seemingly not all files are following the
+ // specification here .. but we can trick.
+ if (effect.mTransparency > 0.f && effect.mTransparency < 1.f) {
+ effect.mTransparency = 1.f- effect.mTransparency;
+ mat.AddProperty( &effect.mTransparency, 1, AI_MATKEY_OPACITY );
+ mat.AddProperty( &effect.mTransparent, 1, AI_MATKEY_COLOR_TRANSPARENT );
+ }
+
+ // add textures, if given
+ if ( !effect.mTexAmbient.mName.empty())
+ /* It is merely a lightmap */
+ AddTexture( mat, pParser, effect, effect.mTexAmbient, aiTextureType_LIGHTMAP);
+
+ if ( !effect.mTexEmissive.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexEmissive, aiTextureType_EMISSIVE);
+
+ if ( !effect.mTexSpecular.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexSpecular, aiTextureType_SPECULAR);
+
+ if ( !effect.mTexDiffuse.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexDiffuse, aiTextureType_DIFFUSE);
+
+ if ( !effect.mTexBump.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexBump, aiTextureType_HEIGHT);
+
+ if ( !effect.mTexTransparent.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexTransparent, aiTextureType_OPACITY);
+
+ if ( !effect.mTexReflective.mName.empty())
+ AddTexture( mat, pParser, effect, effect.mTexReflective, aiTextureType_REFLECTION);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs materials from the collada material definitions
+void ColladaLoader::BuildMaterials( const ColladaParser& pParser, aiScene* /*pScene*/)
+{
+ newMats.reserve(pParser.mMaterialLibrary.size());
+
+ for ( ColladaParser::MaterialLibrary::const_iterator matIt = pParser.mMaterialLibrary.begin(); matIt != pParser.mMaterialLibrary.end(); ++matIt)
+ {
+ const Collada::Material& material = matIt->second;
+ // a material is only a reference to an effect
+ ColladaParser::EffectLibrary::const_iterator effIt = pParser.mEffectLibrary.find( material.mEffect);
+ if ( effIt == pParser.mEffectLibrary.end())
+ continue;
+ const Collada::Effect& effect = effIt->second;
+
+ // create material
+ Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+ aiString name( matIt->first);
+ mat->AddProperty(&name,AI_MATKEY_NAME);
+
+ // MEGA SUPER MONSTER HACK by Alex ... It's all my fault, yes.
+ // We store the reference to the effect in the material and
+ // return ... we'll add the actual material properties later
+ // after we processed all meshes. During mesh processing,
+ // we evaluate vertex input mappings. Afterwards we should be
+ // able to correctly setup source UV channels for textures.
+
+ // ... moved to ColladaLoader::FillMaterials()
+ // *duck*
+
+ // store the material
+ mMaterialIndexByName[matIt->first] = newMats.size();
+ newMats.push_back( std::pair<Collada::Effect*, aiMaterial*>(const_cast<Collada::Effect*>(&effect),mat) );
+ }
+ // ScenePreprocessor generates a default material automatically if none is there.
+ // All further code here in this loader works well without a valid material so
+ // we can safely let it to ScenePreprocessor.
+#if 0
+ if ( newMats.size() == 0)
+ {
+ Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+ aiString name( AI_DEFAULT_MATERIAL_NAME );
+ mat->AddProperty( &name, AI_MATKEY_NAME);
+
+ const int shadeMode = aiShadingMode_Phong;
+ mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
+ aiColor4D colAmbient( 0.2f, 0.2f, 0.2f, 1.0f), colDiffuse( 0.8f, 0.8f, 0.8f, 1.0f), colSpecular( 0.5f, 0.5f, 0.5f, 0.5f);
+ mat->AddProperty( &colAmbient, 1, AI_MATKEY_COLOR_AMBIENT);
+ mat->AddProperty( &colDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat->AddProperty( &colSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
+ const float specExp = 5.0f;
+ mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS);
+ }
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+// Resolves the texture name for the given effect texture entry
+aiString ColladaLoader::FindFilenameForEffectTexture( const ColladaParser& pParser,
+ const Collada::Effect& pEffect, const std::string& pName)
+{
+ // recurse through the param references until we end up at an image
+ std::string name = pName;
+ while ( 1)
+ {
+ // the given string is a param entry. Find it
+ Collada::Effect::ParamLibrary::const_iterator it = pEffect.mParams.find( name);
+ // if not found, we're at the end of the recursion. The resulting string should be the image ID
+ if ( it == pEffect.mParams.end())
+ break;
+
+ // else recurse on
+ name = it->second.mReference;
+ }
+
+ // find the image referred by this name in the image library of the scene
+ ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name);
+ if ( imIt == pParser.mImageLibrary.end())
+ {
+ throw DeadlyImportError( boost::str( boost::format(
+ "Collada: Unable to resolve effect texture entry \"%s\", ended up at ID \"%s\".") % pName % name));
+ }
+
+ aiString result;
+
+ // if this is an embedded texture image setup an aiTexture for it
+ if (imIt->second.mFileName.empty())
+ {
+ if (imIt->second.mImageData.empty()) {
+ throw DeadlyImportError("Collada: Invalid texture, no data or file reference given");
+ }
+
+ aiTexture* tex = new aiTexture();
+
+ // setup format hint
+ if (imIt->second.mEmbeddedFormat.length() > 3) {
+ DefaultLogger::get()->warn("Collada: texture format hint is too long, truncating to 3 characters");
+ }
+ strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3);
+
+ // and copy texture data
+ tex->mHeight = 0;
+ tex->mWidth = imIt->second.mImageData.size();
+ tex->pcData = (aiTexel*)new char[tex->mWidth];
+ memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth);
+
+ // setup texture reference string
+ result.data[0] = '*';
+ result.length = 1 + ASSIMP_itoa10(result.data+1,MAXLEN-1,mTextures.size());
+
+ // and add this texture to the list
+ mTextures.push_back(tex);
+ }
+ else
+ {
+ result.Set( imIt->second.mFileName );
+ ConvertPath(result);
+ }
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a path read from a collada file to the usual representation
+void ColladaLoader::ConvertPath (aiString& ss)
+{
+ // TODO: collada spec, p 22. Handle URI correctly.
+ // For the moment we're just stripping the file:// away to make it work.
+ // Windoes 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';
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a float value from an accessor and its data array.
+float ColladaLoader::ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const
+{
+ // FIXME: (thom) Test for data type here in every access? For the moment, I leave this to the caller
+ size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset + pOffset;
+ ai_assert( pos < pData.mValues.size());
+ return pData.mValues[pos];
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a string value from an accessor and its data array.
+const std::string& ColladaLoader::ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const
+{
+ size_t pos = pAccessor.mStride * pIndex + pAccessor.mOffset;
+ ai_assert( pos < pData.mStrings.size());
+ return pData.mStrings[pos];
+}
+
+// ------------------------------------------------------------------------------------------------
+// Collects all nodes into the given array
+void ColladaLoader::CollectNodes( const aiNode* pNode, std::vector<const aiNode*>& poNodes) const
+{
+ poNodes.push_back( pNode);
+
+ for ( size_t a = 0; a < pNode->mNumChildren; ++a)
+ CollectNodes( pNode->mChildren[a], poNodes);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Finds a node in the collada scene by the given name
+const Collada::Node* ColladaLoader::FindNode( const Collada::Node* pNode, const std::string& pName) const
+{
+ if ( pNode->mName == pName || pNode->mID == pName)
+ return pNode;
+
+ for ( size_t a = 0; a < pNode->mChildren.size(); ++a)
+ {
+ const Collada::Node* node = FindNode( pNode->mChildren[a], pName);
+ if ( node)
+ return node;
+ }
+
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Finds a node in the collada scene by the given SID
+const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const
+{
+ if ( pNode->mSID == pSID)
+ return pNode;
+
+ for ( size_t a = 0; a < pNode->mChildren.size(); ++a)
+ {
+ const Collada::Node* node = FindNodeBySID( pNode->mChildren[a], pSID);
+ if ( node)
+ return node;
+ }
+
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Finds a proper name for a node derived from the collada-node's properties
+std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) const
+{
+ // now setup the name of the node. We take the name if not empty, otherwise the collada ID
+ // FIX: Workaround for XSI calling the instanced visual scene 'untitled' by default.
+ if (!pNode->mName.empty() && pNode->mName != "untitled")
+ return pNode->mName;
+ else if (!pNode->mID.empty())
+ return pNode->mID;
+ else if (!pNode->mSID.empty())
+ return pNode->mSID;
+ else
+ {
+ // No need to worry. Unnamed nodes are no problem at all, except
+ // if cameras or lights need to be assigned to them.
+ return boost::str( boost::format( "$ColladaAutoName$_%d") % clock());
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER
diff --git a/3rdparty/assimp/code/ColladaLoader.h b/3rdparty/assimp/code/ColladaLoader.h
new file mode 100644
index 000000000..1fcdff1e1
--- /dev/null
+++ b/3rdparty/assimp/code/ColladaLoader.h
@@ -0,0 +1,241 @@
+/** Defines the collada loader class */
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 AI_COLLADALOADER_H_INC
+#define AI_COLLADALOADER_H_INC
+
+#include "BaseImporter.h"
+#include "ColladaParser.h"
+
+namespace Assimp
+{
+
+struct ColladaMeshIndex
+{
+ std::string mMeshID;
+ size_t mSubMesh;
+ std::string mMaterial;
+ ColladaMeshIndex( const std::string& pMeshID, size_t pSubMesh, const std::string& pMaterial)
+ : mMeshID( pMeshID), mSubMesh( pSubMesh), mMaterial( pMaterial)
+ { }
+
+ bool operator < (const ColladaMeshIndex& p) const
+ {
+ if ( mMeshID == p.mMeshID)
+ {
+ if ( mSubMesh == p.mSubMesh)
+ return mMaterial < p.mMaterial;
+ else
+ return mSubMesh < p.mSubMesh;
+ } else
+ {
+ return mMeshID < p.mMeshID;
+ }
+ }
+};
+
+/** Loader class to read Collada scenes. Collada is over-engineered to death, with every new iteration bringing
+ * more useless stuff, so I limited the data to what I think is useful for games.
+*/
+class ColladaLoader : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ ColladaLoader();
+
+ /** Destructor, private as well */
+ ~ColladaLoader();
+
+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;
+
+protected:
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList( std::set<std::string>& extensions);
+
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+ /** Recursively constructs a scene node for the given parser node and returns it. */
+ aiNode* BuildHierarchy( const ColladaParser& pParser, const Collada::Node* pNode);
+
+ /** Resolve node instances */
+ void ResolveNodeInstances( const ColladaParser& pParser, const Collada::Node* pNode,
+ std::vector<const Collada::Node*>& resolved);
+
+ /** Builds meshes for the given node and references them */
+ void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode,
+ aiNode* pTarget);
+
+ /** 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,
+ const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace);
+
+ /** Builds cameras for the given node and references them */
+ void BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode,
+ aiNode* pTarget);
+
+ /** Builds lights for the given node and references them */
+ void BuildLightsForNode( const ColladaParser& pParser, const Collada::Node* pNode,
+ aiNode* pTarget);
+
+ /** Stores all meshes in the given scene */
+ void StoreSceneMeshes( aiScene* pScene);
+
+ /** Stores all materials in the given scene */
+ void StoreSceneMaterials( aiScene* pScene);
+
+ /** Stores all lights in the given scene */
+ void StoreSceneLights( aiScene* pScene);
+
+ /** Stores all cameras in the given scene */
+ void StoreSceneCameras( aiScene* pScene);
+
+ /** Stores all textures in the given scene */
+ void StoreSceneTextures( aiScene* pScene);
+
+ /** Stores all animations
+ * @param pScene target scene to store the anims
+ */
+ void StoreAnimations( aiScene* pScene, const ColladaParser& pParser);
+
+ /** Stores all animations for the given source anim and its nested child animations
+ * @param pScene target scene to store the anims
+ * @param pSrcAnim the source animation to process
+ * @param pPrefix Prefix to the name in case of nested animations
+ */
+ void StoreAnimations( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string pPrefix);
+
+ /** Constructs the animation for the given source anim */
+ void CreateAnimation( aiScene* pScene, const ColladaParser& pParser, const Collada::Animation* pSrcAnim, const std::string& pName);
+
+ /** Constructs materials from the collada material definitions */
+ void BuildMaterials( const ColladaParser& pParser, aiScene* pScene);
+
+ /** Fill materials from the collada material definitions */
+ void FillMaterials( const ColladaParser& pParser, aiScene* pScene);
+
+ /** Resolve UV channel mappings*/
+ void ApplyVertexToEffectSemanticMapping(Collada::Sampler& sampler,
+ const Collada::SemanticMappingTable& table);
+
+ /** Add a texture and all of its sampling properties to a material*/
+ void AddTexture ( Assimp::MaterialHelper& mat, const ColladaParser& pParser,
+ const Collada::Effect& effect,
+ const Collada::Sampler& sampler,
+ aiTextureType type, unsigned int idx = 0);
+
+ /** Resolves the texture name for the given effect texture entry */
+ 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
+ * @param pIndex The index of the element to retrieve
+ * @param pOffset Offset into the element, for multipart elements such as vectors or matrices
+ * @return the specified value
+ */
+ float ReadFloat( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const;
+
+ /** Reads a string value from an accessor and its data array.
+ * @param pAccessor The accessor to use for reading
+ * @param pData The data array to read from
+ * @param pIndex The index of the element to retrieve
+ * @return the specified value
+ */
+ const std::string& ReadString( const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex) const;
+
+ /** Recursively collects all nodes into the given array */
+ void CollectNodes( const aiNode* pNode, std::vector<const aiNode*>& poNodes) const;
+
+ /** Finds a node in the collada scene by the given name */
+ const Collada::Node* FindNode( const Collada::Node* pNode, const std::string& pName) const;
+ /** Finds a node in the collada scene by the given SID */
+ const Collada::Node* FindNodeBySID( const Collada::Node* pNode, const std::string& pSID) const;
+
+ /** Finds a proper name for a node derived from the collada-node's properties */
+ std::string FindNameForNode( const Collada::Node* pNode) const;
+
+protected:
+ /** Filename, for a verbose error message */
+ std::string mFileName;
+
+ /** Which mesh-material compound was stored under which mesh ID */
+ std::map<ColladaMeshIndex, size_t> mMeshIndexByID;
+
+ /** Which material was stored under which index in the scene */
+ std::map<std::string, size_t> mMaterialIndexByName;
+
+ /** Accumulated meshes for the target scene */
+ std::vector<aiMesh*> mMeshes;
+
+ /** Temporary material list */
+ std::vector<std::pair<Collada::Effect*, aiMaterial*> > newMats;
+
+ /** Temporary camera list */
+ std::vector<aiCamera*> mCameras;
+
+ /** Temporary light list */
+ std::vector<aiLight*> mLights;
+
+ /** Temporary texture list */
+ std::vector<aiTexture*> mTextures;
+
+ /** Accumulated animations for the target scene */
+ std::vector<aiAnimation*> mAnims;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_COLLADALOADER_H_INC
diff --git a/3rdparty/assimp/code/ColladaParser.cpp b/3rdparty/assimp/code/ColladaParser.cpp
new file mode 100644
index 000000000..e2dfffbcb
--- /dev/null
+++ b/3rdparty/assimp/code/ColladaParser.cpp
@@ -0,0 +1,2796 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_DAE_IMPORTER
+
+#include "ColladaParser.h"
+#include "fast_atof.h"
+#include "ParsingUtils.h"
+
+using namespace Assimp;
+using namespace Assimp::Collada;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ColladaParser::ColladaParser( IOSystem* pIOHandler, const std::string& pFile)
+ : mFileName( pFile)
+{
+ mRootNode = NULL;
+ mUnitSize = 1.0f;
+ mUpDirection = UP_Z;
+
+ // We assume the newest file format by default
+ mFormat = FV_1_5_n;
+
+ // open the file
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open file " + pFile + ".");
+
+ // generate a XML reader for it
+ boost::scoped_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
+float 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;
+ DefaultLogger::get()->debug("Collada schema version is 1.5.n");
+ }
+ else if (!::strncmp(version,"1.4",3)) {
+ mFormat = FV_1_4_n;
+ DefaultLogger::get()->debug("Collada schema version is 1.4.n");
+ }
+ else if (!::strncmp(version,"1.3",3)) {
+ mFormat = FV_1_3_n;
+ DefaultLogger::get()->debug("Collada schema version is 1.3.n");
+ }
+ }
+
+ ReadStructure();
+ } else
+ {
+ DefaultLogger::get()->debug( boost::str( boost::format( "Ignoring global element \"%s\".") % 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_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;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads asset informations such as coordinate system informations 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, "Y_UP", 4) == 0)
+ mUpDirection = UP_Y;
+ else
+ mUpDirection = UP_Z;
+
+ // 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 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;
+ 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 = "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())
+ {
+ // 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);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+
+ 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 neccessary 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)
+{
+ 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"))
+ {
+ // should skip everything inside, so there's no danger of catching elements inbetween
+ SkipElement();
+ }
+ 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 bullshit 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_atof_move( 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
+ {
+ // 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)
+ 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( boost::str( boost::format( "Unsupported URL format in \"%s\"") % attrSource));
+ 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( boost::str( boost::format( "Unknown semantic \"%s\" in joint data") % attrSemantic));
+
+ // 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"))
+ {
+ 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( boost::str( boost::format( "Unsupported URL format in \"%s\"") % attrSource));
+ 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( boost::str( boost::format( "Unknown semantic \"%s\" in vertex_weight data") % attrSemantic));
+
+ // skip inner data, if present
+ if ( !mReader->isEmptyElement())
+ SkipElement();
+ }
+ else if ( IsElement( "vcount"))
+ {
+ // 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 = strtol10( text, &text);
+ numWeights += *it;
+ SkipSpacesAndLineEnd( &text);
+ }
+
+ TestClosing( "vcount");
+
+ // reserve weight count
+ pController.mWeights.resize( numWeights);
+ }
+ else if ( IsElement( "v"))
+ {
+ // 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 = strtol10( text, &text);
+ SkipSpacesAndLineEnd( &text);
+ if ( *text == 0)
+ ThrowException( "Out of data while reading vertex_weights");
+ it->second = strtol10( 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) {
+ DefaultLogger::get()->warn("Collada: Ignoring texture array index");
+ continue;
+ }
+
+ attrib = TestAttribute("mip_index");
+ if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
+ DefaultLogger::get()->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)
+ DefaultLogger::get()->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;
+
+ while ( mReader->read())
+ {
+ if ( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+ {
+ if ( IsElement( "material"))
+ {
+ // read ID. By now you propably 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
+ ReadMaterial(mMaterialLibrary[id] = Material());
+ } 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 propably 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 propably 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_atof_move( content, (float&)pLight.mColor.r);
+ SkipSpacesAndLineEnd( &content);
+
+ content = fast_atof_move( content, (float&)pLight.mColor.g);
+ SkipSpacesAndLineEnd( &content);
+
+ content = fast_atof_move( content, (float&)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 ( 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?
+ // Alex: .... no, not necessary. Please shut up and leave more space for
+ // me to complain about the fucking Collada spec with its fucking
+ // '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
+ }
+
+ /* 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")) {
+ 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.mWrapU = ReadBoolFromTextContent();
+ TestClosing( "wrapV");
+ }
+ else if ( IsElement( "mirrorU")) {
+ out.mMirrorU = ReadBoolFromTextContent();
+ TestClosing( "mirrorU");
+ }
+ else if ( IsElement( "mirrorV")) {
+ out.mMirrorU = 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.x = 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 {
+ DefaultLogger::get()->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_atof_move( content, (float&)pColor.r);
+ SkipSpacesAndLineEnd( &content);
+
+ content = fast_atof_move( content, (float&)pColor.g);
+ SkipSpacesAndLineEnd( &content);
+
+ content = fast_atof_move( content, (float&)pColor.b);
+ SkipSpacesAndLineEnd( &content);
+
+ content = fast_atof_move( content, (float&)pColor.a);
+ SkipSpacesAndLineEnd( &content);
+ TestClosing( "color");
+ }
+ else if ( IsElement( "texture"))
+ {
+ // get name of source textur/sampler
+ int attrTex = GetAttribute( "texture");
+ pSampler.mName = mReader->getAttributeValue( attrTex);
+
+ // get name of UV source channel
+ attrTex = GetAttribute( "texcoord");
+ pSampler.mUVChannel = mReader->getAttributeValue( attrTex);
+ //SkipElement();
+ }
+ 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( float& 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_atof_move( 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"))
+ {
+ // 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
+ {
+ // 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 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 rest
+ 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 fucking care for your profiles bullshit
+ }
+ 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");
+
+ // 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();
+ if (content) { // some exporters write empty data arrays, silently skip over them
+
+ // read values and store inside an array in the data library
+ mDataLibrary[id] = Data();
+ Data& data = mDataLibrary[id];
+ data.mIsStringArray = isStringArray;
+
+ 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.");
+
+ float value;
+ // read a number
+ content = fast_atof_move( content, value);
+ data.mValues.push_back( value);
+ // skip whitespace after it
+ SkipSpacesAndLineEnd( &content);
+ }
+ }
+ }
+
+ // test for closing tag
+ 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( boost::str( boost::format( "Unknown reference format in url \"%s\".") % source));
+ 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( boost::str( boost::format( "Unknown accessor parameter \"%s\". Ignoring data channel.") % name));
+ }
+
+ // 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( "Unexpected sub element 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( "Unexpected sub element 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);
+
+ // material subgroup
+ int attrMaterial = TestAttribute( "material");
+ SubMesh subgroup;
+ if ( attrMaterial > -1)
+ subgroup.mMaterial = mReader->getAttributeValue( attrMaterial);
+ subgroup.mNumFaces = numPrimitives;
+ pMesh->mSubMeshes.push_back( subgroup);
+
+ // 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 propably 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())
+ {
+ // 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) strtol10( 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
+ ReadPrimitives( pMesh, perIndexData, numPrimitives, vcount, primType);
+ }
+ } else
+ {
+ ThrowException( "Unexpected sub element in tag \"vertices\".");
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+ {
+ if ( mReader->getNodeName() != elementName)
+ ThrowException( boost::str( boost::format( "Expected end of \"%s\" element.") % elementName));
+
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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( boost::str( boost::format( "Unknown reference format in url \"%s\".") % source));
+ 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( boost::str( boost::format( "Invalid index \"%i\" for set attribute") % (attrSet)));
+
+ 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
+void 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 = 0xffffffff; // invalid value
+ BOOST_FOREACH( 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:
+ {
+ BOOST_FOREACH( 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);
+
+ 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, strtol10s( 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)
+ 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 neccessary
+ 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. I fucking hate Collada.");
+ continue;
+ }
+
+ // find accessor
+ input.mResolved = &ResolveLibraryReference( mAccessorLibrary, input.mAccessor);
+ // resolve accessor's data pointer as well, if neccessary
+ const Accessor* acc = input.mResolved;
+ if ( !acc->mData)
+ acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
+ }
+
+
+ // now assemble vertex data according to those indices
+ std::vector<size_t>::const_iterator idx = indices.begin();
+
+ // 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;
+
+ pMesh->mFaceSize.reserve( numPrimitives);
+ pMesh->mFacePosIndices.reserve( indices.size() / numOffsets);
+
+ for ( size_t a = 0; a < numPrimitives; a++)
+ {
+ // determine number of points for this primitive
+ size_t numPoints = 0;
+ switch( pPrimType)
+ {
+ case Prim_Lines:
+ numPoints = 2;
+ break;
+ case Prim_Triangles:
+ numPoints = 3;
+ break;
+ case Prim_Polylist:
+ numPoints = pVCount[a];
+ break;
+ case Prim_TriFans:
+ case Prim_Polygon:
+ numPoints = indices.size() / numOffsets;
+ break;
+ default:
+ // LineStrip and TriStrip 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);
+
+ // gather that number of vertices
+ for ( size_t b = 0; b < numPoints; b++)
+ {
+ // read all indices for this vertex. Yes, in a hacky local array
+ assert( numOffsets < 20 && perVertexOffset < 20);
+ size_t vindex[20];
+ for ( size_t offsets = 0; offsets < numOffsets; ++offsets)
+ vindex[offsets] = *idx++;
+
+ // 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, vindex[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, vindex[it->mOffset], pMesh);
+
+ // store the vertex-data index for later assignment of bone vertex weights
+ pMesh->mFacePosIndices.push_back( vindex[perVertexOffset]);
+ }
+ }
+
+
+ // if I ever get my hands on that guy who invented this steaming pile of indirection...
+ TestClosing( "p");
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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( boost::str( boost::format( "Invalid data index (%d/%d) in primitive specification") % pLocalIndex % acc.mCount));
+
+ // get a pointer to the start of the data object referred to by the accessor and the local index
+ const float* 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
+ float 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
+ DefaultLogger::get()->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
+ DefaultLogger::get()->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
+ DefaultLogger::get()->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
+ DefaultLogger::get()->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
+ {
+ DefaultLogger::get()->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));
+
+ pMesh->mColors[pInput.mIndex].push_back( aiColor4D( obj[0], obj[1], obj[2], obj[3]));
+ } else
+ {
+ DefaultLogger::get()->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
+ // 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] != '#')
+ DefaultLogger::get()->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] != '#')
+ DefaultLogger::get()->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)
+ DefaultLogger::get()->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)
+ DefaultLogger::get()->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_atof_move( 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")) {
+ DefaultLogger::get()->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 inbetween
+ 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 occurence
+ if ( mRootNode)
+ ThrowException( "Invalid scene containing multiple root nodes");
+
+ // 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");
+
+ // 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) + "\".");
+ mRootNode = sit->second;
+ } else {
+ SkipElement();
+ }
+ }
+ else if ( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+void ColladaParser::ThrowException( const std::string& pError) const
+{
+ throw DeadlyImportError( boost::str( boost::format( "Collada: %s - %s") % mFileName % pError));
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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( boost::str( boost::format( "Unexpected end of file while beginning of \"%s\" element.") % pName));
+ // whitespace in front is ok, just read again if found
+ if ( mReader->getNodeType() == irr::io::EXN_TEXT)
+ if ( !mReader->read())
+ ThrowException( boost::str( boost::format( "Unexpected end of file while reading beginning of \"%s\" element.") % pName));
+
+ if ( mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp( mReader->getNodeName(), pName) != 0)
+ ThrowException( boost::str( boost::format( "Expected start of \"%s\" element.") % pName));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests for the closing tag of the given element, throws an exception if not found
+void ColladaParser::TestClosing( const char* pName)
+{
+ // read closing tag
+ if ( !mReader->read())
+ ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of \"%s\" element.") % pName));
+ // whitespace in front is ok, just read again if found
+ if ( mReader->getNodeType() == irr::io::EXN_TEXT)
+ if ( !mReader->read())
+ ThrowException( boost::str( boost::format( "Unexpected end of file while reading end of \"%s\" element.") % pName));
+
+ if ( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0)
+ ThrowException( boost::str( boost::format( "Expected end of \"%s\" element.") % pName));
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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( boost::str( boost::format( "Expected attribute \"%s\" at element \"%s\".") % pAttr % 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())
+ ThrowException( "Expected opening element");
+
+ // read contents of the element
+ if ( !mReader->read())
+ ThrowException( "Unexpected end of file while reading n element.");
+ if ( mReader->getNodeType() != irr::io::EXN_TEXT)
+ 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;
+ float angle = tf.f[3] * float( AI_MATH_PI) / 180.0f;
+ 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:
+ assert( false);
+ break;
+ }
+ }
+
+ return res;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Determines the input data type for the given semantic string
+Collada::InputType ColladaParser::GetTypeForSemantic( const std::string& pSemantic)
+{
+ if ( pSemantic == "POSITION")
+ return IT_Position;
+ else if ( pSemantic == "TEXCOORD")
+ return IT_Texcoord;
+ else if ( pSemantic == "NORMAL")
+ return IT_Normal;
+ else if ( pSemantic == "COLOR")
+ return IT_Color;
+ else if ( pSemantic == "VERTEX")
+ return IT_Vertex;
+ else if ( pSemantic == "BINORMAL" || pSemantic == "TEXBINORMAL")
+ return IT_Bitangent;
+ else if ( pSemantic == "TANGENT" || pSemantic == "TEXTANGENT")
+ return IT_Tangent;
+
+ DefaultLogger::get()->warn( boost::str( boost::format( "Unknown vertex input type \"%s\". Ignoring.") % pSemantic));
+ return IT_Invalid;
+}
+
+#endif // !! ASSIMP_BUILD_NO_DAE_IMPORTER
diff --git a/3rdparty/assimp/code/ColladaParser.h b/3rdparty/assimp/code/ColladaParser.h
new file mode 100644
index 000000000..cba184748
--- /dev/null
+++ b/3rdparty/assimp/code/ColladaParser.h
@@ -0,0 +1,341 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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.h
+ * @brief Defines the parser helper class for the collada loader
+ */
+
+#ifndef AI_COLLADAPARSER_H_INC
+#define AI_COLLADAPARSER_H_INC
+
+#include "irrXMLWrapper.h"
+#include "ColladaHelper.h"
+
+namespace Assimp
+{
+
+// ------------------------------------------------------------------------------------------
+/** Parser helper class for the Collada loader.
+ *
+ * Does all the XML reading and builds internal data structures from it,
+ * but leaves the resolving of all the references to the loader.
+*/
+class ColladaParser
+{
+ friend class ColladaLoader;
+
+protected:
+ /** Constructor from XML file */
+ ColladaParser( IOSystem* pIOHandler, const std::string& pFile);
+
+ /** Destructor */
+ ~ColladaParser();
+
+ /** Reads the contents of the file */
+ void ReadContents();
+
+ /** Reads the structure of the file */
+ void ReadStructure();
+
+ /** Reads asset informations such as coordinate system informations and legal blah */
+ void ReadAssetInfo();
+
+ /** Reads the animation library */
+ void ReadAnimationLibrary();
+
+ /** Reads an animation into the given parent structure */
+ void ReadAnimation( Collada::Animation* pParent);
+
+ /** Reads an animation sampler into the given anim channel */
+ void ReadAnimationSampler( Collada::AnimationChannel& pChannel);
+
+ /** Reads the skeleton controller library */
+ void ReadControllerLibrary();
+
+ /** Reads a controller into the given mesh structure */
+ void ReadController( Collada::Controller& pController);
+
+ /** Reads the joint definitions for the given controller */
+ void ReadControllerJoints( Collada::Controller& pController);
+
+ /** Reads the joint weights for the given controller */
+ void ReadControllerWeights( Collada::Controller& pController);
+
+ /** Reads the image library contents */
+ void ReadImageLibrary();
+
+ /** Reads an image entry into the given image */
+ void ReadImage( Collada::Image& pImage);
+
+ /** Reads the material library */
+ void ReadMaterialLibrary();
+
+ /** Reads a material entry into the given material */
+ void ReadMaterial( Collada::Material& pMaterial);
+
+ /** Reads the camera library */
+ void ReadCameraLibrary();
+
+ /** Reads a camera entry into the given camera */
+ void ReadCamera( Collada::Camera& pCamera);
+
+ /** Reads the light library */
+ void ReadLightLibrary();
+
+ /** Reads a light entry into the given light */
+ void ReadLight( Collada::Light& pLight);
+
+ /** Reads the effect library */
+ void ReadEffectLibrary();
+
+ /** Reads an effect entry into the given effect*/
+ void ReadEffect( Collada::Effect& pEffect);
+
+ /** Reads an COMMON effect profile */
+ void ReadEffectProfileCommon( Collada::Effect& pEffect);
+
+ /** Read sampler properties */
+ void ReadSamplerProperties( Collada::Sampler& pSampler);
+
+ /** Reads an effect entry containing a color or a texture defining that color */
+ void ReadEffectColor( aiColor4D& pColor, Collada::Sampler& pSampler);
+
+ /** Reads an effect entry containing a float */
+ void ReadEffectFloat( float& pFloat);
+
+ /** Reads an effect parameter specification of any kind */
+ void ReadEffectParam( Collada::EffectParam& pParam);
+
+ /** Reads the geometry library contents */
+ void ReadGeometryLibrary();
+
+ /** Reads a geometry from the geometry library. */
+ void ReadGeometry( Collada::Mesh* pMesh);
+
+ /** Reads a mesh from the geometry library */
+ void ReadMesh( Collada::Mesh* pMesh);
+
+ /** Reads a source element - a combination of raw data and an accessor defining
+ * things that should not be redefinable. Yes, that's another rant.
+ */
+ void ReadSource();
+
+ /** Reads a data array holding a number of elements, and stores it in the global library.
+ * Currently supported are array of floats and arrays of strings.
+ */
+ void ReadDataArray();
+
+ /** Reads an accessor and stores it in the global library under the given ID -
+ * accessors use the ID of the parent <source> element
+ */
+ void ReadAccessor( const std::string& pID);
+
+ /** Reads input declarations of per-vertex mesh data into the given mesh */
+ void ReadVertexData( Collada::Mesh* pMesh);
+
+ /** Reads input declarations of per-index mesh data into the given mesh */
+ void ReadIndexData( Collada::Mesh* pMesh);
+
+ /** Reads a single input channel element and stores it in the given array, if valid */
+ void ReadInputChannel( std::vector<Collada::InputChannel>& poChannels);
+
+ /** Reads a <p> primitive index list and assembles the mesh data into the given mesh */
+ void ReadPrimitives( Collada::Mesh* pMesh, std::vector<Collada::InputChannel>& pPerIndexChannels,
+ size_t pNumPrimitives, const std::vector<size_t>& pVCount, Collada::PrimitiveType pPrimType);
+
+ /** Extracts a single object from an input channel and stores it in the appropriate mesh data array */
+ void ExtractDataObjectFromChannel( const Collada::InputChannel& pInput, size_t pLocalIndex, Collada::Mesh* pMesh);
+
+ /** Reads the library of node hierarchies and scene parts */
+ void ReadSceneLibrary();
+
+ /** Reads a scene node's contents including children and stores it in the given node */
+ void ReadSceneNode( Collada::Node* pNode);
+
+ /** Reads a node transformation entry of the given type and adds it to the given node's transformation list. */
+ void ReadNodeTransformation( Collada::Node* pNode, Collada::TransformType pType);
+
+ /** Reads a mesh reference in a node and adds it to the node's mesh list */
+ void ReadNodeGeometry( Collada::Node* pNode);
+
+ /** Reads the collada scene */
+ void ReadScene();
+
+ // Processes bind_vertex_input and bind elements
+ void ReadMaterialVertexInputBinding( Collada::SemanticMappingTable& tbl);
+
+protected:
+ /** Aborts the file reading with an exception */
+ void ThrowException( const std::string& pError) const;
+
+ /** Skips all data until the end node of the current element */
+ void SkipElement();
+
+ /** Skips all data until the end node of the given element */
+ void SkipElement( const char* pElement);
+
+ /** Compares the current xml element name to the given string and returns true if equal */
+ bool IsElement( const char* pName) const;
+
+ /** Tests for the opening tag of the given element, throws an exception if not found */
+ void TestOpening( const char* pName);
+
+ /** Tests for the closing tag of the given element, throws an exception if not found */
+ void TestClosing( const char* pName);
+
+ /** Checks the present element for the presence of the attribute, returns its index
+ or throws an exception if not found */
+ int GetAttribute( const char* pAttr) const;
+
+ /** Returns the index of the named attribute or -1 if not found. Does not throw,
+ therefore useful for optional attributes */
+ int TestAttribute( const char* pAttr) const;
+
+ /** Reads the text contents of an element, throws an exception if not given.
+ Skips leading whitespace. */
+ const char* GetTextContent();
+
+ /** Reads the text contents of an element, returns NULL if not given.
+ Skips leading whitespace. */
+ const char* TestTextContent();
+
+ /** Reads a single bool from current text content */
+ bool ReadBoolFromTextContent();
+
+ /** Reads a single float from current text content */
+ float ReadFloatFromTextContent();
+
+ /** Calculates the resulting transformation from all the given transform steps */
+ aiMatrix4x4 CalculateResultTransform( const std::vector<Collada::Transform>& pTransforms) const;
+
+ /** Determines the input data type for the given semantic string */
+ Collada::InputType GetTypeForSemantic( const std::string& pSemantic);
+
+ /** Finds the item in the given library by its reference, throws if not found */
+ template <typename Type> const Type& ResolveLibraryReference(
+ const std::map<std::string, Type>& pLibrary, const std::string& pURL) const;
+
+protected:
+ /** Filename, for a verbose error message */
+ std::string mFileName;
+
+ /** XML reader, member for everyday use */
+ irr::io::IrrXMLReader* mReader;
+
+ /** All data arrays found in the file by ID. Might be referred to by actually
+ everyone. Collada, you are a steaming pile of indirection. */
+ typedef std::map<std::string, Collada::Data> DataLibrary;
+ DataLibrary mDataLibrary;
+
+ /** Same for accessors which define how the data in a data array is accessed. */
+ typedef std::map<std::string, Collada::Accessor> AccessorLibrary;
+ AccessorLibrary mAccessorLibrary;
+
+ /** Mesh library: mesh by ID */
+ typedef std::map<std::string, Collada::Mesh*> MeshLibrary;
+ MeshLibrary mMeshLibrary;
+
+ /** node library: root node of the hierarchy part by ID */
+ typedef std::map<std::string, Collada::Node*> NodeLibrary;
+ NodeLibrary mNodeLibrary;
+
+ /** Image library: stores texture properties by ID */
+ typedef std::map<std::string, Collada::Image> ImageLibrary;
+ ImageLibrary mImageLibrary;
+
+ /** Effect library: surface attributes by ID */
+ typedef std::map<std::string, Collada::Effect> EffectLibrary;
+ EffectLibrary mEffectLibrary;
+
+ /** Material library: surface material by ID */
+ typedef std::map<std::string, Collada::Material> MaterialLibrary;
+ MaterialLibrary mMaterialLibrary;
+
+ /** Light library: surface light by ID */
+ typedef std::map<std::string, Collada::Light> LightLibrary;
+ LightLibrary mLightLibrary;
+
+ /** Camera library: surface material by ID */
+ typedef std::map<std::string, Collada::Camera> CameraLibrary;
+ CameraLibrary mCameraLibrary;
+
+ /** Controller library: joint controllers by ID */
+ typedef std::map<std::string, Collada::Controller> ControllerLibrary;
+ ControllerLibrary mControllerLibrary;
+
+ /** Pointer to the root node. Don't delete, it just points to one of
+ the nodes in the node library. */
+ Collada::Node* mRootNode;
+
+ /** Root animation container */
+ Collada::Animation mAnims;
+
+ /** Size unit: how large compared to a meter */
+ float mUnitSize;
+
+ /** Which is the up vector */
+ enum { UP_X, UP_Y, UP_Z } mUpDirection;
+
+ /** Collada file format version */
+ Collada::FormatVersion mFormat;
+};
+
+// ------------------------------------------------------------------------------------------------
+// Check for element match
+inline bool ColladaParser::IsElement( const char* pName) const
+{
+ ai_assert( mReader->getNodeType() == irr::io::EXN_ELEMENT);
+ return ::strcmp( mReader->getNodeName(), pName) == 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Finds the item in the given library by its reference, throws if not found
+template <typename Type>
+const Type& ColladaParser::ResolveLibraryReference( const std::map<std::string, Type>& pLibrary, const std::string& pURL) const
+{
+ typename std::map<std::string, Type>::const_iterator it = pLibrary.find( pURL);
+ if ( it == pLibrary.end())
+ ThrowException( boost::str( boost::format( "Unable to resolve library reference \"%s\".") % pURL));
+ return it->second;
+}
+
+} // end of namespace Assimp
+
+#endif // AI_COLLADAPARSER_H_INC
diff --git a/3rdparty/assimp/code/ComputeUVMappingProcess.cpp b/3rdparty/assimp/code/ComputeUVMappingProcess.cpp
new file mode 100644
index 000000000..8fdbf69b1
--- /dev/null
+++ b/3rdparty/assimp/code/ComputeUVMappingProcess.cpp
@@ -0,0 +1,504 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 GenUVCoords step */
+
+
+#include "AssimpPCH.h"
+#include "ComputeUVMappingProcess.h"
+#include "ProcessHelper.h"
+
+using namespace Assimp;
+
+namespace {
+
+ const static aiVector3D base_axis_y(0.f,1.f,0.f);
+ const static aiVector3D base_axis_x(1.f,0.f,0.f);
+ const static aiVector3D base_axis_z(0.f,0.f,1.f);
+ const static float angle_epsilon = 0.95f;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ComputeUVMappingProcess::ComputeUVMappingProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ComputeUVMappingProcess::~ComputeUVMappingProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool ComputeUVMappingProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_GenUVCoords) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether a ray intersects a plane and find the intersection point
+inline bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos,
+ const aiVector3D& planeNormal, aiVector3D& pos)
+{
+ const float b = planeNormal * (planePos - ray.pos);
+ float h = ray.dir * planeNormal;
+ if ((h < 10e-5f && h > -10e-5f) || (h = b/h) < 0)
+ return false;
+
+ pos = ray.pos + (ray.dir * h);
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Find the first empty UV channel in a mesh
+inline unsigned int FindEmptyUVChannel (aiMesh* mesh)
+{
+ for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS;++m)
+ if (!mesh->mTextureCoords[m])return m;
+
+ DefaultLogger::get()->error("Unable to compute UV coordinates, no free UV slot found");
+ return 0xffffffff;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to remove UV seams
+void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
+{
+ // TODO: just a very rough algorithm. I think it could be done
+ // much easier, but I don't know how and am currently too tired to
+ // to think about a better solution.
+
+ const static float LOWER_LIMIT = 0.1f;
+ const static float UPPER_LIMIT = 0.9f;
+
+ const static float LOWER_EPSILON = 10e-3f;
+ const static float UPPER_EPSILON = 1.f-10e-3f;
+
+ for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx)
+ {
+ const aiFace& face = mesh->mFaces[fidx];
+ if (face.mNumIndices < 3) continue; // triangles and polygons only, please
+
+ unsigned int small = face.mNumIndices, large = small;
+ bool zero = false, one = false, round_to_zero = false;
+
+ // Check whether this face lies on a UV seam. We can just guess,
+ // but the assumption that a face with at least one very small
+ // on the one side and one very large U coord on the other side
+ // lies on a UV seam should work for most cases.
+ for (unsigned int n = 0; n < face.mNumIndices;++n)
+ {
+ if (out[face.mIndices[n]].x < LOWER_LIMIT)
+ {
+ small = n;
+
+ // If we have a U value very close to 0 we can't
+ // round the others to 0, too.
+ if (out[face.mIndices[n]].x <= LOWER_EPSILON)
+ zero = true;
+ else round_to_zero = true;
+ }
+ if (out[face.mIndices[n]].x > UPPER_LIMIT)
+ {
+ large = n;
+
+ // If we have a U value very close to 1 we can't
+ // round the others to 1, too.
+ if (out[face.mIndices[n]].x >= UPPER_EPSILON)
+ one = true;
+ }
+ }
+ if (small != face.mNumIndices && large != face.mNumIndices)
+ {
+ for (unsigned int n = 0; n < face.mNumIndices;++n)
+ {
+ // If the u value is over the upper limit and no other u
+ // value of that face is 0, round it to 0
+ if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero)
+ out[face.mIndices[n]].x = 0.f;
+
+ // If the u value is below the lower limit and no other u
+ // value of that face is 1, round it to 1
+ else if (out[face.mIndices[n]].x < LOWER_LIMIT && !one)
+ out[face.mIndices[n]].x = 1.f;
+
+ // The face contains both 0 and 1 as UV coords. This can occur
+ // for faces which have an edge that lies directly on the seam.
+ // Due to numerical inaccuracies one U coord becomes 0, the
+ // other 1. But we do still have a third UV coord to determine
+ // to which side we must round to.
+ else if (one && zero)
+ {
+ if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON)
+ out[face.mIndices[n]].x = 0.f;
+ else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON)
+ out[face.mIndices[n]].x = 1.f;
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
+{
+ aiVector3D center, min, max;
+ FindMeshCenter(mesh, center, min, max);
+
+ // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
+ // currently the mapping axis will always be one of x,y,z, except if the
+ // PretransformVertices step is used (it transforms the meshes into worldspace,
+ // thus changing the mapping axis)
+ if (axis * base_axis_x >= angle_epsilon) {
+
+ // For each point get a normalized projection vector in the sphere,
+ // get its longitude and latitude and map them to their respective
+ // UV axes. Problems occur around the poles ... unsolvable.
+ //
+ // The spherical coordinate system looks like this:
+ // x = cos(lon)*cos(lat)
+ // y = sin(lon)*cos(lat)
+ // z = sin(lat)
+ //
+ // Thus we can derive:
+ // lat = arcsin (z)
+ // lon = arctan (y/x)
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
+ out[pnt] = aiVector3D((atan2 (diff.z, diff.y) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (asin (diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.f);
+ }
+ }
+ else if (axis * base_axis_y >= angle_epsilon) {
+ // ... just the same again
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
+ out[pnt] = aiVector3D((atan2 (diff.x, diff.z) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (asin (diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.f);
+ }
+ }
+ else if (axis * base_axis_z >= angle_epsilon) {
+ // ... just the same again
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D diff = (mesh->mVertices[pnt]-center).Normalize();
+ out[pnt] = aiVector3D((atan2 (diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (asin (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.f);
+ }
+ }
+ // slower code path in case the mapping axis is not one of the coordinate system axes
+ else {
+ aiMatrix4x4 mTrafo;
+ aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
+
+ // again the same, except we're applying a transformation now
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D diff = ((mTrafo*mesh->mVertices[pnt])-center).Normalize();
+ out[pnt] = aiVector3D((atan2 (diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F,
+ (asin (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.f);
+ }
+ }
+
+
+ // Now find and remove UV seams. A seam occurs if a face has a tcoord
+ // close to zero on the one side, and a tcoord close to one on the
+ // other side.
+ RemoveUVSeams(mesh,out);
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
+{
+ aiVector3D center, min, max;
+
+ // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
+ // currently the mapping axis will always be one of x,y,z, except if the
+ // PretransformVertices step is used (it transforms the meshes into worldspace,
+ // thus changing the mapping axis)
+ if (axis * base_axis_x >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ const float diff = max.x - min.x;
+
+ // If the main axis is 'z', the z coordinate of a point 'p' is mapped
+ // directly to the texture V axis. The other axis is derived from
+ // the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where
+ // 'c' is the center point of the mesh.
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ aiVector3D& uv = out[pnt];
+
+ uv.y = (pos.x - min.x) / diff;
+ uv.x = (atan2 ( pos.z - center.z, pos.y - center.y) +(float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI;
+ }
+ }
+ else if (axis * base_axis_y >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ const float diff = max.y - min.y;
+
+ // just the same ...
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ aiVector3D& uv = out[pnt];
+
+ uv.y = (pos.y - min.y) / diff;
+ uv.x = (atan2 ( pos.x - center.x, pos.z - center.z) +(float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI;
+ }
+ }
+ else if (axis * base_axis_z >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ const float diff = max.z - min.z;
+
+ // just the same ...
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ aiVector3D& uv = out[pnt];
+
+ uv.y = (pos.z - min.z) / diff;
+ uv.x = (atan2 ( pos.y - center.y, pos.x - center.x) +(float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI;
+ }
+ }
+ // slower code path in case the mapping axis is not one of the coordinate system axes
+ else {
+ aiMatrix4x4 mTrafo;
+ aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
+ FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
+ const float diff = max.y - min.y;
+
+ // again the same, except we're applying a transformation now
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt){
+ const aiVector3D pos = mTrafo* mesh->mVertices[pnt];
+ aiVector3D& uv = out[pnt];
+
+ uv.y = (pos.y - min.y) / diff;
+ uv.x = (atan2 ( pos.x - center.x, pos.z - center.z) +(float)AI_MATH_PI ) / (float)AI_MATH_TWO_PI;
+ }
+ }
+
+ // Now find and remove UV seams. A seam occurs if a face has a tcoord
+ // close to zero on the one side, and a tcoord close to one on the
+ // other side.
+ RemoveUVSeams(mesh,out);
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out)
+{
+ float diffu,diffv;
+ aiVector3D center, min, max;
+
+ // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
+ // currently the mapping axis will always be one of x,y,z, except if the
+ // PretransformVertices step is used (it transforms the meshes into worldspace,
+ // thus changing the mapping axis)
+ if (axis * base_axis_x >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ diffu = max.z - min.z;
+ diffv = max.y - min.y;
+
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ out[pnt].Set((pos.z - min.z) / diffu,(pos.y - min.y) / diffv);
+ }
+ }
+ else if (axis * base_axis_y >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ diffu = max.x - min.x;
+ diffv = max.z - min.z;
+
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv);
+ }
+ }
+ else if (axis * base_axis_z >= angle_epsilon) {
+ FindMeshCenter(mesh, center, min, max);
+ diffu = max.y - min.y;
+ diffv = max.z - min.z;
+
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D& pos = mesh->mVertices[pnt];
+ out[pnt].Set((pos.y - min.y) / diffu,(pos.x - min.x) / diffv);
+ }
+ }
+ // slower code path in case the mapping axis is not one of the coordinate system axes
+ else
+ {
+ aiMatrix4x4 mTrafo;
+ aiMatrix4x4::FromToMatrix(axis,base_axis_y,mTrafo);
+ FindMeshCenterTransformed(mesh, center, min, max,mTrafo);
+ diffu = max.x - min.x;
+ diffv = max.z - min.z;
+
+ // again the same, except we're applying a transformation now
+ for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) {
+ const aiVector3D pos = mTrafo * mesh->mVertices[pnt];
+ out[pnt].Set((pos.x - min.x) / diffu,(pos.z - min.z) / diffv);
+ }
+ }
+
+ // shouldn't be necessary to remove UV seams ...
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::ComputeBoxMapping(aiMesh* /*mesh*/, aiVector3D* /*out*/)
+{
+ DefaultLogger::get()->error("Mapping type currently not implemented");
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("GenUVCoordsProcess begin");
+ char buffer[1024];
+
+ if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT)
+ throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
+
+ std::list<MappingInfo> mappingStack;
+
+ /* Iterate through all materials and search for non-UV mapped textures
+ */
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ {
+ mappingStack.clear();
+ aiMaterial* mat = pScene->mMaterials[i];
+ for (unsigned int a = 0; a < mat->mNumProperties;++a)
+ {
+ aiMaterialProperty* prop = mat->mProperties[a];
+ if (!::strcmp( prop->mKey.data, "$tex.mapping"))
+ {
+ aiTextureMapping& mapping = *((aiTextureMapping*)prop->mData);
+ if (aiTextureMapping_UV != mapping)
+ {
+ if (!DefaultLogger::isNullLogger())
+ {
+ sprintf(buffer, "Found non-UV mapped texture (%s,%i). Mapping type: %s",
+ TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex,
+ MappingTypeToString(mapping));
+
+ DefaultLogger::get()->info(buffer);
+ }
+
+ if (aiTextureMapping_OTHER == mapping)
+ continue;
+
+ MappingInfo info (mapping);
+
+ // Get further properties - currently only the major axis
+ for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2)
+ {
+ aiMaterialProperty* prop2 = mat->mProperties[a2];
+ if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex)
+ continue;
+
+ if ( !::strcmp( prop2->mKey.data, "$tex.mapaxis")) {
+ info.axis = *((aiVector3D*)prop2->mData);
+ break;
+ }
+ }
+
+ unsigned int idx;
+
+ // Check whether we have this mapping mode already
+ std::list<MappingInfo>::iterator it = std::find (mappingStack.begin(),mappingStack.end(), info);
+ if (mappingStack.end() != it)
+ {
+ idx = (*it).uv;
+ }
+ else
+ {
+ /* We have found a non-UV mapped texture. Now
+ * we need to find all meshes using this material
+ * that we can compute UV channels for them.
+ */
+ for (unsigned int m = 0; m < pScene->mNumMeshes;++m)
+ {
+ aiMesh* mesh = pScene->mMeshes[m];
+ unsigned int outIdx;
+ if ( mesh->mMaterialIndex != i || ( outIdx = FindEmptyUVChannel(mesh) ) == 0xffffffff ||
+ !mesh->mNumVertices)
+ {
+ continue;
+ }
+
+ // Allocate output storage
+ aiVector3D* p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices];
+
+ switch (mapping)
+ {
+ case aiTextureMapping_SPHERE:
+ ComputeSphereMapping(mesh,info.axis,p);
+ break;
+ case aiTextureMapping_CYLINDER:
+ ComputeCylinderMapping(mesh,info.axis,p);
+ break;
+ case aiTextureMapping_PLANE:
+ ComputePlaneMapping(mesh,info.axis,p);
+ break;
+ case aiTextureMapping_BOX:
+ ComputeBoxMapping(mesh,p);
+ break;
+ default:
+ ai_assert(false);
+ }
+ if (m && idx != outIdx)
+ {
+ DefaultLogger::get()->warn("UV index mismatch. Not all meshes assigned to "
+ "this material have equal numbers of UV channels. The UV index stored in "
+ "the material structure does therefore not apply for all meshes. ");
+ }
+ idx = outIdx;
+ }
+ info.uv = idx;
+ mappingStack.push_back(info);
+ }
+
+ // Update the material property list
+ mapping = aiTextureMapping_UV;
+ ((MaterialHelper*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex));
+ }
+ }
+ }
+ }
+ DefaultLogger::get()->debug("GenUVCoordsProcess finished");
+}
diff --git a/3rdparty/assimp/code/ComputeUVMappingProcess.h b/3rdparty/assimp/code/ComputeUVMappingProcess.h
new file mode 100644
index 000000000..795cc9fa5
--- /dev/null
+++ b/3rdparty/assimp/code/ComputeUVMappingProcess.h
@@ -0,0 +1,149 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to compute UV coordinates
+ from abstract mappings, such as box or spherical*/
+#ifndef AI_COMPUTEUVMAPPING_H_INC
+#define AI_COMPUTEUVMAPPING_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+
+class ComputeUVMappingTest;
+namespace Assimp
+ {
+
+// ---------------------------------------------------------------------------
+/** ComputeUVMappingProcess - converts special mappings, such as spherical,
+ * cylindrical or boxed to proper UV coordinates for rendering.
+*/
+class ASSIMP_API ComputeUVMappingProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::ComputeUVMappingTest; // grant the unit test full access to us
+
+protected:
+ /** Constructor to be privately used by Importer */
+ ComputeUVMappingProcess();
+
+ /** Destructor, private as well */
+ ~ComputeUVMappingProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Computes spherical UV coordinates for a mesh
+ *
+ * @param mesh Mesh to be processed
+ * @param axis Main axis
+ * @param out Receives output UV coordinates
+ */
+ void ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis,
+ aiVector3D* out);
+
+ // -------------------------------------------------------------------
+ /** Computes cylindrical UV coordinates for a mesh
+ *
+ * @param mesh Mesh to be processed
+ * @param axis Main axis
+ * @param out Receives output UV coordinates
+ */
+ void ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis,
+ aiVector3D* out);
+
+ // -------------------------------------------------------------------
+ /** Computes planar UV coordinates for a mesh
+ *
+ * @param mesh Mesh to be processed
+ * @param axis Main axis
+ * @param out Receives output UV coordinates
+ */
+ void ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis,
+ aiVector3D* out);
+
+ // -------------------------------------------------------------------
+ /** Computes cubic UV coordinates for a mesh
+ *
+ * @param mesh Mesh to be processed
+ * @param out Receives output UV coordinates
+ */
+ void ComputeBoxMapping(aiMesh* mesh, aiVector3D* out);
+
+private:
+
+ // temporary structure to describe a mapping
+ struct MappingInfo
+ {
+ MappingInfo(aiTextureMapping _type)
+ : type (_type)
+ , axis (0.f,1.f,0.f)
+ , uv (0u)
+ {}
+
+ aiTextureMapping type;
+ aiVector3D axis;
+ unsigned int uv;
+
+ bool operator== (const MappingInfo& other)
+ {
+ return type == other.type && axis == other.axis;
+ }
+ };
+};
+
+} // end of namespace Assimp
+
+#endif // AI_COMPUTEUVMAPPING_H_INC
diff --git a/3rdparty/assimp/code/ConvertToLHProcess.cpp b/3rdparty/assimp/code/ConvertToLHProcess.cpp
new file mode 100644
index 000000000..d598ffd18
--- /dev/null
+++ b/3rdparty/assimp/code/ConvertToLHProcess.cpp
@@ -0,0 +1,318 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MakeLeftHandedProcess.cpp
+ * @brief Implementation of the post processing step to convert all
+ * imported data to a left-handed coordinate system.
+ *
+ * Face order & UV flip are also implemented here, for the sake of a
+ * better location.
+ */
+
+#include "AssimpPCH.h"
+#include "ConvertToLHProcess.h"
+
+using namespace Assimp;
+
+#ifndef ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MakeLeftHandedProcess::MakeLeftHandedProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MakeLeftHandedProcess::~MakeLeftHandedProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool MakeLeftHandedProcess::IsActive( unsigned int pFlags) const
+{
+ return 0 != (pFlags & aiProcess_MakeLeftHanded);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void MakeLeftHandedProcess::Execute( aiScene* pScene)
+{
+ // Check for an existent root node to proceed
+ ai_assert(pScene->mRootNode != NULL);
+ DefaultLogger::get()->debug("MakeLeftHandedProcess begin");
+
+ // recursively convert all the nodes
+ ProcessNode( pScene->mRootNode, aiMatrix4x4());
+
+ // process the meshes accordingly
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; ++a)
+ ProcessMesh( pScene->mMeshes[a]);
+
+ // process the materials accordingly
+ for ( unsigned int a = 0; a < pScene->mNumMaterials; ++a)
+ ProcessMaterial( pScene->mMaterials[a]);
+
+ // transform all animation channels as well
+ for ( unsigned int a = 0; a < pScene->mNumAnimations; a++)
+ {
+ aiAnimation* anim = pScene->mAnimations[a];
+ for ( unsigned int b = 0; b < anim->mNumChannels; b++)
+ {
+ aiNodeAnim* nodeAnim = anim->mChannels[b];
+ ProcessAnimation( nodeAnim);
+ }
+ }
+ DefaultLogger::get()->debug("MakeLeftHandedProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively converts a node, all of its children and all of its meshes
+void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation)
+{
+ // mirror all base vectors at the local Z axis
+ pNode->mTransformation.c1 = -pNode->mTransformation.c1;
+ pNode->mTransformation.c2 = -pNode->mTransformation.c2;
+ pNode->mTransformation.c3 = -pNode->mTransformation.c3;
+ pNode->mTransformation.c4 = -pNode->mTransformation.c4;
+
+ // now invert the Z axis again to keep the matrix determinant positive.
+ // The local meshes will be inverted accordingly so that the result should look just fine again.
+ pNode->mTransformation.a3 = -pNode->mTransformation.a3;
+ pNode->mTransformation.b3 = -pNode->mTransformation.b3;
+ pNode->mTransformation.c3 = -pNode->mTransformation.c3;
+ pNode->mTransformation.d3 = -pNode->mTransformation.d3; // useless, but anyways...
+
+ // continue for all children
+ for ( size_t a = 0; a < pNode->mNumChildren; ++a)
+ ProcessNode( pNode->mChildren[a], pParentGlobalRotation * pNode->mTransformation);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single mesh to left handed coordinates.
+void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh)
+{
+ // mirror positions, normals and stuff along the Z axis
+ for ( size_t a = 0; a < pMesh->mNumVertices; ++a)
+ {
+ pMesh->mVertices[a].z *= -1.0f;
+ if ( pMesh->HasNormals())
+ pMesh->mNormals[a].z *= -1.0f;
+ if ( pMesh->HasTangentsAndBitangents())
+ {
+ pMesh->mTangents[a].z *= -1.0f;
+ pMesh->mBitangents[a].z *= -1.0f;
+ }
+ }
+
+ // mirror offset matrices of all bones
+ for ( size_t a = 0; a < pMesh->mNumBones; ++a)
+ {
+ aiBone* bone = pMesh->mBones[a];
+ bone->mOffsetMatrix.a3 = -bone->mOffsetMatrix.a3;
+ bone->mOffsetMatrix.b3 = -bone->mOffsetMatrix.b3;
+ bone->mOffsetMatrix.d3 = -bone->mOffsetMatrix.d3;
+ bone->mOffsetMatrix.c1 = -bone->mOffsetMatrix.c1;
+ bone->mOffsetMatrix.c2 = -bone->mOffsetMatrix.c2;
+ bone->mOffsetMatrix.c4 = -bone->mOffsetMatrix.c4;
+ }
+
+ // mirror bitangents as well as they're derived from the texture coords
+ if ( pMesh->HasTangentsAndBitangents())
+ {
+ for ( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+ pMesh->mBitangents[a] *= -1.0f;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single material to left handed coordinates.
+void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat)
+{
+ MaterialHelper* mat = (MaterialHelper*)_mat;
+ for (unsigned int a = 0; a < mat->mNumProperties;++a) {
+ aiMaterialProperty* prop = mat->mProperties[a];
+
+ // Mapping axis for UV mappings?
+ if (!::strcmp( prop->mKey.data, "$tex.mapaxis")) {
+ ai_assert( prop->mDataLength >= sizeof(aiVector3D)); /* something is wrong with the validation if we end up here */
+ aiVector3D* pff = (aiVector3D*)prop->mData;
+
+ pff->z *= -1.f;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts the given animation to LH coordinates.
+void MakeLeftHandedProcess::ProcessAnimation( aiNodeAnim* pAnim)
+{
+ // position keys
+ for ( unsigned int a = 0; a < pAnim->mNumPositionKeys; a++)
+ pAnim->mPositionKeys[a].mValue.z *= -1.0f;
+
+ // rotation keys
+ for ( unsigned int a = 0; a < pAnim->mNumRotationKeys; a++)
+ {
+ /* That's the safe version, but the float errors add up. So we try the short version instead
+ aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix();
+ rotmat.a3 = -rotmat.a3; rotmat.b3 = -rotmat.b3;
+ rotmat.c1 = -rotmat.c1; rotmat.c2 = -rotmat.c2;
+ aiQuaternion rotquat( rotmat);
+ pAnim->mRotationKeys[a].mValue = rotquat;
+ */
+ pAnim->mRotationKeys[a].mValue.x *= -1.0f;
+ pAnim->mRotationKeys[a].mValue.y *= -1.0f;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS
+#ifndef ASSIMP_BUILD_NO_FLIPUVS_PROCESS
+// # FlipUVsProcess
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FlipUVsProcess::FlipUVsProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FlipUVsProcess::~FlipUVsProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FlipUVsProcess::IsActive( unsigned int pFlags) const
+{
+ return 0 != (pFlags & aiProcess_FlipUVs);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FlipUVsProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("FlipUVsProcess begin");
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ ProcessMesh(pScene->mMeshes[i]);
+
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ ProcessMaterial(pScene->mMaterials[i]);
+ DefaultLogger::get()->debug("FlipUVsProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single material
+void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat)
+{
+ MaterialHelper* mat = (MaterialHelper*)_mat;
+ for (unsigned int a = 0; a < mat->mNumProperties;++a) {
+ aiMaterialProperty* prop = mat->mProperties[a];
+
+ // UV transformation key?
+ if (!::strcmp( prop->mKey.data, "$tex.uvtrafo")) {
+ ai_assert( prop->mDataLength >= sizeof(aiUVTransform)); /* something is wrong with the validation if we end up here */
+ aiUVTransform* uv = (aiUVTransform*)prop->mData;
+
+ // just flip it, that's everything
+ uv->mTranslation.y *= -1.f;
+ uv->mRotation *= -1.f;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single mesh
+void FlipUVsProcess::ProcessMesh( aiMesh* pMesh)
+{
+ // mirror texture y coordinate
+ for ( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) {
+ if ( !pMesh->HasTextureCoords( a))
+ break;
+
+ for ( unsigned int b = 0; b < pMesh->mNumVertices; b++)
+ pMesh->mTextureCoords[a][b].y = 1.0f - pMesh->mTextureCoords[a][b].y;
+ }
+}
+
+#endif // !ASSIMP_BUILD_NO_FLIPUVS_PROCESS
+#ifndef ASSIMP_BUILD_NO_FLIPWINDING_PROCESS
+// # FlipWindingOrderProcess
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FlipWindingOrderProcess::FlipWindingOrderProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FlipWindingOrderProcess::~FlipWindingOrderProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FlipWindingOrderProcess::IsActive( unsigned int pFlags) const
+{
+ return 0 != (pFlags & aiProcess_FlipWindingOrder);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FlipWindingOrderProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("FlipWindingOrderProcess begin");
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ ProcessMesh(pScene->mMeshes[i]);
+ DefaultLogger::get()->debug("FlipWindingOrderProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts a single mesh
+void FlipWindingOrderProcess::ProcessMesh( aiMesh* pMesh)
+{
+ // invert the order of all faces in this mesh
+ for ( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+ {
+ aiFace& face = pMesh->mFaces[a];
+ for ( unsigned int b = 0; b < face.mNumIndices / 2; b++)
+ std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]);
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_FLIPWINDING_PROCESS
diff --git a/3rdparty/assimp/code/ConvertToLHProcess.h b/3rdparty/assimp/code/ConvertToLHProcess.h
new file mode 100644
index 000000000..f953c39a6
--- /dev/null
+++ b/3rdparty/assimp/code/ConvertToLHProcess.h
@@ -0,0 +1,169 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MakeLeftHandedProcess.h
+ * @brief Defines a bunch of post-processing steps to handle
+ * coordinate system conversions.
+ *
+ * - LH to RH
+ * - UV origin upper-left to lower-left
+ * - face order cw to ccw
+ */
+#ifndef AI_CONVERTTOLHPROCESS_H_INC
+#define AI_CONVERTTOLHPROCESS_H_INC
+
+#include "../include/aiTypes.h"
+#include "BaseProcess.h"
+
+struct aiMesh;
+struct aiNodeAnim;
+
+namespace Assimp {
+
+// -----------------------------------------------------------------------------------
+/** @brief The MakeLeftHandedProcess converts all imported data to a left-handed
+ * coordinate system.
+ *
+ * This implies a mirroring of the Z axis of the coordinate system. But to keep
+ * transformation matrices free from reflections we shift the reflection to other
+ * places. We mirror the meshes and adapt the rotations.
+ *
+ * @note RH-LH and LH-RH is the same, so this class can be used for both
+ */
+class ASSIMP_API MakeLeftHandedProcess : public BaseProcess
+{
+ friend class Importer;
+
+public:
+ /** Constructor to be privately used by Importer */
+ MakeLeftHandedProcess();
+
+ /** Destructor, private as well */
+ ~MakeLeftHandedProcess();
+
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Recursively converts a node and all of its children
+ */
+ void ProcessNode( aiNode* pNode, const aiMatrix4x4& pParentGlobalRotation);
+
+ // -------------------------------------------------------------------
+ /** Converts a single mesh to left handed coordinates.
+ * This means that positions, normals and tangents are mirrored at
+ * the local Z axis and the order of all faces are inverted.
+ * @param pMesh The mesh to convert.
+ */
+ void ProcessMesh( aiMesh* pMesh);
+
+ // -------------------------------------------------------------------
+ /** Converts a single material to left-handed coordinates
+ * @param pMat Material to convert
+ */
+ void ProcessMaterial( aiMaterial* pMat);
+
+ // -------------------------------------------------------------------
+ /** Converts the given animation to LH coordinates.
+ * The rotation and translation keys are transformed, the scale keys
+ * work in local space and can therefore be left untouched.
+ * @param pAnim The bone animation to transform
+ */
+ void ProcessAnimation( aiNodeAnim* pAnim);
+};
+
+
+// ---------------------------------------------------------------------------
+/** Postprocessing step to flip the face order of the imported data
+ */
+class ASSIMP_API FlipWindingOrderProcess : public BaseProcess
+{
+ friend class Importer;
+
+public:
+ /** Constructor to be privately used by Importer */
+ FlipWindingOrderProcess();
+
+ /** Destructor, private as well */
+ ~FlipWindingOrderProcess();
+
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+protected:
+ void ProcessMesh( aiMesh* pMesh);
+};
+
+// ---------------------------------------------------------------------------
+/** Postprocessing step to flip the UV coordinate system of the import data
+ */
+class ASSIMP_API FlipUVsProcess : public BaseProcess
+{
+ friend class Importer;
+
+public:
+ /** Constructor to be privately used by Importer */
+ FlipUVsProcess();
+
+ /** Destructor, private as well */
+ ~FlipUVsProcess();
+
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+protected:
+ void ProcessMesh( aiMesh* pMesh);
+ void ProcessMaterial( aiMaterial* mat);
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CONVERTTOLHPROCESS_H_INC
diff --git a/3rdparty/assimp/code/DXFLoader.cpp b/3rdparty/assimp/code/DXFLoader.cpp
new file mode 100644
index 000000000..27ed471f3
--- /dev/null
+++ b/3rdparty/assimp/code/DXFLoader.cpp
@@ -0,0 +1,609 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 DXFLoader.cpp
+ * @brief Implementation of the DXF importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
+
+#include "DXFLoader.h"
+#include "ParsingUtils.h"
+#include "ConvertToLHProcess.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+// AutoCAD Binary DXF<CR><LF><SUB><NULL>
+#define AI_DXF_BINARY_IDENT ("AutoCAD Binary DXF\r\n\x1a\0")
+#define AI_DXF_BINARY_IDENT_LEN (24)
+
+// color indices for DXF - 16 are supported
+static aiColor4D g_aclrDxfIndexColors[] =
+{
+ aiColor4D (0.6f, 0.6f, 0.6f, 1.0f),
+ aiColor4D (1.0f, 0.0f, 0.0f, 1.0f), // red
+ aiColor4D (0.0f, 1.0f, 0.0f, 1.0f), // green
+ aiColor4D (0.0f, 0.0f, 1.0f, 1.0f), // blue
+ aiColor4D (0.3f, 1.0f, 0.3f, 1.0f), // light green
+ aiColor4D (0.3f, 0.3f, 1.0f, 1.0f), // light blue
+ aiColor4D (1.0f, 0.3f, 0.3f, 1.0f), // light red
+ aiColor4D (1.0f, 0.0f, 1.0f, 1.0f), // pink
+ aiColor4D (1.0f, 0.6f, 0.0f, 1.0f), // orange
+ aiColor4D (0.6f, 0.3f, 0.0f, 1.0f), // dark orange
+ aiColor4D (1.0f, 1.0f, 0.0f, 1.0f), // yellow
+ aiColor4D (0.3f, 0.3f, 0.3f, 1.0f), // dark gray
+ aiColor4D (0.8f, 0.8f, 0.8f, 1.0f), // light gray
+ aiColor4D (0.0f, 00.f, 0.0f, 1.0f), // black
+ aiColor4D (1.0f, 1.0f, 1.0f, 1.0f), // white
+ aiColor4D (0.6f, 0.0f, 1.0f, 1.0f) // violet
+};
+#define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0]))
+
+// invalid/unassigned color value
+aiColor4D g_clrInvalid = aiColor4D(get_qnan(),0.f,0.f,1.f);
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+DXFImporter::DXFImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+DXFImporter::~DXFImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool DXFImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const
+{
+ return SimpleExtensionCheck(pFile,"dxf");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all supported file extensions
+void DXFImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("dxf");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a copy of the next data line, skip strange data
+bool DXFImporter::GetNextLine()
+{
+ if (!SkipLine(&buffer))
+ return false;
+ if (!SkipSpaces(&buffer))
+ return GetNextLine();
+ else if (*buffer == '{') {
+ // some strange meta data ...
+ while (true)
+ {
+ if (!SkipLine(&buffer))
+ return false;
+
+ if (SkipSpaces(&buffer) && *buffer == '}')
+ break;
+ }
+ return GetNextLine();
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the next token in the file
+bool DXFImporter::GetNextToken()
+{
+ if (bRepeat) {
+ bRepeat = false;
+ return true;
+ }
+
+ SkipSpaces(&buffer);
+ groupCode = strtol10s(buffer,&buffer);
+ if (!GetNextLine())
+ return false;
+
+ // copy the data line to a separate buffer
+ char* m = cursor, *end = &cursor[4096];
+ while (!IsSpaceOrNewLine( *buffer ) && m < end)
+ *m++ = *buffer++;
+
+ *m = '\0';
+ GetNextLine();
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void DXFImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open DXF file " + pFile + "");
+ }
+
+ // read the contents of the file in a buffer
+ std::vector<char> buffer2;
+ TextFileToBuffer(file.get(),buffer2);
+ buffer = &buffer2[0];
+
+ bRepeat = false;
+ mDefaultLayer = NULL;
+
+ // check whether this is a binaray DXF file - we can't read binary DXF files :-(
+ if (!strncmp(AI_DXF_BINARY_IDENT,buffer,AI_DXF_BINARY_IDENT_LEN))
+ throw DeadlyImportError("DXF: Binary files are not supported at the moment");
+
+ // now get all lines of the file
+ while (GetNextToken()) {
+
+ if (2 == groupCode) {
+
+ // ENTITIES and BLOCKS sections - skip the whole rest, no need to waste our time with them
+ if (!::strcmp(cursor,"ENTITIES") || !::strcmp(cursor,"BLOCKS")) {
+ if (!ParseEntities())
+ break;
+ else bRepeat = true;
+ }
+
+ // other sections - skip them to make sure there will be no name conflicts
+ else {
+ while ( GetNextToken()) {
+ if (!::strcmp(cursor,"ENDSEC"))
+ break;
+ }
+ }
+ }
+ // print comment strings
+ else if (999 == groupCode) {
+ DefaultLogger::get()->info(std::string( cursor ));
+ }
+ else if (!groupCode && !::strcmp(cursor,"EOF"))
+ break;
+ }
+
+ // find out how many valud layers we have
+ for (std::vector<LayerInfo>::const_iterator it = mLayers.begin(),end = mLayers.end(); it != end;++it) {
+ if (!(*it).vPositions.empty())
+ ++pScene->mNumMeshes;
+ }
+
+ if (!pScene->mNumMeshes)
+ throw DeadlyImportError("DXF: this file contains no 3d data");
+
+ pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ];
+ unsigned int m = 0;
+ for (std::vector<LayerInfo>::const_iterator it = mLayers.begin(),end = mLayers.end();it != end;++it) {
+ if ((*it).vPositions.empty()) {
+ continue;
+ }
+ // generate the output mesh
+ aiMesh* pMesh = pScene->mMeshes[m++] = new aiMesh();
+ const std::vector<aiVector3D>& vPositions = (*it).vPositions;
+ const std::vector<aiColor4D>& vColors = (*it).vColors;
+
+ // check whether we need vertex colors here
+ aiColor4D* clrOut = NULL;
+ const aiColor4D* clr = NULL;
+ for (std::vector<aiColor4D>::const_iterator it2 = (*it).vColors.begin(), end2 = (*it).vColors.end();it2 != end2; ++it2) {
+
+ if ((*it2).r == (*it2).r) /* qnan? */ {
+ clrOut = pMesh->mColors[0] = new aiColor4D[vPositions.size()];
+ for (unsigned int i = 0; i < vPositions.size();++i)
+ clrOut[i] = aiColor4D(0.6f,0.6f,0.6f,1.0f);
+
+ clr = &vColors[0];
+ break;
+ }
+ }
+
+ pMesh->mNumFaces = (unsigned int)vPositions.size() / 4u;
+ pMesh->mFaces = new aiFace[pMesh->mNumFaces];
+
+ aiVector3D* vpOut = pMesh->mVertices = new aiVector3D[vPositions.size()];
+ const aiVector3D* vp = &vPositions[0];
+
+ for (unsigned int i = 0; i < pMesh->mNumFaces;++i) {
+ aiFace& face = pMesh->mFaces[i];
+
+ // check whether we need four, three or two indices here
+ if (vp[1] == vp[2]) {
+ face.mNumIndices = 2;
+ }
+ else if (vp[3] == vp[2]) {
+ face.mNumIndices = 3;
+ }
+ else face.mNumIndices = 4;
+ face.mIndices = new unsigned int[face.mNumIndices];
+
+ for (unsigned int a = 0; a < face.mNumIndices;++a) {
+ *vpOut++ = vp[a];
+ if (clr) {
+ if (is_not_qnan( clr[a].r )) {
+ *clrOut = clr[a];
+ }
+ ++clrOut;
+ }
+ face.mIndices[a] = pMesh->mNumVertices++;
+ }
+ vp += 4;
+ }
+ }
+
+ // generate the output scene graph
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<DXF_ROOT>");
+
+ if (1 == pScene->mNumMeshes) {
+ pScene->mRootNode->mMeshes = new unsigned int[ pScene->mRootNode->mNumMeshes = 1 ];
+ pScene->mRootNode->mMeshes[0] = 0;
+ }
+ else
+ {
+ pScene->mRootNode->mChildren = new aiNode*[ pScene->mRootNode->mNumChildren = pScene->mNumMeshes ];
+ for (m = 0; m < pScene->mRootNode->mNumChildren;++m) {
+ aiNode* p = pScene->mRootNode->mChildren[m] = new aiNode();
+ p->mName.length = ::strlen( mLayers[m].name );
+ strcpy(p->mName.data, mLayers[m].name);
+
+ p->mMeshes = new unsigned int[p->mNumMeshes = 1];
+ p->mMeshes[0] = m;
+ p->mParent = pScene->mRootNode;
+ }
+ }
+
+ // generate a default material
+ MaterialHelper* pcMat = new MaterialHelper();
+ aiString s;
+ s.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcMat->AddProperty(&s, AI_MATKEY_NAME);
+
+ aiColor4D clrDiffuse(0.6f,0.6f,0.6f,1.0f);
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ clrDiffuse = aiColor4D(1.0f,1.0f,1.0f,1.0f);
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR);
+
+ clrDiffuse = aiColor4D(0.05f,0.05f,0.05f,1.0f);
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT);
+
+ pScene->mNumMaterials = 1;
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = pcMat;
+
+ // flip winding order to be ccw
+ FlipWindingOrderProcess flipper;
+ flipper.Execute(pScene);
+
+ // --- everything destructs automatically ---
+}
+
+// ------------------------------------------------------------------------------------------------
+bool DXFImporter::ParseEntities()
+{
+ while (GetNextToken()) {
+ if (!groupCode) {
+ if (!::strcmp(cursor,"3DFACE") || !::strcmp(cursor,"LINE") || !::strcmp(cursor,"3DLINE")){
+ //http://sourceforge.net/tracker/index.php?func=detail&aid=2970566&group_id=226462&atid=1067632
+ Parse3DFace();
+ bRepeat = true;
+ }
+ if (!::strcmp(cursor,"POLYLINE") || !::strcmp(cursor,"LWPOLYLINE")){
+ ParsePolyLine();
+ bRepeat = true;
+ }
+ if (!::strcmp(cursor,"ENDSEC")) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::SetLayer(LayerInfo*& out)
+{
+ for (std::vector<LayerInfo>::iterator it = mLayers.begin(),end = mLayers.end();it != end;++it) {
+ if (!::strcmp( (*it).name, cursor )) {
+ out = &(*it);
+ break;
+ }
+ }
+ if (!out) {
+ // we don't have this layer yet
+ mLayers.push_back(LayerInfo());
+ out = &mLayers.back();
+ ::strcpy(out->name,cursor);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::SetDefaultLayer(LayerInfo*& out)
+{
+ if (!mDefaultLayer) {
+ mLayers.push_back(LayerInfo());
+ mDefaultLayer = &mLayers.back();
+ }
+ out = mDefaultLayer;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool DXFImporter::ParsePolyLine()
+{
+ bool ret = false;
+ LayerInfo* out = NULL;
+
+ std::vector<aiVector3D> positions;
+ std::vector<aiColor4D> colors;
+ std::vector<unsigned int> indices;
+ unsigned int flags = 0;
+
+ while (GetNextToken()) {
+ switch (groupCode)
+ {
+ case 0:
+ {
+ if (!::strcmp(cursor,"VERTEX")) {
+ aiVector3D v;aiColor4D clr(g_clrInvalid);
+ unsigned int idx[4] = {0xffffffff,0xffffffff,0xffffffff,0xffffffff};
+ ParsePolyLineVertex(v, clr, idx);
+ if (0xffffffff == idx[0]) {
+ positions.push_back(v);
+ colors.push_back(clr);
+ }
+ else {
+ // check whether we have a fourth coordinate
+ if (0xffffffff == idx[3]) {
+ idx[3] = idx[2];
+ }
+
+ indices.reserve(indices.size()+4);
+ for (unsigned int m = 0; m < 4;++m)
+ indices.push_back(idx[m]);
+ }
+ bRepeat = true;
+ }
+ else if (!::strcmp(cursor,"ENDSEQ")) {
+ ret = true;
+ }
+ break;
+ }
+
+ // flags --- important that we know whether it is a polyface mesh
+ case 70:
+ {
+ if (!flags) {
+ flags = strtol10(cursor);
+ }
+ break;
+ };
+
+ // optional number of vertices
+ case 71:
+ {
+ positions.reserve(strtol10(cursor));
+ break;
+ }
+
+ // optional number of faces
+ case 72:
+ {
+ indices.reserve(strtol10(cursor));
+ break;
+ }
+
+ // 8 specifies the layer
+ case 8:
+ {
+ SetLayer(out);
+ break;
+ }
+ }
+ }
+ if (!(flags & 64)) {
+ DefaultLogger::get()->warn("DXF: Only polyface meshes are currently supported");
+ return ret;
+ }
+
+ if (positions.size() < 3 || indices.size() < 3) {
+ DefaultLogger::get()->warn("DXF: Unable to parse POLYLINE element - not enough vertices");
+ return ret;
+ }
+
+ // use a default layer if necessary
+ if (!out) {
+ SetDefaultLayer(out);
+ }
+
+ flags = (unsigned int)(out->vPositions.size()+indices.size());
+ out->vPositions.reserve(flags);
+ out->vColors.reserve(flags);
+
+ // generate unique vertices
+ for (std::vector<unsigned int>::const_iterator it = indices.begin(), end = indices.end();it != end; ++it) {
+ unsigned int idx = *it;
+ if (idx > positions.size() || !idx) {
+ DefaultLogger::get()->error("DXF: Polyface mesh index os out of range");
+ idx = (unsigned int) positions.size();
+ }
+ out->vPositions.push_back(positions[idx-1]); // indices are one-based.
+ out->vColors.push_back(colors[idx-1]); // indices are one-based.
+ }
+
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool DXFImporter::ParsePolyLineVertex(aiVector3D& out,aiColor4D& clr, unsigned int* outIdx)
+{
+ bool ret = false;
+ while (GetNextToken()) {
+ switch (groupCode)
+ {
+ case 0: ret = true;
+ break;
+
+ // todo - handle the correct layer for the vertex. At the moment it is assumed that all vertices of
+ // a polyline are placed on the same global layer.
+
+ // x position of the first corner
+ case 10: out.x = fast_atof(cursor);break;
+
+ // y position of the first corner
+ case 20: out.y = -fast_atof(cursor);break;
+
+ // z position of the first corner
+ case 30: out.z = fast_atof(cursor);break;
+
+ // POLYFACE vertex indices
+ case 71: outIdx[0] = strtol10(cursor);break;
+ case 72: outIdx[1] = strtol10(cursor);break;
+ case 73: outIdx[2] = strtol10(cursor);break;
+ // case 74: outIdx[3] = strtol10(cursor);break;
+
+ // color
+ case 62: clr = g_aclrDxfIndexColors[strtol10(cursor) % AI_DXF_NUM_INDEX_COLORS]; break;
+ };
+ if (ret) {
+ break;
+ }
+ }
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool DXFImporter::Parse3DFace()
+{
+ bool ret = false;
+ LayerInfo* out = NULL;
+
+ aiVector3D vip[4]; // -- vectors are initialized to zero
+ aiColor4D clr(g_clrInvalid);
+
+ // this is also used for for parsing line entities
+ bool bThird = false;
+
+ while (GetNextToken()) {
+ switch (groupCode) {
+ case 0:
+ ret = true;
+ break;
+
+ // 8 specifies the layer
+ case 8: {
+ SetLayer(out);
+ break;
+ }
+
+ // x position of the first corner
+ case 10: vip[0].x = fast_atof(cursor);break;
+
+ // y position of the first corner
+ case 20: vip[0].y = -fast_atof(cursor);break;
+
+ // z position of the first corner
+ case 30: vip[0].z = fast_atof(cursor);break;
+
+ // x position of the second corner
+ case 11: vip[1].x = fast_atof(cursor);break;
+
+ // y position of the second corner
+ case 21: vip[1].y = -fast_atof(cursor);break;
+
+ // z position of the second corner
+ case 31: vip[1].z = fast_atof(cursor);break;
+
+ // x position of the third corner
+ case 12: vip[2].x = fast_atof(cursor);
+ bThird = true;break;
+
+ // y position of the third corner
+ case 22: vip[2].y = -fast_atof(cursor);
+ bThird = true;break;
+
+ // z position of the third corner
+ case 32: vip[2].z = fast_atof(cursor);
+ bThird = true;break;
+
+ // x position of the fourth corner
+ case 13: vip[3].x = fast_atof(cursor);
+ bThird = true;break;
+
+ // y position of the fourth corner
+ case 23: vip[3].y = -fast_atof(cursor);
+ bThird = true;break;
+
+ // z position of the fourth corner
+ case 33: vip[3].z = fast_atof(cursor);
+ bThird = true;break;
+
+ // color
+ case 62: clr = g_aclrDxfIndexColors[strtol10(cursor) % AI_DXF_NUM_INDEX_COLORS]; break;
+ };
+ if (ret)
+ break;
+ }
+
+ if (!bThird)
+ vip[2] = vip[1];
+
+ // use a default layer if necessary
+ if (!out) {
+ SetDefaultLayer(out);
+ }
+ // add the faces to the face list for this layer
+ out->vPositions.push_back(vip[0]);
+ out->vPositions.push_back(vip[1]);
+ out->vPositions.push_back(vip[2]);
+ out->vPositions.push_back(vip[3]); // might be equal to the third
+
+ for (unsigned int i = 0; i < 4;++i)
+ out->vColors.push_back(clr);
+ return ret;
+}
+
+#endif // !! ASSIMP_BUILD_NO_DXF_IMPORTER
+
diff --git a/3rdparty/assimp/code/DXFLoader.h b/3rdparty/assimp/code/DXFLoader.h
new file mode 100644
index 000000000..88b47ad37
--- /dev/null
+++ b/3rdparty/assimp/code/DXFLoader.h
@@ -0,0 +1,184 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 DXFLoader.h
+ * @brief Declaration of the .dxf importer class.
+ */
+#ifndef AI_DXFLOADER_H_INCLUDED
+#define AI_DXFLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** DXF importer class
+*/
+class DXFImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ DXFImporter();
+
+ /** Destructor, private as well */
+ ~DXFImporter();
+
+
+ // describes a single layer in the DXF file
+ struct LayerInfo
+ {
+ LayerInfo()
+ {
+ name[0] = '\0';
+ }
+
+ char name[4096];
+
+ // face buffer - order is x,y,z v1,v2,v3,v4
+ // if v2 = v3: line
+ // elsif v3 = v2: triangle
+ // else: polygon
+ std::vector<aiVector3D> vPositions;
+ std::vector<aiColor4D> vColors;
+ };
+
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Get the next line from the file.
+ * @return false if the end of the file was reached
+ */
+ bool GetNextLine();
+
+ // -------------------------------------------------------------------
+ /** Get the next token (group code + data line) from the file.
+ * @return false if the end of the file was reached
+ */
+ bool GetNextToken();
+
+ // -------------------------------------------------------------------
+ /** Parses the ENTITIES section in the file
+ * @return false if the end of the file was reached
+ */
+ bool ParseEntities();
+
+ // -------------------------------------------------------------------
+ /** Parses a 3DFACE section in the file
+ * @return false if the end of the file was reached
+ */
+ bool Parse3DFace();
+
+ // -------------------------------------------------------------------
+ /** Parses a POLYLINE section in the file
+ * @return false if the end of the file was reached
+ */
+ bool ParsePolyLine();
+
+ // -------------------------------------------------------------------
+ /** Sets the current layer - cursor must point to the name of it.
+ * @param out Receives a handle to the layer
+ */
+ void SetLayer(LayerInfo*& out);
+
+ // -------------------------------------------------------------------
+ /** Creates a default layer.
+ * @param out Receives a handle to the default layer
+ */
+ void SetDefaultLayer(LayerInfo*& out);
+
+ // -------------------------------------------------------------------
+ /** Parses a VERTEX element in a POLYLINE/POLYFACE
+ * @param out Receives the output vertex.
+ * @param clr Receives the output vertex color - won't be modified
+ * if it is not existing.
+ * @param outIdx Receives the output vertex indices, if present.
+ * Wont't be modified otherwise. Size must be at least 4.
+ * @return false if the end of the file was reached
+ */
+ bool ParsePolyLineVertex(aiVector3D& out, aiColor4D& clr,
+ unsigned int* outIdx);
+
+private:
+
+ // points to the next section
+ const char* buffer;
+
+ // specifies the current group code
+ int groupCode;
+
+ // contains the current data line
+ char cursor[4096];
+
+ // specifies whether the next call to GetNextToken()
+ // should return the current token a second time
+ bool bRepeat;
+
+ // list of all loaded layers
+ std::vector<LayerInfo> mLayers;
+ LayerInfo* mDefaultLayer;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/DefaultIOStream.cpp b/3rdparty/assimp/code/DefaultIOStream.cpp
new file mode 100644
index 000000000..f68ad3846
--- /dev/null
+++ b/3rdparty/assimp/code/DefaultIOStream.cpp
@@ -0,0 +1,139 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 DefaultIOStream.cpp
+ * @brief Default File I/O implementation for #Importer
+ */
+
+#include "AssimpPCH.h"
+
+#include "DefaultIOStream.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+using namespace Assimp;
+
+// ----------------------------------------------------------------------------------
+DefaultIOStream::~DefaultIOStream()
+{
+ if (mFile) {
+ ::fclose(mFile);
+ }
+}
+
+// ----------------------------------------------------------------------------------
+size_t DefaultIOStream::Read(void* pvBuffer,
+ size_t pSize,
+ size_t pCount)
+{
+ ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
+ return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0);
+}
+
+// ----------------------------------------------------------------------------------
+size_t DefaultIOStream::Write(const void* pvBuffer,
+ size_t pSize,
+ size_t pCount)
+{
+ ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
+ return (mFile ? ::fwrite(pvBuffer, pSize, pCount, mFile) : 0);
+}
+
+// ----------------------------------------------------------------------------------
+aiReturn DefaultIOStream::Seek(size_t pOffset,
+ aiOrigin pOrigin)
+{
+ if (!mFile) {
+ return AI_FAILURE;
+ }
+
+ // Just to check whether our enum maps one to one with the CRT constants
+ BOOST_STATIC_ASSERT(aiOrigin_CUR == SEEK_CUR &&
+ aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET);
+
+ // do the seek
+ return (0 == ::fseek(mFile, (long)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE);
+}
+
+// ----------------------------------------------------------------------------------
+size_t DefaultIOStream::Tell() const
+{
+ if (!mFile) {
+ return 0;
+ }
+ return ::ftell(mFile);
+}
+
+// ----------------------------------------------------------------------------------
+size_t DefaultIOStream::FileSize() const
+{
+ if (! mFile || mFilename.empty()) {
+ return 0;
+ }
+
+ if (0xffffffff == cachedSize) {
+
+ // TODO: Is that really faster if we're already owning a handle to the file?
+#if defined _WIN32 && !defined __GNUC__
+ struct __stat64 fileStat;
+ int err = _stat64( mFilename.c_str(), &fileStat );
+ if (0 != err)
+ return 0;
+ cachedSize = (size_t) (fileStat.st_size);
+#else
+ struct stat fileStat;
+ int err = stat(mFilename.c_str(), &fileStat );
+ if (0 != err)
+ return 0;
+ cachedSize = (size_t) (fileStat.st_size);
+#endif
+ }
+ return cachedSize;
+}
+
+// ----------------------------------------------------------------------------------
+void DefaultIOStream::Flush()
+{
+ if (mFile) {
+ ::fflush(mFile);
+ }
+}
+
+// ----------------------------------------------------------------------------------
diff --git a/3rdparty/assimp/code/DefaultIOStream.h b/3rdparty/assimp/code/DefaultIOStream.h
new file mode 100644
index 000000000..fef7565f3
--- /dev/null
+++ b/3rdparty/assimp/code/DefaultIOStream.h
@@ -0,0 +1,133 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Default file I/O using fXXX()-family of functions */
+#ifndef AI_DEFAULTIOSTREAM_H_INC
+#define AI_DEFAULTIOSTREAM_H_INC
+
+#include <stdio.h>
+#include "../include/IOStream.h"
+
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+//! @class DefaultIOStream
+//! @brief Default IO implementation, use standard IO operations
+//! @note An instance of this class can exist without a valid file handle
+//! attached to it. All calls fail, but the instance can nevertheless be
+//! used with no restrictions.
+class DefaultIOStream : public IOStream
+{
+ friend class DefaultIOSystem;
+
+protected:
+ DefaultIOStream ();
+ DefaultIOStream (FILE* pFile, const std::string &strFilename);
+
+public:
+ /** Destructor public to allow simple deletion to close the file. */
+ ~DefaultIOStream ();
+
+ // -------------------------------------------------------------------
+ // Read from stream
+ size_t Read(void* pvBuffer,
+ size_t pSize,
+ size_t pCount);
+
+
+ // -------------------------------------------------------------------
+ // Write to stream
+ size_t Write(const void* pvBuffer,
+ size_t pSize,
+ size_t pCount);
+
+ // -------------------------------------------------------------------
+ // Seek specific position
+ aiReturn Seek(size_t pOffset,
+ aiOrigin pOrigin);
+
+ // -------------------------------------------------------------------
+ // Get current seek position
+ size_t Tell() const;
+
+ // -------------------------------------------------------------------
+ // Get size of file
+ size_t FileSize() const;
+
+ // -------------------------------------------------------------------
+ // Flush file contents
+ void Flush();
+
+private:
+ //! File datastructure, using clib
+ FILE* mFile;
+ //! Filename
+ std::string mFilename;
+
+ //! Cached file size
+ mutable size_t cachedSize;
+};
+
+
+// ----------------------------------------------------------------------------------
+inline DefaultIOStream::DefaultIOStream () :
+ mFile (NULL),
+ mFilename (""),
+ cachedSize (0xffffffff)
+{
+ // empty
+}
+
+
+// ----------------------------------------------------------------------------------
+inline DefaultIOStream::DefaultIOStream (FILE* pFile,
+ const std::string &strFilename) :
+ mFile(pFile),
+ mFilename(strFilename),
+ cachedSize (0xffffffff)
+{
+ // empty
+}
+// ----------------------------------------------------------------------------------
+
+} // ns assimp
+
+#endif //!!AI_DEFAULTIOSTREAM_H_INC
+
diff --git a/3rdparty/assimp/code/DefaultIOSystem.cpp b/3rdparty/assimp/code/DefaultIOSystem.cpp
new file mode 100644
index 000000000..436caa82e
--- /dev/null
+++ b/3rdparty/assimp/code/DefaultIOSystem.cpp
@@ -0,0 +1,167 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Default implementation of IOSystem using the standard C file functions */
+
+#include "AssimpPCH.h"
+
+#include <stdlib.h>
+#include "DefaultIOSystem.h"
+#include "DefaultIOStream.h"
+
+#ifdef __unix__
+#include <sys/param.h>
+#include <stdlib.h>
+#endif
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor.
+DefaultIOSystem::DefaultIOSystem()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor.
+DefaultIOSystem::~DefaultIOSystem()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tests for the existence of a file at the given path.
+bool DefaultIOSystem::Exists( const char* pFile) const
+{
+ FILE* file = ::fopen( pFile, "rb");
+ if ( !file)
+ return false;
+
+ ::fclose( file);
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Open a new file with a given path.
+IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode)
+{
+ ai_assert(NULL != strFile);
+ ai_assert(NULL != strMode);
+
+ FILE* file = ::fopen( strFile, strMode);
+ if ( NULL == file)
+ return NULL;
+
+ return new DefaultIOStream(file, (std::string) strFile);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Closes the given file and releases all resources associated with it.
+void DefaultIOSystem::Close( IOStream* pFile)
+{
+ delete pFile;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the operation specific directory separator
+char DefaultIOSystem::getOsSeparator() const
+{
+#ifndef _WIN32
+ return '/';
+#else
+ return '\\';
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+// IOSystem default implementation (ComparePaths isn't a pure virtual function)
+bool IOSystem::ComparePaths (const char* one, const char* second) const
+{
+ return !ASSIMP_stricmp(one,second);
+}
+
+// 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
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Convert a relative path into an absolute path
+inline void MakeAbsolutePath (const char* in, char* _out)
+{
+ ai_assert(in && _out);
+ char* ret;
+#ifdef _WIN32
+ ret = ::_fullpath(_out, in,PATHLIMIT);
+#else
+ // use realpath
+ ret = realpath(in, _out);
+#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)
+ DefaultLogger::get()->warn("Invalid path: "+std::string(in));
+ strcpy(_out,in);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// DefaultIOSystem's more specialized implementation
+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) )
+ return true;
+
+ char temp1[PATHLIMIT];
+ char temp2[PATHLIMIT];
+
+ MakeAbsolutePath (one, temp1);
+ MakeAbsolutePath (second, temp2);
+
+ return !ASSIMP_stricmp(temp1,temp2);
+}
+
+#undef PATHLIMIT
diff --git a/3rdparty/assimp/code/DefaultIOSystem.h b/3rdparty/assimp/code/DefaultIOSystem.h
new file mode 100644
index 000000000..ef481b3c8
--- /dev/null
+++ b/3rdparty/assimp/code/DefaultIOSystem.h
@@ -0,0 +1,83 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Default implementation of IOSystem using the standard C file functions */
+#ifndef AI_DEFAULTIOSYSTEM_H_INC
+#define AI_DEFAULTIOSYSTEM_H_INC
+
+#include "../include/IOSystem.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Default implementation of IOSystem using the standard C file functions */
+class DefaultIOSystem : public IOSystem
+{
+public:
+ /** Constructor. */
+ DefaultIOSystem();
+
+ /** Destructor. */
+ ~DefaultIOSystem();
+
+ // -------------------------------------------------------------------
+ /** Tests for the existence of a file at the given path. */
+ bool Exists( const char* pFile) const;
+
+ // -------------------------------------------------------------------
+ /** Returns the directory separator. */
+ char getOsSeparator() const;
+
+ // -------------------------------------------------------------------
+ /** Open a new file with a given path. */
+ IOStream* Open( const char* pFile, const char* pMode = "rb");
+
+ // -------------------------------------------------------------------
+ /** Closes the given file and releases all resources associated with it. */
+ void Close( IOStream* pFile);
+
+ // -------------------------------------------------------------------
+ /** Compare two paths */
+ bool ComparePaths (const char* one, const char* second) const;
+};
+
+} //!ns Assimp
+
+#endif //AI_DEFAULTIOSYSTEM_H_INC
diff --git a/3rdparty/assimp/code/DefaultLogger.cpp b/3rdparty/assimp/code/DefaultLogger.cpp
new file mode 100644
index 000000000..02ae0582c
--- /dev/null
+++ b/3rdparty/assimp/code/DefaultLogger.cpp
@@ -0,0 +1,419 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 DefaultLogger.cpp
+ * @brief Implementation of DefaultLogger (and Logger)
+ */
+
+#include "AssimpPCH.h"
+#include "DefaultIOSystem.h"
+
+// Default log streams
+#include "Win32DebugLogStream.h"
+#include "StdOStreamLogStream.h"
+#include "FileLogStream.h"
+
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+# include <boost/thread/thread.hpp>
+# include <boost/thread/mutex.hpp>
+
+boost::mutex loggerMutex;
+#endif
+
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+NullLogger DefaultLogger::s_pNullLogger;
+Logger *DefaultLogger::m_pLogger = &DefaultLogger::s_pNullLogger;
+
+// ----------------------------------------------------------------------------------
+// Represents a logstream + its error severity
+struct LogStreamInfo
+{
+ unsigned int m_uiErrorSeverity;
+ LogStream *m_pStream;
+
+ // Constructor
+ LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) :
+ m_uiErrorSeverity( uiErrorSev ),
+ m_pStream( pStream )
+ {
+ // empty
+ }
+
+ // Destructor
+ ~LogStreamInfo()
+ {
+ delete m_pStream;
+ }
+};
+
+// ----------------------------------------------------------------------------------
+// Construct a default log stream
+LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams,
+ const char* name /*= "AssimpLog.txt"*/,
+ IOSystem* io /*= NULL*/)
+{
+ switch (streams)
+ {
+ // This is a platform-specific feature
+ case aiDefaultLogStream_DEBUGGER:
+#ifdef WIN32
+ return new Win32DebugLogStream();
+#else
+ return NULL;
+#endif
+
+ // Platform-independent default streams
+ case aiDefaultLogStream_STDERR:
+ return new StdOStreamLogStream(std::cerr);
+ case aiDefaultLogStream_STDOUT:
+ return new StdOStreamLogStream(std::cout);
+ case aiDefaultLogStream_FILE:
+ return (name && *name ? new FileLogStream(name,io) : NULL);
+ default:
+ // We don't know this default log stream, so raise an assertion
+ ai_assert(false);
+
+ };
+
+ // For compilers without dead code path detection
+ return NULL;
+}
+
+// ----------------------------------------------------------------------------------
+// Creates the only singleton instance
+Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/,
+ LogSeverity severity /*= NORMAL*/,
+ unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/,
+ IOSystem* io /*= NULL*/)
+{
+ // enter the mutex here to avoid concurrency problems
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+ boost::mutex::scoped_lock lock(loggerMutex);
+#endif
+
+ if (m_pLogger && !isNullLogger() )
+ delete m_pLogger;
+
+ m_pLogger = new DefaultLogger( severity );
+
+ // Attach default log streams
+ // Stream the log to the MSVC debugger?
+ if (defStreams & aiDefaultLogStream_DEBUGGER)
+ m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_DEBUGGER));
+
+ // Stream the log to COUT?
+ if (defStreams & aiDefaultLogStream_STDOUT)
+ m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_STDOUT));
+
+ // Stream the log to CERR?
+ if (defStreams & aiDefaultLogStream_STDERR)
+ m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_STDERR));
+
+ // Stream the log to a file
+ if (defStreams & aiDefaultLogStream_FILE && name && *name)
+ m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_FILE,name,io));
+
+ return m_pLogger;
+}
+
+// ----------------------------------------------------------------------------------
+void Logger::debug(const std::string &message) {
+
+ // SECURITY FIX: otherwise it's easy to produce overruns ...
+ if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
+ ai_assert(false);
+ return;
+ }
+ return OnDebug(message.c_str());
+}
+
+// ----------------------------------------------------------------------------------
+void Logger::info(const std::string &message) {
+
+ // SECURITY FIX: otherwise it's easy to produce overruns ...
+ if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
+ ai_assert(false);
+ return;
+ }
+ return OnInfo(message.c_str());
+}
+
+// ----------------------------------------------------------------------------------
+void Logger::warn(const std::string &message) {
+
+ // SECURITY FIX: otherwise it's easy to produce overruns ...
+ if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
+ ai_assert(false);
+ return;
+ }
+ return OnWarn(message.c_str());
+}
+
+// ----------------------------------------------------------------------------------
+void Logger::error(const std::string &message) {
+
+ // SECURITY FIX: otherwise it's easy to produce overruns ...
+ if (message.length()>MAX_LOG_MESSAGE_LENGTH) {
+ ai_assert(false);
+ return;
+ }
+ return OnError(message.c_str());
+}
+
+// ----------------------------------------------------------------------------------
+void DefaultLogger::set( Logger *logger )
+{
+ // enter the mutex here to avoid concurrency problems
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+ boost::mutex::scoped_lock lock(loggerMutex);
+#endif
+
+ if (!logger)logger = &s_pNullLogger;
+ if (m_pLogger && !isNullLogger() )
+ delete m_pLogger;
+
+ DefaultLogger::m_pLogger = logger;
+}
+
+// ----------------------------------------------------------------------------------
+bool DefaultLogger::isNullLogger()
+{
+ return m_pLogger == &s_pNullLogger;
+}
+
+// ----------------------------------------------------------------------------------
+// Singleton getter
+Logger *DefaultLogger::get()
+{
+ return m_pLogger;
+}
+
+// ----------------------------------------------------------------------------------
+// Kills the only instance
+void DefaultLogger::kill()
+{
+ // enter the mutex here to avoid concurrency problems
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+ boost::mutex::scoped_lock lock(loggerMutex);
+#endif
+
+ if (m_pLogger == &s_pNullLogger)return;
+ delete m_pLogger;
+ m_pLogger = &s_pNullLogger;
+}
+
+// ----------------------------------------------------------------------------------
+// Debug message
+void DefaultLogger::OnDebug( const char* message )
+{
+ if ( m_Severity == Logger::NORMAL )
+ return;
+
+ char msg[MAX_LOG_MESSAGE_LENGTH*2];
+ ::sprintf(msg,"Debug, T%i: %s", GetThreadID(), message );
+
+ WriteToStreams( msg, Logger::DEBUGGING );
+}
+
+// ----------------------------------------------------------------------------------
+// Logs an info
+void DefaultLogger::OnInfo( const char* message )
+{
+ char msg[MAX_LOG_MESSAGE_LENGTH*2];
+ ::sprintf(msg,"Info, T%i: %s", GetThreadID(), message );
+
+ WriteToStreams( msg , Logger::INFO );
+}
+
+// ----------------------------------------------------------------------------------
+// Logs a warning
+void DefaultLogger::OnWarn( const char* message )
+{
+ char msg[MAX_LOG_MESSAGE_LENGTH*2];
+ ::sprintf(msg,"Warn, T%i: %s", GetThreadID(), message );
+
+ WriteToStreams( msg, Logger::WARN );
+}
+
+// ----------------------------------------------------------------------------------
+// Logs an error
+void DefaultLogger::OnError( const char* message )
+{
+ char msg[MAX_LOG_MESSAGE_LENGTH*2];
+ ::sprintf(msg,"Error, T%i: %s", GetThreadID(), message );
+
+ WriteToStreams( msg, Logger::ERR );
+}
+
+// ----------------------------------------------------------------------------------
+// Attachs a new stream
+bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity )
+{
+ if (!pStream)
+ return false;
+
+ if (0 == severity) {
+ severity = Logger::INFO | Logger::ERR | Logger::WARN | Logger::DEBUGGING;
+ }
+
+ for ( StreamIt it = m_StreamArray.begin();
+ it != m_StreamArray.end();
+ ++it )
+ {
+ if ( (*it)->m_pStream == pStream )
+ {
+ (*it)->m_uiErrorSeverity |= severity;
+ return true;
+ }
+ }
+
+ LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream );
+ m_StreamArray.push_back( pInfo );
+ return true;
+}
+
+// ----------------------------------------------------------------------------------
+// Detatch a stream
+bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity )
+{
+ if (!pStream)
+ return false;
+
+ if (0 == severity) {
+ severity = Logger::INFO | Logger::ERR | Logger::WARN | Logger::DEBUGGING;
+ }
+
+ for ( StreamIt it = m_StreamArray.begin();
+ it != m_StreamArray.end();
+ ++it )
+ {
+ if ( (*it)->m_pStream == pStream )
+ {
+ (*it)->m_uiErrorSeverity &= ~severity;
+ if ( (*it)->m_uiErrorSeverity == 0 )
+ {
+ // don't delete the underlying stream 'cause the caller gains ownership again
+ (**it).m_pStream = NULL;
+ delete *it;
+ m_StreamArray.erase( it );
+ break;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+// ----------------------------------------------------------------------------------
+// Constructor
+DefaultLogger::DefaultLogger(LogSeverity severity)
+
+ : Logger ( severity )
+ , noRepeatMsg (false)
+ , lastLen( 0 )
+{
+ lastMsg[0] = '\0';
+}
+
+// ----------------------------------------------------------------------------------
+// Destructor
+DefaultLogger::~DefaultLogger()
+{
+ for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) {
+ // also frees the underlying stream, we are its owner.
+ delete *it;
+ }
+}
+
+// ----------------------------------------------------------------------------------
+// Writes message to stream
+void DefaultLogger::WriteToStreams(const char *message,
+ ErrorSeverity ErrorSev )
+{
+ ai_assert(NULL != message);
+
+ // Check whether this is a repeated message
+ if (! ::strncmp( message,lastMsg, lastLen-1))
+ {
+ if (!noRepeatMsg)
+ {
+ noRepeatMsg = true;
+ message = "Skipping one or more lines with the same contents\n";
+ }
+ else return;
+ }
+ else
+ {
+ // append a new-line character to the message to be printed
+ lastLen = ::strlen(message);
+ ::memcpy(lastMsg,message,lastLen+1);
+ ::strcat(lastMsg+lastLen,"\n");
+
+ message = lastMsg;
+ noRepeatMsg = false;
+ ++lastLen;
+ }
+ for ( ConstStreamIt it = m_StreamArray.begin();
+ it != m_StreamArray.end();
+ ++it)
+ {
+ if ( ErrorSev & (*it)->m_uiErrorSeverity )
+ (*it)->m_pStream->write( message);
+ }
+}
+
+// ----------------------------------------------------------------------------------
+// Returns thread id, if not supported only a zero will be returned.
+unsigned int DefaultLogger::GetThreadID()
+{
+ // fixme: we can get this value via boost::threads
+#ifdef WIN32
+ return (unsigned int)::GetCurrentThreadId();
+#else
+ return 0; // not supported
+#endif
+}
+
+// ----------------------------------------------------------------------------------
+
+} // !namespace Assimp
diff --git a/3rdparty/assimp/code/DefaultProgressHandler.h b/3rdparty/assimp/code/DefaultProgressHandler.h
new file mode 100644
index 000000000..852f54cb1
--- /dev/null
+++ b/3rdparty/assimp/code/DefaultProgressHandler.h
@@ -0,0 +1,64 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 ProgressHandler.h
+ * @brief Abstract base class 'ProgressHandler'.
+ */
+#ifndef INCLUDED_AI_DEFAULTPROGRESSHANDLER_H
+#define INCLUDED_AI_DEFAULTPROGRESSHANDLER_H
+
+#include "../include/ProgressHandler.h"
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------
+/** @brief Internal default implementation of the #ProgressHandler interface. */
+class ASSIMP_API DefaultProgressHandler
+ : public ProgressHandler {
+
+
+ virtual bool Update(float /*percentage*/) {
+ return false;
+ }
+
+
+}; // !class DefaultProgressHandler
+} // Namespace Assimp
+
+#endif
diff --git a/3rdparty/assimp/code/Exceptional.h b/3rdparty/assimp/code/Exceptional.h
new file mode 100644
index 000000000..e9f93186b
--- /dev/null
+++ b/3rdparty/assimp/code/Exceptional.h
@@ -0,0 +1,122 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development 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 Development 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 INCLUDED_EXCEPTIONAL_H
+#define INCLUDED_EXCEPTIONAL_H
+
+#include <stdexcept>
+using std::runtime_error;
+
+#ifdef _MSC_VER
+# pragma warning(disable : 4275)
+#endif
+
+// ---------------------------------------------------------------------------
+/** FOR IMPORTER PLUGINS ONLY: Simple exception class to be thrown if an
+ * unrecoverable error occurs while importing. Loading APIs return
+ * NULL instead of a valid aiScene then. */
+class ASSIMP_API DeadlyImportError
+ : public runtime_error
+{
+public:
+ /** Constructor with arguments */
+ explicit DeadlyImportError( const std::string& pErrorText)
+ : runtime_error(pErrorText)
+ {
+ }
+
+private:
+};
+
+#ifdef _MSC_VER
+# pragma warning(default : 4275)
+#endif
+
+// ---------------------------------------------------------------------------
+template <typename T>
+struct ExceptionSwallower {
+ T operator ()() const {
+ return T();
+ }
+};
+
+// ---------------------------------------------------------------------------
+template <typename T>
+struct ExceptionSwallower<T*> {
+ T* operator ()() const {
+ return NULL;
+ }
+};
+
+// ---------------------------------------------------------------------------
+template <>
+struct ExceptionSwallower<aiReturn> {
+ aiReturn operator ()() const {
+ try {
+ throw;
+ }
+ catch (std::bad_alloc&) {
+ return aiReturn_OUTOFMEMORY;
+ }
+ catch (...) {
+ return aiReturn_FAILURE;
+ }
+ }
+};
+
+// ---------------------------------------------------------------------------
+template <>
+struct ExceptionSwallower<void> {
+ void operator ()() const {
+ return;
+ }
+};
+
+#define ASSIMP_BEGIN_EXCEPTION_REGION()\
+{\
+ try {
+
+#define ASSIMP_END_EXCEPTION_REGION(type)\
+ } catch(...) {\
+ return ExceptionSwallower<type>()();\
+ }\
+}
+
+#endif // INCLUDED_EXCEPTIONAL_H
diff --git a/3rdparty/assimp/code/FileLogStream.h b/3rdparty/assimp/code/FileLogStream.h
new file mode 100644
index 000000000..605ed509e
--- /dev/null
+++ b/3rdparty/assimp/code/FileLogStream.h
@@ -0,0 +1,64 @@
+#ifndef ASSIMP_FILELOGSTREAM_H_INC
+#define ASSIMP_FILELOGSTREAM_H_INC
+
+#include "../include/LogStream.h"
+#include "../include/IOStream.h"
+
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+/** @class FileLogStream
+ * @brief Logstream to write into a file.
+ */
+class FileLogStream :
+ public LogStream
+{
+public:
+ FileLogStream( const char* file, IOSystem* io = NULL );
+ ~FileLogStream();
+ void write( const char* message );
+
+private:
+ IOStream *m_pStream;
+};
+
+// ----------------------------------------------------------------------------------
+// Constructor
+inline FileLogStream::FileLogStream( const char* file, IOSystem* io ) :
+ m_pStream(NULL)
+{
+ if ( !file || 0 == *file )
+ return;
+
+ // If no IOSystem is specified: take a default one
+ if (!io)
+ {
+ DefaultIOSystem FileSystem;
+ m_pStream = FileSystem.Open( file, "wt");
+ }
+ else m_pStream = io->Open( file, "wt" );
+}
+
+// ----------------------------------------------------------------------------------
+// Destructor
+inline FileLogStream::~FileLogStream()
+{
+ // The virtual d'tor should destroy the underlying file
+ delete m_pStream;
+}
+
+// ----------------------------------------------------------------------------------
+// Write method
+inline void FileLogStream::write( const char* message )
+{
+ if (m_pStream != NULL)
+ {
+ m_pStream->Write(message, sizeof(char), ::strlen(message));
+ m_pStream->Flush();
+ }
+}
+
+// ----------------------------------------------------------------------------------
+} // !Namespace Assimp
+
+#endif // !! ASSIMP_FILELOGSTREAM_H_INC
diff --git a/3rdparty/assimp/code/FileSystemFilter.h b/3rdparty/assimp/code/FileSystemFilter.h
new file mode 100644
index 000000000..a23c84125
--- /dev/null
+++ b/3rdparty/assimp/code/FileSystemFilter.h
@@ -0,0 +1,244 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development 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 Development 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 FileSystemFilter.h
+ * Implements a filter system to filter calls to Exists() and Open()
+ * in order to improve the sucess rate of file opening ...
+ */
+#ifndef AI_FILESYSTEMFILTER_H_INC
+#define AI_FILESYSTEMFILTER_H_INC
+
+#include "../include/IOSystem.h"
+#include "fast_atof.h"
+#include "ParsingUtils.h"
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** File system filter
+ */
+class FileSystemFilter : public IOSystem
+{
+public:
+ /** Constructor. */
+ FileSystemFilter(const std::string& file, IOSystem* old)
+ : wrapped (old)
+ , src_file (file)
+ {
+ ai_assert(NULL != wrapped);
+
+ // Determine base directory
+ base = src_file;
+ std::string::size_type ss2;
+ if (std::string::npos != (ss2 = base.find_last_of("\\/"))) {
+ base.erase(ss2,base.length()-ss2);
+ }
+ else {
+ base = "";
+ // return;
+ }
+
+ // make sure the directory is terminated properly
+ char s;
+
+ if (base.length() == 0) {
+ base = ".";
+ base += getOsSeparator();
+ }
+ else if ((s = *(base.end()-1)) != '\\' && s != '/')
+ base += getOsSeparator();
+
+ DefaultLogger::get()->info("Import root directory is \'" + base + "\'");
+ }
+
+ /** Destructor. */
+ ~FileSystemFilter()
+ {
+ // haha
+ }
+
+ // -------------------------------------------------------------------
+ /** Tests for the existence of a file at the given path. */
+ bool Exists( const char* pFile) const
+ {
+ std::string tmp = pFile;
+
+ // Currently this IOSystem is also used to open THE ONE FILE.
+ if (tmp != src_file) {
+ BuildPath(tmp);
+ Cleanup(tmp);
+ }
+
+ return wrapped->Exists(tmp);
+ }
+
+ // -------------------------------------------------------------------
+ /** Returns the directory separator. */
+ char getOsSeparator() const
+ {
+ return wrapped->getOsSeparator();
+ }
+
+ // -------------------------------------------------------------------
+ /** Open a new file with a given path. */
+ IOStream* Open( const char* pFile, const char* pMode = "rb")
+ {
+ ai_assert(pFile);
+ ai_assert(pMode);
+
+ // First try the unchanged path
+ IOStream* s = wrapped->Open(pFile,pMode);
+
+ if (!s) {
+ std::string tmp = pFile;
+
+ // Try to convert between absolute and relative paths
+ BuildPath(tmp);
+ s = wrapped->Open(tmp,pMode);
+
+ if (!s) {
+ // Finally, look for typical issues with paths
+ // and try to correct them. This is our last
+ // resort.
+ Cleanup(tmp);
+ s = wrapped->Open(tmp,pMode);
+ }
+ }
+
+ return s;
+ }
+
+ // -------------------------------------------------------------------
+ /** Closes the given file and releases all resources associated with it. */
+ void Close( IOStream* pFile)
+ {
+ return wrapped->Close(pFile);
+ }
+
+ // -------------------------------------------------------------------
+ /** Compare two paths */
+ bool ComparePaths (const char* one, const char* second) const
+ {
+ return wrapped->ComparePaths (one,second);
+ }
+
+private:
+ IOSystem* wrapped;
+ std::string src_file, base;
+
+ // -------------------------------------------------------------------
+ /** Build a valid path from a given relative or absolute path.
+ */
+ void BuildPath (std::string& in) const
+ {
+ // if we can already access the file, great.
+ if (in.length() < 3 || wrapped->Exists(in.c_str())) {
+ return;
+ }
+
+ // Determine whether this is a relative path (Windows-specific - most assets are packaged on Windows).
+ if (in[1] != ':') {
+
+ // append base path and try
+ in = base + in;
+ if (wrapped->Exists(in.c_str())) {
+ return;
+ }
+ }
+
+ // hopefully the underyling file system has another few tricks to access this file ...
+ }
+
+ // -------------------------------------------------------------------
+ /** Cleanup the given path
+ */
+ void Cleanup (std::string& in) const
+ {
+ char last = 0;
+
+ // Remove a very common issue when we're parsing file names: spaces at the
+ // beginning of the path.
+ std::string::iterator it = in.begin();
+ while (IsSpaceOrNewLine( *it ))++it;
+ if (it != in.begin())
+ in.erase(in.begin(),it+1);
+
+ const char sep = getOsSeparator();
+ for (it = in.begin(); it != in.end(); ++it) {
+ // Exclude :// and \\, which remain untouched.
+ // https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632
+ if ( !strncmp(&*it, "://", 3 )) {
+ it += 3;
+ continue;
+ }
+ if (it == in.begin() && !strncmp(&*it, "\\\\", 2)) {
+ it += 2;
+ continue;
+ }
+
+ // Cleanup path delimiters
+ if (*it == '/' || (*it) == '\\') {
+ *it = sep;
+
+ // And we're removing double delimiters, frequent issue with
+ // incorrectly composited paths ...
+ if (last == *it) {
+ it = in.erase(it);
+ --it;
+ }
+ }
+ else if (*it == '%' && in.end() - it > 2) {
+
+ // Hex sequence in URIs
+ uint32_t tmp;
+ if ( 0xffffffff != (tmp = HexOctetToDecimal(&*it))) {
+ *it = (char)tmp;
+ it = in.erase(it+1,it+2);
+ --it;
+ }
+ }
+
+ last = *it;
+ }
+ }
+};
+
+} //!ns Assimp
+
+#endif //AI_DEFAULTIOSYSTEM_H_INC
diff --git a/3rdparty/assimp/code/FindDegenerates.cpp b/3rdparty/assimp/code/FindDegenerates.cpp
new file mode 100644
index 000000000..3cdd325dd
--- /dev/null
+++ b/3rdparty/assimp/code/FindDegenerates.cpp
@@ -0,0 +1,216 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 FindDegenerates.cpp
+ * @brief Implementation of the FindDegenerates post-process step.
+*/
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "ProcessHelper.h"
+#include "FindDegenerates.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FindDegeneratesProcess::FindDegeneratesProcess()
+: configRemoveDegenerates (false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FindDegeneratesProcess::~FindDegeneratesProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const
+{
+ return 0 != (pFlags & aiProcess_FindDegenerates);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import configuration
+void FindDegeneratesProcess::SetupProperties(const Importer* pImp)
+{
+ // Get the current value of AI_CONFIG_PP_FD_REMOVE
+ configRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FindDegeneratesProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("FindDegeneratesProcess begin");
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i){
+ ExecuteOnMesh( pScene->mMeshes[i]);
+ }
+ DefaultLogger::get()->debug("FindDegeneratesProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported mesh
+void FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh)
+{
+ mesh->mPrimitiveTypes = 0;
+
+ std::vector<bool> remove_me;
+ if (configRemoveDegenerates)
+ remove_me.resize(mesh->mNumFaces,false);
+
+ unsigned int deg = 0, limit;
+ for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
+ {
+ aiFace& face = mesh->mFaces[a];
+ bool first = true;
+
+ // check whether the face contains degenerated entries
+ for (register unsigned int i = 0; i < face.mNumIndices; ++i)
+ {
+ // Polygons with more than 4 points are allowed to have double points, that is
+ // simulating polygons with holes just with concave polygons. However,
+ // double points may not come directly after another.
+ limit = face.mNumIndices;
+ if (face.mNumIndices > 4)
+ limit = std::min(limit,i+2);
+
+ for (register unsigned int t = i+1; t < limit; ++t)
+ {
+ if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]])
+ {
+ // we have found a matching vertex position
+ // remove the corresponding index from the array
+ --face.mNumIndices;--limit;
+ for (unsigned int m = t; m < face.mNumIndices; ++m)
+ {
+ face.mIndices[m] = face.mIndices[m+1];
+ }
+ --t;
+
+ // NOTE: we set the removed vertex index to an unique value
+ // to make sure the developer gets notified when his
+ // application attemps to access this data.
+ face.mIndices[face.mNumIndices] = 0xdeadbeef;
+
+ if (first)
+ {
+ ++deg;
+ first = false;
+ }
+
+ if (configRemoveDegenerates) {
+ remove_me[a] = true;
+ goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
+ }
+ }
+ }
+ }
+
+ // We need to update the primitive flags array of the mesh.
+ switch (face.mNumIndices)
+ {
+ case 1u:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ break;
+ case 2u:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ break;
+ case 3u:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ break;
+ default:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ break;
+ };
+evil_jump_outside:
+ continue;
+ }
+
+ // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
+ if (configRemoveDegenerates && deg) {
+ unsigned int n = 0;
+ for (unsigned int a = 0; a < mesh->mNumFaces; ++a)
+ {
+ aiFace& face_src = mesh->mFaces[a];
+ if (!remove_me[a]) {
+ aiFace& face_dest = mesh->mFaces[n++];
+
+ // Do a manual copy, keep the index array
+ face_dest.mNumIndices = face_src.mNumIndices;
+ face_dest.mIndices = face_src.mIndices;
+
+ if (&face_src != &face_dest) {
+ // clear source
+ face_src.mNumIndices = 0;
+ face_src.mIndices = NULL;
+ }
+ }
+ else {
+ // Otherwise delete it if we don't need this face
+ delete[] face_src.mIndices;
+ face_src.mIndices = NULL;
+ face_src.mNumIndices = 0;
+ }
+ }
+ // Just leave the rest of the array unreferenced, we don't care for now
+ mesh->mNumFaces = n;
+ if (!mesh->mNumFaces) {
+ // WTF!?
+ // OK ... for completeness and because I'm not yet tired,
+ // let's write code that willl hopefully never be called
+ // (famous last words)
+
+ // OK ... bad idea.
+ throw DeadlyImportError("Mesh is empty after removal of degenerated primitives ... WTF!?");
+ }
+ }
+
+ if (deg && !DefaultLogger::isNullLogger())
+ {
+ char s[64];
+ ASSIMP_itoa10(s,deg);
+ DefaultLogger::get()->warn(std::string("Found ") + s + " degenerated primitives");
+ }
+}
diff --git a/3rdparty/assimp/code/FindDegenerates.h b/3rdparty/assimp/code/FindDegenerates.h
new file mode 100644
index 000000000..a4417919b
--- /dev/null
+++ b/3rdparty/assimp/code/FindDegenerates.h
@@ -0,0 +1,110 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to search all meshes for
+ degenerated faces */
+#ifndef AI_FINDDEGENERATESPROCESS_H_INC
+#define AI_FINDDEGENERATESPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+
+class FindDegeneratesProcessTest;
+namespace Assimp {
+
+
+// ---------------------------------------------------------------------------
+/** FindDegeneratesProcess: Searches a mesh for degenerated triangles.
+*/
+class ASSIMP_API FindDegeneratesProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::FindDegeneratesProcessTest; // grant the unit test full access to us
+
+protected:
+ /** Constructor to be privately used by Importer */
+ FindDegeneratesProcess();
+
+ /** Destructor, private as well */
+ ~FindDegeneratesProcess();
+
+public:
+
+ // -------------------------------------------------------------------
+ // Check whether step is active
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Execute step on a given scene
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ // Setup import settings
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ // Execute step on a given mesh
+ void ExecuteOnMesh( aiMesh* mesh);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Enable the instant removal of degenerated primitives
+ * @param d hm ... difficult to guess what this means, hu!?
+ */
+ void EnableInstantRemoval(bool d) {
+ configRemoveDegenerates = d;
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Check whether instant removal is currently enabled
+ * @return ...
+ */
+ bool IsInstantRemoval() const {
+ return configRemoveDegenerates;
+ }
+
+private:
+
+ //! Configuration option: remove degenerates faces immediately
+ bool configRemoveDegenerates;
+};
+}
+
+#endif // !! AI_FINDDEGENERATESPROCESS_H_INC
diff --git a/3rdparty/assimp/code/FindInstancesProcess.cpp b/3rdparty/assimp/code/FindInstancesProcess.cpp
new file mode 100644
index 000000000..537f2ea4a
--- /dev/null
+++ b/3rdparty/assimp/code/FindInstancesProcess.cpp
@@ -0,0 +1,288 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 FindInstancesProcess.cpp
+ * @brief Implementation of the aiProcess_FindInstances postprocessing step
+*/
+
+#include "AssimpPCH.h"
+#include "FindInstancesProcess.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FindInstancesProcess::FindInstancesProcess()
+: configSpeedFlag (false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FindInstancesProcess::~FindInstancesProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FindInstancesProcess::IsActive( unsigned int pFlags) const
+{
+ // FindInstances makes absolutely no sense together with PreTransformVertices
+ // fixme: spawn error message somewhere else?
+ return 0 != (pFlags & aiProcess_FindInstances) && 0 == (pFlags & aiProcess_PreTransformVertices);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties for the step
+void FindInstancesProcess::SetupProperties(const Importer* pImp)
+{
+ // AI_CONFIG_FAVOUR_SPEED
+ configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Compare the bones of two meshes
+bool CompareBones(const aiMesh* orig, const aiMesh* inst)
+{
+ for (unsigned int i = 0; i < orig->mNumBones;++i) {
+ aiBone* aha = orig->mBones[i];
+ aiBone* oha = inst->mBones[i];
+
+ if (aha->mNumWeights != oha->mNumWeights ||
+ aha->mOffsetMatrix != oha->mOffsetMatrix ||
+ aha->mNumWeights != oha->mNumWeights) {
+ return false;
+ }
+
+ // compare weight per weight ---
+ for (unsigned int n = 0; n < aha->mNumWeights;++n) {
+ if (aha->mWeights[n].mVertexId != oha->mWeights[n].mVertexId ||
+ (aha->mWeights[n].mWeight - oha->mWeights[n].mWeight) < 10e-3f) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Update mesh indices in the node graph
+void UpdateMeshIndices(aiNode* node, unsigned int* lookup)
+{
+ for (unsigned int n = 0; n < node->mNumMeshes;++n)
+ node->mMeshes[n] = lookup[node->mMeshes[n]];
+
+ for (unsigned int n = 0; n < node->mNumChildren;++n)
+ UpdateMeshIndices(node->mChildren[n],lookup);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FindInstancesProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("FindInstancesProcess begin");
+ if (pScene->mNumMeshes) {
+
+ // use a pseudo hash for all meshes in the scene to quickly find
+ // the ones which are possibly equal. This step is executed early
+ // in the pipeline, so we could, depending on the file format,
+ // have several thousand small meshes. That's too much for a brute
+ // everyone-against-everyone check involving up to 10 comparisons
+ // each.
+ boost::scoped_array<uint64_t> hashes (new uint64_t[pScene->mNumMeshes]);
+ boost::scoped_array<unsigned int> remapping (new unsigned int[pScene->mNumMeshes]);
+
+ unsigned int numMeshesOut = 0;
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+
+ aiMesh* inst = pScene->mMeshes[i];
+ hashes[i] = GetMeshHash(inst);
+
+ for (int a = i-1; a >= 0; --a) {
+ if (hashes[i] == hashes[a])
+ {
+ aiMesh* orig = pScene->mMeshes[a];
+ if (!orig)
+ continue;
+
+ // check for hash collision .. we needn't check
+ // the vertex format, it *must* match due to the
+ // (brilliant) construction of the hash
+ if (orig->mNumBones != inst->mNumBones ||
+ orig->mNumFaces != inst->mNumFaces ||
+ orig->mNumVertices != inst->mNumVertices ||
+ orig->mMaterialIndex != inst->mMaterialIndex ||
+ orig->mPrimitiveTypes != inst->mPrimitiveTypes)
+ continue;
+
+ // up to now the meshes are equal. find an appropriate
+ // epsilon to compare position differences against
+ float epsilon = ComputePositionEpsilon(inst);
+ epsilon *= epsilon;
+
+ // now compare vertex positions, normals,
+ // tangents and bitangents using this epsilon.
+ if (orig->HasPositions()) {
+ if (!CompareArrays(orig->mVertices,inst->mVertices,orig->mNumVertices,epsilon))
+ continue;
+ }
+ if (orig->HasNormals()) {
+ if (!CompareArrays(orig->mNormals,inst->mNormals,orig->mNumVertices,epsilon))
+ continue;
+ }
+ if (orig->HasTangentsAndBitangents()) {
+ if (!CompareArrays(orig->mTangents,inst->mTangents,orig->mNumVertices,epsilon) ||
+ !CompareArrays(orig->mBitangents,inst->mBitangents,orig->mNumVertices,epsilon))
+ continue;
+ }
+
+ // use a constant epsilon for colors and UV coordinates
+ static const float uvEpsilon = 10e-4f;
+
+ BOOST_STATIC_ASSERT(4 == AI_MAX_NUMBER_OF_COLOR_SETS);
+
+ // as in JIV: manually unrolled as continue wouldn't work as desired in inner loops
+ if (orig->mTextureCoords[0]) {
+ if (!CompareArrays(orig->mTextureCoords[0],inst->mTextureCoords[0],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[1]) {
+ if (!CompareArrays(orig->mTextureCoords[1],inst->mTextureCoords[1],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[2]) {
+ if (!CompareArrays(orig->mTextureCoords[2],inst->mTextureCoords[2],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[3]) {
+ if (!CompareArrays(orig->mTextureCoords[3],inst->mTextureCoords[3],orig->mNumVertices,uvEpsilon))
+ continue;
+ }
+ }
+ }
+ }
+
+ BOOST_STATIC_ASSERT(4 == AI_MAX_NUMBER_OF_COLOR_SETS);
+
+ // and the same nasty stuff for vertex colors ...
+ if (orig->mColors[0]) {
+ if (!CompareArrays(orig->mColors[0],inst->mColors[0],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[1]) {
+ if (!CompareArrays(orig->mColors[1],inst->mColors[1],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[2]) {
+ if (!CompareArrays(orig->mColors[2],inst->mColors[2],orig->mNumVertices,uvEpsilon))
+ continue;
+ if (orig->mTextureCoords[3]) {
+ if (!CompareArrays(orig->mColors[3],inst->mColors[3],orig->mNumVertices,uvEpsilon))
+ continue;
+ }
+ }
+ }
+ }
+
+ // These two checks are actually quite expensive and almost *never* required.
+ // Almost. That's why they're still here. But there's no reason to do them
+ // in speed-targeted imports.
+ if (!configSpeedFlag) {
+
+ // It seems to be strange, but we really need to check whether the
+ // bones are identical too. Although it's extremely unprobable
+ // that they're not if control reaches here, we need to deal
+ // with unprobable cases, too. It could still be that there are
+ // equal shapes which are deformed differently.
+ if (!CompareBones(orig,inst))
+ continue;
+
+ // For completeness ... compare even the index buffers for equality
+ // face order & winding order doesn't care. Input data is in verbose format.
+ boost::scoped_array<unsigned int> ftbl_orig(new unsigned int[orig->mNumVertices]);
+ boost::scoped_array<unsigned int> ftbl_inst(new unsigned int[orig->mNumVertices]);
+
+ for (unsigned int tt = 0; tt < orig->mNumFaces;++tt) {
+ aiFace& f = orig->mFaces[tt];
+ for (unsigned int nn = 0; nn < f.mNumIndices;++nn)
+ ftbl_orig[f.mIndices[nn]] = tt;
+
+ aiFace& f2 = inst->mFaces[tt];
+ for (unsigned int nn = 0; nn < f2.mNumIndices;++nn)
+ ftbl_inst[f2.mIndices[nn]] = tt;
+ }
+ if (0 != ::memcmp(ftbl_inst.get(),ftbl_orig.get(),orig->mNumVertices*sizeof(unsigned int)))
+ continue;
+ }
+
+ // We're still here. Or in other words: 'inst' is an instance of 'orig'.
+ // Place a marker in our list that we can easily update mesh indices.
+ remapping[i] = remapping[a];
+
+ // Delete the instanced mesh, we don't need it anymore
+ delete inst;
+ pScene->mMeshes[i] = NULL;
+ break;
+ }
+ }
+
+ // If we didn't find a match for the current mesh: keep it
+ if (pScene->mMeshes[i]) {
+ remapping[i] = numMeshesOut++;
+ }
+ }
+ ai_assert(0 != numMeshesOut);
+ if (numMeshesOut != pScene->mNumMeshes) {
+
+ // Collapse the meshes array by removing all NULL entries
+ for (unsigned int real = 0, i = 0; real < numMeshesOut; ++i) {
+ if (pScene->mMeshes[i])
+ pScene->mMeshes[real++] = pScene->mMeshes[i];
+ }
+
+ // And update the nodegraph with our nice lookup table
+ UpdateMeshIndices(pScene->mRootNode,remapping.get());
+
+ // write to log
+ if (!DefaultLogger::isNullLogger()) {
+
+ char buffer[512];
+ ::sprintf(buffer,"FindInstancesProcess finished. Found %i instances",pScene->mNumMeshes-numMeshesOut);
+ DefaultLogger::get()->info(buffer);
+ }
+ pScene->mNumMeshes = numMeshesOut;
+ }
+ else DefaultLogger::get()->debug("FindInstancesProcess finished. No instanced meshes found");
+ }
+}
diff --git a/3rdparty/assimp/code/FindInstancesProcess.h b/3rdparty/assimp/code/FindInstancesProcess.h
new file mode 100644
index 000000000..bba2cada1
--- /dev/null
+++ b/3rdparty/assimp/code/FindInstancesProcess.h
@@ -0,0 +1,140 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 FindInstancesProcess.h
+ * @brief Declares the aiProcess_FindInstances post-process step
+ */
+#ifndef AI_FINDINSTANCES_H_INC
+#define AI_FINDINSTANCES_H_INC
+
+#include "BaseProcess.h"
+#include "ProcessHelper.h"
+
+class FindInstancesProcessTest;
+namespace Assimp {
+
+// -------------------------------------------------------------------------------
+/** @brief Get a pseudo(!)-hash representing a mesh.
+ *
+ * The hash is built from number of vertices, faces, primitive types,
+ * .... but *not* from the real mesh data. It isn't absolutely unique.
+ * @param in Input mesh
+ * @return Hash.
+ */
+inline uint64_t GetMeshHash(aiMesh* in)
+{
+ ai_assert(NULL != in);
+
+ // ... get an unique value representing the vertex format of the mesh
+ const unsigned int fhash = GetMeshVFormatUnique(in);
+
+ // and bake it with number of vertices/faces/bones/matidx/ptypes
+ return ((uint64_t)fhash << 32u) | ((
+ (in->mNumBones << 16u) ^ (in->mNumVertices) ^
+ (in->mNumFaces<<4u) ^ (in->mMaterialIndex<<15) ^
+ (in->mPrimitiveTypes<<28)) & 0xffffffff );
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Perform a component-wise comparison of two arrays
+ *
+ * @param first First array
+ * @param second Second aray
+ * @param size Size of both arrays
+ * @param e Epsilon
+ * @return true if the arrays are identical
+ */
+inline bool CompareArrays(const aiVector3D* first, const aiVector3D* second,
+ unsigned int size, float e)
+{
+ for (const aiVector3D* end = first+size; first != end; ++first,++second) {
+ if ( (*first - *second).SquareLength() >= e)
+ return false;
+ }
+ return true;
+}
+
+// and the same for colors ...
+inline bool CompareArrays(const aiColor4D* first, const aiColor4D* second,
+ unsigned int size, float e)
+{
+ for (const aiColor4D* end = first+size; first != end; ++first,++second) {
+ if ( GetColorDifference(*first,*second) >= e)
+ return false;
+ }
+ return true;
+}
+
+// ---------------------------------------------------------------------------
+/** @brief A post-processing steps to search for instanced meshes
+*/
+class ASSIMP_API FindInstancesProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::FindInstancesProcessTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ FindInstancesProcess();
+
+ /** Destructor, private as well */
+ ~FindInstancesProcess();
+
+public:
+ // -------------------------------------------------------------------
+ // Check whether step is active in given flags combination
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Execute step on a given scene
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ // Setup properties prior to executing the process
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+ bool configSpeedFlag;
+
+}; // ! end class FindInstancesProcess
+} // ! end namespace Assimp
+
+#endif // !! AI_FINDINSTANCES_H_INC
diff --git a/3rdparty/assimp/code/FindInvalidDataProcess.cpp b/3rdparty/assimp/code/FindInvalidDataProcess.cpp
new file mode 100644
index 000000000..4e2d3cbdd
--- /dev/null
+++ b/3rdparty/assimp/code/FindInvalidDataProcess.cpp
@@ -0,0 +1,419 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to search an importer's output
+ for data that is obviously invalid */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
+
+// internal headers
+#include "FindInvalidDataProcess.h"
+#include "ProcessHelper.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FindInvalidDataProcess::FindInvalidDataProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FindInvalidDataProcess::~FindInvalidDataProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FindInvalidDataProcess::IsActive( unsigned int pFlags) const
+{
+ return 0 != (pFlags & aiProcess_FindInvalidData);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import configuration
+void FindInvalidDataProcess::SetupProperties(const Importer* pImp)
+{
+ // Get the current value of AI_CONFIG_PP_FID_ANIM_ACCURACY
+ configEpsilon = (0 != pImp->GetPropertyFloat(AI_CONFIG_PP_FID_ANIM_ACCURACY,0.f));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Update mesh references in the node graph
+void UpdateMeshReferences(aiNode* node, const std::vector<unsigned int>& meshMapping)
+{
+ if (node->mNumMeshes) {
+ unsigned int out = 0;
+ for (unsigned int a = 0; a < node->mNumMeshes;++a) {
+
+ register unsigned int ref = node->mMeshes[a];
+ if (0xffffffff != (ref = meshMapping[ref])) {
+ node->mMeshes[out++] = ref;
+ }
+ }
+ // just let the members that are unused, that's much cheaper
+ // than a full array realloc'n'copy party ...
+ if (!(node->mNumMeshes = out)) {
+
+ delete[] node->mMeshes;
+ node->mMeshes = NULL;
+ }
+ }
+ // recursively update all children
+ for (unsigned int i = 0; i < node->mNumChildren;++i) {
+ UpdateMeshReferences(node->mChildren[i],meshMapping);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FindInvalidDataProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("FindInvalidDataProcess begin");
+
+ bool out = false;
+ std::vector<unsigned int> meshMapping(pScene->mNumMeshes);
+ unsigned int real = 0;
+
+ // Process meshes
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+
+ int result;
+ if ((result = ProcessMesh( pScene->mMeshes[a]))) {
+ out = true;
+
+ if (2 == result) {
+ // remove this mesh
+ delete pScene->mMeshes[a];
+ AI_DEBUG_INVALIDATE_PTR(pScene->mMeshes[a]);
+
+ meshMapping[a] = 0xffffffff;
+ continue;
+ }
+ }
+ pScene->mMeshes[real] = pScene->mMeshes[a];
+ meshMapping[a] = real++;
+ }
+
+ // Process animations
+ for (unsigned int a = 0; a < pScene->mNumAnimations;++a) {
+ ProcessAnimation( pScene->mAnimations[a]);
+ }
+
+
+ if (out) {
+ if ( real != pScene->mNumMeshes) {
+ if (!real) {
+ throw DeadlyImportError("No meshes remaining");
+ }
+
+ // we need to remove some meshes.
+ // therefore we'll also need to remove all references
+ // to them from the scenegraph
+ UpdateMeshReferences(pScene->mRootNode,meshMapping);
+ pScene->mNumMeshes = real;
+ }
+
+ DefaultLogger::get()->info("FindInvalidDataProcess finished. Found issues ...");
+ }
+ else DefaultLogger::get()->debug("FindInvalidDataProcess finished. Everything seems to be OK.");
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline const char* ValidateArrayContents(const T* arr, unsigned int size,
+ const std::vector<bool>& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true)
+{
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <>
+inline const char* ValidateArrayContents<aiVector3D>(const aiVector3D* arr, unsigned int size,
+ const std::vector<bool>& dirtyMask, bool mayBeIdentical , bool mayBeZero )
+{
+ bool b = false;
+ unsigned int cnt = 0;
+ for (unsigned int i = 0; i < size;++i) {
+
+ if (dirtyMask.size() && dirtyMask[i]) {
+ continue;
+ }
+ ++cnt;
+
+ const aiVector3D& v = arr[i];
+ if (is_special_float(v.x) || is_special_float(v.y) || is_special_float(v.z)) {
+ return "INF/NAN was found in a vector component";
+ }
+ if (!mayBeZero && !v.x && !v.y && !v.z ) {
+ return "Found zero-length vector";
+ }
+ if (i && v != arr[i-1])b = true;
+ }
+ if (cnt > 1 && !b && !mayBeIdentical) {
+ return "All vectors are identical";
+ }
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline bool ProcessArray(T*& in, unsigned int num,const char* name,
+ const std::vector<bool>& dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true)
+{
+ const char* err = ValidateArrayContents(in,num,dirtyMask,mayBeIdentical,mayBeZero);
+ if (err) {
+ DefaultLogger::get()->error(std::string("FindInvalidDataProcess fails on mesh ") + name + ": " + err);
+
+ delete[] in;
+ in = NULL;
+ return true;
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+AI_FORCE_INLINE bool EpsilonCompare(const T& n, const T& s, float epsilon);
+
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE bool EpsilonCompare(float n, float s, float epsilon) {
+ return fabs(n-s)>epsilon;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <>
+bool EpsilonCompare<aiVectorKey>(const aiVectorKey& n, const aiVectorKey& s, float epsilon) {
+ return
+ EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) &&
+ EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) &&
+ EpsilonCompare(n.mValue.z,s.mValue.z,epsilon);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <>
+bool EpsilonCompare<aiQuatKey>(const aiQuatKey& n, const aiQuatKey& s, float epsilon) {
+ return
+ EpsilonCompare(n.mValue.x,s.mValue.x,epsilon) &&
+ EpsilonCompare(n.mValue.y,s.mValue.y,epsilon) &&
+ EpsilonCompare(n.mValue.z,s.mValue.z,epsilon) &&
+ EpsilonCompare(n.mValue.w,s.mValue.w,epsilon);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline bool AllIdentical(T* in, unsigned int num, float epsilon)
+{
+ if (num <= 1) {
+ return true;
+ }
+
+ if (epsilon > 0.f) {
+ for (unsigned int i = 0; i < num-1;++i) {
+
+ if (!EpsilonCompare(in[i],in[i+1],epsilon)) {
+ return false;
+ }
+ }
+ }
+ else {
+ for (unsigned int i = 0; i < num-1;++i) {
+
+ if (in[i] != in[i+1]) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Search an animation for invalid content
+void FindInvalidDataProcess::ProcessAnimation (aiAnimation* anim)
+{
+ // Process all animation channels
+ for (unsigned int a = 0; a < anim->mNumChannels;++a) {
+ ProcessAnimationChannel( anim->mChannels[a]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim)
+{
+ int i = 0;
+
+ // ScenePreprocessor's work ...
+ ai_assert((0 != anim->mPositionKeys && 0 != anim->mRotationKeys && 0 != anim->mScalingKeys));
+
+ // Check whether all values in a tracks are identical - in this case
+ // we can remove al keys except one.
+ // POSITIONS
+ if (anim->mNumPositionKeys > 1 && AllIdentical(anim->mPositionKeys,anim->mNumPositionKeys,configEpsilon))
+ {
+ aiVectorKey v = anim->mPositionKeys[0];
+
+ // Reallocate ... we need just ONE element, it makes no sense to reuse the array
+ delete[] anim->mPositionKeys;
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys = 1];
+ anim->mPositionKeys[0] = v;
+ i = 1;
+ }
+
+ // ROTATIONS
+ if (anim->mNumRotationKeys > 1 && AllIdentical(anim->mRotationKeys,anim->mNumRotationKeys,configEpsilon))
+ {
+ aiQuatKey v = anim->mRotationKeys[0];
+
+ // Reallocate ... we need just ONE element, it makes no sense to reuse the array
+ delete[] anim->mRotationKeys;
+ anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys = 1];
+ anim->mRotationKeys[0] = v;
+ i = 1;
+ }
+
+ // SCALINGS
+ if (anim->mNumScalingKeys > 1 && AllIdentical(anim->mScalingKeys,anim->mNumScalingKeys,configEpsilon))
+ {
+ aiVectorKey v = anim->mScalingKeys[0];
+
+ // Reallocate ... we need just ONE element, it makes no sense to reuse the array
+ delete[] anim->mScalingKeys;
+ anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys = 1];
+ anim->mScalingKeys[0] = v;
+ i = 1;
+ }
+ if (1 == i)
+ DefaultLogger::get()->warn("Simplified dummy tracks with just one key");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Search a mesh for invalid contents
+int FindInvalidDataProcess::ProcessMesh (aiMesh* pMesh)
+{
+ bool ret = false;
+ std::vector<bool> dirtyMask(pMesh->mNumVertices,(pMesh->mNumFaces ? true : false));
+
+ // Ignore elements that are not referenced by vertices.
+ // (they are, for example, caused by the FindDegenerates step)
+ for (unsigned int m = 0; m < pMesh->mNumFaces;++m) {
+ const aiFace& f = pMesh->mFaces[m];
+
+ for (unsigned int i = 0; i < f.mNumIndices;++i) {
+ dirtyMask[f.mIndices[i]] = false;
+ }
+ }
+
+ // Process vertex positions
+ if (pMesh->mVertices && ProcessArray(pMesh->mVertices,pMesh->mNumVertices,"positions",dirtyMask)) {
+ DefaultLogger::get()->error("Deleting mesh: Unable to continue without vertex positions");
+ return 2;
+ }
+
+ // process texture coordinates
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS && pMesh->mTextureCoords[i];++i) {
+ if (ProcessArray(pMesh->mTextureCoords[i],pMesh->mNumVertices,"uvcoords",dirtyMask)) {
+
+ // delete all subsequent texture coordinate sets.
+ for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) {
+ delete[] pMesh->mTextureCoords[a]; pMesh->mTextureCoords[a] = NULL;
+ }
+ ret = true;
+ }
+ }
+
+ // -- we don't validate vertex colors, it's difficult to say whether
+ // they are invalid or not.
+
+ // Normals and tangents are undefined for point and line faces.
+ if (pMesh->mNormals || pMesh->mTangents) {
+
+ if (aiPrimitiveType_POINT & pMesh->mPrimitiveTypes ||
+ aiPrimitiveType_LINE & pMesh->mPrimitiveTypes)
+ {
+ if (aiPrimitiveType_TRIANGLE & pMesh->mPrimitiveTypes ||
+ aiPrimitiveType_POLYGON & pMesh->mPrimitiveTypes)
+ {
+ // We need to update the lookup-table
+ for (unsigned int m = 0; m < pMesh->mNumFaces;++m)
+ {
+ const aiFace& f = pMesh->mFaces[m];
+
+ if (f.mNumIndices < 3) {
+ dirtyMask[f.mIndices[0]] = true;
+
+ if (f.mNumIndices == 2) {
+ dirtyMask[f.mIndices[1]] = true;
+ }
+ }
+ }
+ }
+ // Normals, tangents and bitangents are undefined for
+ // the whole mesh (and should not even be there)
+ else return ret;
+ }
+
+ // Process mesh normals
+ if (pMesh->mNormals && ProcessArray(pMesh->mNormals,pMesh->mNumVertices,
+ "normals",dirtyMask,true,false))
+ ret = true;
+
+ // Process mesh tangents
+ if (pMesh->mTangents && ProcessArray(pMesh->mTangents,pMesh->mNumVertices,"tangents",dirtyMask)) {
+ delete[] pMesh->mBitangents; pMesh->mBitangents = NULL;
+ ret = true;
+ }
+
+ // Process mesh bitangents
+ if (pMesh->mBitangents && ProcessArray(pMesh->mBitangents,pMesh->mNumVertices,"bitangents",dirtyMask)) {
+ delete[] pMesh->mTangents; pMesh->mTangents = NULL;
+ ret = true;
+ }
+ }
+ return ret ? 1 : 0;
+}
+
+
+#endif // !! ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
diff --git a/3rdparty/assimp/code/FindInvalidDataProcess.h b/3rdparty/assimp/code/FindInvalidDataProcess.h
new file mode 100644
index 000000000..304a0ff26
--- /dev/null
+++ b/3rdparty/assimp/code/FindInvalidDataProcess.h
@@ -0,0 +1,111 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to search an importer's output
+ for data that is obviously invalid */
+#ifndef AI_FINDINVALIDDATA_H_INC
+#define AI_FINDINVALIDDATA_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiTypes.h"
+
+struct aiMesh;
+class FindInvalidDataProcessTest;
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** The FindInvalidData postprocessing step. It searches the mesh data
+ * for parts that are obviously invalid and removes them.
+ *
+ * Originally this was a workaround for some models written by Blender
+ * which have zero normal vectors. */
+class ASSIMP_API FindInvalidDataProcess
+ : public BaseProcess
+{
+ friend class Importer;
+ friend class ::FindInvalidDataProcessTest;
+
+protected:
+
+ /** Constructor to be privately used by Importer */
+ FindInvalidDataProcess();
+
+ /** Destructor, private as well */
+ ~FindInvalidDataProcess();
+
+public:
+
+ // -------------------------------------------------------------------
+ //
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Setup import settings
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ // Run the step
+ void Execute( aiScene* pScene);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Executes the postprocessing step on the given mesh
+ * @param pMesh The mesh to process.
+ * @return 0 - nothing, 1 - removed sth, 2 - please delete me */
+ int ProcessMesh( aiMesh* pMesh);
+
+ // -------------------------------------------------------------------
+ /** Executes the postprocessing step on the given animation
+ * @param anim The animation to process. */
+ void ProcessAnimation (aiAnimation* anim);
+
+ // -------------------------------------------------------------------
+ /** Executes the postprocessing step on the given anim channel
+ * @param anim The animation channel to process.*/
+ void ProcessAnimationChannel (aiNodeAnim* anim);
+
+private:
+ float configEpsilon;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_AI_FINDINVALIDDATA_H_INC
diff --git a/3rdparty/assimp/code/FixNormalsStep.cpp b/3rdparty/assimp/code/FixNormalsStep.cpp
new file mode 100644
index 000000000..fd10f4d17
--- /dev/null
+++ b/3rdparty/assimp/code/FixNormalsStep.cpp
@@ -0,0 +1,176 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the post processing step to invert
+ * all normals in meshes with infacing normals.
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "FixNormalsStep.h"
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+FixInfacingNormalsProcess::FixInfacingNormalsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+FixInfacingNormalsProcess::~FixInfacingNormalsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool FixInfacingNormalsProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_FixInfacingNormals) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void FixInfacingNormalsProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("FixInfacingNormalsProcess begin");
+
+ bool bHas = false;
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ if (ProcessMesh( pScene->mMeshes[a],a))bHas = true;
+
+ if (bHas)
+ DefaultLogger::get()->debug("FixInfacingNormalsProcess finished. Found issues.");
+ else DefaultLogger::get()->debug("FixInfacingNormalsProcess finished. No changes to the scene.");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Apply the step to the mesh
+bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index)
+{
+ ai_assert(NULL != pcMesh);
+
+ // Nothing to do if there are no model normals
+ if (!pcMesh->HasNormals())return false;
+
+ // Compute the bounding box of both the model vertices + normals and
+ // the umodified model vertices. Then check whether the first BB
+ // is smaller than the second. In this case we can assume that the
+ // normals need to be flipped, although there are a few special cases ..
+ // convex, concave, planar models ...
+
+ aiVector3D vMin0 (1e10f,1e10f,1e10f);
+ aiVector3D vMin1 (1e10f,1e10f,1e10f);
+ aiVector3D vMax0 (-1e10f,-1e10f,-1e10f);
+ aiVector3D vMax1 (-1e10f,-1e10f,-1e10f);
+
+ for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
+ {
+ vMin1.x = std::min(vMin1.x,pcMesh->mVertices[i].x);
+ vMin1.y = std::min(vMin1.y,pcMesh->mVertices[i].y);
+ vMin1.z = std::min(vMin1.z,pcMesh->mVertices[i].z);
+
+ vMax1.x = std::max(vMax1.x,pcMesh->mVertices[i].x);
+ vMax1.y = std::max(vMax1.y,pcMesh->mVertices[i].y);
+ vMax1.z = std::max(vMax1.z,pcMesh->mVertices[i].z);
+
+ const aiVector3D vWithNormal = pcMesh->mVertices[i] + pcMesh->mNormals[i];
+
+ vMin0.x = std::min(vMin0.x,vWithNormal.x);
+ vMin0.y = std::min(vMin0.y,vWithNormal.y);
+ vMin0.z = std::min(vMin0.z,vWithNormal.z);
+
+ vMax0.x = std::max(vMax0.x,vWithNormal.x);
+ vMax0.y = std::max(vMax0.y,vWithNormal.y);
+ vMax0.z = std::max(vMax0.z,vWithNormal.z);
+ }
+
+ const float fDelta0_x = (vMax0.x - vMin0.x);
+ const float fDelta0_y = (vMax0.y - vMin0.y);
+ const float fDelta0_z = (vMax0.z - vMin0.z);
+
+ const float fDelta1_x = (vMax1.x - vMin1.x);
+ const float fDelta1_y = (vMax1.y - vMin1.y);
+ const float fDelta1_z = (vMax1.z - vMin1.z);
+
+ // Check whether the boxes are overlapping
+ if ((fDelta0_x > 0.0f) != (fDelta1_x > 0.0f))return false;
+ if ((fDelta0_y > 0.0f) != (fDelta1_y > 0.0f))return false;
+ if ((fDelta0_z > 0.0f) != (fDelta1_z > 0.0f))return false;
+
+ // Check whether this is a planar surface
+ const float fDelta1_yz = fDelta1_y * fDelta1_z;
+
+ if (fDelta1_x < 0.05f * sqrtf( fDelta1_yz ))return false;
+ if (fDelta1_y < 0.05f * sqrtf( fDelta1_z * fDelta1_x ))return false;
+ if (fDelta1_z < 0.05f * sqrtf( fDelta1_y * fDelta1_x ))return false;
+
+ // now compare the volumes of the bounding boxes
+ if (::fabsf(fDelta0_x * fDelta1_yz) <
+ ::fabsf(fDelta1_x * fDelta1_y * fDelta1_z))
+ {
+ if (!DefaultLogger::isNullLogger())
+ {
+ char buffer[128]; // should be sufficiently large
+ ::sprintf(buffer,"Mesh %i: Normals are facing inwards (or the mesh is planar)",index);
+ DefaultLogger::get()->info(buffer);
+ }
+
+ // Invert normals
+ for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
+ pcMesh->mNormals[i] *= -1.0f;
+
+ // ... and flip faces
+ for (unsigned int i = 0; i < pcMesh->mNumFaces;++i)
+ {
+ aiFace& face = pcMesh->mFaces[i];
+ for ( unsigned int b = 0; b < face.mNumIndices / 2; b++)
+ std::swap( face.mIndices[b], face.mIndices[ face.mNumIndices - 1 - b]);
+ }
+ return true;
+ }
+ return false;
+}
diff --git a/3rdparty/assimp/code/FixNormalsStep.h b/3rdparty/assimp/code/FixNormalsStep.h
new file mode 100644
index 000000000..972001e88
--- /dev/null
+++ b/3rdparty/assimp/code/FixNormalsStep.h
@@ -0,0 +1,96 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to fix infacing normals */
+#ifndef AI_FIXNORMALSPROCESS_H_INC
+#define AI_FIXNORMALSPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The FixInfacingNormalsProcess tries to deteermine whether the normal
+ * vectors of an object are facing inwards. In this case they will be
+ * flipped.
+ */
+ class ASSIMP_API FixInfacingNormalsProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ FixInfacingNormalsProcess();
+
+ /** Destructor, private as well */
+ ~FixInfacingNormalsProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Executes the step on the given mesh
+ * @param pMesh The mesh to process.
+ */
+ bool ProcessMesh( aiMesh* pMesh, unsigned int index);
+};
+
+} // end of namespace Assimp
+
+#endif // AI_FIXNORMALSPROCESS_H_INC
diff --git a/3rdparty/assimp/code/GenFaceNormalsProcess.cpp b/3rdparty/assimp/code/GenFaceNormalsProcess.cpp
new file mode 100644
index 000000000..f0987c85d
--- /dev/null
+++ b/3rdparty/assimp/code/GenFaceNormalsProcess.cpp
@@ -0,0 +1,138 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the post processing step to generate face
+* normals for all imported faces.
+*/
+
+#include "AssimpPCH.h"
+#include "GenFaceNormalsProcess.h"
+
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+GenFaceNormalsProcess::GenFaceNormalsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+GenFaceNormalsProcess::~GenFaceNormalsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_GenNormals) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenFaceNormalsProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("GenFaceNormalsProcess begin");
+
+ if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
+ throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
+ }
+
+ bool bHas = false;
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+ if (this->GenMeshFaceNormals( pScene->mMeshes[a])) {
+ bHas = true;
+ }
+ }
+ if (bHas) {
+ DefaultLogger::get()->info("GenFaceNormalsProcess finished. "
+ "Face normals have been calculated");
+ }
+ else DefaultLogger::get()->debug("GenFaceNormalsProcess finished. "
+ "Normals are already there");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
+{
+ if (NULL != pMesh->mNormals) {
+ return false;
+ }
+
+ // If the mesh consists of lines and/or points but not of
+ // triangles or higher-order polygons the normal vectors
+ // are undefined.
+ if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) {
+ DefaultLogger::get()->info("Normal vectors are undefined for line and point meshes");
+ return false;
+ }
+
+ // allocate an array to hold the output normals
+ pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+ const float qnan = get_qnan();
+
+ // iterate through all faces and compute per-face normals but store them per-vertex.
+ for ( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
+ const aiFace& face = pMesh->mFaces[a];
+ if (face.mNumIndices < 3) {
+ // either a point or a line -> no well-defined normal vector
+ for (unsigned int i = 0;i < face.mNumIndices;++i) {
+ pMesh->mNormals[face.mIndices[i]] = qnan;
+ }
+ continue;
+ }
+
+ const aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
+ const aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
+ const aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]];
+ aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).Normalize();
+
+ for (unsigned int i = 0;i < face.mNumIndices;++i) {
+ pMesh->mNormals[face.mIndices[i]] = vNor;
+ }
+ }
+ return true;
+}
diff --git a/3rdparty/assimp/code/GenFaceNormalsProcess.h b/3rdparty/assimp/code/GenFaceNormalsProcess.h
new file mode 100644
index 000000000..87ca6f22d
--- /dev/null
+++ b/3rdparty/assimp/code/GenFaceNormalsProcess.h
@@ -0,0 +1,88 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to compute face normals for all loaded faces*/
+#ifndef AI_GENFACENORMALPROCESS_H_INC
+#define AI_GENFACENORMALPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The GenFaceNormalsProcess computes face normals for all faces of all meshes
+*/
+class ASSIMP_API GenFaceNormalsProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ GenFaceNormalsProcess();
+
+ /** Destructor, private as well */
+ ~GenFaceNormalsProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** 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:
+ bool GenMeshFaceNormals (aiMesh* pcMesh);
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_GENFACENORMALPROCESS_H_INC
diff --git a/3rdparty/assimp/code/GenVertexNormalsProcess.cpp b/3rdparty/assimp/code/GenVertexNormalsProcess.cpp
new file mode 100644
index 000000000..260ad68fd
--- /dev/null
+++ b/3rdparty/assimp/code/GenVertexNormalsProcess.cpp
@@ -0,0 +1,228 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the post processing step to generate face
+* normals for all imported faces.
+*/
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "GenVertexNormalsProcess.h"
+#include "ProcessHelper.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+GenVertexNormalsProcess::GenVertexNormalsProcess()
+{
+ this->configMaxAngle = AI_DEG_TO_RAD(175.f);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+GenVertexNormalsProcess::~GenVertexNormalsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_GenSmoothNormals) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenVertexNormalsProcess::SetupProperties(const Importer* pImp)
+{
+ // Get the current value of the AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE property
+ configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,175.f);
+ configMaxAngle = AI_DEG_TO_RAD(std::max(std::min(configMaxAngle,175.0f),0.0f));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void GenVertexNormalsProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("GenVertexNormalsProcess begin");
+
+ if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT)
+ throw DeadlyImportError("Post-processing order mismatch: expecting pseudo-indexed (\"verbose\") vertices here");
+
+ bool bHas = false;
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ {
+ if (GenMeshVertexNormals( pScene->mMeshes[a],a))
+ bHas = true;
+ }
+
+ if (bHas) {
+ DefaultLogger::get()->info("GenVertexNormalsProcess finished. "
+ "Vertex normals have been calculated");
+ }
+ else DefaultLogger::get()->debug("GenVertexNormalsProcess finished. "
+ "Normals are already there");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int meshIndex)
+{
+ if (NULL != pMesh->mNormals)
+ return false;
+
+ // If the mesh consists of lines and/or points but not of
+ // triangles or higher-order polygons the normal vectors
+ // are undefined.
+ if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON)))
+ {
+ DefaultLogger::get()->info("Normal vectors are undefined for line and point meshes");
+ return false;
+ }
+
+ // Allocate the array to hold the output normals
+ const float qnan = std::numeric_limits<float>::quiet_NaN();
+ pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+
+ // Compute per-face normals but store them per-vertex
+ for ( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+ {
+ const aiFace& face = pMesh->mFaces[a];
+ if (face.mNumIndices < 3)
+ {
+ // either a point or a line -> no normal vector
+ for (unsigned int i = 0;i < face.mNumIndices;++i)
+ pMesh->mNormals[face.mIndices[i]] = qnan;
+ continue;
+ }
+
+ aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]];
+ aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]];
+ aiVector3D* pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices-1]];
+ aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).Normalize();
+
+ for (unsigned int i = 0;i < face.mNumIndices;++i)
+ pMesh->mNormals[face.mIndices[i]] = vNor;
+ }
+
+ // Set up a SpatialSort to quickly find all vertices close to a given position
+ // check whether we can reuse the SpatialSort of a previous step.
+ SpatialSort* vertexFinder = NULL;
+ SpatialSort _vertexFinder;
+ float posEpsilon = 1e-5f;
+ if (shared) {
+ std::vector<std::pair<SpatialSort,float> >* avf;
+ shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
+ if (avf)
+ {
+ std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
+ vertexFinder = &blubb.first;
+ posEpsilon = blubb.second;
+ }
+ }
+ if (!vertexFinder) {
+ _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
+ vertexFinder = &_vertexFinder;
+ posEpsilon = ComputePositionEpsilon(pMesh);
+ }
+ std::vector<unsigned int> verticesFound;
+ aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices];
+
+ if (configMaxAngle >= AI_DEG_TO_RAD( 175.f )) {
+ // There is no angle limit. Thus all vertices with positions close
+ // to each other will receive the same vertex normal. This allows us
+ // to optimize the whole algorithm a little bit ...
+ std::vector<bool> abHad(pMesh->mNumVertices,false);
+ for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
+ if (abHad[i])continue;
+
+ // Get all vertices that share this one ...
+ vertexFinder->FindPositions( pMesh->mVertices[i], posEpsilon, verticesFound);
+
+ aiVector3D pcNor;
+ for (unsigned int a = 0; a < verticesFound.size(); ++a) {
+ const aiVector3D& v = pMesh->mNormals[verticesFound[a]];
+ if (is_not_qnan(v.x))pcNor += v;
+ }
+ pcNor.Normalize();
+
+ // Write the smoothed normal back to all affected normals
+ for (unsigned int a = 0; a < verticesFound.size(); ++a)
+ {
+ register unsigned int vidx = verticesFound[a];
+ pcNew[vidx] = pcNor;
+ abHad[vidx] = true;
+ }
+ }
+ }
+ // Slower code path if a smooth angle is set. There are many ways to achieve
+ // the effect, this one is the most straightforward one.
+ else {
+ const float fLimit = ::cos(configMaxAngle);
+ for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
+ // Get all vertices that share this one ...
+ vertexFinder->FindPositions( pMesh->mVertices[i] , posEpsilon, verticesFound);
+
+ aiVector3D pcNor;
+ for (unsigned int a = 0; a < verticesFound.size(); ++a) {
+ const aiVector3D& v = pMesh->mNormals[verticesFound[a]];
+
+ // check whether the angle between the two normals is not too large
+ // HACK: if v.x is qnan the dot product will become qnan, too
+ // therefore the comparison against fLimit should be false
+ // in every case.
+ if (v * pMesh->mNormals[i] < fLimit)
+ continue;
+
+ pcNor += v;
+ }
+ pcNew[i] = pcNor.Normalize();
+ }
+ }
+
+ delete[] pMesh->mNormals;
+ pMesh->mNormals = pcNew;
+
+ return true;
+}
diff --git a/3rdparty/assimp/code/GenVertexNormalsProcess.h b/3rdparty/assimp/code/GenVertexNormalsProcess.h
new file mode 100644
index 000000000..294cb7ef8
--- /dev/null
+++ b/3rdparty/assimp/code/GenVertexNormalsProcess.h
@@ -0,0 +1,118 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to compute vertex normals
+ for all loaded vertizes */
+#ifndef AI_GENVERTEXNORMALPROCESS_H_INC
+#define AI_GENVERTEXNORMALPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+
+class GenNormalsTest;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** The GenFaceNormalsProcess computes vertex normals for all vertizes
+*/
+class ASSIMP_API GenVertexNormalsProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::GenNormalsTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ GenVertexNormalsProcess();
+
+ /** Destructor, private as well */
+ ~GenVertexNormalsProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag.
+ * @param pFlags The processing flags the importer was called with.
+ * A bitwise combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields,
+ * false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ExecuteOnScene().
+ * The function is a request to the process to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+
+ // setter for configMaxAngle
+ inline void SetMaxSmoothAngle(float f)
+ {
+ configMaxAngle =f;
+ }
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Computes normals for a specific mesh
+ * @param pcMesh Mesh
+ * @param meshIndex Index of the mesh
+ * @return true if vertex normals have been computed
+ */
+ bool GenMeshVertexNormals (aiMesh* pcMesh, unsigned int meshIndex);
+
+private:
+
+ /** Configuration option: maximum smoothing angle, in radians*/
+ float configMaxAngle;
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_GENVERTEXNORMALPROCESS_H_INC
+
diff --git a/3rdparty/assimp/code/GenericProperty.h b/3rdparty/assimp/code/GenericProperty.h
new file mode 100644
index 000000000..c64d392fe
--- /dev/null
+++ b/3rdparty/assimp/code/GenericProperty.h
@@ -0,0 +1,112 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 AI_GENERIC_PROPERTY_H_INCLUDED
+#define AI_GENERIC_PROPERTY_H_INCLUDED
+
+#include "./../include/assimp.hpp"
+#include "Hash.h"
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline void SetGenericProperty(std::map< unsigned int, T >& list,
+ const char* szName, const T& value, bool* bWasExisting = NULL)
+{
+ ai_assert(NULL != szName);
+ const uint32_t hash = SuperFastHash(szName);
+
+ typename std::map<unsigned int, T>::iterator it = list.find(hash);
+ if (it == list.end()) {
+ if (bWasExisting)
+ *bWasExisting = false;
+ list.insert(std::pair<unsigned int, T>( hash, value ));
+ return;
+ }
+ (*it).second = value;
+ if (bWasExisting)
+ *bWasExisting = true;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline const T& GetGenericProperty(const std::map< unsigned int, T >& list,
+ const char* szName, const T& errorReturn)
+{
+ ai_assert(NULL != szName);
+ const uint32_t hash = SuperFastHash(szName);
+
+ typename std::map<unsigned int, T>::const_iterator it = list.find(hash);
+ if (it == list.end())
+ return errorReturn;
+
+ return (*it).second;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Special version for pointer types - they will be deleted when replaced with another value
+// passing NULL removes the whole property
+template <class T>
+inline void SetGenericPropertyPtr(std::map< unsigned int, T* >& list,
+ const char* szName, T* value, bool* bWasExisting = NULL)
+{
+ ai_assert(NULL != szName);
+ const uint32_t hash = SuperFastHash(szName);
+
+ typename std::map<unsigned int, T*>::iterator it = list.find(hash);
+ if (it == list.end()) {
+ if (bWasExisting)
+ *bWasExisting = false;
+
+ list.insert(std::pair<unsigned int,T*>( hash, value ));
+ return;
+ }
+ if ((*it).second != value) {
+ delete (*it).second;
+ (*it).second = value;
+ }
+ if (!value) {
+ list.erase(it);
+ }
+ if (bWasExisting)
+ *bWasExisting = true;
+}
+
+
+#endif // !! AI_GENERIC_PROPERTY_H_INCLUDED
diff --git a/3rdparty/assimp/code/HMPFileData.h b/3rdparty/assimp/code/HMPFileData.h
new file mode 100644
index 000000000..5ac01bd9b
--- /dev/null
+++ b/3rdparty/assimp/code/HMPFileData.h
@@ -0,0 +1,134 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Data structures for the 3D Game Studio Heightmap format (HMP)
+//!
+
+namespace Assimp {
+namespace HMP {
+
+#include "./../include/Compiler/pushpack1.h"
+
+// to make it easier for us, we test the magic word against both "endianesses"
+#define AI_HMP_MAGIC_NUMBER_BE_4 AI_MAKE_MAGIC("HMP4")
+#define AI_HMP_MAGIC_NUMBER_LE_4 AI_MAKE_MAGIC("4PMH")
+
+#define AI_HMP_MAGIC_NUMBER_BE_5 AI_MAKE_MAGIC("HMP5")
+#define AI_HMP_MAGIC_NUMBER_LE_5 AI_MAKE_MAGIC("5PMH")
+
+#define AI_HMP_MAGIC_NUMBER_BE_7 AI_MAKE_MAGIC("HMP7")
+#define AI_HMP_MAGIC_NUMBER_LE_7 AI_MAKE_MAGIC("7PMH")
+
+// ---------------------------------------------------------------------------
+/** Data structure for the header of a HMP5 file.
+ * This is also used by HMP4 and HMP7, but with modifications
+*/
+struct Header_HMP5
+{
+ int8_t ident[4]; // "HMP5"
+ int32_t version;
+
+ // ignored
+ float scale[3];
+ float scale_origin[3];
+ float boundingradius;
+
+ //! Size of one triangle in x direction
+ float ftrisize_x;
+ //! Size of one triangle in y direction
+ float ftrisize_y;
+ //! Number of vertices in x direction
+ float fnumverts_x;
+
+ //! Number of skins in the file
+ int32_t numskins;
+
+ // can ignore this?
+ int32_t skinwidth;
+ int32_t skinheight;
+
+ //!Number of vertices in the file
+ int32_t numverts;
+
+ // ignored and zero
+ int32_t numtris;
+
+ //! only one supported ...
+ int32_t numframes;
+
+ //! Always 0 ...
+ int32_t num_stverts;
+ int32_t flags;
+ float size;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** Data structure for a terrain vertex in a HMP4 file
+*/
+struct Vertex_HMP4
+{
+ uint16_t p_pos[3];
+ uint8_t normals162index;
+ uint8_t pad;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** Data structure for a terrain vertex in a HMP5 file
+*/
+struct Vertex_HMP5
+{
+ uint16_t z;
+ uint8_t normals162index;
+ uint8_t pad;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** Data structure for a terrain vertex in a HMP7 file
+*/
+struct Vertex_HMP7
+{
+ uint16_t z;
+ int8_t normal_x,normal_y;
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+} //! namespace HMP
+} //! namespace Assimp
diff --git a/3rdparty/assimp/code/HMPLoader.cpp b/3rdparty/assimp/code/HMPLoader.cpp
new file mode 100644
index 000000000..f9323687b
--- /dev/null
+++ b/3rdparty/assimp/code/HMPLoader.cpp
@@ -0,0 +1,497 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the MDL importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER
+
+// internal headers
+#include "MaterialSystem.h"
+#include "HMPLoader.h"
+#include "MD2FileData.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+HMPImporter::HMPImporter()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+HMPImporter::~HMPImporter()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool HMPImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "hmp" )
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens
+ if (!extension.length() || cs) {
+ uint32_t tokens[3];
+ tokens[0] = AI_HMP_MAGIC_NUMBER_LE_4;
+ tokens[1] = AI_HMP_MAGIC_NUMBER_LE_5;
+ tokens[2] = AI_HMP_MAGIC_NUMBER_LE_7;
+ return CheckMagicToken(pIOHandler,pFile,tokens,3,0);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get list of all file extensions that are handled by this loader
+void HMPImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("hmp");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void HMPImporter::InternReadFile( const std::string& pFile,
+ aiScene* _pScene, IOSystem* _pIOHandler)
+{
+ pScene = _pScene;
+ pIOHandler = _pIOHandler;
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open HMP file " + pFile + ".");
+
+ // Check whether the HMP file is large enough to contain
+ // at least the file header
+ const size_t fileSize = file->FileSize();
+ if ( fileSize < 50)
+ throw DeadlyImportError( "HMP File is too small.");
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector<uint8_t> buffer(fileSize);
+ mBuffer = &buffer[0];
+ file->Read( (void*)mBuffer, 1, fileSize);
+ iFileSize = (unsigned int)fileSize;
+
+ // Determine the file subtype and call the appropriate member function
+ const uint32_t iMagic = *((uint32_t*)this->mBuffer);
+
+ // HMP4 format
+ if (AI_HMP_MAGIC_NUMBER_LE_4 == iMagic ||
+ AI_HMP_MAGIC_NUMBER_BE_4 == iMagic)
+ {
+ DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A4, magic word is HMP4");
+ InternReadFile_HMP4();
+ }
+ // HMP5 format
+ else if (AI_HMP_MAGIC_NUMBER_LE_5 == iMagic ||
+ AI_HMP_MAGIC_NUMBER_BE_5 == iMagic)
+ {
+ DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A5, magic word is HMP5");
+ InternReadFile_HMP5();
+ }
+ // HMP7 format
+ else if (AI_HMP_MAGIC_NUMBER_LE_7 == iMagic ||
+ AI_HMP_MAGIC_NUMBER_BE_7 == iMagic)
+ {
+ DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A7, magic word is HMP7");
+ InternReadFile_HMP7();
+ }
+ else
+ {
+ // Print the magic word to the logger
+ char szBuffer[5];
+ szBuffer[0] = ((char*)&iMagic)[0];
+ szBuffer[1] = ((char*)&iMagic)[1];
+ szBuffer[2] = ((char*)&iMagic)[2];
+ szBuffer[3] = ((char*)&iMagic)[3];
+ szBuffer[4] = '\0';
+
+ // We're definitely unable to load this file
+ throw DeadlyImportError( "Unknown HMP subformat " + pFile +
+ ". Magic word (" + szBuffer + ") is not known");
+ }
+
+ // Set the AI_SCENE_FLAGS_TERRAIN bit
+ pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN;
+
+ // File buffer destructs automatically now
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::ValidateHeader_HMP457( )
+{
+ const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)mBuffer;
+
+ if (120 > iFileSize)
+ {
+ throw DeadlyImportError("HMP file is too small (header size is "
+ "120 bytes, this file is smaller)");
+ }
+
+ if (!pcHeader->ftrisize_x || !pcHeader->ftrisize_y)
+ throw DeadlyImportError("Size of triangles in either x or y direction is zero");
+
+ if (pcHeader->fnumverts_x < 1.0f || (pcHeader->numverts/pcHeader->fnumverts_x) < 1.0f)
+ throw DeadlyImportError("Number of triangles in either x or y direction is zero");
+
+ if (!pcHeader->numframes)
+ throw DeadlyImportError("There are no frames. At least one should be there");
+
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::InternReadFile_HMP4( )
+{
+ throw DeadlyImportError("HMP4 is currently not supported");
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::InternReadFile_HMP5( )
+{
+ // read the file header and skip everything to byte 84
+ const HMP::Header_HMP5* pcHeader = (const HMP::Header_HMP5*)mBuffer;
+ const unsigned char* szCurrent = (const unsigned char*)(mBuffer+84);
+ ValidateHeader_HMP457();
+
+ // generate an output mesh
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+ aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
+
+ pcMesh->mMaterialIndex = 0;
+ pcMesh->mVertices = new aiVector3D[pcHeader->numverts];
+ pcMesh->mNormals = new aiVector3D[pcHeader->numverts];
+
+ const unsigned int height = (unsigned int)(pcHeader->numverts / pcHeader->fnumverts_x);
+ const unsigned int width = (unsigned int)pcHeader->fnumverts_x;
+
+ // generate/load a material for the terrain
+ CreateMaterial(szCurrent,&szCurrent);
+
+ // goto offset 120, I don't know why ...
+ // (fixme) is this the frame header? I assume yes since it starts with 2.
+ szCurrent += 36;
+ SizeCheck(szCurrent + sizeof(const HMP::Vertex_HMP7)*height*width);
+
+ // now load all vertices from the file
+ aiVector3D* pcVertOut = pcMesh->mVertices;
+ aiVector3D* pcNorOut = pcMesh->mNormals;
+ const HMP::Vertex_HMP5* src = (const HMP::Vertex_HMP5*) szCurrent;
+ for (unsigned int y = 0; y < height;++y)
+ {
+ for (unsigned int x = 0; x < width;++x)
+ {
+ pcVertOut->x = x * pcHeader->ftrisize_x;
+ pcVertOut->y = y * pcHeader->ftrisize_y;
+ pcVertOut->z = (((float)src->z / 0xffff)-0.5f) * pcHeader->ftrisize_x * 8.0f;
+ MD2::LookupNormalIndex(src->normals162index, *pcNorOut );
+ ++pcVertOut;++pcNorOut;++src;
+ }
+ }
+
+ // generate texture coordinates if necessary
+ if (pcHeader->numskins)
+ GenerateTextureCoords(width,height);
+
+ // now build a list of faces
+ CreateOutputFaceList(width,height);
+
+ // there is no nodegraph in HMP files. Simply assign the one mesh
+ // (no, not the one ring) to the root node
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("terrain_root");
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::InternReadFile_HMP7( )
+{
+ // read the file header and skip everything to byte 84
+ const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)mBuffer;
+ const unsigned char* szCurrent = (const unsigned char*)(mBuffer+84);
+ ValidateHeader_HMP457();
+
+ // generate an output mesh
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+ aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
+
+ pcMesh->mMaterialIndex = 0;
+ pcMesh->mVertices = new aiVector3D[pcHeader->numverts];
+ pcMesh->mNormals = new aiVector3D[pcHeader->numverts];
+
+ const unsigned int height = (unsigned int)(pcHeader->numverts / pcHeader->fnumverts_x);
+ const unsigned int width = (unsigned int)pcHeader->fnumverts_x;
+
+ // generate/load a material for the terrain
+ CreateMaterial(szCurrent,&szCurrent);
+
+ // goto offset 120, I don't know why ...
+ // (fixme) is this the frame header? I assume yes since it starts with 2.
+ szCurrent += 36;
+
+ SizeCheck(szCurrent + sizeof(const HMP::Vertex_HMP7)*height*width);
+
+ // now load all vertices from the file
+ aiVector3D* pcVertOut = pcMesh->mVertices;
+ aiVector3D* pcNorOut = pcMesh->mNormals;
+ const HMP::Vertex_HMP7* src = (const HMP::Vertex_HMP7*) szCurrent;
+ for (unsigned int y = 0; y < height;++y)
+ {
+ for (unsigned int x = 0; x < width;++x)
+ {
+ pcVertOut->x = x * pcHeader->ftrisize_x;
+ pcVertOut->y = y * pcHeader->ftrisize_y;
+
+ // FIXME: What exctly is the correct scaling factor to use?
+ // possibly pcHeader->scale_origin[2] in combination with a
+ // signed interpretation of src->z?
+ pcVertOut->z = (((float)src->z / 0xffff)-0.5f) * pcHeader->ftrisize_x * 8.0f;
+
+ pcNorOut->x = ((float)src->normal_x / 0x80 ); // * pcHeader->scale_origin[0];
+ pcNorOut->y = ((float)src->normal_y / 0x80 ); // * pcHeader->scale_origin[1];
+ pcNorOut->z = 1.0f;
+ pcNorOut->Normalize();
+
+ ++pcVertOut;++pcNorOut;++src;
+ }
+ }
+
+ // generate texture coordinates if necessary
+ if (pcHeader->numskins)GenerateTextureCoords(width,height);
+
+ // now build a list of faces
+ CreateOutputFaceList(width,height);
+
+ // there is no nodegraph in HMP files. Simply assign the one mesh
+ // (no, not the One Ring) to the root node
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("terrain_root");
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::CreateMaterial(const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut)
+{
+ aiMesh* const pcMesh = pScene->mMeshes[0];
+ const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)mBuffer;
+
+ // we don't need to generate texture coordinates if
+ // we have no textures in the file ...
+ if (pcHeader->numskins)
+ {
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcHeader->numverts];
+ pcMesh->mNumUVComponents[0] = 2;
+
+ // now read the first skin and skip all others
+ ReadFirstSkin(pcHeader->numskins,szCurrent,&szCurrent);
+ }
+ else
+ {
+ // generate a default material
+ const int iMode = (int)aiShadingMode_Gouraud;
+ MaterialHelper* pcHelper = new MaterialHelper();
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.6f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ aiString szName;
+ szName.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+
+ // add the material to the scene
+ pScene->mNumMaterials = 1;
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = pcHelper;
+ }
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::CreateOutputFaceList(unsigned int width,unsigned int height)
+{
+ aiMesh* const pcMesh = this->pScene->mMeshes[0];
+
+ // Allocate enough storage
+ pcMesh->mNumFaces = (width-1) * (height-1);
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+ pcMesh->mNumVertices = pcMesh->mNumFaces*4;
+ aiVector3D* pcVertices = new aiVector3D[pcMesh->mNumVertices];
+ aiVector3D* pcNormals = new aiVector3D[pcMesh->mNumVertices];
+
+ aiFace* pcFaceOut(pcMesh->mFaces);
+ aiVector3D* pcVertOut = pcVertices;
+ aiVector3D* pcNorOut = pcNormals;
+
+ aiVector3D* pcUVs = pcMesh->mTextureCoords[0] ? new aiVector3D[pcMesh->mNumVertices] : NULL;
+ aiVector3D* pcUVOut(pcUVs);
+
+ // Build the terrain square
+ unsigned int iCurrent = 0;
+ for (unsigned int y = 0; y < height-1;++y) {
+ for (unsigned int x = 0; x < width-1;++x,++pcFaceOut) {
+ pcFaceOut->mNumIndices = 4;
+ pcFaceOut->mIndices = new unsigned int[4];
+
+ *pcVertOut++ = pcMesh->mVertices[y*width+x];
+ *pcVertOut++ = pcMesh->mVertices[(y+1)*width+x];
+ *pcVertOut++ = pcMesh->mVertices[(y+1)*width+x+1];
+ *pcVertOut++ = pcMesh->mVertices[y*width+x+1];
+
+
+ *pcNorOut++ = pcMesh->mNormals[y*width+x];
+ *pcNorOut++ = pcMesh->mNormals[(y+1)*width+x];
+ *pcNorOut++ = pcMesh->mNormals[(y+1)*width+x+1];
+ *pcNorOut++ = pcMesh->mNormals[y*width+x+1];
+
+ if (pcMesh->mTextureCoords[0])
+ {
+ *pcUVOut++ = pcMesh->mTextureCoords[0][y*width+x];
+ *pcUVOut++ = pcMesh->mTextureCoords[0][(y+1)*width+x];
+ *pcUVOut++ = pcMesh->mTextureCoords[0][(y+1)*width+x+1];
+ *pcUVOut++ = pcMesh->mTextureCoords[0][y*width+x+1];
+ }
+
+ for (unsigned int i = 0; i < 4;++i)
+ pcFaceOut->mIndices[i] = iCurrent++;
+ }
+ }
+ delete[] pcMesh->mVertices;
+ pcMesh->mVertices = pcVertices;
+
+ delete[] pcMesh->mNormals;
+ pcMesh->mNormals = pcNormals;
+
+ if (pcMesh->mTextureCoords[0])
+ {
+ delete[] pcMesh->mTextureCoords[0];
+ pcMesh->mTextureCoords[0] = pcUVs;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char* szCursor,
+ const unsigned char** szCursorOut)
+{
+ ai_assert(0 != iNumSkins && NULL != szCursor);
+
+ // read the type of the skin ...
+ // sometimes we need to skip 12 bytes here, I don't know why ...
+ uint32_t iType = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
+ if (0 == iType)
+ {
+ szCursor += sizeof(uint32_t) * 2;
+ iType = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
+ if (!iType)
+ throw DeadlyImportError("Unable to read HMP7 skin chunk");
+
+ }
+ // read width and height
+ uint32_t iWidth = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
+ uint32_t iHeight = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
+
+ // allocate an output material
+ MaterialHelper* pcMat = new MaterialHelper();
+
+ // read the skin, this works exactly as for MDL7
+ ParseSkinLump_3DGS_MDL7(szCursor,&szCursor,
+ pcMat,iType,iWidth,iHeight);
+
+ // now we need to skip any other skins ...
+ for (unsigned int i = 1; i< iNumSkins;++i)
+ {
+ iType = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
+ iWidth = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
+ iHeight = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
+
+ SkipSkinLump_3DGS_MDL7(szCursor,&szCursor,iType,iWidth,iHeight);
+ SizeCheck(szCursor);
+ }
+
+ // setup the material ...
+ pScene->mNumMaterials = 1;
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = pcMat;
+
+ *szCursorOut = szCursor;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Generate proepr texture coords
+void HMPImporter::GenerateTextureCoords(
+ const unsigned int width, const unsigned int height)
+{
+ ai_assert(NULL != pScene->mMeshes && NULL != pScene->mMeshes[0] &&
+ NULL != pScene->mMeshes[0]->mTextureCoords[0]);
+
+ aiVector3D* uv = pScene->mMeshes[0]->mTextureCoords[0];
+
+ const float fY = (1.0f / height) + (1.0f / height) / (height-1);
+ const float fX = (1.0f / width) + (1.0f / width) / (width-1);
+
+ for (unsigned int y = 0; y < height;++y) {
+ for (unsigned int x = 0; x < width;++x,++uv) {
+ uv->y = fY*y;
+ uv->x = fX*x;
+ uv->z = 0.0f;
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_HMP_IMPORTER
diff --git a/3rdparty/assimp/code/HMPLoader.h b/3rdparty/assimp/code/HMPLoader.h
new file mode 100644
index 000000000..fdf84baaf
--- /dev/null
+++ b/3rdparty/assimp/code/HMPLoader.h
@@ -0,0 +1,159 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 HMPLoader.h
+ * @brief Declaration of the HMP importer class
+ */
+
+#ifndef AI_HMPLOADER_H_INCLUDED
+#define AI_HMPLOADER_H_INCLUDED
+
+// public ASSIMP headers
+#include "../include/aiTypes.h"
+#include "../include/aiTexture.h"
+#include "../include/aiMaterial.h"
+
+// internal headers
+#include "BaseImporter.h"
+#include "MDLLoader.h"
+#include "HMPFileData.h"
+
+namespace Assimp {
+using namespace HMP;
+
+// ---------------------------------------------------------------------------
+/** Used to load 3D GameStudio HMP files (terrains)
+*/
+class HMPImporter : public MDLImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ HMPImporter();
+
+ /** Destructor, private as well */
+ ~HMPImporter();
+
+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;
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Import a HMP4 file
+ */
+ void InternReadFile_HMP4( );
+
+ // -------------------------------------------------------------------
+ /** Import a HMP5 file
+ */
+ void InternReadFile_HMP5( );
+
+ // -------------------------------------------------------------------
+ /** Import a HMP7 file
+ */
+ void InternReadFile_HMP7( );
+
+ // -------------------------------------------------------------------
+ /** Validate a HMP 5,4,7 file header
+ */
+ void ValidateHeader_HMP457( );
+
+ // -------------------------------------------------------------------
+ /** Try to load one material from the file, if this fails create
+ * a default material
+ */
+ void CreateMaterial(const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Build a list of output faces and vertices. The function
+ * triangulates the height map read from the file
+ * \param width Width of the height field
+ * \param width Height of the height field
+ */
+ void CreateOutputFaceList(unsigned int width,unsigned int height);
+
+ // -------------------------------------------------------------------
+ /** Generate planar texture coordinates for a terrain
+ * \param width Width of the terrain, in vertices
+ * \param height Height of the terrain, in vertices
+ */
+ void GenerateTextureCoords(const unsigned int width,
+ const unsigned int height);
+
+ // -------------------------------------------------------------------
+ /** Read the first skin from the file and skip all others ...
+ * \param iNumSkins Number of skins in the file
+ * \param szCursor Position of the first skin (offset 84)
+ */
+ void ReadFirstSkin(unsigned int iNumSkins, const unsigned char* szCursor,
+ const unsigned char** szCursorOut);
+
+private:
+
+};
+
+} // end of namespace Assimp
+
+#endif // AI_HMPIMPORTER_H_INC
+
diff --git a/3rdparty/assimp/code/HalfLifeFileData.h b/3rdparty/assimp/code/HalfLifeFileData.h
new file mode 100644
index 000000000..ab2cbbd54
--- /dev/null
+++ b/3rdparty/assimp/code/HalfLifeFileData.h
@@ -0,0 +1,150 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 in-memory structures for the HL2 MDL file format
+// and for the HalfLife text format (SMD)
+//
+// The specification has been taken from various sources on the internet.
+
+
+#ifndef AI_MDLFILEHELPER2_H_INC
+#define AI_MDLFILEHELPER2_H_INC
+
+#include "./../include/Compiler/pushpack1.h"
+
+#include "MDLFileData.h"
+
+namespace Assimp {
+namespace MDL {
+
+// magic bytes used in Half Life 2 MDL models
+#define AI_MDL_MAGIC_NUMBER_BE_HL2a AI_MAKE_MAGIC("IDST")
+#define AI_MDL_MAGIC_NUMBER_LE_HL2a AI_MAKE_MAGIC("TSDI")
+#define AI_MDL_MAGIC_NUMBER_BE_HL2b AI_MAKE_MAGIC("IDSQ")
+#define AI_MDL_MAGIC_NUMBER_LE_HL2b AI_MAKE_MAGIC("QSDI")
+
+// ---------------------------------------------------------------------------
+/** \struct Header_HL2
+ * \brief Data structure for the HL2 main header
+ */
+// ---------------------------------------------------------------------------
+struct Header_HL2
+{
+ //! magic number: "IDST"/"IDSQ"
+ char ident[4];
+
+ //! Version number
+ int32_t version;
+
+ //! Original file name in pak ?
+ char name[64];
+
+ //! Length of file name/length of file?
+ int32_t length;
+
+ //! For viewer, ignored
+ aiVector3D eyeposition;
+ aiVector3D min;
+ aiVector3D max;
+
+ //! AABB of the model
+ aiVector3D bbmin;
+ aiVector3D bbmax;
+
+ // File flags
+ int32_t flags;
+
+ //! NUmber of bones contained in the file
+ int32_t numbones;
+ int32_t boneindex;
+
+ //! Number of bone controllers for bone animation
+ int32_t numbonecontrollers;
+ int32_t bonecontrollerindex;
+
+ //! More bounding boxes ...
+ int32_t numhitboxes;
+ int32_t hitboxindex;
+
+ //! Animation sequences in the file
+ int32_t numseq;
+ int32_t seqindex;
+
+ //! Loaded sequences. Ignored
+ int32_t numseqgroups;
+ int32_t seqgroupindex;
+
+ //! Raw texture data
+ int32_t numtextures;
+ int32_t textureindex;
+ int32_t texturedataindex;
+
+ //! Number of skins (=textures?)
+ int32_t numskinref;
+ int32_t numskinfamilies;
+ int32_t skinindex;
+
+ //! Number of parts
+ int32_t numbodyparts;
+ int32_t bodypartindex;
+
+ //! attachable points for gameplay and physics
+ int32_t numattachments;
+ int32_t attachmentindex;
+
+ //! Table of sound effects associated with the model
+ int32_t soundtable;
+ int32_t soundindex;
+ int32_t soundgroups;
+ int32_t soundgroupindex;
+
+ //! Number of animation transitions
+ int32_t numtransitions;
+ int32_t transitionindex;
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+}
+} // end namespaces
+
+#endif // ! AI_MDLFILEHELPER2_H_INC
diff --git a/3rdparty/assimp/code/Hash.h b/3rdparty/assimp/code/Hash.h
new file mode 100644
index 000000000..9ba79c512
--- /dev/null
+++ b/3rdparty/assimp/code/Hash.h
@@ -0,0 +1,108 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 AI_HASH_H_INCLUDED
+#define AI_HASH_H_INCLUDED
+
+// ------------------------------------------------------------------------------------------------
+// hashing function taken from
+// http://www.azillionmonkeys.com/qed/hash.html
+// (incremental version of the hashing function)
+// (stdint.h should have been been included here)
+// ------------------------------------------------------------------------------------------------
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+
+// ------------------------------------------------------------------------------------------------
+inline unsigned int SuperFastHash (const char * data, unsigned int len = 0, unsigned int hash = 0) {
+unsigned int tmp;
+int rem;
+
+ if (!data) return 0;
+ if (!len)len = (unsigned int)::strlen(data);
+
+ rem = len & 3;
+ len >>= 2;
+
+ /* Main loop */
+ for (;len > 0; len--) {
+ hash += get16bits (data);
+ tmp = (get16bits (data+2) << 11) ^ hash;
+ hash = (hash << 16) ^ tmp;
+ data += 2*sizeof (uint16_t);
+ hash += hash >> 11;
+ }
+
+ /* Handle end cases */
+ switch (rem) {
+ case 3: hash += get16bits (data);
+ hash ^= hash << 16;
+ hash ^= data[sizeof (uint16_t)] << 18;
+ hash += hash >> 11;
+ break;
+ case 2: hash += get16bits (data);
+ hash ^= hash << 11;
+ hash += hash >> 17;
+ break;
+ case 1: hash += *data;
+ hash ^= hash << 10;
+ hash += hash >> 1;
+ }
+
+ /* Force "avalanching" of final 127 bits */
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+
+ return hash;
+}
+
+#endif // !! AI_HASH_H_INCLUDED
diff --git a/3rdparty/assimp/code/IFF.h b/3rdparty/assimp/code/IFF.h
new file mode 100644
index 000000000..e1bfa2103
--- /dev/null
+++ b/3rdparty/assimp/code/IFF.h
@@ -0,0 +1,102 @@
+
+
+// Definitions for the Interchange File Format (IFF)
+// Alexander Gessler, 2006
+// Adapted to Assimp August 2008
+
+#ifndef AI_IFF_H_INCLUDED
+#define AI_IFF_H_INCLUDED
+
+#include "ByteSwap.h"
+
+namespace Assimp {
+namespace IFF {
+
+#include "./../include/Compiler/pushpack1.h"
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Describes an IFF chunk header
+/////////////////////////////////////////////////////////////////////////////////
+struct ChunkHeader
+{
+ //! Type of the chunk header - FourCC
+ uint32_t type;
+
+ //! Length of the chunk data, in bytes
+ uint32_t length;
+} PACK_STRUCT;
+
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Describes an IFF sub chunk header
+/////////////////////////////////////////////////////////////////////////////////
+struct SubChunkHeader
+{
+ //! Type of the chunk header - FourCC
+ uint32_t type;
+
+ //! Length of the chunk data, in bytes
+ uint16_t length;
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+
+#define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \
+ ((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d)))
+
+
+#define AI_IFF_FOURCC_FORM AI_IFF_FOURCC('F','O','R','M')
+
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Load a chunk header
+//! @param outFile Pointer to the file data - points to the chunk data afterwards
+//! @return Pointer to the chunk header
+/////////////////////////////////////////////////////////////////////////////////
+inline ChunkHeader* LoadChunk(uint8_t*& outFile)
+{
+ ChunkHeader* head = (ChunkHeader*) outFile;
+ AI_LSWAP4(head->length);
+ AI_LSWAP4(head->type);
+ outFile += sizeof(ChunkHeader);
+ return head;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Load a sub chunk header
+//! @param outFile Pointer to the file data - points to the chunk data afterwards
+//! @return Pointer to the sub chunk header
+/////////////////////////////////////////////////////////////////////////////////
+inline SubChunkHeader* LoadSubChunk(uint8_t*& outFile)
+{
+ SubChunkHeader* head = (SubChunkHeader*) outFile;
+ AI_LSWAP2(head->length);
+ AI_LSWAP4(head->type);
+ outFile += sizeof(SubChunkHeader);
+ return head;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+//! Read the file header and return the type of the file and its size
+//! @param outFile Pointer to the file data. The buffer must at
+//! least be 12 bytes large.
+//! @param fileType Receives the type of the file
+//! @return 0 if everything was OK, otherwise an error message
+/////////////////////////////////////////////////////////////////////////////////
+inline const char* ReadHeader(uint8_t* outFile,uint32_t& fileType)
+{
+ ChunkHeader* head = LoadChunk(outFile);
+ if (AI_IFF_FOURCC_FORM != head->type)
+ {
+ return "The file is not an IFF file: FORM chunk is missing";
+ }
+ fileType = *((uint32_t*)(head+1));
+ AI_LSWAP4(fileType);
+ return 0;
+}
+
+
+}}
+
+#endif // !! AI_IFF_H_INCLUDED
diff --git a/3rdparty/assimp/code/IRRLoader.cpp b/3rdparty/assimp/code/IRRLoader.cpp
new file mode 100644
index 000000000..20a94c09e
--- /dev/null
+++ b/3rdparty/assimp/code/IRRLoader.cpp
@@ -0,0 +1,1462 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 IRRLoader.cpp
+ * @brief Implementation of the Irr importer class
+ */
+
+#include "AssimpPCH.h"
+
+#include "IRRLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+#include "GenericProperty.h"
+
+#include "SceneCombiner.h"
+#include "StandardShapes.h"
+
+
+// We need boost::common_factor to compute the lcm/gcd of a number
+#include <boost/math/common_factor_rt.hpp>
+
+using namespace Assimp;
+using namespace irr;
+using namespace irr::io;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+IRRImporter::IRRImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+IRRImporter::~IRRImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool IRRImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ /* NOTE: A simple check for the file extension is not enough
+ * here. Irrmesh and irr are easy, but xml is too generic
+ * and could be collada, too. So we need to open the file and
+ * search for typical tokens.
+ */
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "irr")return true;
+ else if (extension == "xml" || checkSig)
+ {
+ /* If CanRead() is called in order to check whether we
+ * support a specific file extension in general pIOHandler
+ * might be NULL and it's our duty to return true here.
+ */
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"irr_scene"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("irr");
+ extensions.insert("xml");
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::SetupProperties(const Importer* pImp)
+{
+ // read the output frame rate of all node animation channels
+ fps = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_IRR_ANIM_FPS,100);
+ if (fps < 10.) {
+ DefaultLogger::get()->error("IRR: Invalid FPS configuration");
+ fps = 100;
+ }
+
+ // AI_CONFIG_FAVOUR_SPEED
+ configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a mesh tha consists of a single squad (a side of a skybox)
+aiMesh* IRRImporter::BuildSingleQuadMesh(const SkyboxVertex& v1,
+ const SkyboxVertex& v2,
+ const SkyboxVertex& v3,
+ const SkyboxVertex& v4)
+{
+ // allocate and prepare the mesh
+ aiMesh* out = new aiMesh();
+
+ out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
+ out->mNumFaces = 1;
+
+ // build the face
+ out->mFaces = new aiFace[1];
+ aiFace& face = out->mFaces[0];
+
+ face.mNumIndices = 4;
+ face.mIndices = new unsigned int[4];
+ for (unsigned int i = 0; i < 4;++i)
+ face.mIndices[i] = i;
+
+ out->mNumVertices = 4;
+
+ // copy vertex positions
+ aiVector3D* vec = out->mVertices = new aiVector3D[4];
+ *vec++ = v1.position;
+ *vec++ = v2.position;
+ *vec++ = v3.position;
+ *vec = v4.position;
+
+ // copy vertex normals
+ vec = out->mNormals = new aiVector3D[4];
+ *vec++ = v1.normal;
+ *vec++ = v2.normal;
+ *vec++ = v3.normal;
+ *vec = v4.normal;
+
+ // copy texture coordinates
+ vec = out->mTextureCoords[0] = new aiVector3D[4];
+ *vec++ = v1.uv;
+ *vec++ = v2.uv;
+ *vec++ = v3.uv;
+ *vec = v4.uv;
+ return out;
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::BuildSkybox(std::vector<aiMesh*>& meshes, std::vector<aiMaterial*> materials)
+{
+ // Update the material of the skybox - replace the name and disable shading for skyboxes.
+ for (unsigned int i = 0; i < 6;++i) {
+ MaterialHelper* out = ( MaterialHelper* ) (*(materials.end()-(6-i)));
+
+ aiString s;
+ s.length = ::sprintf( s.data, "SkyboxSide_%i",i );
+ out->AddProperty(&s,AI_MATKEY_NAME);
+
+ int shading = aiShadingMode_NoShading;
+ out->AddProperty(&shading,1,AI_MATKEY_SHADING_MODEL);
+ }
+
+ // Skyboxes are much more difficult. They are represented
+ // by six single planes with different textures, so we'll
+ // need to build six meshes.
+
+ const float l = 10.f; // the size used by Irrlicht
+
+ // FRONT SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex(-l,-l,-l, 0, 0, 1, 1.f,1.f),
+ SkyboxVertex( l,-l,-l, 0, 0, 1, 0.f,1.f),
+ SkyboxVertex( l, l,-l, 0, 0, 1, 0.f,0.f),
+ SkyboxVertex(-l, l,-l, 0, 0, 1, 1.f,0.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-6u;
+
+ // LEFT SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex( l,-l,-l, -1, 0, 0, 1.f,1.f),
+ SkyboxVertex( l,-l, l, -1, 0, 0, 0.f,1.f),
+ SkyboxVertex( l, l, l, -1, 0, 0, 0.f,0.f),
+ SkyboxVertex( l, l,-l, -1, 0, 0, 1.f,0.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-5u;
+
+ // BACK SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex( l,-l, l, 0, 0, -1, 1.f,1.f),
+ SkyboxVertex(-l,-l, l, 0, 0, -1, 0.f,1.f),
+ SkyboxVertex(-l, l, l, 0, 0, -1, 0.f,0.f),
+ SkyboxVertex( l, l, l, 0, 0, -1, 1.f,0.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-4u;
+
+ // RIGHT SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex(-l,-l, l, 1, 0, 0, 1.f,1.f),
+ SkyboxVertex(-l,-l,-l, 1, 0, 0, 0.f,1.f),
+ SkyboxVertex(-l, l,-l, 1, 0, 0, 0.f,0.f),
+ SkyboxVertex(-l, l, l, 1, 0, 0, 1.f,0.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-3u;
+
+ // TOP SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex( l, l,-l, 0, -1, 0, 1.f,1.f),
+ SkyboxVertex( l, l, l, 0, -1, 0, 0.f,1.f),
+ SkyboxVertex(-l, l, l, 0, -1, 0, 0.f,0.f),
+ SkyboxVertex(-l, l,-l, 0, -1, 0, 1.f,0.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-2u;
+
+ // BOTTOM SIDE
+ meshes.push_back( BuildSingleQuadMesh(
+ SkyboxVertex( l,-l, l, 0, 1, 0, 0.f,0.f),
+ SkyboxVertex( l,-l,-l, 0, 1, 0, 1.f,0.f),
+ SkyboxVertex(-l,-l,-l, 0, 1, 0, 1.f,1.f),
+ SkyboxVertex(-l,-l, l, 0, 1, 0, 0.f,1.f)) );
+ meshes.back()->mMaterialIndex = materials.size()-1u;
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::CopyMaterial(std::vector<aiMaterial*>& materials,
+ std::vector< std::pair<aiMaterial*, unsigned int> >& inmaterials,
+ unsigned int& defMatIdx,
+ aiMesh* mesh)
+{
+ if (inmaterials.empty()) {
+ // Do we have a default material? If not we need to create one
+ if (0xffffffff == defMatIdx)
+ {
+ defMatIdx = (unsigned int)materials.size();
+ MaterialHelper* mat = new MaterialHelper();
+
+ aiString s;
+ s.Set(AI_DEFAULT_MATERIAL_NAME);
+ mat->AddProperty(&s,AI_MATKEY_NAME);
+
+ aiColor3D c(0.6f,0.6f,0.6f);
+ mat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);
+ }
+ mesh->mMaterialIndex = defMatIdx;
+ return;
+ }
+ else if (inmaterials.size() > 1) {
+ DefaultLogger::get()->info("IRR: Skipping additional materials");
+ }
+
+ mesh->mMaterialIndex = (unsigned int)materials.size();
+ materials.push_back(inmaterials[0].first);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+inline int ClampSpline(int idx, int size)
+{
+ return ( idx<0 ? size+idx : ( idx>=size ? idx-size : idx ) );
+}
+
+// ------------------------------------------------------------------------------------------------
+inline void FindSuitableMultiple(int& angle)
+{
+ if (angle < 3)angle = 3;
+ else if (angle < 10) angle = 10;
+ else if (angle < 20) angle = 20;
+ else if (angle < 30) angle = 30;
+ else
+ {
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vector<aiNodeAnim*>& anims)
+{
+ ai_assert(NULL != root && NULL != real);
+
+ // XXX totally WIP - doesn't produce proper results, need to evaluate
+ // whether there's any use for Irrlicht's proprietary scene format
+ // outside Irrlicht ...
+
+ if (root->animators.empty()) {
+ return;
+ }
+ unsigned int total = 0;
+ for (std::list<Animator>::iterator it = root->animators.begin();it != root->animators.end(); ++it) {
+ if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER) {
+ DefaultLogger::get()->warn("IRR: Skipping unknown or unsupported animator");
+ continue;
+ }
+ ++total;
+ }
+ if (!total)return;
+ else if (1 == total) {
+ DefaultLogger::get()->warn("IRR: Adding dummy nodes to simulate multiple animators");
+ }
+
+ // NOTE: 1 tick == i millisecond
+
+ unsigned int cur = 0;
+ for (std::list<Animator>::iterator it = root->animators.begin();
+ it != root->animators.end(); ++it)
+ {
+ if ((*it).type == Animator::UNKNOWN || (*it).type == Animator::OTHER)continue;
+
+ Animator& in = *it ;
+ aiNodeAnim* anim = new aiNodeAnim();
+
+ if (cur != total-1) {
+ // Build a new name - a prefix instead of a suffix because it is
+ // easier to check against
+ anim->mNodeName.length = ::sprintf(anim->mNodeName.data,
+ "$INST_DUMMY_%i_%s",total-1,
+ (root->name.length() ? root->name.c_str() : ""));
+
+ // we'll also need to insert a dummy in the node hierarchy.
+ aiNode* dummy = new aiNode();
+
+ for (unsigned int i = 0; i < real->mParent->mNumChildren;++i)
+ if (real->mParent->mChildren[i] == real)
+ real->mParent->mChildren[i] = dummy;
+
+ dummy->mParent = real->mParent;
+ dummy->mName = anim->mNodeName;
+
+ dummy->mNumChildren = 1;
+ dummy->mChildren = new aiNode*[dummy->mNumChildren];
+ dummy->mChildren[0] = real;
+
+ // the transformation matrix of the dummy node is the identity
+
+ real->mParent = dummy;
+ }
+ else anim->mNodeName.Set(root->name);
+ ++cur;
+
+ switch (in.type) {
+ case Animator::ROTATION:
+ {
+ // -----------------------------------------------------
+ // find out how long a full rotation will take
+ // This is the least common multiple of 360.f and all
+ // three euler angles. Although we'll surely find a
+ // possible multiple (haha) it could be somewhat large
+ // for our purposes. So we need to modify the angles
+ // here in order to get good results.
+ // -----------------------------------------------------
+ int angles[3];
+ angles[0] = (int)(in.direction.x*100);
+ angles[1] = (int)(in.direction.y*100);
+ angles[2] = (int)(in.direction.z*100);
+
+ angles[0] %= 360;
+ angles[1] %= 360;
+ angles[2] %= 360;
+
+ if ((angles[0]*angles[1]) && (angles[1]*angles[2]))
+ {
+ FindSuitableMultiple(angles[0]);
+ FindSuitableMultiple(angles[1]);
+ FindSuitableMultiple(angles[2]);
+ }
+
+ int lcm = 360;
+
+ if (angles[0])
+ lcm = boost::math::lcm(lcm,angles[0]);
+
+ if (angles[1])
+ lcm = boost::math::lcm(lcm,angles[1]);
+
+ if (angles[2])
+ lcm = boost::math::lcm(lcm,angles[2]);
+
+ if (360 == lcm)
+ break;
+
+#if 0
+ // This can be a division through zero, but we don't care
+ float f1 = (float)lcm / angles[0];
+ float f2 = (float)lcm / angles[1];
+ float f3 = (float)lcm / angles[2];
+#endif
+
+ // find out how many time units we'll need for the finest
+ // track (in seconds) - this defines the number of output
+ // keys (fps * seconds)
+ float max = 0.f;
+ if (angles[0])
+ max = (float)lcm / angles[0];
+ if (angles[1])
+ max = std::max(max, (float)lcm / angles[1]);
+ if (angles[2])
+ max = std::max(max, (float)lcm / angles[2]);
+
+ anim->mNumRotationKeys = (unsigned int)(max*fps);
+ anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
+
+ // begin with a zero angle
+ aiVector3D angle;
+ for (unsigned int i = 0; i < anim->mNumRotationKeys;++i)
+ {
+ // build the quaternion for the given euler angles
+ aiQuatKey& q = anim->mRotationKeys[i];
+
+ q.mValue = aiQuaternion(angle.x, angle.y, angle.z);
+ q.mTime = (double)i;
+
+ // increase the angle
+ angle += in.direction;
+ }
+
+ // This animation is repeated and repeated ...
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+ }
+ break;
+
+ case Animator::FLY_CIRCLE:
+ {
+ // -----------------------------------------------------
+ // Find out how much time we'll need to perform a
+ // full circle.
+ // -----------------------------------------------------
+ const double seconds = (1. / in.speed) / 1000.;
+ const double tdelta = 1000. / fps;
+
+ anim->mNumPositionKeys = (unsigned int) (fps * seconds);
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ // from Irrlicht, what else should we do than copying it?
+ aiVector3D vecU,vecV;
+ if (in.direction.y) {
+ vecV = aiVector3D(50,0,0) ^ in.direction;
+ }
+ else vecV = aiVector3D(0,50,00) ^ in.direction;
+ vecV.Normalize();
+ vecU = (vecV ^ in.direction).Normalize();
+
+ // build the output keys
+ for (unsigned int i = 0; i < anim->mNumPositionKeys;++i) {
+ aiVectorKey& key = anim->mPositionKeys[i];
+ key.mTime = i * tdelta;
+
+ const float t = (float) ( in.speed * key.mTime );
+ key.mValue = in.circleCenter + in.circleRadius * ((vecU*::cos(t)) + (vecV*::sin(t)));
+ }
+
+ // This animation is repeated and repeated ...
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+ }
+ break;
+
+ case Animator::FLY_STRAIGHT:
+ {
+ anim->mPostState = anim->mPreState = (in.loop ? aiAnimBehaviour_REPEAT : aiAnimBehaviour_CONSTANT);
+ const double seconds = in.timeForWay / 1000.;
+ const double tdelta = 1000. / fps;
+
+ anim->mNumPositionKeys = (unsigned int) (fps * seconds);
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ aiVector3D diff = in.direction - in.circleCenter;
+ const float lengthOfWay = diff.Length();
+ diff.Normalize();
+
+ const double timeFactor = lengthOfWay / in.timeForWay;
+
+ // build the output keys
+ for (unsigned int i = 0; i < anim->mNumPositionKeys;++i) {
+ aiVectorKey& key = anim->mPositionKeys[i];
+ key.mTime = i * tdelta;
+ key.mValue = in.circleCenter + diff * float(timeFactor * key.mTime);
+ }
+ }
+ break;
+
+ case Animator::FOLLOW_SPLINE:
+ {
+ // repeat outside the defined time range
+ anim->mPostState = anim->mPreState = aiAnimBehaviour_REPEAT;
+ const int size = (int)in.splineKeys.size();
+ if (!size) {
+ // We have no point in the spline. That's bad. Really bad.
+ DefaultLogger::get()->warn("IRR: Spline animators with no points defined");
+
+ delete anim;anim = NULL;
+ break;
+ }
+ else if (size == 1) {
+ // We have just one point in the spline so we don't need the full calculation
+ anim->mNumPositionKeys = 1;
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ anim->mPositionKeys[0].mValue = in.splineKeys[0].mValue;
+ anim->mPositionKeys[0].mTime = 0.f;
+ break;
+ }
+
+ unsigned int ticksPerFull = 15;
+ anim->mNumPositionKeys = (unsigned int) ( ticksPerFull * fps );
+ anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
+
+ for (unsigned int i = 0; i < anim->mNumPositionKeys;++i)
+ {
+ aiVectorKey& key = anim->mPositionKeys[i];
+
+ const float dt = (i * in.speed * 0.001f );
+ const float u = dt - floor(dt);
+ const int idx = (int)floor(dt) % size;
+
+ // get the 4 current points to evaluate the spline
+ const aiVector3D& p0 = in.splineKeys[ ClampSpline( idx - 1, size ) ].mValue;
+ const aiVector3D& p1 = in.splineKeys[ ClampSpline( idx + 0, size ) ].mValue;
+ const aiVector3D& p2 = in.splineKeys[ ClampSpline( idx + 1, size ) ].mValue;
+ const aiVector3D& p3 = in.splineKeys[ ClampSpline( idx + 2, size ) ].mValue;
+
+ // compute polynomials
+ const float u2 = u*u;
+ const float u3 = u2*2;
+
+ const float h1 = 2.0f * u3 - 3.0f * u2 + 1.0f;
+ const float h2 = -2.0f * u3 + 3.0f * u3;
+ const float h3 = u3 - 2.0f * u3;
+ const float h4 = u3 - u2;
+
+ // compute the spline tangents
+ const aiVector3D t1 = ( p2 - p0 ) * in.tightness;
+ aiVector3D t2 = ( p3 - p1 ) * in.tightness;
+
+ // and use them to get the interpolated point
+ t2 = (h1 * p1 + p2 * h2 + t1 * h3 + h4 * t2);
+
+ // build a simple translation matrix from it
+ key.mValue = t2.x;
+ key.mTime = (double) i;
+ }
+ }
+ break;
+ default:
+ // UNKNOWN , OTHER
+ break;
+ };
+ if (anim) {
+ anims.push_back(anim);
+ ++total;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// This function is maybe more generic than we'd need it here
+void SetupMapping (MaterialHelper* mat, aiTextureMapping mode, const aiVector3D& axis = aiVector3D(0.f,0.f,-1.f))
+{
+ // Check whether there are texture properties defined - setup
+ // the desired texture mapping mode for all of them and ignore
+ // all UV settings we might encounter. WE HAVE NO UVS!
+
+ std::vector<aiMaterialProperty*> p;
+ p.reserve(mat->mNumProperties+1);
+
+ for (unsigned int i = 0; i < mat->mNumProperties;++i)
+ {
+ aiMaterialProperty* prop = mat->mProperties[i];
+ if (!::strcmp( prop->mKey.data, "$tex.file")) {
+ // Setup the mapping key
+ aiMaterialProperty* m = new aiMaterialProperty();
+ m->mKey.Set("$tex.mapping");
+ m->mIndex = prop->mIndex;
+ m->mSemantic = prop->mSemantic;
+ m->mType = aiPTI_Integer;
+
+ m->mDataLength = 4;
+ m->mData = new char[4];
+ *((int*)m->mData) = mode;
+
+ p.push_back(prop);
+ p.push_back(m);
+
+ // Setup the mapping axis
+ if (mode == aiTextureMapping_CYLINDER || mode == aiTextureMapping_PLANE || mode == aiTextureMapping_SPHERE) {
+ m = new aiMaterialProperty();
+ m->mKey.Set("$tex.mapaxis");
+ m->mIndex = prop->mIndex;
+ m->mSemantic = prop->mSemantic;
+ m->mType = aiPTI_Float;
+
+ m->mDataLength = 12;
+ m->mData = new char[12];
+ *((aiVector3D*)m->mData) = axis;
+ p.push_back(m);
+ }
+ }
+ else if (! ::strcmp( prop->mKey.data, "$tex.uvwsrc")) {
+ delete mat->mProperties[i];
+ }
+ else p.push_back(prop);
+ }
+
+ if (p.empty())return;
+
+ // rebuild the output array
+ if (p.size() > mat->mNumAllocated) {
+ delete[] mat->mProperties;
+ mat->mProperties = new aiMaterialProperty*[p.size()*2];
+
+ mat->mNumAllocated = p.size()*2;
+ }
+ mat->mNumProperties = (unsigned int)p.size();
+ ::memcpy(mat->mProperties,&p[0],sizeof(void*)*mat->mNumProperties);
+}
+
+// ------------------------------------------------------------------------------------------------
+void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
+ BatchLoader& batch,
+ std::vector<aiMesh*>& meshes,
+ std::vector<aiNodeAnim*>& anims,
+ std::vector<AttachmentInfo>& attach,
+ std::vector<aiMaterial*>& materials,
+ unsigned int& defMatIdx)
+{
+ unsigned int oldMeshSize = (unsigned int)meshes.size();
+ //unsigned int meshTrafoAssign = 0;
+
+ // Now determine the type of the node
+ switch (root->type)
+ {
+ case Node::ANIMMESH:
+ case Node::MESH:
+ {
+ if (!root->meshPath.length())
+ break;
+
+ // Get the loaded mesh from the scene and add it to
+ // the list of all scenes to be attached to the
+ // graph we're currently building
+ aiScene* scene = batch.GetImport(root->id);
+ if (!scene) {
+ DefaultLogger::get()->error("IRR: Unable to load external file: " + root->meshPath);
+ break;
+ }
+ attach.push_back(AttachmentInfo(scene,rootOut));
+
+ // Now combine the material we've loaded for this mesh
+ // with the real materials we got from the file. As we
+ // don't execute any pp-steps on the file, the numbers
+ // should be equal. If they are not, we can impossibly
+ // do this ...
+ if (root->materials.size() != (unsigned int)scene->mNumMaterials) {
+ DefaultLogger::get()->warn("IRR: Failed to match imported materials "
+ "with the materials found in the IRR scene file");
+
+ break;
+ }
+ for (unsigned int i = 0; i < scene->mNumMaterials;++i) {
+ // Delete the old material, we don't need it anymore
+ delete scene->mMaterials[i];
+
+ std::pair<aiMaterial*, unsigned int>& src = root->materials[i];
+ scene->mMaterials[i] = src.first;
+ }
+
+ // NOTE: Each mesh should have exactly one material assigned,
+ // but we do it in a separate loop if this behaviour changes
+ // in future.
+ for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
+ // Process material flags
+ aiMesh* mesh = scene->mMeshes[i];
+
+
+ // If "trans_vertex_alpha" mode is enabled, search all vertex colors
+ // and check whether they have a common alpha value. This is quite
+ // often the case so we can simply extract it to a shared oacity
+ // value.
+ std::pair<aiMaterial*, unsigned int>& src = root->materials[mesh->mMaterialIndex];
+ MaterialHelper* mat = (MaterialHelper*)src.first;
+
+ if (mesh->HasVertexColors(0) && src.second & AI_IRRMESH_MAT_trans_vertex_alpha)
+ {
+ bool bdo = true;
+ for (unsigned int a = 1; a < mesh->mNumVertices;++a) {
+
+ if (mesh->mColors[0][a].a != mesh->mColors[0][a-1].a) {
+ bdo = false;
+ break;
+ }
+ }
+ if (bdo) {
+ DefaultLogger::get()->info("IRR: Replacing mesh vertex alpha with common opacity");
+
+ for (unsigned int a = 0; a < mesh->mNumVertices;++a)
+ mesh->mColors[0][a].a = 1.f;
+
+ mat->AddProperty(& mesh->mColors[0][0].a, 1, AI_MATKEY_OPACITY);
+ }
+ }
+
+ // If we have a second texture coordinate set and a second texture
+ // (either lightmap, normalmap, 2layered material) we need to
+ // setup the correct UV index for it. The texture can either
+ // be diffuse (lightmap & 2layer) or a normal map (normal & parallax)
+ if (mesh->HasTextureCoords(1)) {
+
+ int idx = 1;
+ if (src.second & (AI_IRRMESH_MAT_solid_2layer | AI_IRRMESH_MAT_lightmap)) {
+ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_DIFFUSE(0));
+ }
+ else if (src.second & AI_IRRMESH_MAT_normalmap_solid) {
+ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_NORMALS(0));
+ }
+ }
+ }
+ }
+ break;
+
+ case Node::LIGHT:
+ case Node::CAMERA:
+
+ // We're already finished with lights and cameras
+ break;
+
+
+ case Node::SPHERE:
+ {
+ // Generate the sphere model. Our input parameter to
+ // the sphere generation algorithm is the number of
+ // subdivisions of each triangle - but here we have
+ // the number of poylgons on a specific axis. Just
+ // use some hardcoded limits to approximate this ...
+ unsigned int mul = root->spherePolyCountX*root->spherePolyCountY;
+ if (mul < 100)mul = 2;
+ else if (mul < 300)mul = 3;
+ else mul = 4;
+
+ meshes.push_back(StandardShapes::MakeMesh(mul,
+ &StandardShapes::MakeSphere));
+
+ // Adjust scaling
+ root->scaling *= root->sphereRadius/2;
+
+ // Copy one output material
+ CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
+
+ // Now adjust this output material - if there is a first texture
+ // set, setup spherical UV mapping around the Y axis.
+ SetupMapping ( (MaterialHelper*) materials.back(), aiTextureMapping_SPHERE);
+ }
+ break;
+
+ case Node::CUBE:
+ {
+ // Generate an unit cube first
+ meshes.push_back(StandardShapes::MakeMesh(
+ &StandardShapes::MakeHexahedron));
+
+ // Adjust scaling
+ root->scaling *= root->sphereRadius;
+
+ // Copy one output material
+ CopyMaterial(materials, root->materials, defMatIdx, meshes.back());
+
+ // Now adjust this output material - if there is a first texture
+ // set, setup cubic UV mapping
+ SetupMapping ( (MaterialHelper*) materials.back(), aiTextureMapping_BOX );
+ }
+ break;
+
+
+ case Node::SKYBOX:
+ {
+ // A skybox is defined by six materials
+ if (root->materials.size() < 6) {
+ DefaultLogger::get()->error("IRR: There should be six materials for a skybox");
+ break;
+ }
+
+ // copy those materials and generate 6 meshes for our new skybox
+ materials.reserve(materials.size() + 6);
+ for (unsigned int i = 0; i < 6;++i)
+ materials.insert(materials.end(),root->materials[i].first);
+
+ BuildSkybox(meshes,materials);
+
+ // *************************************************************
+ // Skyboxes will require a different code path for rendering,
+ // so there must be a way for the user to add special support
+ // for IRR skyboxes. We add a 'IRR.SkyBox_' prefix to the node.
+ // *************************************************************
+ root->name = "IRR.SkyBox_" + root->name;
+ DefaultLogger::get()->info("IRR: Loading skybox, this will "
+ "require special handling to be displayed correctly");
+ }
+ break;
+
+ case Node::TERRAIN:
+ {
+ // to support terrains, we'd need to have a texture decoder
+ DefaultLogger::get()->error("IRR: Unsupported node - TERRAIN");
+ }
+ break;
+ default:
+ // DUMMY
+ break;
+ };
+
+ // Check whether we added a mesh (or more than one ...). In this case
+ // we'll also need to attach it to the node
+ if (oldMeshSize != (unsigned int) meshes.size()) {
+
+ rootOut->mNumMeshes = (unsigned int)meshes.size() - oldMeshSize;
+ rootOut->mMeshes = new unsigned int[rootOut->mNumMeshes];
+
+ for (unsigned int a = 0; a < rootOut->mNumMeshes;++a) {
+ rootOut->mMeshes[a] = oldMeshSize+a;
+ }
+ }
+
+ // Setup the name of this node
+ rootOut->mName.Set(root->name);
+
+ // Now compute the final local transformation matrix of the
+ // node from the given translation, rotation and scaling values.
+ // (the rotation is given in Euler angles, XYZ order)
+ //std::swap((float&)root->rotation.z,(float&)root->rotation.y);
+ rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation) );
+
+ // apply scaling
+ aiMatrix4x4& mat = rootOut->mTransformation;
+ mat.a1 *= root->scaling.x;
+ mat.b1 *= root->scaling.x;
+ mat.c1 *= root->scaling.x;
+ mat.a2 *= root->scaling.y;
+ mat.b2 *= root->scaling.y;
+ mat.c2 *= root->scaling.y;
+ mat.a3 *= root->scaling.z;
+ mat.b3 *= root->scaling.z;
+ mat.c3 *= root->scaling.z;
+
+ // apply translation
+ mat.a4 += root->position.x;
+ mat.b4 += root->position.y;
+ mat.c4 += root->position.z;
+
+ // now compute animations for the node
+ ComputeAnimations(root,rootOut, anims);
+
+ // Add all children recursively. First allocate enough storage
+ // for them, then call us again
+ rootOut->mNumChildren = (unsigned int)root->children.size();
+ if (rootOut->mNumChildren) {
+
+ rootOut->mChildren = new aiNode*[rootOut->mNumChildren];
+ for (unsigned int i = 0; i < rootOut->mNumChildren;++i) {
+
+ aiNode* node = rootOut->mChildren[i] = new aiNode();
+ node->mParent = rootOut;
+ GenerateGraph(root->children[i],node,scene,batch,meshes,
+ anims,attach,materials,defMatIdx);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void IRRImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open IRR file " + pFile + "");
+
+ // Construct the irrXML parser
+ CIrrXML_IOStreamReader st(file.get());
+ reader = createIrrXMLReader((IFileReadCallBack*) &st);
+
+ // The root node of the scene
+ Node* root = new Node(Node::DUMMY);
+ root->parent = NULL;
+ root->name = "<IRRSceneRoot>";
+
+ // Current node parent
+ Node* curParent = root;
+
+ // Scenegraph node we're currently working on
+ Node* curNode = NULL;
+
+ // List of output cameras
+ std::vector<aiCamera*> cameras;
+
+ // List of output lights
+ std::vector<aiLight*> lights;
+
+ // Batch loader used to load external models
+ BatchLoader batch(pIOHandler);
+// batch.SetBasePath(pFile);
+
+ cameras.reserve(5);
+ lights.reserve(5);
+
+ bool inMaterials = false, inAnimator = false;
+ unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0;
+
+ // Parse the XML file
+ while (reader->read()) {
+ switch (reader->getNodeType()) {
+ case EXN_ELEMENT:
+
+ if (!ASSIMP_stricmp(reader->getNodeName(),"node")) {
+ // ***********************************************************************
+ /* What we're going to do with the node depends
+ * on its type:
+ *
+ * "mesh" - Load a mesh from an external file
+ * "cube" - Generate a cube
+ * "skybox" - Generate a skybox
+ * "light" - A light source
+ * "sphere" - Generate a sphere mesh
+ * "animatedMesh" - Load an animated mesh from an external file
+ * and join its animation channels with ours.
+ * "empty" - A dummy node
+ * "camera" - A camera
+ * "terrain" - a terrain node (data comes from a heightmap)
+ * "billboard", ""
+ *
+ * Each of these nodes can be animated and all can have multiple
+ * materials assigned (except lights, cameras and dummies, of course).
+ */
+ // ***********************************************************************
+ const char* sz = reader->getAttributeValueSafe("type");
+ Node* nd;
+ if (!ASSIMP_stricmp(sz,"mesh") || !ASSIMP_stricmp(sz,"octTree")) {
+ // OctTree's and meshes are treated equally
+ nd = new Node(Node::MESH);
+ }
+ else if (!ASSIMP_stricmp(sz,"cube")) {
+ nd = new Node(Node::CUBE);
+ ++guessedMeshCnt;
+ // meshes.push_back(StandardShapes::MakeMesh(&StandardShapes::MakeHexahedron));
+ }
+ else if (!ASSIMP_stricmp(sz,"skybox")) {
+ nd = new Node(Node::SKYBOX);
+ guessedMeshCnt += 6;
+ }
+ else if (!ASSIMP_stricmp(sz,"camera")) {
+ nd = new Node(Node::CAMERA);
+
+ // Setup a temporary name for the camera
+ aiCamera* cam = new aiCamera();
+ cam->mName.Set( nd->name );
+ cameras.push_back(cam);
+ }
+ else if (!ASSIMP_stricmp(sz,"light")) {
+ nd = new Node(Node::LIGHT);
+
+ // Setup a temporary name for the light
+ aiLight* cam = new aiLight();
+ cam->mName.Set( nd->name );
+ lights.push_back(cam);
+ }
+ else if (!ASSIMP_stricmp(sz,"sphere")) {
+ nd = new Node(Node::SPHERE);
+ ++guessedMeshCnt;
+ }
+ else if (!ASSIMP_stricmp(sz,"animatedMesh")) {
+ nd = new Node(Node::ANIMMESH);
+ }
+ else if (!ASSIMP_stricmp(sz,"empty")) {
+ nd = new Node(Node::DUMMY);
+ }
+ else if (!ASSIMP_stricmp(sz,"terrain")) {
+ nd = new Node(Node::TERRAIN);
+ }
+ else if (!ASSIMP_stricmp(sz,"billBoard")) {
+ // We don't support billboards, so ignore them
+ DefaultLogger::get()->error("IRR: Billboards are not supported by Assimp");
+ nd = new Node(Node::DUMMY);
+ }
+ else {
+ DefaultLogger::get()->warn("IRR: Found unknown node: " + std::string(sz));
+
+ /* We skip the contents of nodes we don't know.
+ * We parse the transformation and all animators
+ * and skip the rest.
+ */
+ nd = new Node(Node::DUMMY);
+ }
+
+ /* Attach the newly created node to the scenegraph
+ */
+ curNode = nd;
+ nd->parent = curParent;
+ curParent->children.push_back(nd);
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"materials")) {
+ inMaterials = true;
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"animators")) {
+ inAnimator = true;
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"attributes")) {
+ /* We should have a valid node here
+ * FIX: no ... the scene root node is also contained in an attributes block
+ */
+ if (!curNode) {
+#if 0
+ DefaultLogger::get()->error("IRR: Encountered <attributes> element, but "
+ "there is no node active");
+#endif
+ continue;
+ }
+
+ Animator* curAnim = NULL;
+
+ // Materials can occur for nearly any type of node
+ if (inMaterials && curNode->type != Node::DUMMY) {
+ /* This is a material description - parse it!
+ */
+ curNode->materials.push_back(std::pair< aiMaterial*, unsigned int > () );
+ std::pair< aiMaterial*, unsigned int >& p = curNode->materials.back();
+
+ p.first = ParseMaterial(p.second);
+
+ ++guessedMatCnt;
+ continue;
+ }
+ else if (inAnimator) {
+ /* This is an animation path - add a new animator
+ * to the list.
+ */
+ curNode->animators.push_back(Animator());
+ curAnim = & curNode->animators.back();
+
+ ++guessedAnimCnt;
+ }
+
+ /* Parse all elements in the attributes block
+ * and process them.
+ */
+ while (reader->read()) {
+ if (reader->getNodeType() == EXN_ELEMENT) {
+ if (!ASSIMP_stricmp(reader->getNodeName(),"vector3d")) {
+ VectorProperty prop;
+ ReadVectorProperty(prop);
+
+ if (inAnimator) {
+ if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") {
+ // We store the rotation euler angles in 'direction'
+ curAnim->direction = prop.value;
+ }
+ else if (curAnim->type == Animator::FOLLOW_SPLINE) {
+ // Check whether the vector follows the PointN naming scheme,
+ // here N is the ONE-based index of the point
+ if (prop.name.length() >= 6 && prop.name.substr(0,5) == "Point") {
+ // Add a new key to the list
+ curAnim->splineKeys.push_back(aiVectorKey());
+ aiVectorKey& key = curAnim->splineKeys.back();
+
+ // and parse its properties
+ key.mValue = prop.value;
+ key.mTime = strtol10(&prop.name[5]);
+ }
+ }
+ else if (curAnim->type == Animator::FLY_CIRCLE) {
+ if (prop.name == "Center") {
+ curAnim->circleCenter = prop.value;
+ }
+ else if (prop.name == "Direction") {
+ curAnim->direction = prop.value;
+
+ // From Irrlicht's source - a workaround for backward compatibility with Irrlicht 1.1
+ if (curAnim->direction == aiVector3D()) {
+ curAnim->direction = aiVector3D(0.f,1.f,0.f);
+ }
+ else curAnim->direction.Normalize();
+ }
+ }
+ else if (curAnim->type == Animator::FLY_STRAIGHT) {
+ if (prop.name == "Start") {
+ // We reuse the field here
+ curAnim->circleCenter = prop.value;
+ }
+ else if (prop.name == "End") {
+ // We reuse the field here
+ curAnim->direction = prop.value;
+ }
+ }
+ }
+ else {
+ if (prop.name == "Position") {
+ curNode->position = prop.value;
+ }
+ else if (prop.name == "Rotation") {
+ curNode->rotation = prop.value;
+ }
+ else if (prop.name == "Scale") {
+ curNode->scaling = prop.value;
+ }
+ else if (Node::CAMERA == curNode->type)
+ {
+ aiCamera* cam = cameras.back();
+ if (prop.name == "Target") {
+ cam->mLookAt = prop.value;
+ }
+ else if (prop.name == "UpVector") {
+ cam->mUp = prop.value;
+ }
+ }
+ }
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"bool")) {
+ BoolProperty prop;
+ ReadBoolProperty(prop);
+
+ if (inAnimator && curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
+ curAnim->loop = prop.value;
+ }
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"float")) {
+ FloatProperty prop;
+ ReadFloatProperty(prop);
+
+ if (inAnimator) {
+ // The speed property exists for several animators
+ if (prop.name == "Speed") {
+ curAnim->speed = prop.value;
+ }
+ else if (curAnim->type == Animator::FLY_CIRCLE && prop.name == "Radius") {
+ curAnim->circleRadius = prop.value;
+ }
+ else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") {
+ curAnim->tightness = prop.value;
+ }
+ }
+ else {
+ if (prop.name == "FramesPerSecond" && Node::ANIMMESH == curNode->type) {
+ curNode->framesPerSecond = prop.value;
+ }
+ else if (Node::CAMERA == curNode->type) {
+ /* This is the vertical, not the horizontal FOV.
+ * We need to compute the right FOV from the
+ * screen aspect which we don't know yet.
+ */
+ if (prop.name == "Fovy") {
+ cameras.back()->mHorizontalFOV = prop.value;
+ }
+ else if (prop.name == "Aspect") {
+ cameras.back()->mAspect = prop.value;
+ }
+ else if (prop.name == "ZNear") {
+ cameras.back()->mClipPlaneNear = prop.value;
+ }
+ else if (prop.name == "ZFar") {
+ cameras.back()->mClipPlaneFar = prop.value;
+ }
+ }
+ else if (Node::LIGHT == curNode->type) {
+ /* Additional light information
+ */
+ if (prop.name == "Attenuation") {
+ lights.back()->mAttenuationLinear = prop.value;
+ }
+ else if (prop.name == "OuterCone") {
+ lights.back()->mAngleOuterCone = AI_DEG_TO_RAD( prop.value );
+ }
+ else if (prop.name == "InnerCone") {
+ lights.back()->mAngleInnerCone = AI_DEG_TO_RAD( prop.value );
+ }
+ }
+ // radius of the sphere to be generated -
+ // or alternatively, size of the cube
+ else if ((Node::SPHERE == curNode->type && prop.name == "Radius")
+ || (Node::CUBE == curNode->type && prop.name == "Size" )) {
+
+ curNode->sphereRadius = prop.value;
+ }
+ }
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"int")) {
+ IntProperty prop;
+ ReadIntProperty(prop);
+
+ if (inAnimator) {
+ if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
+ curAnim->timeForWay = prop.value;
+ }
+ }
+ else {
+ // sphere polgon numbers in each direction
+ if (Node::SPHERE == curNode->type) {
+
+ if (prop.name == "PolyCountX") {
+ curNode->spherePolyCountX = prop.value;
+ }
+ else if (prop.name == "PolyCountY") {
+ curNode->spherePolyCountY = prop.value;
+ }
+ }
+ }
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"string") ||!ASSIMP_stricmp(reader->getNodeName(),"enum")) {
+ StringProperty prop;
+ ReadStringProperty(prop);
+ if (prop.value.length()) {
+ if (prop.name == "Name") {
+ curNode->name = prop.value;
+
+ /* If we're either a camera or a light source
+ * we need to update the name in the aiLight/
+ * aiCamera structure, too.
+ */
+ if (Node::CAMERA == curNode->type) {
+ cameras.back()->mName.Set(prop.value);
+ }
+ else if (Node::LIGHT == curNode->type) {
+ lights.back()->mName.Set(prop.value);
+ }
+ }
+ else if (Node::LIGHT == curNode->type && "LightType" == prop.name)
+ {
+ if (prop.value == "Spot")
+ lights.back()->mType = aiLightSource_SPOT;
+ else if (prop.value == "Point")
+ lights.back()->mType = aiLightSource_POINT;
+ else if (prop.value == "Directional")
+ lights.back()->mType = aiLightSource_DIRECTIONAL;
+ else
+ {
+ // We won't pass the validation with aiLightSourceType_UNDEFINED,
+ // so we remove the light and replace it with a silly dummy node
+ delete lights.back();
+ lights.pop_back();
+ curNode->type = Node::DUMMY;
+
+ DefaultLogger::get()->error("Ignoring light of unknown type: " + prop.value);
+ }
+ }
+ else if ((prop.name == "Mesh" && Node::MESH == curNode->type) ||
+ Node::ANIMMESH == curNode->type)
+ {
+ /* This is the file name of the mesh - either
+ * animated or not. We need to make sure we setup
+ * the correct postprocessing settings here.
+ */
+ unsigned int pp = 0;
+ BatchLoader::PropertyMap map;
+
+ /* If the mesh is a static one remove all animations from the impor data
+ */
+ if (Node::ANIMMESH != curNode->type) {
+ pp |= aiProcess_RemoveComponent;
+ SetGenericProperty<int>(map.ints,AI_CONFIG_PP_RVC_FLAGS,
+ aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
+ }
+
+ /* TODO: maybe implement the protection against recursive
+ * loading calls directly in BatchLoader? The current
+ * implementation is not absolutely safe. A LWS and an IRR
+ * file referencing each other *could* cause the system to
+ * recurse forever.
+ */
+
+ const std::string extension = GetExtension(prop.value);
+ if ("irr" == extension) {
+ DefaultLogger::get()->error("IRR: Can't load another IRR file recursively");
+ }
+ else
+ {
+ curNode->id = batch.AddLoadRequest(prop.value,pp,&map);
+ curNode->meshPath = prop.value;
+ }
+ }
+ else if (inAnimator && prop.name == "Type")
+ {
+ // type of the animator
+ if (prop.value == "rotation") {
+ curAnim->type = Animator::ROTATION;
+ }
+ else if (prop.value == "flyCircle") {
+ curAnim->type = Animator::FLY_CIRCLE;
+ }
+ else if (prop.value == "flyStraight") {
+ curAnim->type = Animator::FLY_CIRCLE;
+ }
+ else if (prop.value == "followSpline") {
+ curAnim->type = Animator::FOLLOW_SPLINE;
+ }
+ else {
+ DefaultLogger::get()->warn("IRR: Ignoring unknown animator: "
+ + prop.value);
+
+ curAnim->type = Animator::UNKNOWN;
+ }
+ }
+ }
+ }
+ }
+ else if (reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(reader->getNodeName(),"attributes")) {
+ break;
+ }
+ }
+ }
+ break;
+
+ case EXN_ELEMENT_END:
+
+ // If we reached the end of a node, we need to continue processing its parent
+ if (!ASSIMP_stricmp(reader->getNodeName(),"node")) {
+ if (!curNode) {
+ // currently is no node set. We need to go
+ // back in the node hierarchy
+ if (!curParent) {
+ curParent = root;
+ DefaultLogger::get()->error("IRR: Too many closing <node> elements");
+ }
+ else curParent = curParent->parent;
+ }
+ else curNode = NULL;
+ }
+ // clear all flags
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"materials")) {
+ inMaterials = false;
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"animators")) {
+ inAnimator = false;
+ }
+ break;
+
+ default:
+ // GCC complains that not all enumeration values are handled
+ break;
+ }
+ }
+
+ /* Now iterate through all cameras and compute their final (horizontal) FOV
+ */
+ for (std::vector<aiCamera*>::iterator it = cameras.begin(), end = cameras.end();it != end; ++it) {
+ aiCamera* cam = *it;
+
+ // screen aspect could be missing
+ if (cam->mAspect) {
+ cam->mHorizontalFOV *= cam->mAspect;
+ }
+ else DefaultLogger::get()->warn("IRR: Camera aspect is not given, can't compute horizontal FOV");
+ }
+
+ batch.LoadAll();
+
+ /* Allocate a tempoary scene data structure
+ */
+ aiScene* tempScene = new aiScene();
+ tempScene->mRootNode = new aiNode();
+ tempScene->mRootNode->mName.Set("<IRRRoot>");
+
+ /* Copy the cameras to the output array
+ */
+ if (!cameras.empty()) {
+ tempScene->mNumCameras = (unsigned int)cameras.size();
+ tempScene->mCameras = new aiCamera*[tempScene->mNumCameras];
+ ::memcpy(tempScene->mCameras,&cameras[0],sizeof(void*)*tempScene->mNumCameras);
+ }
+
+ /* Copy the light sources to the output array
+ */
+ if (!lights.empty()) {
+ tempScene->mNumLights = (unsigned int)lights.size();
+ tempScene->mLights = new aiLight*[tempScene->mNumLights];
+ ::memcpy(tempScene->mLights,&lights[0],sizeof(void*)*tempScene->mNumLights);
+ }
+
+ // temporary data
+ std::vector< aiNodeAnim*> anims;
+ std::vector< aiMaterial*> materials;
+ std::vector< AttachmentInfo > attach;
+ std::vector<aiMesh*> meshes;
+
+ // try to guess how much storage we'll need
+ anims.reserve (guessedAnimCnt + (guessedAnimCnt >> 2));
+ meshes.reserve (guessedMeshCnt + (guessedMeshCnt >> 2));
+ materials.reserve (guessedMatCnt + (guessedMatCnt >> 2));
+
+ /* Now process our scenegraph recursively: generate final
+ * meshes and generate animation channels for all nodes.
+ */
+ unsigned int defMatIdx = 0xffffffff;
+ GenerateGraph(root,tempScene->mRootNode, tempScene,
+ batch, meshes, anims, attach, materials, defMatIdx);
+
+ if (!anims.empty())
+ {
+ tempScene->mNumAnimations = 1;
+ tempScene->mAnimations = new aiAnimation*[tempScene->mNumAnimations];
+ aiAnimation* an = tempScene->mAnimations[0] = new aiAnimation();
+
+ // ***********************************************************
+ // This is only the global animation channel of the scene.
+ // If there are animated models, they will have separate
+ // animation channels in the scene. To display IRR scenes
+ // correctly, users will need to combine the global anim
+ // channel with all the local animations they want to play
+ // ***********************************************************
+ an->mName.Set("Irr_GlobalAnimChannel");
+
+ // copy all node animation channels to the global channel
+ an->mNumChannels = (unsigned int)anims.size();
+ an->mChannels = new aiNodeAnim*[an->mNumChannels];
+ ::memcpy(an->mChannels, & anims [0], sizeof(void*)*an->mNumChannels);
+ }
+ if (!meshes.empty()) {
+ // copy all meshes to the temporary scene
+ tempScene->mNumMeshes = (unsigned int)meshes.size();
+ tempScene->mMeshes = new aiMesh*[tempScene->mNumMeshes];
+ ::memcpy(tempScene->mMeshes,&meshes[0],tempScene->mNumMeshes*
+ sizeof(void*));
+ }
+
+ /* Copy all materials to the output array
+ */
+ if (!materials.empty()) {
+ tempScene->mNumMaterials = (unsigned int)materials.size();
+ tempScene->mMaterials = new aiMaterial*[tempScene->mNumMaterials];
+ ::memcpy(tempScene->mMaterials,&materials[0],sizeof(void*)*
+ tempScene->mNumMaterials);
+ }
+
+ /* Now merge all sub scenes and attach them to the correct
+ * attachment points in the scenegraph.
+ */
+ SceneCombiner::MergeScenes(&pScene,tempScene,attach,
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) : 0));
+
+
+ /* If we have no meshes | no materials now set the INCOMPLETE
+ * scene flag. This is necessary if we failed to load all
+ * models from external files
+ */
+ if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
+ DefaultLogger::get()->warn("IRR: No meshes loaded, setting AI_SCENE_FLAGS_INCOMPLETE");
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+
+ /* Finished ... everything destructs automatically and all
+ * temporary scenes have already been deleted by MergeScenes()
+ */
+ return;
+}
diff --git a/3rdparty/assimp/code/IRRLoader.h b/3rdparty/assimp/code/IRRLoader.h
new file mode 100644
index 000000000..569d59db8
--- /dev/null
+++ b/3rdparty/assimp/code/IRRLoader.h
@@ -0,0 +1,312 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 IRRLoader.h
+ * @brief Declaration of the .irrMesh (Irrlight Engine Mesh Format)
+ * importer class.
+ */
+#ifndef AI_IRRLOADER_H_INCLUDED
+#define AI_IRRLOADER_H_INCLUDED
+
+#include "IRRShared.h"
+#include "SceneCombiner.h"
+
+namespace Assimp {
+
+
+// ---------------------------------------------------------------------------
+/** Irr importer class.
+ *
+ * Irr is the native scene file format of the Irrlight engine and its editor
+ * irrEdit. As IrrEdit itself is capable of importing quite many file formats,
+ * it might be a good file format for data exchange.
+ */
+class IRRImporter : public BaseImporter, public IrrlichtBase
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ IRRImporter();
+
+ /** Destructor, private as well */
+ ~IRRImporter();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /**
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /**
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /**
+ */
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+ /** Data structure for a scenegraph node animator
+ */
+ struct Animator
+ {
+ // Type of the animator
+ enum AT
+ {
+ UNKNOWN = 0x0,
+ ROTATION = 0x1,
+ FLY_CIRCLE = 0x2,
+ FLY_STRAIGHT = 0x3,
+ FOLLOW_SPLINE = 0x4,
+ OTHER = 0x5
+
+ } type;
+
+ Animator(AT t = UNKNOWN)
+ : type (t)
+ , speed (0.001f)
+ , direction (0.f,1.f,0.f)
+ , circleRadius (1.f)
+ , tightness (0.5f)
+ , loop (true)
+ , timeForWay (100)
+ {
+ }
+
+
+ // common parameters
+ float speed;
+ aiVector3D direction;
+
+ // FLY_CIRCLE
+ aiVector3D circleCenter;
+ float circleRadius;
+
+ // FOLLOW_SPLINE
+ float tightness;
+ std::vector<aiVectorKey> splineKeys;
+
+ // ROTATION (angles given in direction)
+
+ // FLY STRAIGHT
+ // circleCenter = start, direction = end
+ bool loop;
+ int timeForWay;
+ };
+
+ /** Data structure for a scenegraph node in an IRR file
+ */
+ struct Node
+ {
+ // Type of the node
+ enum ET
+ {
+ LIGHT,
+ CUBE,
+ MESH,
+ SKYBOX,
+ DUMMY,
+ CAMERA,
+ TERRAIN,
+ SPHERE,
+ ANIMMESH
+ } type;
+
+ Node(ET t)
+ : type (t)
+ , scaling (1.f,1.f,1.f) // assume uniform scaling by default
+ , framesPerSecond (0.f)
+ , sphereRadius (1.f)
+ , spherePolyCountX (100)
+ , spherePolyCountY (100)
+ {
+
+ // Generate a default name for the node
+ char buffer[128];
+ static int cnt;
+ ::sprintf(buffer,"IrrNode_%i",cnt++);
+ name = std::string(buffer);
+
+ // reserve space for up to 5 materials
+ materials.reserve(5);
+
+ // reserve space for up to 5 children
+ children.reserve(5);
+ }
+
+ // Transformation of the node
+ aiVector3D position, rotation, scaling;
+
+ // Name of the node
+ std::string name;
+
+ // List of all child nodes
+ std::vector<Node*> children;
+
+ // Parent node
+ Node* parent;
+
+ // Animated meshes: frames per second
+ // 0.f if not specified
+ float framesPerSecond;
+
+ // Meshes: path to the mesh to be loaded
+ std::string meshPath;
+ unsigned int id;
+
+ // Meshes: List of materials to be assigned
+ // along with their corresponding material flags
+ std::vector< std::pair<aiMaterial*, unsigned int> > materials;
+
+ // Spheres: radius of the sphere to be generates
+ float sphereRadius;
+
+ // Spheres: Number of polygons in the x,y direction
+ unsigned int spherePolyCountX,spherePolyCountY;
+
+ // List of all animators assigned to the node
+ std::list<Animator> animators;
+ };
+
+ /** Data structure for a vertex in an IRR skybox
+ */
+ struct SkyboxVertex
+ {
+ SkyboxVertex()
+ {}
+
+ //! Construction from single vertex components
+ SkyboxVertex(float px, float py, float pz,
+ float nx, float ny, float nz,
+ float uvx, float uvy)
+
+ : position (px,py,pz)
+ , normal (nx,ny,nz)
+ , uv (uvx,uvy,0.f)
+ {}
+
+ aiVector3D position, normal, uv;
+ };
+
+
+ // -------------------------------------------------------------------
+ /** Fill the scenegraph recursively
+ */
+ void GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
+ BatchLoader& batch,
+ std::vector<aiMesh*>& meshes,
+ std::vector<aiNodeAnim*>& anims,
+ std::vector<AttachmentInfo>& attach,
+ std::vector<aiMaterial*>& materials,
+ unsigned int& defaultMatIdx);
+
+
+ // -------------------------------------------------------------------
+ /** Generate a mesh that consists of just a single quad
+ */
+ aiMesh* BuildSingleQuadMesh(const SkyboxVertex& v1,
+ const SkyboxVertex& v2,
+ const SkyboxVertex& v3,
+ const SkyboxVertex& v4);
+
+
+ // -------------------------------------------------------------------
+ /** Build a skybox
+ *
+ * @param meshes Receives 6 output meshes
+ * @param materials The last 6 materials are assigned to the newly
+ * created meshes. The names of the materials are adjusted.
+ */
+ void BuildSkybox(std::vector<aiMesh*>& meshes,
+ std::vector<aiMaterial*> materials);
+
+
+ // -------------------------------------------------------------------
+ /** Copy a material for a mesh to the output material list
+ *
+ * @param materials Receives an output material
+ * @param inmaterials List of input materials
+ * @param defMatIdx Default material index - 0xffffffff if not there
+ * @param mesh Mesh to work on
+ */
+ void CopyMaterial(std::vector<aiMaterial*>& materials,
+ std::vector< std::pair<aiMaterial*, unsigned int> >& inmaterials,
+ unsigned int& defMatIdx,
+ aiMesh* mesh);
+
+
+ // -------------------------------------------------------------------
+ /** Compute animations for a specific node
+ *
+ * @param root Node to be processed
+ * @param anims The list of output animations
+ */
+ void ComputeAnimations(Node* root, aiNode* real,
+ std::vector<aiNodeAnim*>& anims);
+
+
+private:
+
+ /** Configuration option: desired output FPS */
+ double fps;
+
+ /** Configuration option: speed flag was set? */
+ bool configSpeedFlag;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_IRRIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/IRRMeshLoader.cpp b/3rdparty/assimp/code/IRRMeshLoader.cpp
new file mode 100644
index 000000000..69e29a5be
--- /dev/null
+++ b/3rdparty/assimp/code/IRRMeshLoader.cpp
@@ -0,0 +1,499 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the IrrMesh importer class */
+
+#include "AssimpPCH.h"
+
+#include "IRRMeshLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace irr;
+using namespace irr::io;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+IRRMeshImporter::IRRMeshImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+IRRMeshImporter::~IRRMeshImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool IRRMeshImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ /* NOTE: A simple check for the file extension is not enough
+ * here. Irrmesh and irr are easy, but xml is too generic
+ * and could be collada, too. So we need to open the file and
+ * search for typical tokens.
+ */
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "irrmesh")return true;
+ else if (extension == "xml" || checkSig)
+ {
+ /* If CanRead() is called to check whether the loader
+ * supports a specific file extension in general we
+ * must return true here.
+ */
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"irrmesh"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all file extensions which are handled by this class
+void IRRMeshImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("xml");
+ extensions.insert("irrmesh");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void IRRMeshImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open IRRMESH file " + pFile + "");
+
+ // Construct the irrXML parser
+ CIrrXML_IOStreamReader st(file.get());
+ reader = createIrrXMLReader((IFileReadCallBack*) &st);
+
+ // final data
+ std::vector<aiMaterial*> materials;
+ std::vector<aiMesh*> meshes;
+ materials.reserve (5);
+ meshes.reserve (5);
+
+ // temporary data - current mesh buffer
+ aiMaterial* curMat = NULL;
+ aiMesh* curMesh = NULL;
+ unsigned int curMatFlags;
+
+ std::vector<aiVector3D> curVertices,curNormals,curTangents,curBitangents;
+ std::vector<aiColor4D> curColors;
+ std::vector<aiVector3D> curUVs,curUV2s;
+
+ // some temporary variables
+ int textMeaning = 0;
+ int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
+ bool useColors = false;
+
+ // Parse the XML file
+ while (reader->read()) {
+ switch (reader->getNodeType()) {
+ case EXN_ELEMENT:
+
+ if (!ASSIMP_stricmp(reader->getNodeName(),"buffer") && (curMat || curMesh)) {
+ // end of previous buffer. A material and a mesh should be there
+ if ( !curMat || !curMesh) {
+ DefaultLogger::get()->error("IRRMESH: A buffer must contain a mesh and a material");
+ delete curMat;
+ delete curMesh;
+ }
+ else {
+ materials.push_back(curMat);
+ meshes.push_back(curMesh);
+ }
+ curMat = NULL;
+ curMesh = NULL;
+
+ curVertices.clear();
+ curColors.clear();
+ curNormals.clear();
+ curUV2s.clear();
+ curUVs.clear();
+ curTangents.clear();
+ curBitangents.clear();
+ }
+
+
+ if (!ASSIMP_stricmp(reader->getNodeName(),"material")) {
+ if (curMat) {
+ DefaultLogger::get()->warn("IRRMESH: Only one material description per buffer, please");
+ delete curMat;curMat = NULL;
+ }
+ curMat = ParseMaterial(curMatFlags);
+ }
+ /* no else here! */ if (!ASSIMP_stricmp(reader->getNodeName(),"vertices"))
+ {
+ int num = reader->getAttributeValueAsInt("vertexCount");
+
+ if (!num) {
+ // This is possible ... remove the mesh from the list and skip further reading
+ DefaultLogger::get()->warn("IRRMESH: Found mesh with zero vertices");
+
+ delete curMat;curMat = NULL;
+
+ curMesh = NULL;
+ textMeaning = 0;
+ continue;
+ }
+
+ curVertices.reserve (num);
+ curNormals.reserve (num);
+ curColors.reserve (num);
+ curUVs.reserve (num);
+
+ // Determine the file format
+ const char* t = reader->getAttributeValueSafe("type");
+ if (!ASSIMP_stricmp("2tcoords", t)) {
+ curUV2s.reserve (num);
+ vertexFormat = 1;
+
+ if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
+ // *********************************************************
+ // We have a second texture! So use this UV channel
+ // for it. The 2nd texture can be either a normal
+ // texture (solid_2layer or lightmap_xxx) or a normal
+ // map (normal_..., parallax_...)
+ // *********************************************************
+ int idx = 1;
+ MaterialHelper* mat = ( MaterialHelper* ) curMat;
+
+ if (curMatFlags & AI_IRRMESH_MAT_lightmap){
+ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_LIGHTMAP(0));
+ }
+ else if (curMatFlags & AI_IRRMESH_MAT_normalmap_solid){
+ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_NORMALS(0));
+ }
+ else if (curMatFlags & AI_IRRMESH_MAT_solid_2layer) {
+ mat->AddProperty(&idx,1,AI_MATKEY_UVWSRC_DIFFUSE(1));
+ }
+ }
+ }
+ else if (!ASSIMP_stricmp("tangents", t)) {
+ curTangents.reserve (num);
+ curBitangents.reserve (num);
+ vertexFormat = 2;
+ }
+ else if (ASSIMP_stricmp("standard", t)) {
+ delete curMat;
+ DefaultLogger::get()->warn("IRRMESH: Unknown vertex format");
+ }
+ else vertexFormat = 0;
+ textMeaning = 1;
+ }
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"indices")) {
+ if (curVertices.empty() && curMat) {
+ delete curMat;
+ throw DeadlyImportError("IRRMESH: indices must come after vertices");
+ }
+
+ textMeaning = 2;
+
+ // start a new mesh
+ curMesh = new aiMesh();
+
+ // allocate storage for all faces
+ curMesh->mNumVertices = reader->getAttributeValueAsInt("indexCount");
+ if (!curMesh->mNumVertices) {
+ // This is possible ... remove the mesh from the list and skip further reading
+ DefaultLogger::get()->warn("IRRMESH: Found mesh with zero indices");
+
+ // mesh - away
+ delete curMesh; curMesh = NULL;
+
+ // material - away
+ delete curMat;curMat = NULL;
+
+ textMeaning = 0;
+ continue;
+ }
+
+ if (curMesh->mNumVertices % 3) {
+ DefaultLogger::get()->warn("IRRMESH: Number if indices isn't divisible by 3");
+ }
+
+ curMesh->mNumFaces = curMesh->mNumVertices / 3;
+ curMesh->mFaces = new aiFace[curMesh->mNumFaces];
+
+ // setup some members
+ curMesh->mMaterialIndex = (unsigned int)materials.size();
+ curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // allocate storage for all vertices
+ curMesh->mVertices = new aiVector3D[curMesh->mNumVertices];
+
+ if (curNormals.size() == curVertices.size()) {
+ curMesh->mNormals = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curTangents.size() == curVertices.size()) {
+ curMesh->mTangents = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curBitangents.size() == curVertices.size()) {
+ curMesh->mBitangents = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curColors.size() == curVertices.size() && useColors) {
+ curMesh->mColors[0] = new aiColor4D[curMesh->mNumVertices];
+ }
+ if (curUVs.size() == curVertices.size()) {
+ curMesh->mTextureCoords[0] = new aiVector3D[curMesh->mNumVertices];
+ }
+ if (curUV2s.size() == curVertices.size()) {
+ curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices];
+ }
+ }
+ break;
+
+ case EXN_TEXT:
+ {
+ const char* sz = reader->getNodeData();
+ if (textMeaning == 1) {
+ textMeaning = 0;
+
+ // read vertices
+ do {
+ SkipSpacesAndLineEnd(&sz);
+ aiVector3D temp;aiColor4D c;
+
+ // Read the vertex position
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.z);
+ SkipSpaces(&sz);
+ curVertices.push_back(temp);
+
+ // Read the vertex normals
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.z);
+ SkipSpaces(&sz);
+ curNormals.push_back(temp);
+
+ // read the vertex colors
+ uint32_t clr = strtol16(sz,&sz);
+ ColorFromARGBPacked(clr,c);
+
+ if (!curColors.empty() && c != *(curColors.end()-1))
+ useColors = true;
+
+ curColors.push_back(c);
+ SkipSpaces(&sz);
+
+
+ // read the first UV coordinate set
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ SkipSpaces(&sz);
+ temp.z = 0.f;
+ temp.y = 1.f - temp.y; // DX to OGL
+ curUVs.push_back(temp);
+
+ // read the (optional) second UV coordinate set
+ if (vertexFormat == 1) {
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ temp.y = 1.f - temp.y; // DX to OGL
+ curUV2s.push_back(temp);
+ }
+ // read optional tangent and bitangent vectors
+ else if (vertexFormat == 2) {
+ // tangents
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.z);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ SkipSpaces(&sz);
+ temp.y *= -1.0f;
+ curTangents.push_back(temp);
+
+ // bitangents
+ sz = fast_atof_move(sz,(float&)temp.x);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.z);
+ SkipSpaces(&sz);
+
+ sz = fast_atof_move(sz,(float&)temp.y);
+ SkipSpaces(&sz);
+ temp.y *= -1.0f;
+ curBitangents.push_back(temp);
+ }
+ }
+
+ /* IMPORTANT: We assume that each vertex is specified in one
+ line. So we can skip the rest of the line - unknown vertex
+ elements are ignored.
+ */
+
+ while (SkipLine(&sz));
+ }
+ else if (textMeaning == 2) {
+ textMeaning = 0;
+
+ // read indices
+ aiFace* curFace = curMesh->mFaces;
+ aiFace* const faceEnd = curMesh->mFaces + curMesh->mNumFaces;
+
+ aiVector3D* pcV = curMesh->mVertices;
+ aiVector3D* pcN = curMesh->mNormals;
+ aiVector3D* pcT = curMesh->mTangents;
+ aiVector3D* pcB = curMesh->mBitangents;
+ aiColor4D* pcC0 = curMesh->mColors[0];
+ aiVector3D* pcT0 = curMesh->mTextureCoords[0];
+ aiVector3D* pcT1 = curMesh->mTextureCoords[1];
+
+ unsigned int curIdx = 0;
+ unsigned int total = 0;
+ while (SkipSpacesAndLineEnd(&sz)) {
+ if (curFace >= faceEnd) {
+ DefaultLogger::get()->error("IRRMESH: Too many indices");
+ break;
+ }
+ if (!curIdx) {
+ curFace->mNumIndices = 3;
+ curFace->mIndices = new unsigned int[3];
+ }
+
+ unsigned int idx = strtol10(sz,&sz);
+ if (idx >= curVertices.size()) {
+ DefaultLogger::get()->error("IRRMESH: Index out of range");
+ idx = 0;
+ }
+
+ curFace->mIndices[curIdx] = total++;
+
+ *pcV++ = curVertices[idx];
+ if (pcN)*pcN++ = curNormals[idx];
+ if (pcT)*pcT++ = curTangents[idx];
+ if (pcB)*pcB++ = curBitangents[idx];
+ if (pcC0)*pcC0++ = curColors[idx];
+ if (pcT0)*pcT0++ = curUVs[idx];
+ if (pcT1)*pcT1++ = curUV2s[idx];
+
+ if (++curIdx == 3) {
+ ++curFace;
+ curIdx = 0;
+ }
+ }
+
+ if (curFace != faceEnd)
+ DefaultLogger::get()->error("IRRMESH: Not enough indices");
+
+ // Finish processing the mesh - do some small material workarounds
+ if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) {
+ // Take the opacity value of the current material
+ // from the common vertex color alpha
+ MaterialHelper* mat = (MaterialHelper*)curMat;
+ mat->AddProperty(&curColors[0].a,1,AI_MATKEY_OPACITY);
+ }
+ }}
+ break;
+
+ default:
+
+ // GCC complains here ...
+ break;
+
+ };
+ }
+
+ // End of the last buffer. A material and a mesh should be there
+ if (curMat || curMesh) {
+ if ( !curMat || !curMesh) {
+ DefaultLogger::get()->error("IRRMESH: A buffer must contain a mesh and a material");
+ delete curMat;
+ delete curMesh;
+ }
+ else {
+ materials.push_back(curMat);
+ meshes.push_back(curMesh);
+ }
+ }
+
+ if (materials.empty())
+ throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
+
+
+ // now generate the output scene
+ pScene->mNumMeshes = (unsigned int)meshes.size();
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i) {
+ pScene->mMeshes[i] = meshes[i];
+
+ // clean this value ...
+ pScene->mMeshes[i]->mNumUVComponents[3] = 0;
+ }
+
+ pScene->mNumMaterials = (unsigned int)materials.size();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ ::memcpy(pScene->mMaterials,&materials[0],sizeof(void*)*pScene->mNumMaterials);
+
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<IRRMesh>");
+ pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+ pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mRootNode->mMeshes[i] = i;
+
+ // clean up and return
+ delete reader;
+ AI_DEBUG_INVALIDATE_PTR(reader);
+}
diff --git a/3rdparty/assimp/code/IRRMeshLoader.h b/3rdparty/assimp/code/IRRMeshLoader.h
new file mode 100644
index 000000000..06dde8334
--- /dev/null
+++ b/3rdparty/assimp/code/IRRMeshLoader.h
@@ -0,0 +1,99 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 IRRMeshLoader.h
+ * @brief Declaration of the .irrMesh (Irrlight Engine Mesh Format)
+ * importer class.
+ */
+#ifndef AI_IRRMESHLOADER_H_INCLUDED
+#define AI_IRRMESHLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "IRRShared.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** IrrMesh importer class.
+ *
+ * IrrMesh is the native file format of the Irrlight engine and its editor
+ * irrEdit. As IrrEdit itself is capable of importing quite many file formats,
+ * it might be a good file format for data exchange.
+ */
+class IRRMeshImporter : public BaseImporter, public IrrlichtBase
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ IRRMeshImporter();
+
+ /** Destructor, private as well */
+ ~IRRMeshImporter();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+};
+
+} // end of namespace Assimp
+
+#endif // AI_IRRMESHIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/IRRShared.cpp b/3rdparty/assimp/code/IRRShared.cpp
new file mode 100644
index 000000000..89aced7ae
--- /dev/null
+++ b/3rdparty/assimp/code/IRRShared.cpp
@@ -0,0 +1,496 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 IRRShared.cpp
+ * @brief Shared utilities for the IRR and IRRMESH loaders
+ */
+
+#include "AssimpPCH.h"
+
+#include "IRRShared.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace irr;
+using namespace irr::io;
+
+// Transformation matrix to convert from Assimp to IRR space
+const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 (
+ 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+
+// ------------------------------------------------------------------------------------------------
+// read a property in hexadecimal format (i.e. ffffffff)
+void IrrlichtBase::ReadHexProperty (HexProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // parse the hexadecimal value
+ out.value = strtol16(reader->getAttributeValue(i));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a decimal property
+void IrrlichtBase::ReadIntProperty (IntProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // parse the ecimal value
+ out.value = strtol10s(reader->getAttributeValue(i));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a string property
+void IrrlichtBase::ReadStringProperty (StringProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // simple copy the string
+ out.value = std::string (reader->getAttributeValue(i));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a boolean property
+void IrrlichtBase::ReadBoolProperty (BoolProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // true or false, case insensitive
+ out.value = (ASSIMP_stricmp( reader->getAttributeValue(i),
+ "true") ? false : true);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a float property
+void IrrlichtBase::ReadFloatProperty (FloatProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // just parse the float
+ out.value = fast_atof( reader->getAttributeValue(i) );
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read a vector property
+void IrrlichtBase::ReadVectorProperty (VectorProperty& out)
+{
+ for (int i = 0; i < reader->getAttributeCount();++i)
+ {
+ if (!ASSIMP_stricmp(reader->getAttributeName(i),"name"))
+ {
+ out.name = std::string( reader->getAttributeValue(i) );
+ }
+ else if (!ASSIMP_stricmp(reader->getAttributeName(i),"value"))
+ {
+ // three floats, separated with commas
+ const char* ptr = reader->getAttributeValue(i);
+
+ SkipSpaces(&ptr);
+ ptr = fast_atof_move( ptr,(float&)out.value.x );
+ SkipSpaces(&ptr);
+ if (',' != *ptr)
+ {
+ DefaultLogger::get()->error("IRR(MESH): Expected comma in vector definition");
+ }
+ else SkipSpaces(ptr+1,&ptr);
+ ptr = fast_atof_move( ptr,(float&)out.value.y );
+ SkipSpaces(&ptr);
+ if (',' != *ptr)
+ {
+ DefaultLogger::get()->error("IRR(MESH): Expected comma in vector definition");
+ }
+ else SkipSpaces(ptr+1,&ptr);
+ ptr = fast_atof_move( ptr,(float&)out.value.z );
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a string to a proper aiMappingMode
+int ConvertMappingMode(const std::string& mode)
+{
+ if (mode == "texture_clamp_repeat")
+ {
+ return aiTextureMapMode_Wrap;
+ }
+ else if (mode == "texture_clamp_mirror")
+ return aiTextureMapMode_Mirror;
+
+ return aiTextureMapMode_Clamp;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a material from the XML file
+aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags)
+{
+ MaterialHelper* mat = new MaterialHelper();
+ aiColor4D clr;
+ aiString s;
+
+ matFlags = 0; // zero output flags
+ int cnt = 0; // number of used texture channels
+ unsigned int nd = 0;
+
+ // Continue reading from the file
+ while (reader->read())
+ {
+ switch (reader->getNodeType())
+ {
+ case EXN_ELEMENT:
+
+ // Hex properties
+ if (!ASSIMP_stricmp(reader->getNodeName(),"color"))
+ {
+ HexProperty prop;
+ ReadHexProperty(prop);
+ if (prop.name == "Diffuse")
+ {
+ ColorFromARGBPacked(prop.value,clr);
+ mat->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+ }
+ else if (prop.name == "Ambient")
+ {
+ ColorFromARGBPacked(prop.value,clr);
+ mat->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
+ }
+ else if (prop.name == "Specular")
+ {
+ ColorFromARGBPacked(prop.value,clr);
+ mat->AddProperty(&clr,1,AI_MATKEY_COLOR_SPECULAR);
+ }
+
+ // NOTE: The 'emissive' property causes problems. It is
+ // often != 0, even if there is obviously no light
+ // emitted by the described surface. In fact I think
+ // IRRLICHT ignores this property, too.
+#if 0
+ else if (prop.name == "Emissive")
+ {
+ ColorFromARGBPacked(prop.value,clr);
+ mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
+ }
+#endif
+ }
+ // Float properties
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"float"))
+ {
+ FloatProperty prop;
+ ReadFloatProperty(prop);
+ if (prop.name == "Shininess")
+ {
+ mat->AddProperty(&prop.value,1,AI_MATKEY_SHININESS);
+ }
+ }
+ // Bool properties
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"bool"))
+ {
+ BoolProperty prop;
+ ReadBoolProperty(prop);
+ if (prop.name == "Wireframe")
+ {
+ int val = (prop.value ? true : false);
+ mat->AddProperty(&val,1,AI_MATKEY_ENABLE_WIREFRAME);
+ }
+ else if (prop.name == "GouraudShading")
+ {
+ int val = (prop.value ? aiShadingMode_Gouraud
+ : aiShadingMode_NoShading);
+ mat->AddProperty(&val,1,AI_MATKEY_SHADING_MODEL);
+ }
+ else if (prop.name == "BackfaceCulling")
+ {
+ int val = (!prop.value);
+ mat->AddProperty(&val,1,AI_MATKEY_TWOSIDED);
+ }
+ }
+ // String properties - textures and texture related properties
+ else if (!ASSIMP_stricmp(reader->getNodeName(),"texture") ||
+ !ASSIMP_stricmp(reader->getNodeName(),"enum"))
+ {
+ StringProperty prop;
+ ReadStringProperty(prop);
+ if (prop.value.length())
+ {
+ // material type (shader)
+ if (prop.name == "Type")
+ {
+ if (prop.value == "solid")
+ {
+ // default material ...
+ }
+ else if (prop.value == "trans_vertex_alpha")
+ {
+ matFlags = AI_IRRMESH_MAT_trans_vertex_alpha;
+ }
+ else if (prop.value == "lightmap")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap;
+ }
+ else if (prop.value == "solid_2layer")
+ {
+ matFlags = AI_IRRMESH_MAT_solid_2layer;
+ }
+ else if (prop.value == "lightmap_m2")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_m2;
+ }
+ else if (prop.value == "lightmap_m4")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_m4;
+ }
+ else if (prop.value == "lightmap_light")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_light;
+ }
+ else if (prop.value == "lightmap_light_m2")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_light_m2;
+ }
+ else if (prop.value == "lightmap_light_m4")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_light_m4;
+ }
+ else if (prop.value == "lightmap_add")
+ {
+ matFlags = AI_IRRMESH_MAT_lightmap_add;
+ }
+ // Normal and parallax maps are treated equally
+ else if (prop.value == "normalmap_solid" ||
+ prop.value == "parallaxmap_solid")
+ {
+ matFlags = AI_IRRMESH_MAT_normalmap_solid;
+ }
+ else if (prop.value == "normalmap_trans_vertex_alpha" ||
+ prop.value == "parallaxmap_trans_vertex_alpha")
+ {
+ matFlags = AI_IRRMESH_MAT_normalmap_tva;
+ }
+ else if (prop.value == "normalmap_trans_add" ||
+ prop.value == "parallaxmap_trans_add")
+ {
+ matFlags = AI_IRRMESH_MAT_normalmap_ta;
+ }
+ else {
+ DefaultLogger::get()->warn("IRRMat: Unrecognized material type: " + prop.value);
+ }
+ }
+
+ // Up to 4 texture channels are supported
+ if (prop.name == "Texture1")
+ {
+ // Always accept the primary texture channel
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ else if (prop.name == "Texture2" && cnt == 1)
+ {
+ // 2-layer material lightmapped?
+ if (matFlags & AI_IRRMESH_MAT_lightmap) {
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_LIGHTMAP(0));
+
+ // set the corresponding material flag
+ matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+ }
+ // alternatively: normal or parallax mapping
+ else if (matFlags & AI_IRRMESH_MAT_normalmap_solid) {
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_NORMALS(0));
+
+ // set the corresponding material flag
+ matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+ }
+ // or just as second diffuse texture
+ else if (matFlags & AI_IRRMESH_MAT_solid_2layer) {
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(1));
+ ++nd;
+
+ // set the corresponding material flag
+ matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
+ }
+ else DefaultLogger::get()->warn("IRRmat: Skipping second texture");
+ }
+
+ else if (prop.name == "Texture3" && cnt == 2)
+ {
+ // Irrlicht does not seem to use these channels.
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(nd+1));
+ }
+ else if (prop.name == "Texture4" && cnt == 3)
+ {
+ // Irrlicht does not seem to use these channels.
+ ++cnt;
+ s.Set(prop.value);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(nd+2));
+ }
+
+ // Texture mapping options
+ if (prop.name == "TextureWrap1" && cnt >= 1)
+ {
+ int map = ConvertMappingMode(prop.value);
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
+ }
+ else if (prop.name == "TextureWrap2" && cnt >= 2)
+ {
+ int map = ConvertMappingMode(prop.value);
+ if (matFlags & AI_IRRMESH_MAT_lightmap) {
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_LIGHTMAP(0));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_LIGHTMAP(0));
+ }
+ else if (matFlags & (AI_IRRMESH_MAT_normalmap_solid)) {
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_NORMALS(0));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_NORMALS(0));
+ }
+ else if (matFlags & AI_IRRMESH_MAT_solid_2layer) {
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(1));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(1));
+ }
+ }
+ else if (prop.name == "TextureWrap3" && cnt >= 3)
+ {
+ int map = ConvertMappingMode(prop.value);
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd+1));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd+1));
+ }
+ else if (prop.name == "TextureWrap4" && cnt >= 4)
+ {
+ int map = ConvertMappingMode(prop.value);
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd+2));
+ mat->AddProperty(&map,1,AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd+2));
+ }
+ }
+ }
+ break;
+ case EXN_ELEMENT_END:
+
+ /* Assume there are no further nested nodes in <material> elements
+ */
+ if (/* IRRMESH */ !ASSIMP_stricmp(reader->getNodeName(),"material") ||
+ /* IRR */ !ASSIMP_stricmp(reader->getNodeName(),"attributes"))
+ {
+ // Now process lightmapping flags
+ // We should have at least one textur to do that ..
+ if (cnt && matFlags & AI_IRRMESH_MAT_lightmap)
+ {
+ float f = 1.f;
+ unsigned int unmasked = matFlags&~AI_IRRMESH_MAT_lightmap;
+
+ // Additive lightmap?
+ int op = (unmasked & AI_IRRMESH_MAT_lightmap_add
+ ? aiTextureOp_Add : aiTextureOp_Multiply);
+
+ // Handle Irrlicht's lightmapping scaling factor
+ if (unmasked & AI_IRRMESH_MAT_lightmap_m2 ||
+ unmasked & AI_IRRMESH_MAT_lightmap_light_m2)
+ {
+ f = 2.f;
+ }
+ else if (unmasked & AI_IRRMESH_MAT_lightmap_m4 ||
+ unmasked & AI_IRRMESH_MAT_lightmap_light_m4)
+ {
+ f = 4.f;
+ }
+ mat->AddProperty( &f, 1, AI_MATKEY_TEXBLEND_LIGHTMAP(0));
+ mat->AddProperty( &op,1, AI_MATKEY_TEXOP_LIGHTMAP(0));
+ }
+
+ return mat;
+ }
+ default:
+
+ // GCC complains here ...
+ break;
+ }
+ }
+ DefaultLogger::get()->error("IRRMESH: Unexpected end of file. Material is not complete");
+ return mat;
+}
diff --git a/3rdparty/assimp/code/IRRShared.h b/3rdparty/assimp/code/IRRShared.h
new file mode 100644
index 000000000..03e0c78bd
--- /dev/null
+++ b/3rdparty/assimp/code/IRRShared.h
@@ -0,0 +1,115 @@
+
+
+/** @file IRRShared.h
+ * @brief Shared utilities for the IRR and IRRMESH loaders
+ */
+
+#ifndef INCLUDED_AI_IRRSHARED_H
+#define INCLUDED_AI_IRRSHARED_H
+
+#include "irrXMLWrapper.h"
+#include "BaseImporter.h"
+
+namespace Assimp {
+
+
+/** @brief Matrix to convert from Assimp to IRR and backwards
+ */
+extern const aiMatrix4x4 AI_TO_IRR_MATRIX;
+
+
+// Default: 0 = solid, one texture
+#define AI_IRRMESH_MAT_solid_2layer 0x10000
+
+// Transparency flags
+#define AI_IRRMESH_MAT_trans_vertex_alpha 0x1
+#define AI_IRRMESH_MAT_trans_add 0x2
+
+// Lightmapping flags
+#define AI_IRRMESH_MAT_lightmap 0x2
+#define AI_IRRMESH_MAT_lightmap_m2 (AI_IRRMESH_MAT_lightmap|0x4)
+#define AI_IRRMESH_MAT_lightmap_m4 (AI_IRRMESH_MAT_lightmap|0x8)
+#define AI_IRRMESH_MAT_lightmap_light (AI_IRRMESH_MAT_lightmap|0x10)
+#define AI_IRRMESH_MAT_lightmap_light_m2 (AI_IRRMESH_MAT_lightmap|0x20)
+#define AI_IRRMESH_MAT_lightmap_light_m4 (AI_IRRMESH_MAT_lightmap|0x40)
+#define AI_IRRMESH_MAT_lightmap_add (AI_IRRMESH_MAT_lightmap|0x80)
+
+// Standard NormalMap (or Parallax map, they're treated equally)
+#define AI_IRRMESH_MAT_normalmap_solid (0x100)
+
+// Normal map combined with vertex alpha
+#define AI_IRRMESH_MAT_normalmap_tva \
+ (AI_IRRMESH_MAT_normalmap_solid | AI_IRRMESH_MAT_trans_vertex_alpha)
+
+// Normal map combined with additive transparency
+#define AI_IRRMESH_MAT_normalmap_ta \
+ (AI_IRRMESH_MAT_normalmap_solid | AI_IRRMESH_MAT_trans_add)
+
+// Special flag. It indicates a second texture has been found
+// Its type depends ... either a normal textue or a normal map
+#define AI_IRRMESH_EXTRA_2ND_TEXTURE 0x100000
+
+// ---------------------------------------------------------------------------
+/** Base class for the Irr and IrrMesh importers.
+ *
+ * Declares some irrlight-related xml parsing utilities and provides tools
+ * to load materials from IRR and IRRMESH files.
+ */
+class IrrlichtBase
+{
+protected:
+
+ /** @brief Data structure for a simple name-value property
+ */
+ template <class T>
+ struct Property
+ {
+ std::string name;
+ T value;
+ };
+
+ typedef Property<uint32_t> HexProperty;
+ typedef Property<std::string> StringProperty;
+ typedef Property<bool> BoolProperty;
+ typedef Property<float> FloatProperty;
+ typedef Property<aiVector3D> VectorProperty;
+ typedef Property<int> IntProperty;
+
+ /** XML reader instance
+ */
+ irr::io::IrrXMLReader* reader;
+
+ // -------------------------------------------------------------------
+ /** Parse a material description from the XML
+ * @return The created material
+ * @param matFlags Receives AI_IRRMESH_MAT_XX flags
+ */
+ aiMaterial* ParseMaterial(unsigned int& matFlags);
+
+ // -------------------------------------------------------------------
+ /** Read a property of the specified type from the current XML element.
+ * @param out Recives output data
+ */
+ void ReadHexProperty (HexProperty& out);
+ void ReadStringProperty (StringProperty& out);
+ void ReadBoolProperty (BoolProperty& out);
+ void ReadFloatProperty (FloatProperty& out);
+ void ReadVectorProperty (VectorProperty& out);
+ void ReadIntProperty (IntProperty& out);
+};
+
+
+// ------------------------------------------------------------------------------------------------
+// Unpack a hex color, e.g. 0xdcdedfff
+inline void ColorFromARGBPacked(uint32_t in, aiColor4D& clr)
+{
+ clr.a = ((in >> 24) & 0xff) / 255.f;
+ clr.r = ((in >> 16) & 0xff) / 255.f;
+ clr.g = ((in >> 8) & 0xff) / 255.f;
+ clr.b = ((in ) & 0xff) / 255.f;
+}
+
+
+} // end namespace Assimp
+
+#endif // !! INCLUDED_AI_IRRSHARED_H
diff --git a/3rdparty/assimp/code/Importer.cpp b/3rdparty/assimp/code/Importer.cpp
new file mode 100644
index 000000000..92715ebb0
--- /dev/null
+++ b/3rdparty/assimp/code/Importer.cpp
@@ -0,0 +1,1415 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Importer.cpp
+ * @brief Implementation of the CPP-API class #Importer
+ */
+
+#include "AssimpPCH.h"
+#include "../include/aiVersion.h"
+
+// ------------------------------------------------------------------------------------------------
+/* Uncomment this line to prevent Assimp from catching unknown exceptions.
+ *
+ * Note that any Exception except DeadlyImportError may lead to
+ * undefined behaviour -> loaders could remain in an unusable state and
+ * further imports with the same Importer instance could fail/crash/burn ...
+ */
+// ------------------------------------------------------------------------------------------------
+#define ASSIMP_CATCH_GLOBAL_EXCEPTIONS
+
+
+// ------------------------------------------------------------------------------------------------
+// Internal headers
+// ------------------------------------------------------------------------------------------------
+#include "BaseImporter.h"
+#include "BaseProcess.h"
+#include "DefaultIOStream.h"
+#include "DefaultIOSystem.h"
+#include "DefaultProgressHandler.h"
+#include "GenericProperty.h"
+#include "ProcessHelper.h"
+#include "ScenePreprocessor.h"
+#include "MemoryIOWrapper.h"
+#include "Profiler.h"
+#include "TinyFormatter.h"
+
+using namespace Assimp::Profiling;
+using namespace Assimp::Formatter;
+
+// ------------------------------------------------------------------------------------------------
+// Importers
+// (include_new_importers_here)
+// ------------------------------------------------------------------------------------------------
+#ifndef ASSIMP_BUILD_NO_X_IMPORTER
+# include "XFileImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
+# include "3DSLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
+# include "MD3Loader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
+# include "MDLLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER
+# include "MD2Loader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
+# include "PlyLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
+# include "ASELoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
+# include "ObjFileImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER
+# include "HMPLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
+# include "SMDLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
+# include "MDCLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
+# include "MD5Loader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_STL_IMPORTER
+# include "STLLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
+# include "LWOLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
+# include "DXFLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
+# include "NFFLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
+# include "RawLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
+# include "OFFLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_AC_IMPORTER
+# include "ACLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
+# include "BVHLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
+# include "IRRMeshLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
+# include "IRRLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
+# include "Q3DLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
+# include "B3DImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
+# include "ColladaLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
+# include "TerragenLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER
+# include "CSMLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_3D_IMPORTER
+# include "UnrealLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER
+# include "LWSLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
+# include "OgreImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
+# include "MS3DLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
+# include "COBLoader.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+# include "BlenderLoader.h"
+#endif
+//#ifndef ASSIMP_BUILD_NO_SWORDOFMOONLIGHT_IMPORTER
+//# include "SomLoader.h"
+//#endif
+#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
+# include "Q3BSPFileImporter.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER
+# include "NDOLoader.h"
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Post processing-Steps
+// ------------------------------------------------------------------------------------------------
+#ifndef ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS
+# include "CalcTangentsProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
+# include "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"
+#endif
+#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
+# include "TriangulateProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS
+# include "GenFaceNormalsProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS
+# include "GenVertexNormalsProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_REMOVEVC_PROCESS
+# include "RemoveVCProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS
+# include "SplitLargeMeshes.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS
+# include "PretransformVertices.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS
+# include "LimitBoneWeightsProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
+# include "ValidateDataStructure.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS
+# include "ImproveCacheLocality.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS
+# include "FixNormalsStep.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS
+# include "RemoveRedundantMaterials.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS
+# include "FindInvalidDataProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS
+# include "FindDegenerates.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS
+# include "SortByPTypeProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS
+# include "ComputeUVMappingProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
+# include "TextureTransform.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS
+# include "FindInstancesProcess.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
+# include "OptimizeMeshes.h"
+#endif
+#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
+# include "OptimizeGraph.h"
+#endif
+
+using namespace Assimp;
+using namespace Assimp::Intern;
+
+// ------------------------------------------------------------------------------------------------
+// Intern::AllocateFromAssimpHeap serves as abstract base class. It overrides
+// new and delete (and their array counterparts) of public API classes (e.g. Logger) to
+// utilize our DLL heap.
+// See http://www.gotw.ca/publications/mill15.htm
+// ------------------------------------------------------------------------------------------------
+void* AllocateFromAssimpHeap::operator new ( size_t num_bytes) {
+ return ::operator new(num_bytes);
+}
+
+void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothrow_t& ) throw() {
+ try {
+ return AllocateFromAssimpHeap::operator new( num_bytes );
+ }
+ catch( ... ) {
+ return NULL;
+ }
+}
+
+void AllocateFromAssimpHeap::operator delete ( void* data) {
+ return ::operator delete(data);
+}
+
+void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes) {
+ return ::operator new[](num_bytes);
+}
+
+void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() {
+ try {
+ return AllocateFromAssimpHeap::operator new[]( num_bytes );
+ }
+ catch( ... ) {
+ return NULL;
+ }
+}
+
+void AllocateFromAssimpHeap::operator delete[] ( void* data) {
+ return ::operator delete[](data);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer constructor.
+Importer::Importer()
+{
+ // allocate the pimpl first
+ pimpl = new ImporterPimpl();
+
+ pimpl->mScene = NULL;
+ pimpl->mErrorString = "";
+
+ // Allocate a default IO handler
+ pimpl->mIOHandler = new DefaultIOSystem;
+ pimpl->mIsDefaultHandler = true;
+ pimpl->bExtraVerbose = false; // disable extra verbose mode by default
+
+ pimpl->mProgressHandler = new DefaultProgressHandler();
+ pimpl->mIsDefaultProgressHandler = true;
+
+ // ----------------------------------------------------------------------------
+ // Add an instance of each worker class here
+ // (register_new_importers_here)
+ // ----------------------------------------------------------------------------
+ pimpl->mImporter.reserve(64);
+#if (!defined ASSIMP_BUILD_NO_X_IMPORTER)
+ pimpl->mImporter.push_back( new XFileImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_OBJ_IMPORTER)
+ pimpl->mImporter.push_back( new ObjFileImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
+ pimpl->mImporter.push_back( new Discreet3DSImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER)
+ pimpl->mImporter.push_back( new MD3Importer());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MD2_IMPORTER)
+ pimpl->mImporter.push_back( new MD2Importer());
+#endif
+#if (!defined ASSIMP_BUILD_NO_PLY_IMPORTER)
+ pimpl->mImporter.push_back( new PLYImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MDL_IMPORTER)
+ pimpl->mImporter.push_back( new MDLImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_ASE_IMPORTER)
+ pimpl->mImporter.push_back( new ASEImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_HMP_IMPORTER)
+ pimpl->mImporter.push_back( new HMPImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_SMD_IMPORTER)
+ pimpl->mImporter.push_back( new SMDImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MDC_IMPORTER)
+ pimpl->mImporter.push_back( new MDCImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MD5_IMPORTER)
+ pimpl->mImporter.push_back( new MD5Importer());
+#endif
+#if (!defined ASSIMP_BUILD_NO_STL_IMPORTER)
+ pimpl->mImporter.push_back( new STLImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER)
+ pimpl->mImporter.push_back( new LWOImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_DXF_IMPORTER)
+ pimpl->mImporter.push_back( new DXFImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_NFF_IMPORTER)
+ pimpl->mImporter.push_back( new NFFImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_RAW_IMPORTER)
+ pimpl->mImporter.push_back( new RAWImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_OFF_IMPORTER)
+ pimpl->mImporter.push_back( new OFFImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_AC_IMPORTER)
+ pimpl->mImporter.push_back( new AC3DImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_BVH_IMPORTER)
+ pimpl->mImporter.push_back( new BVHLoader());
+#endif
+#if (!defined ASSIMP_BUILD_NO_IRRMESH_IMPORTER)
+ pimpl->mImporter.push_back( new IRRMeshImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_IRR_IMPORTER)
+ pimpl->mImporter.push_back( new IRRImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_Q3D_IMPORTER)
+ pimpl->mImporter.push_back( new Q3DImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_B3D_IMPORTER)
+ pimpl->mImporter.push_back( new B3DImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_COLLADA_IMPORTER)
+ pimpl->mImporter.push_back( new ColladaLoader());
+#endif
+#if (!defined ASSIMP_BUILD_NO_TERRAGEN_IMPORTER)
+ pimpl->mImporter.push_back( new TerragenImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_CSM_IMPORTER)
+ pimpl->mImporter.push_back( new CSMImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_3D_IMPORTER)
+ pimpl->mImporter.push_back( new UnrealImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_LWS_IMPORTER)
+ pimpl->mImporter.push_back( new LWSImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_OGRE_IMPORTER)
+ pimpl->mImporter.push_back( new Ogre::OgreImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MS3D_IMPORTER)
+ pimpl->mImporter.push_back( new MS3DImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_COB_IMPORTER)
+ pimpl->mImporter.push_back( new COBImporter());
+#endif
+#if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER)
+ pimpl->mImporter.push_back( new BlenderImporter());
+#endif
+//#if (!defined ASSIMP_BUILD_NO_SWORDOFMOONLIGHT_IMPORTER)
+// pimpl->mImporter.push_back( new SomImporter());
+//#endif
+#if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER)
+ pimpl->mImporter.push_back( new Q3BSPFileImporter() );
+#endif
+#if (!defined ASSIMP_BUILD_NO_NDO_IMPORTER)
+ pimpl->mImporter.push_back( new NDOImporter() );
+#endif
+
+ // ----------------------------------------------------------------------------
+ // Add an instance of each post processing step here in the order
+ // of sequence it is executed. Steps that are added here are not
+ // validated - as RegisterPPStep() does - all dependencies must be given.
+ // ----------------------------------------------------------------------------
+ pimpl->mPostProcessingSteps.reserve(25);
+#if (!defined ASSIMP_BUILD_NO_REMOVEVC_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new RemoveVCProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new RemoveRedundantMatsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FINDINSTANCES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FindInstancesProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new OptimizeGraphProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new OptimizeMeshesProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FINDDEGENERATES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FindDegeneratesProcess());
+#endif
+#ifndef ASSIMP_BUILD_NO_GENUVCOORDS_PROCESS
+ pimpl->mPostProcessingSteps.push_back( new ComputeUVMappingProcess());
+#endif
+#ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
+ pimpl->mPostProcessingSteps.push_back( new TextureTransformStep());
+#endif
+#if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new PretransformVertices());
+#endif
+#if (!defined ASSIMP_BUILD_NO_TRIANGULATE_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new TriangulateProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_SORTBYPTYPE_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new SortByPTypeProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FINDINVALIDDATA_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FindInvalidDataProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FIXINFACINGNORMALS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FixInfacingNormalsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Triangle());
+#endif
+#if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new GenFaceNormalsProcess());
+#endif
+
+ // .........................................................................
+ // DON'T change the order of these five!
+ pimpl->mPostProcessingSteps.push_back( new ComputeSpatialSortProcess());
+ // .........................................................................
+
+#if (!defined ASSIMP_BUILD_NO_GENVERTEXNORMALS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new GenVertexNormalsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_CALCTANGENTS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new CalcTangentsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_JOINVERTICES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new JoinVerticesProcess());
+#endif
+
+ // .........................................................................
+ pimpl->mPostProcessingSteps.push_back( new DestroySpatialSortProcess());
+ // .........................................................................
+
+#if (!defined ASSIMP_BUILD_NO_SPLITLARGEMESHES_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new SplitLargeMeshesProcess_Vertex());
+#endif
+#if (!defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new MakeLeftHandedProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FLIPUVS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FlipUVsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_FLIPWINDINGORDER_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new FlipWindingOrderProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_LIMITBONEWEIGHTS_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new LimitBoneWeightsProcess());
+#endif
+#if (!defined ASSIMP_BUILD_NO_IMPROVECACHELOCALITY_PROCESS)
+ pimpl->mPostProcessingSteps.push_back( new ImproveCacheLocalityProcess());
+#endif
+
+ // Allocate a SharedPostProcessInfo object and store pointers to it in all post-process steps in the list.
+ pimpl->mPPShared = new SharedPostProcessInfo();
+ for (std::vector<BaseProcess*>::iterator it = pimpl->mPostProcessingSteps.begin();
+ it != pimpl->mPostProcessingSteps.end();
+ ++it) {
+
+ (*it)->SetSharedData(pimpl->mPPShared);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor of Importer
+Importer::~Importer()
+{
+ // Delete all import plugins
+ for ( unsigned int a = 0; a < pimpl->mImporter.size(); a++)
+ delete pimpl->mImporter[a];
+
+ // Delete all post-processing plug-ins
+ for ( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++)
+ delete pimpl->mPostProcessingSteps[a];
+
+ // Delete the assigned IO handler
+ delete pimpl->mIOHandler;
+
+ // Kill imported scene. Destructors should do that recursivly
+ delete pimpl->mScene;
+
+ // Delete shared post-processing data
+ delete pimpl->mPPShared;
+
+ // and finally the pimpl itself
+ delete pimpl;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Copy constructor - copies the config of another Importer, not the scene
+Importer::Importer(const Importer &other)
+{
+ new(this) Importer();
+
+ pimpl->mIntProperties = other.pimpl->mIntProperties;
+ pimpl->mFloatProperties = other.pimpl->mFloatProperties;
+ pimpl->mStringProperties = other.pimpl->mStringProperties;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Register a custom post-processing step
+aiReturn Importer::RegisterPPStep(BaseProcess* pImp)
+{
+ ai_assert(NULL != pImp);
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ pimpl->mPostProcessingSteps.push_back(pImp);
+ DefaultLogger::get()->info("Registering custom post-processing step");
+
+ ASSIMP_END_EXCEPTION_REGION(aiReturn);
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Register a custom loader plugin
+aiReturn Importer::RegisterLoader(BaseImporter* pImp)
+{
+ ai_assert(NULL != pImp);
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // --------------------------------------------------------------------
+ // Check whether we would have two loaders for the same file extension
+ // This is absolutely OK, but we should warn the developer of the new
+ // loader that his code will probably never be called if the first
+ // loader is a bit too lazy in his file checking.
+ // --------------------------------------------------------------------
+ std::set<std::string> st;
+ std::string baked;
+ pImp->GetExtensionList(st);
+
+ for (std::set<std::string>::const_iterator it = st.begin(); it != st.end(); ++it) {
+
+#ifdef _DEBUG
+ if (IsExtensionSupported(*it)) {
+ DefaultLogger::get()->warn("The file extension " + *it + " is already in use");
+ }
+#endif
+ baked += *it;
+ }
+
+ // add the loader
+ pimpl->mImporter.push_back(pImp);
+ DefaultLogger::get()->info("Registering custom importer for these file extensions: " + baked);
+ ASSIMP_END_EXCEPTION_REGION(aiReturn);
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unregister a custom loader plugin
+aiReturn Importer::UnregisterLoader(BaseImporter* pImp)
+{
+ if (!pImp) {
+ // unregistering a NULL importer is no problem for us ... really!
+ return AI_SUCCESS;
+ }
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ std::vector<BaseImporter*>::iterator it = std::find(pimpl->mImporter.begin(),
+ pimpl->mImporter.end(),pImp);
+
+ if (it != pimpl->mImporter.end()) {
+ pimpl->mImporter.erase(it);
+
+ std::set<std::string> st;
+ pImp->GetExtensionList(st);
+
+ DefaultLogger::get()->info("Unregistering custom importer: ");
+ return AI_SUCCESS;
+ }
+ DefaultLogger::get()->warn("Unable to remove custom importer: I can't find you ...");
+ ASSIMP_END_EXCEPTION_REGION(aiReturn);
+ return AI_FAILURE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unregister a custom loader plugin
+aiReturn Importer::UnregisterPPStep(BaseProcess* pImp)
+{
+ if (!pImp) {
+ // unregistering a NULL ppstep is no problem for us ... really!
+ return AI_SUCCESS;
+ }
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ std::vector<BaseProcess*>::iterator it = std::find(pimpl->mPostProcessingSteps.begin(),
+ pimpl->mPostProcessingSteps.end(),pImp);
+
+ if (it != pimpl->mPostProcessingSteps.end()) {
+ pimpl->mPostProcessingSteps.erase(it);
+ DefaultLogger::get()->info("Unregistering custom post-processing step");
+ return AI_SUCCESS;
+ }
+ DefaultLogger::get()->warn("Unable to remove custom post-processing step: I can't find you ..");
+ ASSIMP_END_EXCEPTION_REGION(aiReturn);
+ return AI_FAILURE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Supplies a custom IO handler to the importer to open and access files.
+void Importer::SetIOHandler( IOSystem* pIOHandler)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ // If the new handler is zero, allocate a default IO implementation.
+ if (!pIOHandler)
+ {
+ // Release pointer in the possession of the caller
+ pimpl->mIOHandler = new DefaultIOSystem();
+ pimpl->mIsDefaultHandler = true;
+ }
+ // Otherwise register the custom handler
+ else if (pimpl->mIOHandler != pIOHandler)
+ {
+ delete pimpl->mIOHandler;
+ pimpl->mIOHandler = pIOHandler;
+ pimpl->mIsDefaultHandler = false;
+ }
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the currently set IO handler
+IOSystem* Importer::GetIOHandler() const
+{
+ return pimpl->mIOHandler;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether a custom IO handler is currently set
+bool Importer::IsDefaultIOHandler() const
+{
+ return pimpl->mIsDefaultHandler;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Supplies a custom progress handler to get regular callbacks during importing
+void Importer::SetProgressHandler ( ProgressHandler* pHandler )
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ // If the new handler is zero, allocate a default implementation.
+ if (!pHandler)
+ {
+ // Release pointer in the possession of the caller
+ pimpl->mProgressHandler = new DefaultProgressHandler();
+ pimpl->mIsDefaultProgressHandler = true;
+ }
+ // Otherwise register the custom handler
+ else if (pimpl->mProgressHandler != pHandler)
+ {
+ delete pimpl->mProgressHandler;
+ pimpl->mProgressHandler = pHandler;
+ pimpl->mIsDefaultProgressHandler = false;
+ }
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the currently set progress handler
+ProgressHandler* Importer::GetProgressHandler() const
+{
+ return pimpl->mProgressHandler;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether a custom progress handler is currently set
+bool Importer::IsDefaultProgressHandler() const
+{
+ return pimpl->mIsDefaultProgressHandler;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Validate post process step flags
+bool _ValidateFlags(unsigned int pFlags)
+{
+ if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) {
+ DefaultLogger::get()->error("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible");
+ return false;
+ }
+ if (pFlags & aiProcess_OptimizeGraph && pFlags & aiProcess_PreTransformVertices) {
+ DefaultLogger::get()->error("#aiProcess_OptimizeGraph and #aiProcess_PreTransformVertices are incompatible");
+ return false;
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Free the current scene
+void Importer::FreeScene( )
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ delete pimpl->mScene;
+ pimpl->mScene = NULL;
+
+ pimpl->mErrorString = "";
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the current error string, if any
+const char* Importer::GetErrorString() const
+{
+ /* Must remain valid as long as ReadFile() or FreeFile() are not called */
+ return pimpl->mErrorString.c_str();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Enable extra-verbose mode
+void Importer::SetExtraVerbose(bool bDo)
+{
+ pimpl->bExtraVerbose = bDo;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the current scene
+const aiScene* Importer::GetScene() const
+{
+ return pimpl->mScene;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Orphan the current scene and return it.
+aiScene* Importer::GetOrphanedScene()
+{
+ aiScene* s = pimpl->mScene;
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ pimpl->mScene = NULL;
+
+ pimpl->mErrorString = ""; /* reset error string */
+ ASSIMP_END_EXCEPTION_REGION(aiScene*);
+ return s;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Validate post-processing flags
+bool Importer::ValidateFlags(unsigned int pFlags) const
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ // run basic checks for mutually exclusive flags
+ if (!_ValidateFlags(pFlags)) {
+ return false;
+ }
+
+ // ValidateDS does not anymore occur in the pp list, it plays an awesome extra role ...
+#ifdef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
+ if (pFlags & aiProcess_ValidateDataStructure) {
+ return false;
+ }
+#endif
+ pFlags &= ~aiProcess_ValidateDataStructure;
+
+ // Now iterate through all bits which are set in the flags and check whether we find at least
+ // one pp plugin which handles it.
+ for (unsigned int mask = 1; mask < (1u << (sizeof(unsigned int)*8-1));mask <<= 1) {
+
+ if (pFlags & mask) {
+
+ bool have = false;
+ for ( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
+ if (pimpl->mPostProcessingSteps[a]-> IsActive(mask) ) {
+
+ have = true;
+ break;
+ }
+ }
+ if (!have) {
+ return false;
+ }
+ }
+ }
+ ASSIMP_END_EXCEPTION_REGION(bool);
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
+ size_t pLength,
+ unsigned int pFlags,
+ const char* pHint /*= ""*/)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ if (!pHint) {
+ pHint = "";
+ }
+
+ if (!pBuffer || !pLength || strlen(pHint) > 100) {
+ pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()";
+ return NULL;
+ }
+
+ // prevent deletion of the previous IOHandler
+ IOSystem* io = pimpl->mIOHandler;
+ pimpl->mIOHandler = NULL;
+
+ SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength));
+
+ // read the file and recover the previous IOSystem
+ char fbuff[128];
+ sprintf(fbuff,"%s.%s",AI_MEMORYIO_MAGIC_FILENAME,pHint);
+
+ ReadFile(fbuff,pFlags);
+ SetIOHandler(io);
+
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+ return pimpl->mScene;
+}
+
+// ------------------------------------------------------------------------------------------------
+void WriteLogOpening(const std::string& file)
+{
+ Logger* l = DefaultLogger::get();
+ if (!l) {
+ return;
+ }
+ l->info("Load " + file);
+
+ // print a full version dump. This is nice because we don't
+ // need to ask the authors of incoming bug reports for
+ // the library version they're using - a log dump is
+ // sufficient.
+ const unsigned int flags = aiGetCompileFlags();
+ l->debug(format()
+ << "Assimp "
+ << aiGetVersionMajor()
+ << "."
+ << aiGetVersionMinor()
+ << "."
+ << aiGetVersionRevision()
+
+#if defined(ASSIMP_BUILD_X86_32BIT_ARCHITECTURE)
+ << " x86"
+#elif defined(ASSIMP_BUILD_X86_64BIT_ARCHITECTURE)
+ << " amd64"
+#elif defined(ASSIMP_BUILD_IA_64BIT_ARCHITECTURE)
+ << " itanium"
+#elif defined(ASSIMP_BUILD_PPC_32BIT_ARCHITECTURE)
+ << " ppc32"
+#elif defined(ASSIMP_BUILD_ARM_32BIT_ARCHITECTURE)
+ << " arm32"
+#else
+# error unknown architecture
+#endif
+
+#if defined(_MSC_VER)
+ << " msvc"
+#elif defined(__GNUC__)
+ << " gcc"
+#else
+# error unknown compiler
+#endif
+
+#ifndef NDEBUG
+ << " debug"
+#endif
+
+ << (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "")
+ << (flags & ASSIMP_CFLAGS_SHARED ? " shared" : "")
+ << (flags & ASSIMP_CFLAGS_SINGLETHREADED ? " singlethreaded" : "")
+ );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the given file and returns its contents if successful.
+const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ const std::string pFile(_pFile);
+
+ // ----------------------------------------------------------------------
+ // Put a large try block around everything to catch all std::exception's
+ // that might be thrown by STL containers or by new().
+ // ImportErrorException's are throw by ourselves and caught elsewhere.
+ //-----------------------------------------------------------------------
+
+ WriteLogOpening(pFile);
+
+#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
+ try
+#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
+ {
+ // Check whether this Importer instance has already loaded
+ // a scene. In this case we need to delete the old one
+ if (pimpl->mScene) {
+
+ DefaultLogger::get()->debug("(Deleting previous scene)");
+ FreeScene();
+ }
+
+ // First check if the file is accessable at all
+ if ( !pimpl->mIOHandler->Exists( pFile)) {
+
+ pimpl->mErrorString = "Unable to open file \"" + pFile + "\".";
+ DefaultLogger::get()->error(pimpl->mErrorString);
+ return NULL;
+ }
+
+ boost::scoped_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
+ if (profiler) {
+ profiler->BeginRegion("total");
+ }
+
+ // Find an worker class which can handle the file
+ BaseImporter* imp = NULL;
+ for ( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
+
+ if ( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) {
+ imp = pimpl->mImporter[a];
+ break;
+ }
+ }
+
+ if (!imp) {
+ // not so bad yet ... try format auto detection.
+ const std::string::size_type s = pFile.find_last_of('.');
+ if (s != std::string::npos) {
+ DefaultLogger::get()->info("File extension now known, trying signature-based detection");
+ for ( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
+
+ if ( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) {
+ imp = pimpl->mImporter[a];
+ break;
+ }
+ }
+ }
+ // Put a proper error message if no suitable importer was found
+ if ( !imp) {
+ pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
+ DefaultLogger::get()->error(pimpl->mErrorString);
+ return NULL;
+ }
+ }
+
+ // Dispatch the reading to the worker class for this format
+ DefaultLogger::get()->info("Found a matching importer for this file format");
+ pimpl->mProgressHandler->Update();
+
+ if (profiler) {
+ profiler->BeginRegion("import");
+ }
+
+ pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler);
+ pimpl->mProgressHandler->Update();
+
+ if (profiler) {
+ profiler->EndRegion("import");
+ }
+
+ // If successful, apply all active post processing steps to the imported data
+ if ( pimpl->mScene) {
+
+#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
+ // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called.
+ if (pFlags & aiProcess_ValidateDataStructure)
+ {
+ ValidateDSProcess ds;
+ ds.ExecuteOnScene (this);
+ if (!pimpl->mScene) {
+ return NULL;
+ }
+ }
+#endif // no validation
+
+ // Preprocess the scene and prepare it for post-processing
+ if (profiler) {
+ profiler->BeginRegion("preprocess");
+ }
+
+ ScenePreprocessor pre(pimpl->mScene);
+ pre.ProcessScene();
+
+ pimpl->mProgressHandler->Update();
+ if (profiler) {
+ profiler->EndRegion("preprocess");
+ }
+
+ // Ensure that the validation process won't be called twice
+ ApplyPostProcessing(pFlags & (~aiProcess_ValidateDataStructure));
+ }
+ // if failed, extract the error string
+ else if ( !pimpl->mScene) {
+ pimpl->mErrorString = imp->GetErrorText();
+ }
+
+ // clear any data allocated by post-process steps
+ pimpl->mPPShared->Clean();
+
+ if (profiler) {
+ profiler->EndRegion("total");
+ }
+ }
+#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
+ catch (std::exception &e)
+ {
+#if (defined _MSC_VER) && (defined _CPPRTTI)
+ // if we have RTTI get the full name of the exception that occured
+ pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what();
+#else
+ pimpl->mErrorString = std::string("std::exception: ") + e.what();
+#endif
+
+ DefaultLogger::get()->error(pimpl->mErrorString);
+ delete pimpl->mScene; pimpl->mScene = NULL;
+ }
+#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
+
+ // either successful or failure - the pointer expresses it anyways
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+ return pimpl->mScene;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Apply post-processing to the currently bound scene
+const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ // Return immediately if no scene is active
+ if (!pimpl->mScene) {
+ return NULL;
+ }
+
+ // If no flags are given, return the current scene with no further action
+ if (!pFlags) {
+ return pimpl->mScene;
+ }
+
+ // In debug builds: run basic flag validation
+ ai_assert(_ValidateFlags(pFlags));
+ DefaultLogger::get()->info("Entering post processing pipeline");
+
+#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
+ // The ValidateDS process plays an exceptional role. It isn't contained in the global
+ // list of post-processing steps, so we need to call it manually.
+ if (pFlags & aiProcess_ValidateDataStructure)
+ {
+ ValidateDSProcess ds;
+ ds.ExecuteOnScene (this);
+ if (!pimpl->mScene) {
+ return NULL;
+ }
+ }
+#endif // no validation
+#ifdef _DEBUG
+ if (pimpl->bExtraVerbose)
+ {
+#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
+ DefaultLogger::get()->error("Verbose Import is not available due to build settings");
+#endif // no validation
+ pFlags |= aiProcess_ValidateDataStructure;
+ }
+#else
+ if (pimpl->bExtraVerbose) {
+ DefaultLogger::get()->warn("Not a debug build, ignoring extra verbose setting");
+ }
+#endif // ! DEBUG
+
+ boost::scoped_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
+ for ( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
+
+ BaseProcess* process = pimpl->mPostProcessingSteps[a];
+ if ( process->IsActive( pFlags)) {
+
+ if (profiler) {
+ profiler->BeginRegion("postprocess");
+ }
+
+ process->ExecuteOnScene ( this );
+ pimpl->mProgressHandler->Update();
+
+ if (profiler) {
+ profiler->EndRegion("postprocess");
+ }
+ }
+ if ( !pimpl->mScene) {
+ break;
+ }
+#ifdef _DEBUG
+
+#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
+ continue;
+#endif // no validation
+
+ // If the extra verbose mode is active, execute the ValidateDataStructureStep again - after each step
+ if (pimpl->bExtraVerbose) {
+ DefaultLogger::get()->debug("Verbose Import: revalidating data structures");
+
+ ValidateDSProcess ds;
+ ds.ExecuteOnScene (this);
+ if ( !pimpl->mScene) {
+ DefaultLogger::get()->error("Verbose Import: failed to revalidate data structures");
+ break;
+ }
+ }
+#endif // ! DEBUG
+ }
+
+ // clear any data allocated by post-process steps
+ pimpl->mPPShared->Clean();
+ DefaultLogger::get()->info("Leaving post processing pipeline");
+
+ ASSIMP_END_EXCEPTION_REGION(const aiScene*);
+ return pimpl->mScene;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Helper function to check whether an extension is supported by ASSIMP
+bool Importer::IsExtensionSupported(const char* szExtension) const
+{
+ return NULL != FindLoader(szExtension);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Find a loader plugin for a given file extension
+BaseImporter* Importer::FindLoader (const char* szExtension) const
+{
+ ai_assert(szExtension);
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+
+ // skip over wildcard and dot characters at string head --
+ for (;*szExtension == '*' || *szExtension == '.'; ++szExtension) {};
+
+ std::string ext(szExtension);
+ if (ext.empty()) {
+ return NULL;
+ }
+ std::transform(ext.begin(),ext.end(), ext.begin(), tolower);
+
+ std::set<std::string> str;
+ for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) {
+ str.clear();
+
+ (*i)->GetExtensionList(str);
+ for (std::set<std::string>::const_iterator it = str.begin(); it != str.end(); ++it) {
+ if (ext == *it) {
+ return (*i);
+ }
+ }
+ }
+ ASSIMP_END_EXCEPTION_REGION(BaseImporter*);
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Helper function to build a list of all file extensions supported by ASSIMP
+void Importer::GetExtensionList(aiString& szOut) const
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ std::set<std::string> str;
+ for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) {
+ (*i)->GetExtensionList(str);
+ }
+
+ for (std::set<std::string>::const_iterator it = str.begin();; ) {
+ szOut.Append("*.");
+ szOut.Append((*it).c_str());
+
+ if (++it == str.end()) {
+ break;
+ }
+ szOut.Append(";");
+ }
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Set a configuration property
+void Importer::SetPropertyInteger(const char* szName, int iValue,
+ bool* bWasExisting /*= NULL*/)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue,bWasExisting);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Set a configuration property
+void Importer::SetPropertyFloat(const char* szName, float iValue,
+ bool* bWasExisting /*= NULL*/)
+{
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ SetGenericProperty<float>(pimpl->mFloatProperties, szName,iValue,bWasExisting);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Set a configuration property
+void Importer::SetPropertyString(const char* szName, const std::string& value,
+ bool* bWasExisting /*= NULL*/)
+{
+ try {
+ std::cout << "";
+ }
+ catch (...) {
+ try {
+ throw;
+ }
+ catch(std::exception&) {
+ return;
+ }
+ }
+
+ ASSIMP_BEGIN_EXCEPTION_REGION();
+ SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value,bWasExisting);
+ ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a configuration property
+int Importer::GetPropertyInteger(const char* szName,
+ int iErrorReturn /*= 0xffffffff*/) const
+{
+ return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a configuration property
+float Importer::GetPropertyFloat(const char* szName,
+ float iErrorReturn /*= 10e10*/) const
+{
+ return GetGenericProperty<float>(pimpl->mFloatProperties,szName,iErrorReturn);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a configuration property
+const std::string& Importer::GetPropertyString(const char* szName,
+ const std::string& iErrorReturn /*= ""*/) const
+{
+ return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the memory requirements of a single node
+inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode)
+{
+ iScene += sizeof(aiNode);
+ iScene += sizeof(unsigned int) * pcNode->mNumMeshes;
+ iScene += sizeof(void*) * pcNode->mNumChildren;
+
+ for (unsigned int i = 0; i < pcNode->mNumChildren;++i) {
+ AddNodeWeight(iScene,pcNode->mChildren[i]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the memory requirements of the scene
+void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
+{
+ in = aiMemoryInfo();
+ aiScene* mScene = pimpl->mScene;
+
+ // return if we have no scene loaded
+ if (!pimpl->mScene)
+ return;
+
+
+ in.total = sizeof(aiScene);
+
+ // add all meshes
+ for (unsigned int i = 0; i < mScene->mNumMeshes;++i)
+ {
+ in.meshes += sizeof(aiMesh);
+ if (mScene->mMeshes[i]->HasPositions()) {
+ in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
+ }
+
+ if (mScene->mMeshes[i]->HasNormals()) {
+ in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
+ }
+
+ if (mScene->mMeshes[i]->HasTangentsAndBitangents()) {
+ in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices * 2;
+ }
+
+ for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) {
+ if (mScene->mMeshes[i]->HasVertexColors(a)) {
+ in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices;
+ }
+ else break;
+ }
+ for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) {
+ if (mScene->mMeshes[i]->HasTextureCoords(a)) {
+ in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
+ }
+ else break;
+ }
+ if (mScene->mMeshes[i]->HasBones()) {
+ in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones;
+ for (unsigned int p = 0; p < mScene->mMeshes[i]->mNumBones;++p) {
+ in.meshes += sizeof(aiBone);
+ in.meshes += mScene->mMeshes[i]->mBones[p]->mNumWeights * sizeof(aiVertexWeight);
+ }
+ }
+ in.meshes += (sizeof(aiFace) + 3 * sizeof(unsigned int))*mScene->mMeshes[i]->mNumFaces;
+ }
+ in.total += in.meshes;
+
+ // add all embedded textures
+ for (unsigned int i = 0; i < mScene->mNumTextures;++i) {
+ const aiTexture* pc = mScene->mTextures[i];
+ in.textures += sizeof(aiTexture);
+ if (pc->mHeight) {
+ in.textures += 4 * pc->mHeight * pc->mWidth;
+ }
+ else in.textures += pc->mWidth;
+ }
+ in.total += in.textures;
+
+ // add all animations
+ for (unsigned int i = 0; i < mScene->mNumAnimations;++i) {
+ const aiAnimation* pc = mScene->mAnimations[i];
+ in.animations += sizeof(aiAnimation);
+
+ // add all bone anims
+ for (unsigned int a = 0; a < pc->mNumChannels; ++a) {
+ const aiNodeAnim* pc2 = pc->mChannels[i];
+ in.animations += sizeof(aiNodeAnim);
+ in.animations += pc2->mNumPositionKeys * sizeof(aiVectorKey);
+ in.animations += pc2->mNumScalingKeys * sizeof(aiVectorKey);
+ in.animations += pc2->mNumRotationKeys * sizeof(aiQuatKey);
+ }
+ }
+ in.total += in.animations;
+
+ // add all cameras and all lights
+ in.total += in.cameras = sizeof(aiCamera) * mScene->mNumCameras;
+ in.total += in.lights = sizeof(aiLight) * mScene->mNumLights;
+
+ // add all nodes
+ AddNodeWeight(in.nodes,mScene->mRootNode);
+ in.total += in.nodes;
+
+ // add all materials
+ for (unsigned int i = 0; i < mScene->mNumMaterials;++i) {
+ const aiMaterial* pc = mScene->mMaterials[i];
+ in.materials += sizeof(aiMaterial);
+ in.materials += pc->mNumAllocated * sizeof(void*);
+
+ for (unsigned int a = 0; a < pc->mNumProperties;++a) {
+ in.materials += pc->mProperties[a]->mDataLength;
+ }
+ }
+ in.total += in.materials;
+}
+
diff --git a/3rdparty/assimp/code/ImproveCacheLocality.cpp b/3rdparty/assimp/code/ImproveCacheLocality.cpp
new file mode 100644
index 000000000..a25d88789
--- /dev/null
+++ b/3rdparty/assimp/code/ImproveCacheLocality.cpp
@@ -0,0 +1,378 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the post processing step to improve the cache locality of a mesh.
+ * <br>
+ * The algorithm is roughly basing on this paper:
+ * http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf
+ * .. although overdraw rduction isn't implemented yet ...
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "ImproveCacheLocality.h"
+#include "VertexTriangleAdjacency.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ImproveCacheLocalityProcess::ImproveCacheLocalityProcess() {
+ configCacheDepth = PP_ICL_PTCACHE_SIZE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ImproveCacheLocalityProcess::~ImproveCacheLocalityProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool ImproveCacheLocalityProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_ImproveCacheLocality) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration
+void ImproveCacheLocalityProcess::SetupProperties(const Importer* pImp)
+{
+ // AI_CONFIG_PP_ICL_PTCACHE_SIZE controls the target cache size for the optimizer
+ configCacheDepth = pImp->GetPropertyInteger(AI_CONFIG_PP_ICL_PTCACHE_SIZE,PP_ICL_PTCACHE_SIZE);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void ImproveCacheLocalityProcess::Execute( aiScene* pScene)
+{
+ if (!pScene->mNumMeshes) {
+ DefaultLogger::get()->debug("ImproveCacheLocalityProcess skipped; there are no meshes");
+ return;
+ }
+
+ DefaultLogger::get()->debug("ImproveCacheLocalityProcess begin");
+
+ float out = 0.f;
+ unsigned int numf = 0, numm = 0;
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++){
+ const float res = ProcessMesh( pScene->mMeshes[a],a);
+ if (res) {
+ numf += pScene->mMeshes[a]->mNumFaces;
+ out += res;
+ ++numm;
+ }
+ }
+ if (!DefaultLogger::isNullLogger()) {
+ char szBuff[128]; // should be sufficiently large in every case
+ ::sprintf(szBuff,"Cache relevant are %i meshes (%i faces). Average output ACMR is %f",
+ numm,numf,out/numf);
+
+ DefaultLogger::get()->info(szBuff);
+ DefaultLogger::get()->debug("ImproveCacheLocalityProcess finished. ");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Improves the cache coherency of a specific mesh
+float ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshNum)
+{
+ // TODO: rewrite this to use std::vector or boost::shared_array
+ ai_assert(NULL != pMesh);
+
+ // Check whether the input data is valid
+ // - there must be vertices and faces
+ // - all faces must be triangulated or we can't operate on them
+ if (!pMesh->HasFaces() || !pMesh->HasPositions())
+ return 0.f;
+
+ if (pMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) {
+ DefaultLogger::get()->error("This algorithm works on triangle meshes only");
+ return 0.f;
+ }
+
+ float fACMR = 3.f;
+ const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces;
+
+ // Input ACMR is for logging purposes only
+ if (!DefaultLogger::isNullLogger()) {
+
+ unsigned int* piFIFOStack = new unsigned int[configCacheDepth];
+ memset(piFIFOStack,0xff,configCacheDepth*sizeof(unsigned int));
+ unsigned int* piCur = piFIFOStack;
+ const unsigned int* const piCurEnd = piFIFOStack + configCacheDepth;
+
+ // count the number of cache misses
+ unsigned int iCacheMisses = 0;
+ for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace) {
+
+ for (unsigned int qq = 0; qq < 3;++qq) {
+ bool bInCache = false;
+
+ for (unsigned int* pp = piFIFOStack;pp < piCurEnd;++pp) {
+ if (*pp == pcFace->mIndices[qq]) {
+ // the vertex is in cache
+ bInCache = true;
+ break;
+ }
+ }
+ if (!bInCache) {
+ ++iCacheMisses;
+ if (piCurEnd == piCur) {
+ piCur = piFIFOStack;
+ }
+ *piCur++ = pcFace->mIndices[qq];
+ }
+ }
+ }
+ delete[] piFIFOStack;
+ fACMR = (float)iCacheMisses / pMesh->mNumFaces;
+ if (3.0 == fACMR) {
+ char szBuff[128]; // should be sufficiently large in every case
+
+ // the JoinIdenticalVertices process has not been executed on this
+ // mesh, otherwise this value would normally be at least minimally
+ // smaller than 3.0 ...
+ sprintf(szBuff,"Mesh %i: Not suitable for vcache optimization",meshNum);
+ DefaultLogger::get()->warn(szBuff);
+ return 0.f;
+ }
+ }
+
+ // first we need to build a vertex-triangle adjacency list
+ VertexTriangleAdjacency adj(pMesh->mFaces,pMesh->mNumFaces, pMesh->mNumVertices,true);
+
+ // build a list to store per-vertex caching time stamps
+ unsigned int* const piCachingStamps = new unsigned int[pMesh->mNumVertices];
+ memset(piCachingStamps,0x0,pMesh->mNumVertices*sizeof(unsigned int));
+
+ // allocate an empty output index buffer. We store the output indices in one large array.
+ // Since the number of triangles won't change the input faces can be reused. This is how
+ // we save thousands of redundant mini allocations for aiFace::mIndices
+ const unsigned int iIdxCnt = pMesh->mNumFaces*3;
+ unsigned int* const piIBOutput = new unsigned int[iIdxCnt];
+ unsigned int* piCSIter = piIBOutput;
+
+ // allocate the flag array to hold the information
+ // whether a face has already been emitted or not
+ std::vector<bool> abEmitted(pMesh->mNumFaces,false);
+
+ // dead-end vertex index stack
+ std::stack<unsigned int> sDeadEndVStack;
+
+ // create a copy of the piNumTriPtr buffer
+ unsigned int* const piNumTriPtr = adj.mLiveTriangles;
+ const unsigned int* const piNumTriPtrNoModify = new unsigned int[pMesh->mNumVertices];
+ memcpy(const_cast<unsigned int* const> (piNumTriPtrNoModify),piNumTriPtr,
+ pMesh->mNumVertices * sizeof(unsigned int));
+
+ // get the largest number of referenced triangles and allocate the "candidate buffer"
+ unsigned int iMaxRefTris = 0; {
+ const unsigned int* piCur = adj.mLiveTriangles;
+ const unsigned int* const piCurEnd = adj.mLiveTriangles+pMesh->mNumVertices;
+ for (;piCur != piCurEnd;++piCur) {
+ iMaxRefTris = std::max(iMaxRefTris,*piCur);
+ }
+ }
+ unsigned int* piCandidates = new unsigned int[iMaxRefTris*3];
+ unsigned int iCacheMisses = 0;
+
+ // ...................................................................................
+ /** PSEUDOCODE for the algorithm
+
+ A = Build-Adjacency(I) Vertex-triangle adjacency
+ L = Get-Triangle-Counts(A) Per-vertex live triangle counts
+ C = Zero(Vertex-Count(I)) Per-vertex caching time stamps
+ D = Empty-Stack() Dead-end vertex stack
+ E = False(Triangle-Count(I)) Per triangle emitted flag
+ O = Empty-Index-Buffer() Empty output buffer
+ f = 0 Arbitrary starting vertex
+ s = k+1, i = 1 Time stamp and cursor
+ while f >= 0 For all valid fanning vertices
+ N = Empty-Set() 1-ring of next candidates
+ for each Triangle t in Neighbors(A, f)
+ if !Emitted(E,t)
+ for each Vertex v in t
+ Append(O,v) Output vertex
+ Push(D,v) Add to dead-end stack
+ Insert(N,v) Register as candidate
+ L[v] = L[v]-1 Decrease live triangle count
+ if s-C[v] > k If not in cache
+ C[v] = s Set time stamp
+ s = s+1 Increment time stamp
+ E[t] = true Flag triangle as emitted
+ Select next fanning vertex
+ f = Get-Next-Vertex(I,i,k,N,C,s,L,D)
+ return O
+ */
+ // ...................................................................................
+
+ int ivdx = 0;
+ int ics = 1;
+ int iStampCnt = configCacheDepth+1;
+ while (ivdx >= 0) {
+
+ unsigned int icnt = piNumTriPtrNoModify[ivdx];
+ unsigned int* piList = adj.GetAdjacentTriangles(ivdx);
+ unsigned int* piCurCandidate = piCandidates;
+
+ // get all triangles in the neighborhood
+ for (unsigned int tri = 0; tri < icnt;++tri) {
+
+ // if they have not yet been emitted, add them to the output IB
+ const unsigned int fidx = *piList++;
+ if (!abEmitted[fidx]) {
+
+ // so iterate through all vertices of the current triangle
+ const aiFace* pcFace = &pMesh->mFaces[ fidx ];
+ for (unsigned int* p = pcFace->mIndices, *p2 = pcFace->mIndices+3;p != p2;++p) {
+ const unsigned int dp = *p;
+
+ // the current vertex won't have any free triangles after this step
+ if (ivdx != (int)dp) {
+ // append the vertex to the dead-end stack
+ sDeadEndVStack.push(dp);
+
+ // register as candidate for the next step
+ *piCurCandidate++ = dp;
+
+ // decrease the per-vertex triangle counts
+ piNumTriPtr[dp]--;
+ }
+
+ // append the vertex to the output index buffer
+ *piCSIter++ = dp;
+
+ // if the vertex is not yet in cache, set its cache count
+ if (iStampCnt-piCachingStamps[dp] > configCacheDepth) {
+ piCachingStamps[dp] = iStampCnt++;
+ ++iCacheMisses;
+ }
+ }
+ // flag triangle as emitted
+ abEmitted[fidx] = true;
+ }
+ }
+
+ // the vertex has now no living adjacent triangles anymore
+ piNumTriPtr[ivdx] = 0;
+
+ // get next fanning vertex
+ ivdx = -1;
+ int max_priority = -1;
+ for (unsigned int* piCur = piCandidates;piCur != piCurCandidate;++piCur) {
+ register const unsigned int dp = *piCur;
+
+ // must have live triangles
+ if (piNumTriPtr[dp] > 0) {
+ int priority = 0;
+
+ // will the vertex be in cache, even after fanning occurs?
+ unsigned int tmp;
+ if ((tmp = iStampCnt-piCachingStamps[dp]) + 2*piNumTriPtr[dp] <= configCacheDepth) {
+ priority = tmp;
+ }
+
+ // keep best candidate
+ if (priority > max_priority) {
+ max_priority = priority;
+ ivdx = dp;
+ }
+ }
+ }
+ // did we reach a dead end?
+ if (-1 == ivdx) {
+ // need to get a non-local vertex for which we have a good chance that it is still
+ // in the cache ...
+ while (!sDeadEndVStack.empty()) {
+ unsigned int iCachedIdx = sDeadEndVStack.top();
+ sDeadEndVStack.pop();
+ if (piNumTriPtr[ iCachedIdx ] > 0) {
+ ivdx = iCachedIdx;
+ break;
+ }
+ }
+
+ if (-1 == ivdx) {
+ // well, there isn't such a vertex. Simply get the next vertex in input order and
+ // hope it is not too bad ...
+ while (ics < (int)pMesh->mNumVertices) {
+ ++ics;
+ if (piNumTriPtr[ics] > 0) {
+ ivdx = ics;
+ break;
+ }
+ }
+ }
+ }
+ }
+ float fACMR2 = 0.0f;
+ if (!DefaultLogger::isNullLogger()) {
+ fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
+
+ // very intense verbose logging ... prepare for much text if there are many meshes
+ if ( DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
+ char szBuff[128]; // should be sufficiently large in every case
+
+ ::sprintf(szBuff,"Mesh %i | ACMR in: %f out: %f | ~%.1f%%",meshNum,fACMR,fACMR2,
+ ((fACMR - fACMR2) / fACMR) * 100.f);
+ DefaultLogger::get()->debug(szBuff);
+ }
+
+ fACMR2 *= pMesh->mNumFaces;
+ }
+ // sort the output index buffer back to the input array
+ piCSIter = piIBOutput;
+ for (aiFace* pcFace = pMesh->mFaces; pcFace != pcEnd;++pcFace) {
+ pcFace->mIndices[0] = *piCSIter++;
+ pcFace->mIndices[1] = *piCSIter++;
+ pcFace->mIndices[2] = *piCSIter++;
+ }
+
+ // delete temporary storage
+ delete[] piCachingStamps;
+ delete[] piIBOutput;
+ delete[] piCandidates;
+ delete[] piNumTriPtrNoModify;
+ return fACMR2;
+}
diff --git a/3rdparty/assimp/code/ImproveCacheLocality.h b/3rdparty/assimp/code/ImproveCacheLocality.h
new file mode 100644
index 000000000..58613e04c
--- /dev/null
+++ b/3rdparty/assimp/code/ImproveCacheLocality.h
@@ -0,0 +1,102 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to reorder faces for
+ better cache locality*/
+#ifndef AI_IMPROVECACHELOCALITY_H_INC
+#define AI_IMPROVECACHELOCALITY_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiTypes.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The ImproveCacheLocalityProcess reorders all faces for improved vertex
+ * cache locality. It tries to arrange all faces to fans and to render
+ * faces which share vertices directly one after the other.
+ *
+ * @note This step expects triagulated input data.
+ */
+class ASSIMP_API ImproveCacheLocalityProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ ImproveCacheLocalityProcess();
+
+ /** Destructor, private as well */
+ ~ImproveCacheLocalityProcess();
+
+public:
+
+ // -------------------------------------------------------------------
+ // Check whether the pp step is active
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Executes the pp step on a given scene
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ // Configures the pp step
+ void SetupProperties(const Importer* pImp);
+
+protected:
+ // -------------------------------------------------------------------
+ /** Executes the postprocessing step on the given mesh
+ * @param pMesh The mesh to process.
+ * @param meshNum Index of the mesh to process
+ */
+ float ProcessMesh( aiMesh* pMesh, unsigned int meshNum);
+
+private:
+ //! Configuration parameter: specifies the size of the cache to
+ //! optimize the vertex data for.
+ unsigned int configCacheDepth;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_IMPROVECACHELOCALITY_H_INC
diff --git a/3rdparty/assimp/code/JoinVerticesProcess.cpp b/3rdparty/assimp/code/JoinVerticesProcess.cpp
new file mode 100644
index 000000000..b98927181
--- /dev/null
+++ b/3rdparty/assimp/code/JoinVerticesProcess.cpp
@@ -0,0 +1,395 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the post processing step to join identical vertices
+ * for all imported meshes
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
+
+#include "JoinVerticesProcess.h"
+#include "ProcessHelper.h"
+#include "Vertex.h"
+#include "TinyFormatter.h"
+
+using namespace Assimp;
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+JoinVerticesProcess::JoinVerticesProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+JoinVerticesProcess::~JoinVerticesProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool JoinVerticesProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_JoinIdenticalVertices) != 0;
+}
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void JoinVerticesProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("JoinVerticesProcess begin");
+
+ // get the total number of vertices BEFORE the step is executed
+ int iNumOldVertices = 0;
+ if (!DefaultLogger::isNullLogger()) {
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+ iNumOldVertices += pScene->mMeshes[a]->mNumVertices;
+ }
+ }
+
+ // execute the step
+ int iNumVertices = 0;
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ iNumVertices += ProcessMesh( pScene->mMeshes[a],a);
+
+ // if logging is active, print detailed statistics
+ if (!DefaultLogger::isNullLogger())
+ {
+ if (iNumOldVertices == iNumVertices)
+ {
+ DefaultLogger::get()->debug("JoinVerticesProcess finished ");
+ } else
+ {
+ char szBuff[128]; // should be sufficiently large in every case
+ sprintf(szBuff,"JoinVerticesProcess finished | Verts in: %i out: %i | ~%.1f%%",
+ iNumOldVertices,
+ iNumVertices,
+ ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f);
+ DefaultLogger::get()->info(szBuff);
+ }
+ }
+
+ pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unites identical vertices in the given mesh
+int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
+{
+ BOOST_STATIC_ASSERT( AI_MAX_NUMBER_OF_COLOR_SETS == 4);
+ BOOST_STATIC_ASSERT( AI_MAX_NUMBER_OF_TEXTURECOORDS == 4);
+
+ // Return early if we don't have any positions
+ if (!pMesh->HasPositions() || !pMesh->HasFaces()) {
+ return 0;
+ }
+
+ // We'll never have more vertices afterwards.
+ std::vector<Vertex> uniqueVertices;
+ uniqueVertices.reserve( pMesh->mNumVertices);
+
+ // For each vertex the index of the vertex it was replaced by.
+ // Since the maximal number of vertices is 2^31-1, the most significand bit can be used to mark
+ // whether a new vertex was created for the index (true) or if it was replaced by an existing
+ // unique vertex (false). This saves an additional std::vector<bool> and greatly enhances
+ // branching performance.
+ BOOST_STATIC_ASSERT(AI_MAX_VERTICES == 0x7fffffff);
+ std::vector<unsigned int> replaceIndex( pMesh->mNumVertices, 0xffffffff);
+
+ // A little helper to find locally close vertices faster.
+ // Try to reuse the lookup table from the last step.
+ const static float epsilon = 1e-5f;
+ float posEpsilonSqr;
+ SpatialSort* vertexFinder = NULL;
+ SpatialSort _vertexFinder;
+
+ typedef std::pair<SpatialSort,float> SpatPair;
+ if (shared) {
+ std::vector<SpatPair >* avf;
+ shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
+ if (avf) {
+ SpatPair& blubb = (*avf)[meshIndex];
+ vertexFinder = &blubb.first;
+ posEpsilonSqr = blubb.second;
+ }
+ }
+ if (!vertexFinder) {
+ // bad, need to compute it.
+ _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
+ vertexFinder = &_vertexFinder;
+ posEpsilonSqr = ComputePositionEpsilon(pMesh);
+ }
+
+ // Squared because we check against squared length of the vector difference
+ static const float squareEpsilon = epsilon * epsilon;
+
+ // Again, better waste some bytes than a realloc ...
+ std::vector<unsigned int> verticesFound;
+ verticesFound.reserve(10);
+
+ // Run an optimized code path if we don't have multiple UVs or vertex colors.
+ // This should yield false in more than 99% of all imports ...
+ const bool complex = (
+ pMesh->mTextureCoords[1] ||
+ pMesh->mTextureCoords[2] ||
+ pMesh->mTextureCoords[3] ||
+ pMesh->mColors[0] ||
+ pMesh->mColors[1] ||
+ pMesh->mColors[2] ||
+ pMesh->mColors[3] );
+
+ // Now check each vertex if it brings something new to the table
+ for ( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
+ // collect the vertex data
+ Vertex v(pMesh,a);
+
+ // collect all vertices that are close enough to the given position
+ vertexFinder->FindIdenticalPositions( v.position, verticesFound);
+ unsigned int matchIndex = 0xffffffff;
+
+ // check all unique vertices close to the position if this vertex is already present among them
+ for ( unsigned int b = 0; b < verticesFound.size(); b++) {
+
+ const unsigned int vidx = verticesFound[b];
+ const unsigned int uidx = replaceIndex[ vidx];
+ if ( uidx & 0x80000000)
+ continue;
+
+ const Vertex& uv = uniqueVertices[ uidx];
+ // Position mismatch is impossible - the vertex finder already discarded all non-matching positions
+
+ // We just test the other attributes even if they're not present in the mesh.
+ // In this case they're initialized to 0 so the comparision succeeds.
+ // By this method the non-present attributes are effectively ignored in the comparision.
+ if ( (uv.normal - v.normal).SquareLength() > squareEpsilon)
+ continue;
+ if ( (uv.texcoords[0] - v.texcoords[0]).SquareLength() > squareEpsilon)
+ continue;
+ if ( (uv.tangent - v.tangent).SquareLength() > squareEpsilon)
+ continue;
+ if ( (uv.bitangent - v.bitangent).SquareLength() > squareEpsilon)
+ continue;
+
+ // Usually we won't have vertex colors or multiple UVs, so we can skip from here
+ // Actually this increases runtime performance slightly, at least if branch
+ // prediction is on our side.
+ if (complex){
+ // manually unrolled because continue wouldn't work as desired in an inner loop
+ if ( GetColorDifference( uv.colors[0], v.colors[0]) > squareEpsilon)
+ continue;
+ if ( GetColorDifference( uv.colors[1], v.colors[1]) > squareEpsilon)
+ continue;
+ if ( GetColorDifference( uv.colors[2], v.colors[2]) > squareEpsilon)
+ continue;
+ if ( GetColorDifference( uv.colors[3], v.colors[3]) > squareEpsilon)
+ continue;
+
+ // texture coord matching manually unrolled as well
+ if ( (uv.texcoords[1] - v.texcoords[1]).SquareLength() > squareEpsilon)
+ continue;
+ if ( (uv.texcoords[2] - v.texcoords[2]).SquareLength() > squareEpsilon)
+ continue;
+ if ( (uv.texcoords[3] - v.texcoords[3]).SquareLength() > squareEpsilon)
+ continue;
+ }
+
+ // we're still here -> this vertex perfectly matches our given vertex
+ matchIndex = uidx;
+ break;
+ }
+
+ // found a replacement vertex among the uniques?
+ if ( matchIndex != 0xffffffff)
+ {
+ // store where to found the matching unique vertex
+ replaceIndex[a] = matchIndex | 0x80000000;
+ }
+ else
+ {
+ // no unique vertex matches it upto now -> so add it
+ replaceIndex[a] = (unsigned int)uniqueVertices.size();
+ uniqueVertices.push_back( v);
+ }
+ }
+
+ if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) {
+ DefaultLogger::get()->debug((Formatter::format(),
+ "Mesh ",meshIndex,
+ " (",
+ (pMesh->mName.length ? pMesh->mName.data : "unnamed"),
+ ") | Verts in: ",pMesh->mNumVertices,
+ " out: ",
+ uniqueVertices.size(),
+ " | ~",
+ ((pMesh->mNumVertices - uniqueVertices.size()) / (float)pMesh->mNumVertices) * 100.f,
+ "%"
+ ));
+ }
+
+ // replace vertex data with the unique data sets
+ pMesh->mNumVertices = (unsigned int)uniqueVertices.size();
+
+ // ----------------------------------------------------------------------------
+ // NOTE - we're *not* calling Vertex::SortBack() because it would check for
+ // presence of every single vertex component once PER VERTEX. And our CPU
+ // dislikes branches, even if they're easily predictable.
+ // ----------------------------------------------------------------------------
+
+ // Position
+ delete [] pMesh->mVertices;
+ pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+ for ( unsigned int a = 0; a < pMesh->mNumVertices; a++)
+ pMesh->mVertices[a] = uniqueVertices[a].position;
+
+ // Normals, if present
+ if ( pMesh->mNormals)
+ {
+ delete [] pMesh->mNormals;
+ pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+ for ( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
+ pMesh->mNormals[a] = uniqueVertices[a].normal;
+ }
+ }
+ // Tangents, if present
+ if ( pMesh->mTangents)
+ {
+ delete [] pMesh->mTangents;
+ pMesh->mTangents = new aiVector3D[pMesh->mNumVertices];
+ for ( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
+ pMesh->mTangents[a] = uniqueVertices[a].tangent;
+ }
+ }
+ // Bitangents as well
+ if ( pMesh->mBitangents)
+ {
+ delete [] pMesh->mBitangents;
+ pMesh->mBitangents = new aiVector3D[pMesh->mNumVertices];
+ for ( unsigned int a = 0; a < pMesh->mNumVertices; a++) {
+ pMesh->mBitangents[a] = uniqueVertices[a].bitangent;
+ }
+ }
+ // Vertex colors
+ for ( unsigned int a = 0; pMesh->HasVertexColors(a); a++)
+ {
+ delete [] pMesh->mColors[a];
+ pMesh->mColors[a] = new aiColor4D[pMesh->mNumVertices];
+ for ( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
+ pMesh->mColors[a][b] = uniqueVertices[b].colors[a];
+ }
+ }
+ // Texture coords
+ for ( unsigned int a = 0; pMesh->HasTextureCoords(a); a++)
+ {
+ delete [] pMesh->mTextureCoords[a];
+ pMesh->mTextureCoords[a] = new aiVector3D[pMesh->mNumVertices];
+ for ( unsigned int b = 0; b < pMesh->mNumVertices; b++) {
+ pMesh->mTextureCoords[a][b] = uniqueVertices[b].texcoords[a];
+ }
+ }
+
+ // adjust the indices in all faces
+ for ( unsigned int a = 0; a < pMesh->mNumFaces; a++)
+ {
+ aiFace& face = pMesh->mFaces[a];
+ for ( unsigned int b = 0; b < face.mNumIndices; b++) {
+ face.mIndices[b] = replaceIndex[face.mIndices[b]] & ~0x80000000;
+ }
+ }
+
+ // adjust bone vertex weights.
+ for ( int a = 0; a < (int)pMesh->mNumBones; a++)
+ {
+ aiBone* bone = pMesh->mBones[a];
+ std::vector<aiVertexWeight> newWeights;
+ newWeights.reserve( bone->mNumWeights);
+
+ for ( unsigned int b = 0; b < bone->mNumWeights; b++)
+ {
+ const aiVertexWeight& ow = bone->mWeights[b];
+ // if the vertex is a unique one, translate it
+ if ( !(replaceIndex[ow.mVertexId] & 0x80000000))
+ {
+ aiVertexWeight nw;
+ nw.mVertexId = replaceIndex[ow.mVertexId];
+ nw.mWeight = ow.mWeight;
+ newWeights.push_back( nw);
+ }
+ }
+
+ if (newWeights.size() > 0) {
+ // kill the old and replace them with the translated weights
+ delete [] bone->mWeights;
+ bone->mNumWeights = (unsigned int)newWeights.size();
+
+ bone->mWeights = new aiVertexWeight[bone->mNumWeights];
+ memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight));
+ }
+ else {
+
+ /* NOTE:
+ *
+ * In the algorithm above we're assuming that there are no vertices
+ * with a different bone weight setup at the same position. That wouldn't
+ * make sense, but it is not absolutely impossible. SkeletonMeshBuilder
+ * for example generates such input data if two skeleton points
+ * share the same position. Again this doesn't make sense but is
+ * reality for some model formats (MD5 for example uses these special
+ * nodes as attachment tags for its weapons).
+ *
+ * Then it is possible that a bone has no weights anymore .... as a quick
+ * workaround, we're just removing these bones. If they're animated,
+ * model geometry might be modified but at least there's no risk of a crash.
+ */
+ delete bone;
+ --pMesh->mNumBones;
+ for (unsigned int n = a; n < pMesh->mNumBones; ++n) {
+ pMesh->mBones[n] = pMesh->mBones[n+1];
+ }
+
+ --a;
+ DefaultLogger::get()->warn("Removing bone -> no weights remaining");
+ }
+ }
+ return pMesh->mNumVertices;
+}
+
+#endif // !! ASSIMP_BUILD_NO_JOINVERTICES_PROCESS
diff --git a/3rdparty/assimp/code/JoinVerticesProcess.h b/3rdparty/assimp/code/JoinVerticesProcess.h
new file mode 100644
index 000000000..0ce00ab58
--- /dev/null
+++ b/3rdparty/assimp/code/JoinVerticesProcess.h
@@ -0,0 +1,105 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to join identical vertices
+ on all imported meshes.*/
+#ifndef AI_JOINVERTICESPROCESS_H_INC
+#define AI_JOINVERTICESPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiTypes.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+
+class JoinVerticesTest;
+
+// ---------------------------------------------------------------------------
+/** The JoinVerticesProcess unites identical vertices in all imported meshes.
+ * By default the importer returns meshes where each face addressed its own
+ * set of vertices even if that means that identical vertices are stored multiple
+ * times. The JoinVerticesProcess finds these identical vertices and
+ * erases all but one of the copies. This usually reduces the number of vertices
+ * in a mesh by a serious amount and is the standard form to render a mesh.
+ */
+class ASSIMP_API JoinVerticesProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class JoinVerticesTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ JoinVerticesProcess();
+
+ /** Destructor, private as well */
+ ~JoinVerticesProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+protected:
+ // -------------------------------------------------------------------
+ /** Unites identical vertices in the given mesh.
+ * @param pMesh The mesh to process.
+ * @param meshIndex Index of the mesh to process
+ */
+ int ProcessMesh( aiMesh* pMesh, unsigned int meshIndex);
+
+private:
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CALCTANGENTSPROCESS_H_INC
diff --git a/3rdparty/assimp/code/LWOAnimation.cpp b/3rdparty/assimp/code/LWOAnimation.cpp
new file mode 100644
index 000000000..dcf2ac793
--- /dev/null
+++ b/3rdparty/assimp/code/LWOAnimation.cpp
@@ -0,0 +1,585 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 LWOAnimation.cpp
+ * @brief LWOAnimationResolver utility class
+ *
+ * It's a very generic implementation of LightWave's system of
+ * componentwise-animated stuff. The one and only fully free
+ * implementation of LightWave envelopes of which I know.
+*/
+
+#include "AssimpPCH.h"
+#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER) && (!defined ASSIMP_BUILD_NO_LWS_IMPORTER)
+
+// internal headers
+#include "LWOFileData.h"
+
+using namespace Assimp;
+using namespace Assimp::LWO;
+
+// ------------------------------------------------------------------------------------------------
+// Construct an animation resolver from a given list of envelopes
+AnimResolver::AnimResolver(std::list<Envelope>& _envelopes,double tick)
+ : envelopes (_envelopes)
+ , sample_rate (0.)
+{
+ trans_x = trans_y = trans_z = NULL;
+ rotat_x = rotat_y = rotat_z = NULL;
+ scale_x = scale_y = scale_z = NULL;
+
+ first = last = 150392.;
+
+ // find transformation envelopes
+ for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
+
+ (*it).old_first = 0;
+ (*it).old_last = (*it).keys.size()-1;
+
+ if ((*it).keys.empty()) continue;
+ switch ((*it).type) {
+
+ // translation
+ case LWO::EnvelopeType_Position_X:
+ trans_x = &*it;break;
+ case LWO::EnvelopeType_Position_Y:
+ trans_y = &*it;break;
+ case LWO::EnvelopeType_Position_Z:
+ trans_z = &*it;break;
+
+ // rotation
+ case LWO::EnvelopeType_Rotation_Heading:
+ rotat_x = &*it;break;
+ case LWO::EnvelopeType_Rotation_Pitch:
+ rotat_y = &*it;break;
+ case LWO::EnvelopeType_Rotation_Bank:
+ rotat_z = &*it;break;
+
+ // scaling
+ case LWO::EnvelopeType_Scaling_X:
+ scale_x = &*it;break;
+ case LWO::EnvelopeType_Scaling_Y:
+ scale_y = &*it;break;
+ case LWO::EnvelopeType_Scaling_Z:
+ scale_z = &*it;break;
+ default:
+ continue;
+ };
+
+ // convert from seconds to ticks
+ for (std::vector<LWO::Key>::iterator d = (*it).keys.begin(); d != (*it).keys.end(); ++d)
+ (*d).time *= tick;
+
+ // set default animation range (minimum and maximum time value for which we have a keyframe)
+ first = std::min(first, (*it).keys.front().time );
+ last = std::max(last, (*it).keys.back().time );
+ }
+
+ // deferred setup of animation range to increase performance.
+ // typically the application will want to specify its own.
+ need_to_setup = true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reset all envelopes to their original contents
+void AnimResolver::ClearAnimRangeSetup()
+{
+ for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
+
+ (*it).keys.erase((*it).keys.begin(),(*it).keys.begin()+(*it).old_first);
+ (*it).keys.erase((*it).keys.begin()+(*it).old_last+1,(*it).keys.end());
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Insert additional keys to match LWO's pre& post behaviours.
+void AnimResolver::UpdateAnimRangeSetup()
+{
+ // XXX doesn't work yet (hangs if more than one envelope channels needs to be interpolated)
+
+ for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
+ if ((*it).keys.empty()) continue;
+
+ const double my_first = (*it).keys.front().time;
+ const double my_last = (*it).keys.back().time;
+
+ const double delta = my_last-my_first;
+ const size_t old_size = (*it).keys.size();
+
+ const float value_delta = (*it).keys.back().value - (*it).keys.front().value;
+
+ // NOTE: We won't handle reset, linear and constant here.
+ // See DoInterpolation() for their implementation.
+
+ // process pre behaviour
+ switch ((*it).pre) {
+ case LWO::PrePostBehaviour_OffsetRepeat:
+ case LWO::PrePostBehaviour_Repeat:
+ case LWO::PrePostBehaviour_Oscillate:
+ {
+ const double start_time = delta - fmod(my_first-first,delta);
+ std::vector<LWO::Key>::iterator n = std::find_if((*it).keys.begin(),(*it).keys.end(),
+ std::bind1st(std::greater<double>(),start_time)),m;
+
+ size_t ofs = 0;
+ if (n != (*it).keys.end()) {
+ // copy from here - don't use iterators, insert() would invalidate them
+ ofs = (*it).keys.end()-n;
+ (*it).keys.insert((*it).keys.begin(),ofs,LWO::Key());
+
+ std::copy((*it).keys.end()-ofs,(*it).keys.end(),(*it).keys.begin());
+ }
+
+ // do full copies. again, no iterators
+ const unsigned int num = (unsigned int)((my_first-first) / delta);
+ (*it).keys.resize((*it).keys.size() + num*old_size);
+
+ n = (*it).keys.begin()+ofs;
+ bool reverse = false;
+ for (unsigned int i = 0; i < num; ++i) {
+ m = n+old_size*(i+1);
+ std::copy(n,n+old_size,m);
+
+ if ((*it).pre == LWO::PrePostBehaviour_Oscillate && (reverse = !reverse))
+ std::reverse(m,m+old_size-1);
+ }
+
+ // update time values
+ n = (*it).keys.end() - (old_size+1);
+ double cur_minus = delta;
+ unsigned int tt = 1;
+ for (const double tmp = delta*(num+1);cur_minus <= tmp;cur_minus += delta,++tt) {
+ m = (delta == tmp ? (*it).keys.begin() : n - (old_size+1));
+ for (;m != n; --n) {
+ (*n).time -= cur_minus;
+
+ // offset repeat? add delta offset to key value
+ if ((*it).pre == LWO::PrePostBehaviour_OffsetRepeat) {
+ (*n).value += tt * value_delta;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ // silence compiler warning
+ break;
+ }
+
+ // process post behaviour
+ switch ((*it).post) {
+
+ case LWO::PrePostBehaviour_OffsetRepeat:
+ case LWO::PrePostBehaviour_Repeat:
+ case LWO::PrePostBehaviour_Oscillate:
+
+ break;
+
+ default:
+ // silence compiler warning
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Extract bind pose matrix
+void AnimResolver::ExtractBindPose(aiMatrix4x4& out)
+{
+ // If we have no envelopes, return identity
+ if (envelopes.empty()) {
+ out = aiMatrix4x4();
+ return;
+ }
+ aiVector3D angles, scaling(1.f,1.f,1.f), translation;
+
+ if (trans_x) translation.x = trans_x->keys[0].value;
+ if (trans_y) translation.y = trans_y->keys[0].value;
+ if (trans_z) translation.z = trans_z->keys[0].value;
+
+ if (rotat_x) angles.x = rotat_x->keys[0].value;
+ if (rotat_y) angles.y = rotat_y->keys[0].value;
+ if (rotat_z) angles.z = rotat_z->keys[0].value;
+
+ if (scale_x) scaling.x = scale_x->keys[0].value;
+ if (scale_y) scaling.y = scale_y->keys[0].value;
+ if (scale_z) scaling.z = scale_z->keys[0].value;
+
+ // build the final matrix
+ aiMatrix4x4 s,r,t;
+
+ r.FromEulerAnglesXYZ(angles);
+ //aiMatrix4x4::RotationY(angles.y,r);
+ // fixme: make FromEulerAngles static, too
+ aiMatrix4x4::Translation(translation,t);
+ aiMatrix4x4::Scaling(scaling,s);
+ out = s*r*t;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Do a single interpolation on a channel
+void AnimResolver::DoInterpolation(std::vector<LWO::Key>::const_iterator cur,
+ LWO::Envelope* envl,double time, float& fill)
+{
+ if (envl->keys.size() == 1) {
+ fill = envl->keys[0].value;
+ return;
+ }
+
+ // check whether we're at the beginning of the animation track
+ if (cur == envl->keys.begin()) {
+
+ // ok ... this depends on pre behaviour now
+ // we don't need to handle repeat&offset repeat&oszillate here, see UpdateAnimRangeSetup()
+ switch (envl->pre)
+ {
+ case LWO::PrePostBehaviour_Linear:
+ DoInterpolation2(cur,cur+1,time,fill);
+ return;
+
+ case LWO::PrePostBehaviour_Reset:
+ fill = 0.f;
+ return;
+
+ default : //case LWO::PrePostBehaviour_Constant:
+ fill = (*cur).value;
+ return;
+ }
+ }
+ // check whether we're at the end of the animation track
+ else if (cur == envl->keys.end()-1 && time > envl->keys.rbegin()->time) {
+ // ok ... this depends on post behaviour now
+ switch (envl->post)
+ {
+ case LWO::PrePostBehaviour_Linear:
+ DoInterpolation2(cur,cur-1,time,fill);
+ return;
+
+ case LWO::PrePostBehaviour_Reset:
+ fill = 0.f;
+ return;
+
+ default : //case LWO::PrePostBehaviour_Constant:
+ fill = (*cur).value;
+ return;
+ }
+ }
+
+ // Otherwise do a simple interpolation
+ DoInterpolation2(cur-1,cur,time,fill);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Almost the same, except we won't handle pre/post conditions here
+void AnimResolver::DoInterpolation2(std::vector<LWO::Key>::const_iterator beg,
+ std::vector<LWO::Key>::const_iterator end,double time, float& fill)
+{
+ switch ((*end).inter) {
+
+ case LWO::IT_STEP:
+ // no interpolation at all - take the value of the last key
+ fill = (*beg).value;
+ return;
+ default:
+
+ // silence compiler warning
+ break;
+ }
+ // linear interpolation - default
+ fill = (*beg).value + ((*end).value - (*beg).value)*(float)(((time - (*beg).time) / ((*end).time - (*beg).time)));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Subsample animation track by given key values
+void AnimResolver::SubsampleAnimTrack(std::vector<aiVectorKey>& /*out*/,
+ double /*time*/ ,double /*sample_delta*/ )
+{
+ //ai_assert(out.empty() && sample_delta);
+
+ //const double time_start = out.back().mTime;
+// for ()
+}
+
+// ------------------------------------------------------------------------------------------------
+// Track interpolation
+void AnimResolver::InterpolateTrack(std::vector<aiVectorKey>& out,aiVectorKey& fill,double time)
+{
+ // subsample animation track?
+ if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
+ SubsampleAnimTrack(out,time, sample_delta);
+ }
+
+ fill.mTime = time;
+
+ // get x
+ if ((*cur_x).time == time) {
+ fill.mValue.x = (*cur_x).value;
+
+ if (cur_x != envl_x->keys.end()-1) /* increment x */
+ ++cur_x;
+ else end_x = true;
+ }
+ else DoInterpolation(cur_x,envl_x,time,(float&)fill.mValue.x);
+
+ // get y
+ if ((*cur_y).time == time) {
+ fill.mValue.y = (*cur_y).value;
+
+ if (cur_y != envl_y->keys.end()-1) /* increment y */
+ ++cur_y;
+ else end_y = true;
+ }
+ else DoInterpolation(cur_y,envl_y,time,(float&)fill.mValue.y);
+
+ // get z
+ if ((*cur_z).time == time) {
+ fill.mValue.z = (*cur_z).value;
+
+ if (cur_z != envl_z->keys.end()-1) /* increment z */
+ ++cur_z;
+ else end_x = true;
+ }
+ else DoInterpolation(cur_z,envl_z,time,(float&)fill.mValue.z);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build linearly subsampled keys from three single envelopes, one for each component (x,y,z)
+void AnimResolver::GetKeys(std::vector<aiVectorKey>& out,
+ LWO::Envelope* _envl_x,
+ LWO::Envelope* _envl_y,
+ LWO::Envelope* _envl_z,
+ unsigned int _flags)
+{
+ envl_x = _envl_x;
+ envl_y = _envl_y;
+ envl_z = _envl_z;
+ flags = _flags;
+
+ // generate default channels if none are given
+ LWO::Envelope def_x, def_y, def_z;
+ LWO::Key key_dummy;
+ key_dummy.time = 0.f;
+ if ((envl_x && envl_x->type == LWO::EnvelopeType_Scaling_X) ||
+ (envl_y && envl_y->type == LWO::EnvelopeType_Scaling_Y) ||
+ (envl_z && envl_z->type == LWO::EnvelopeType_Scaling_Z)) {
+ key_dummy.value = 1.f;
+ }
+ else key_dummy.value = 0.f;
+
+ if (!envl_x) {
+ envl_x = &def_x;
+ envl_x->keys.push_back(key_dummy);
+ }
+ if (!envl_y) {
+ envl_y = &def_y;
+ envl_y->keys.push_back(key_dummy);
+ }
+ if (!envl_z) {
+ envl_z = &def_z;
+ envl_z->keys.push_back(key_dummy);
+ }
+
+ // guess how many keys we'll get
+ size_t reserve;
+ double sr = 1.;
+ if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
+ if (!sample_rate)
+ sr = 100.f;
+ else sr = sample_rate;
+ sample_delta = 1.f / sr;
+
+ reserve = (size_t)(
+ std::max( envl_x->keys.end()->time,
+ std::max( envl_y->keys.end()->time, envl_z->keys.end()->time )) * sr);
+ }
+ else reserve = std::max(envl_x->keys.size(),std::max(envl_x->keys.size(),envl_z->keys.size()));
+ out.reserve(reserve+(reserve>>1));
+
+ // Iterate through all three arrays at once - it's tricky, but
+ // rather interesting to implement.
+ double lasttime = std::min(envl_x->keys[0].time,std::min(envl_y->keys[0].time,envl_z->keys[0].time));
+
+ cur_x = envl_x->keys.begin();
+ cur_y = envl_y->keys.begin();
+ cur_z = envl_z->keys.begin();
+
+ end_x = end_y = end_z = false;
+ while (1) {
+
+ aiVectorKey fill;
+
+ if ((*cur_x).time == (*cur_y).time && (*cur_x).time == (*cur_z).time ) {
+
+ // we have a keyframe for all of them defined .. great,
+ // we don't need to fucking interpolate here ...
+ fill.mTime = (*cur_x).time;
+
+ fill.mValue.x = (*cur_x).value;
+ fill.mValue.y = (*cur_y).value;
+ fill.mValue.z = (*cur_z).value;
+
+ // subsample animation track
+ if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
+ //SubsampleAnimTrack(out,cur_x, cur_y, cur_z, d, sample_delta);
+ }
+
+ if (cur_x != envl_x->keys.end()-1)
+ ++cur_x;
+ else end_x = true;
+ if (cur_y != envl_y->keys.end()-1)
+ ++cur_y;
+ else end_y = true;
+ if (cur_z != envl_z->keys.end()-1)
+ ++cur_z;
+ else end_z = true;
+ }
+
+ // Find key with lowest time value
+ else if ((*cur_x).time <= (*cur_y).time && !end_x) {
+
+ if ((*cur_z).time <= (*cur_x).time && !end_z) {
+ InterpolateTrack(out,fill,(*cur_z).time);
+ }
+ else {
+ InterpolateTrack(out,fill,(*cur_x).time);
+ }
+ }
+ else if ((*cur_z).time <= (*cur_y).time && !end_z) {
+ InterpolateTrack(out,fill,(*cur_z).time);
+ }
+ else if (!end_y) {
+ // welcome on the server, y
+ InterpolateTrack(out,fill,(*cur_y).time);
+ }
+ else {
+ // we have reached the end of at least 2 channels,
+ // only one is remaining. Extrapolate the 2.
+ if (end_y) {
+ InterpolateTrack(out,fill,(end_x ? (*cur_z) : (*cur_x)).time);
+ }
+ else if (end_x) {
+ InterpolateTrack(out,fill,(end_z ? (*cur_y) : (*cur_z)).time);
+ }
+ else { // if (end_z)
+ InterpolateTrack(out,fill,(end_y ? (*cur_x) : (*cur_y)).time);
+ }
+ }
+ lasttime = fill.mTime;
+ out.push_back(fill);
+
+ if ( end_x && end_y && end_z ) /* finished? */
+ break;
+ }
+
+ if (flags & AI_LWO_ANIM_FLAG_START_AT_ZERO) {
+ for (std::vector<aiVectorKey>::iterator it = out.begin(); it != out.end(); ++it)
+ (*it).mTime -= first;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Extract animation channel
+void AnimResolver::ExtractAnimChannel(aiNodeAnim** out, unsigned int /*flags = 0*/)
+{
+ *out = NULL;
+
+
+ //FIXME: crashes if more than one component is animated at different timings, to be resolved.
+ return;
+
+#if 0
+ // If we have no envelopes, return NULL
+ if (envelopes.empty()) {
+ return;
+ }
+
+ // We won't spawn an animation channel if we don't have at least one envelope with more than one keyframe defined.
+ const bool trans = (trans_x && trans_x->keys.size() > 1 || trans_y && trans_y->keys.size() > 1 || trans_z && trans_z->keys.size() > 1);
+ const bool rotat = (rotat_x && rotat_x->keys.size() > 1 || rotat_y && rotat_y->keys.size() > 1 || rotat_z && rotat_z->keys.size() > 1);
+ const bool scale = (scale_x && scale_x->keys.size() > 1 || scale_y && scale_y->keys.size() > 1 || scale_z && scale_z->keys.size() > 1);
+ if (!trans && !rotat && !scale)
+ return;
+
+ // Allocate the output animation
+ aiNodeAnim* anim = *out = new aiNodeAnim();
+
+ // Setup default animation setup if necessary
+ if (need_to_setup) {
+ UpdateAnimRangeSetup();
+ need_to_setup = false;
+ }
+
+ // copy translation keys
+ if (trans) {
+ std::vector<aiVectorKey> keys;
+ GetKeys(keys,trans_x,trans_y,trans_z,flags);
+
+ anim->mPositionKeys = new aiVectorKey[ anim->mNumPositionKeys = keys.size() ];
+ std::copy(keys.begin(),keys.end(),anim->mPositionKeys);
+ }
+
+ // copy rotation keys
+ if (rotat) {
+ std::vector<aiVectorKey> keys;
+ GetKeys(keys,rotat_x,rotat_y,rotat_z,flags);
+
+ anim->mRotationKeys = new aiQuatKey[ anim->mNumRotationKeys = keys.size() ];
+
+ // convert heading, pitch, bank to quaternion
+ for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
+ aiQuatKey& qk = anim->mRotationKeys[i];
+ qk.mTime = keys[i].mTime;
+ qk.mValue = aiQuaternion( keys[i].mValue.x ,keys[i].mValue.z ,keys[i].mValue.y );
+ }
+ }
+
+ // copy scaling keys
+ if (scale) {
+ std::vector<aiVectorKey> keys;
+ GetKeys(keys,scale_x,scale_y,scale_z,flags);
+
+ anim->mScalingKeys = new aiVectorKey[ anim->mNumScalingKeys = keys.size() ];
+ std::copy(keys.begin(),keys.end(),anim->mScalingKeys);
+ }
+#endif
+}
+
+
+#endif // no lwo or no lws
diff --git a/3rdparty/assimp/code/LWOAnimation.h b/3rdparty/assimp/code/LWOAnimation.h
new file mode 100644
index 000000000..0fa96691d
--- /dev/null
+++ b/3rdparty/assimp/code/LWOAnimation.h
@@ -0,0 +1,336 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 LWOAnimation.h
+ * @brief LWOAnimationResolver utility class
+ *
+ * This is for all lightwave-related file format, not only LWO.
+ * LWS isthe main purpose.
+*/
+#ifndef AI_LWO_ANIMATION_INCLUDED
+#define AI_LWO_ANIMATION_INCLUDED
+
+namespace Assimp {
+namespace LWO {
+
+// ---------------------------------------------------------------------------
+/** \brief List of recognized LWO envelopes
+ */
+enum EnvelopeType
+{
+ EnvelopeType_Position_X = 0x1,
+ EnvelopeType_Position_Y = 0x2,
+ EnvelopeType_Position_Z = 0x3,
+
+ EnvelopeType_Rotation_Heading = 0x4,
+ EnvelopeType_Rotation_Pitch = 0x5,
+ EnvelopeType_Rotation_Bank = 0x6,
+
+ EnvelopeType_Scaling_X = 0x7,
+ EnvelopeType_Scaling_Y = 0x8,
+ EnvelopeType_Scaling_Z = 0x9,
+
+ // -- currently not yet handled
+ EnvelopeType_Color_R = 0xa,
+ EnvelopeType_Color_G = 0xb,
+ EnvelopeType_Color_B = 0xc,
+
+ EnvelopeType_Falloff_X = 0xd,
+ EnvelopeType_Falloff_Y = 0xe,
+ EnvelopeType_Falloff_Z = 0xf,
+
+ EnvelopeType_Unknown
+};
+
+// ---------------------------------------------------------------------------
+/** \brief List of recognized LWO interpolation modes
+ */
+enum InterpolationType
+{
+ IT_STEP, IT_LINE, IT_TCB, IT_HERM, IT_BEZI, IT_BEZ2
+};
+
+
+// ---------------------------------------------------------------------------
+/** \brief List of recognized LWO pre or post range behaviours
+ */
+enum PrePostBehaviour
+{
+ PrePostBehaviour_Reset = 0x0,
+ PrePostBehaviour_Constant = 0x1,
+ PrePostBehaviour_Repeat = 0x2,
+ PrePostBehaviour_Oscillate = 0x3,
+ PrePostBehaviour_OffsetRepeat = 0x4,
+ PrePostBehaviour_Linear = 0x5
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO animation keyframe
+ */
+struct Key
+{
+ Key()
+ : inter (IT_LINE)
+ {}
+
+ //! Current time
+ double time;
+
+ //! Current value
+ float value;
+
+ //! How to interpolate this key with previous key?
+ InterpolationType inter;
+
+ //! Interpolation parameters
+ float params[5];
+
+
+ // for std::find()
+ operator double () {
+ return time;
+ }
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO animation envelope
+ */
+struct Envelope
+{
+ Envelope()
+ : type (EnvelopeType_Unknown)
+ , pre (PrePostBehaviour_Constant)
+ , post (PrePostBehaviour_Constant)
+
+ , old_first (0)
+ , old_last (0)
+ {}
+
+ //! Index of this envelope
+ unsigned int index;
+
+ //! Type of envelope
+ EnvelopeType type;
+
+ //! Pre and post-behaviour
+ PrePostBehaviour pre,post;
+
+ //! Keyframes for this envelope
+ std::vector<Key> keys;
+
+
+ // temporary data for AnimResolver
+ size_t old_first,old_last;
+};
+
+// ---------------------------------------------------------------------------
+//! @def AI_LWO_ANIM_FLAG_SAMPLE_ANIMS
+//! Flag for AnimResolver, subsamples the input data with the rate specified
+//! by AnimResolver::SetSampleRate().
+#define AI_LWO_ANIM_FLAG_SAMPLE_ANIMS 0x1
+
+
+// ---------------------------------------------------------------------------
+//! @def AI_LWO_ANIM_FLAG_START_AT_ZERO
+//! Flag for AnimResolver, ensures that the animations starts at zero.
+#define AI_LWO_ANIM_FLAG_START_AT_ZERO 0x2
+
+// ---------------------------------------------------------------------------
+/** @brief Utility class to build Assimp animations from LWO envelopes.
+ *
+ * Used for both LWO and LWS (MOT also).
+ */
+class AnimResolver
+{
+public:
+
+ // ------------------------------------------------------------------
+ /** @brief Construct an AnimResolver from a given list of envelopes
+ * @param envelopes Input envelopes. May be empty.
+ * @param Output tick rate, per second
+ * @note The input envelopes are possibly modified.
+ */
+ AnimResolver(std::list<Envelope>& envelopes,
+ double tick);
+
+public:
+
+ // ------------------------------------------------------------------
+ /** @brief Extract the bind-pose transformation matrix.
+ * @param out Receives bind-pose transformation matrix
+ */
+ void ExtractBindPose(aiMatrix4x4& out);
+
+ // ------------------------------------------------------------------
+ /** @brief Extract a node animation channel
+ * @param out Receives a pointer to a newly allocated node anim.
+ * If there's just one keyframe defined, *out is set to NULL and
+ * no animation channel is computed.
+ * @param flags Any combination of the AI_LWO_ANIM_FLAG_XXX flags.
+ */
+ void ExtractAnimChannel(aiNodeAnim** out, unsigned int flags = 0);
+
+
+ // ------------------------------------------------------------------
+ /** @brief Set the sampling rate for ExtractAnimChannel().
+ *
+ * Non-linear interpolations are subsampled with this rate (keys
+ * per second). Closer sampling positions, if existent, are kept.
+ * The sampling rate defaults to 0, if this value is not changed and
+ * AI_LWO_ANIM_FLAG_SAMPLE_ANIMS is specified for ExtractAnimChannel(),
+ * the class finds a suitable sample rate by itself.
+ */
+ void SetSampleRate(double sr) {
+ sample_rate = sr;
+ }
+
+ // ------------------------------------------------------------------
+ /** @brief Getter for SetSampleRate()
+ */
+ double GetSampleRate() const {
+ return sample_rate;
+ }
+
+ // ------------------------------------------------------------------
+ /** @brief Set the animation time range
+ *
+ * @param first Time where the animation starts, in ticks
+ * @param last Time where the animation ends, in ticks
+ */
+ void SetAnimationRange(double _first, double _last) {
+ first = _first;
+ last = _last;
+
+ ClearAnimRangeSetup();
+ UpdateAnimRangeSetup();
+ }
+
+protected:
+
+ // ------------------------------------------------------------------
+ /** @brief Build linearly subsampled keys from 3 single envelopes
+ * @param out Receives output keys
+ * @param envl_x X-component envelope
+ * @param envl_y Y-component envelope
+ * @param envl_z Z-component envelope
+ * @param flags Any combination of the AI_LWO_ANIM_FLAG_XXX flags.
+ * @note Up to two input envelopes may be NULL
+ */
+ void GetKeys(std::vector<aiVectorKey>& out,
+ LWO::Envelope* envl_x,
+ LWO::Envelope* envl_y,
+ LWO::Envelope* envl_z,
+ unsigned int flags);
+
+ // ------------------------------------------------------------------
+ /** @brief Resolve a single animation key by applying the right
+ * interpolation to it.
+ * @param cur Current key
+ * @param envl Envelope working on
+ * @param time time to be interpolated
+ * @param fill Receives the interpolated output value.
+ */
+ void DoInterpolation(std::vector<LWO::Key>::const_iterator cur,
+ LWO::Envelope* envl,double time, float& fill);
+
+ // ------------------------------------------------------------------
+ /** @brief Almost the same, except we won't handle pre/post
+ * conditions here.
+ * @see DoInterpolation
+ */
+ void DoInterpolation2(std::vector<LWO::Key>::const_iterator beg,
+ std::vector<LWO::Key>::const_iterator end,double time, float& fill);
+
+ // ------------------------------------------------------------------
+ /** @brief Interpolate 2 tracks if one is given
+ *
+ * @param out Receives extra output keys
+ * @param key_out Primary output key
+ * @param time Time to interpolate for
+ */
+ void InterpolateTrack(std::vector<aiVectorKey>& out,
+ aiVectorKey& key_out,double time);
+
+ // ------------------------------------------------------------------
+ /** @brief Subsample an animation track by a given sampling rate
+ *
+ * @param out Receives output keys. Last key at input defines the
+ * time where subsampling starts.
+ * @param time Time to end subsampling at
+ * @param sample_delta Time delta between two samples
+ */
+ void SubsampleAnimTrack(std::vector<aiVectorKey>& out,
+ double time,double sample_delta);
+
+ // ------------------------------------------------------------------
+ /** @brief Delete all keys which we inserted to match anim setup
+ */
+ void ClearAnimRangeSetup();
+
+ // ------------------------------------------------------------------
+ /** @brief Insert extra keys to match LWO's pre and post behaviours
+ * in a given time range [first...last]
+ */
+ void UpdateAnimRangeSetup();
+
+private:
+ std::list<Envelope>& envelopes;
+ double sample_rate;
+
+ LWO::Envelope* trans_x, *trans_y, *trans_z;
+ LWO::Envelope* rotat_x, *rotat_y, *rotat_z;
+ LWO::Envelope* scale_x, *scale_y, *scale_z;
+
+ double first, last;
+ bool need_to_setup;
+
+ // temporary storage
+ LWO::Envelope* envl_x, * envl_y, * envl_z;
+ std::vector<LWO::Key>::const_iterator cur_x,cur_y,cur_z;
+ bool end_x, end_y, end_z;
+
+ unsigned int flags;
+ double sample_delta;
+};
+
+} // end namespace LWO
+} // end namespace Assimp
+
+#endif // !! AI_LWO_ANIMATION_INCLUDED
diff --git a/3rdparty/assimp/code/LWOBLoader.cpp b/3rdparty/assimp/code/LWOBLoader.cpp
new file mode 100644
index 000000000..339fcec53
--- /dev/null
+++ b/3rdparty/assimp/code/LWOBLoader.cpp
@@ -0,0 +1,396 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the LWO importer class for the older LWOB
+ file formats, including materials */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
+
+// Internal headers
+#include "LWOLoader.h"
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOBFile()
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + fileSize;
+ bool running = true;
+ while (running)
+ {
+ if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break;
+ LE_NCONST IFF::ChunkHeader* const head = IFF::LoadChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ {
+ throw DeadlyImportError("LWOB: Invalid chunk length");
+ break;
+ }
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ // vertex list
+ case AI_LWO_PNTS:
+ {
+ if (!mCurLayer->mTempPoints.empty())
+ DefaultLogger::get()->warn("LWO: PNTS chunk encountered twice");
+ else LoadLWOPoints(head->length);
+ break;
+ }
+ // face list
+ case AI_LWO_POLS:
+ {
+ if (!mCurLayer->mFaces.empty())
+ DefaultLogger::get()->warn("LWO: POLS chunk encountered twice");
+ else LoadLWOBPolygons(head->length);
+ break;
+ }
+ // list of tags
+ case AI_LWO_SRFS:
+ {
+ if (!mTags->empty())
+ DefaultLogger::get()->warn("LWO: SRFS chunk encountered twice");
+ else LoadLWOTags(head->length);
+ break;
+ }
+
+ // surface chunk
+ case AI_LWO_SURF:
+ {
+ LoadLWOBSurface(head->length);
+ break;
+ }
+ }
+ mFileBuffer = next;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOBPolygons(unsigned int length)
+{
+ // first find out how many faces and vertices we'll finally need
+ LE_NCONST uint16_t* const end = (LE_NCONST uint16_t*)(mFileBuffer+length);
+ LE_NCONST uint16_t* cursor = (LE_NCONST uint16_t*)mFileBuffer;
+
+ // perform endianess conversions
+#ifndef AI_BUILD_BIG_ENDIAN
+ while (cursor < end)ByteSwap::Swap2(cursor++);
+ cursor = (LE_NCONST uint16_t*)mFileBuffer;
+#endif
+
+ unsigned int iNumFaces = 0,iNumVertices = 0;
+ CountVertsAndFacesLWOB(iNumVertices,iNumFaces,cursor,end);
+
+ // allocate the output array and copy face indices
+ if (iNumFaces)
+ {
+ cursor = (LE_NCONST uint16_t*)mFileBuffer;
+
+ mCurLayer->mFaces.resize(iNumFaces);
+ FaceList::iterator it = mCurLayer->mFaces.begin();
+ CopyFaceIndicesLWOB(it,cursor,end);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::CountVertsAndFacesLWOB(unsigned int& verts, unsigned int& faces,
+ LE_NCONST uint16_t*& cursor, const uint16_t* const end, unsigned int max)
+{
+ while (cursor < end && max--)
+ {
+ uint16_t numIndices = *cursor++;
+ verts += numIndices;faces++;
+ cursor += numIndices;
+ int16_t surface = *cursor++;
+ if (surface < 0)
+ {
+ // there are detail polygons
+ numIndices = *cursor++;
+ CountVertsAndFacesLWOB(verts,faces,cursor,end,numIndices);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator& it,
+ LE_NCONST uint16_t*& cursor,
+ const uint16_t* const end,
+ unsigned int max)
+{
+ while (cursor < end && max--)
+ {
+ LWO::Face& face = *it;++it;
+ if ((face.mNumIndices = *cursor++))
+ {
+ if (cursor + face.mNumIndices >= end)break;
+ face.mIndices = new unsigned int[face.mNumIndices];
+ for (unsigned int i = 0; i < face.mNumIndices;++i)
+ {
+ unsigned int & mi = face.mIndices[i] = *cursor++;
+ if (mi > mCurLayer->mTempPoints.size())
+ {
+ DefaultLogger::get()->warn("LWOB: face index is out of range");
+ mi = (unsigned int)mCurLayer->mTempPoints.size()-1;
+ }
+ }
+ }
+ else DefaultLogger::get()->warn("LWOB: Face has 0 indices");
+ int16_t surface = *cursor++;
+ if (surface < 0)
+ {
+ surface = -surface;
+
+ // there are detail polygons.
+ const uint16_t numPolygons = *cursor++;
+ if (cursor < end)CopyFaceIndicesLWOB(it,cursor,end,numPolygons);
+ }
+ face.surfaceIndex = surface-1;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+LWO::Texture* LWOImporter::SetupNewTextureLWOB(LWO::TextureList& list,unsigned int size)
+{
+ list.push_back(LWO::Texture());
+ LWO::Texture* tex = &list.back();
+
+ std::string type;
+ GetS0(type,size);
+ const char* s = type.c_str();
+
+ if (strstr(s, "Image Map"))
+ {
+ // Determine mapping type
+ if (strstr(s, "Planar"))
+ tex->mapMode = LWO::Texture::Planar;
+ else if (strstr(s, "Cylindrical"))
+ tex->mapMode = LWO::Texture::Cylindrical;
+ else if (strstr(s, "Spherical"))
+ tex->mapMode = LWO::Texture::Spherical;
+ else if (strstr(s, "Cubic"))
+ tex->mapMode = LWO::Texture::Cubic;
+ else if (strstr(s, "Front"))
+ tex->mapMode = LWO::Texture::FrontProjection;
+ }
+ else
+ {
+ // procedural or gradient, not supported
+ DefaultLogger::get()->error("LWOB: Unsupported legacy texture: " + type);
+ }
+
+ return tex;
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOBSurface(unsigned int size)
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + size;
+
+ mSurfaces->push_back( LWO::Surface () );
+ LWO::Surface& surf = mSurfaces->back();
+ LWO::Texture* pTex = NULL;
+
+ GetS0(surf.mName,size);
+ bool runnning = true;
+ while (runnning) {
+ if (mFileBuffer + 6 >= end)
+ break;
+
+ IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ /* A single test file (sonycam.lwo) seems to have invalid surface chunks.
+ * I'm assuming it's the fault of a single, unknown exporter so there are
+ * probably THOUSANDS of them. Here's a dirty workaround:
+ *
+ * We don't break if the chunk limit is exceeded. Instead, we're computing
+ * how much storage is actually left and work with this value from now on.
+ */
+ if (mFileBuffer + head->length > end) {
+ DefaultLogger::get()->error("LWOB: Invalid surface chunk length. Trying to continue.");
+ head->length = (uint16_t) (end - mFileBuffer);
+ }
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ // diffuse color
+ case AI_LWO_COLR:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,COLR,3);
+ surf.mColor.r = GetU1() / 255.0f;
+ surf.mColor.g = GetU1() / 255.0f;
+ surf.mColor.b = GetU1() / 255.0f;
+ break;
+ }
+ // diffuse strength ...
+ case AI_LWO_DIFF:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,DIFF,2);
+ surf.mDiffuseValue = GetU2() / 255.0f;
+ break;
+ }
+ // specular strength ...
+ case AI_LWO_SPEC:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPEC,2);
+ surf.mSpecularValue = GetU2() / 255.0f;
+ break;
+ }
+ // luminosity ...
+ case AI_LWO_LUMI:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LUMI,2);
+ surf.mLuminosity = GetU2() / 255.0f;
+ break;
+ }
+ // transparency
+ case AI_LWO_TRAN:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TRAN,2);
+ surf.mTransparency = GetU2() / 255.0f;
+ break;
+ }
+ // surface flags
+ case AI_LWO_FLAG:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,FLAG,2);
+ uint16_t flag = GetU2();
+ if (flag & 0x4 ) surf.mMaximumSmoothAngle = 1.56207f;
+ if (flag & 0x8 ) surf.mColorHighlights = 1.f;
+ if (flag & 0x100) surf.bDoubleSided = true;
+ break;
+ }
+ // maximum smoothing angle
+ case AI_LWO_SMAN:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
+ surf.mMaximumSmoothAngle = fabs( GetF4() );
+ break;
+ }
+ // glossiness
+ case AI_LWO_GLOS:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,GLOS,2);
+ surf.mGlossiness = (float)GetU2();
+ break;
+ }
+ // color texture
+ case AI_LWO_CTEX:
+ {
+ pTex = SetupNewTextureLWOB(surf.mColorTextures,
+ head->length);
+ break;
+ }
+ // diffuse texture
+ case AI_LWO_DTEX:
+ {
+ pTex = SetupNewTextureLWOB(surf.mDiffuseTextures,
+ head->length);
+ break;
+ }
+ // specular texture
+ case AI_LWO_STEX:
+ {
+ pTex = SetupNewTextureLWOB(surf.mSpecularTextures,
+ head->length);
+ break;
+ }
+ // bump texture
+ case AI_LWO_BTEX:
+ {
+ pTex = SetupNewTextureLWOB(surf.mBumpTextures,
+ head->length);
+ break;
+ }
+ // transparency texture
+ case AI_LWO_TTEX:
+ {
+ pTex = SetupNewTextureLWOB(surf.mOpacityTextures,
+ head->length);
+ break;
+ }
+ // texture path
+ case AI_LWO_TIMG:
+ {
+ if (pTex) {
+ GetS0(pTex->mFileName,head->length);
+ }
+ else DefaultLogger::get()->warn("LWOB: Unexpected TIMG chunk");
+ break;
+ }
+ // texture strength
+ case AI_LWO_TVAL:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TVAL,1);
+ if (pTex) {
+ pTex->mStrength = (float)GetU1()/ 255.f;
+ }
+ else DefaultLogger::get()->warn("LWOB: Unexpected TVAL chunk");
+ break;
+ }
+ // texture flags
+ case AI_LWO_TFLG:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TFLG,2);
+
+ if (pTex)
+ {
+ const uint16_t s = GetU2();
+ if (s & 1)
+ pTex->majorAxis = LWO::Texture::AXIS_X;
+ else if (s & 2)
+ pTex->majorAxis = LWO::Texture::AXIS_Y;
+ else if (s & 4)
+ pTex->majorAxis = LWO::Texture::AXIS_Z;
+
+ if (s & 16)
+ DefaultLogger::get()->warn("LWOB: Ignoring \'negate\' flag on texture");
+ }
+ else DefaultLogger::get()->warn("LWOB: Unexpected TFLG chunk");
+ break;
+ }
+ }
+ mFileBuffer = next;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER
diff --git a/3rdparty/assimp/code/LWOFileData.h b/3rdparty/assimp/code/LWOFileData.h
new file mode 100644
index 000000000..562f2f3dd
--- /dev/null
+++ b/3rdparty/assimp/code/LWOFileData.h
@@ -0,0 +1,699 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 LWOFileData.h
+ * @brief Defines chunk constants used by the LWO file format
+
+The chunks are taken from the official LightWave SDK headers.
+
+*/
+#ifndef AI_LWO_FILEDATA_INCLUDED
+#define AI_LWO_FILEDATA_INCLUDED
+
+// STL headers
+#include <vector>
+#include <list>
+
+// public ASSIMP headers
+#include "../include/aiMesh.h"
+
+// internal headers
+#include "IFF.h"
+#include "LWOAnimation.h"
+
+namespace Assimp {
+namespace LWO {
+
+#define AI_LWO_FOURCC_LWOB AI_IFF_FOURCC('L','W','O','B')
+#define AI_LWO_FOURCC_LWO2 AI_IFF_FOURCC('L','W','O','2')
+#define AI_LWO_FOURCC_LXOB AI_IFF_FOURCC('L','X','O','B')
+
+// chunks specific to the LWOB format
+#define AI_LWO_SRFS AI_IFF_FOURCC('S','R','F','S')
+#define AI_LWO_FLAG AI_IFF_FOURCC('F','L','A','G')
+#define AI_LWO_VLUM AI_IFF_FOURCC('V','L','U','M')
+#define AI_LWO_VDIF AI_IFF_FOURCC('V','D','I','F')
+#define AI_LWO_VSPC AI_IFF_FOURCC('V','S','P','C')
+#define AI_LWO_RFLT AI_IFF_FOURCC('R','F','L','T')
+#define AI_LWO_BTEX AI_IFF_FOURCC('B','T','E','X')
+#define AI_LWO_CTEX AI_IFF_FOURCC('C','T','E','X')
+#define AI_LWO_DTEX AI_IFF_FOURCC('D','T','E','X')
+#define AI_LWO_LTEX AI_IFF_FOURCC('L','T','E','X')
+#define AI_LWO_RTEX AI_IFF_FOURCC('R','T','E','X')
+#define AI_LWO_STEX AI_IFF_FOURCC('S','T','E','X')
+#define AI_LWO_TTEX AI_IFF_FOURCC('T','T','E','X')
+#define AI_LWO_TFLG AI_IFF_FOURCC('T','F','L','G')
+#define AI_LWO_TSIZ AI_IFF_FOURCC('T','S','I','Z')
+#define AI_LWO_TCTR AI_IFF_FOURCC('T','C','T','R')
+#define AI_LWO_TFAL AI_IFF_FOURCC('T','F','A','L')
+#define AI_LWO_TVEL AI_IFF_FOURCC('T','V','E','L')
+#define AI_LWO_TCLR AI_IFF_FOURCC('T','C','L','R')
+#define AI_LWO_TVAL AI_IFF_FOURCC('T','V','A','L')
+#define AI_LWO_TAMP AI_IFF_FOURCC('T','A','M','P')
+#define AI_LWO_TIMG AI_IFF_FOURCC('T','I','M','G')
+#define AI_LWO_TAAS AI_IFF_FOURCC('T','A','A','S')
+#define AI_LWO_TREF AI_IFF_FOURCC('T','R','E','F')
+#define AI_LWO_TOPC AI_IFF_FOURCC('T','O','P','C')
+#define AI_LWO_SDAT AI_IFF_FOURCC('S','D','A','T')
+#define AI_LWO_TFP0 AI_IFF_FOURCC('T','F','P','0')
+#define AI_LWO_TFP1 AI_IFF_FOURCC('T','F','P','1')
+
+
+/* top-level chunks */
+#define AI_LWO_LAYR AI_IFF_FOURCC('L','A','Y','R')
+#define AI_LWO_TAGS AI_IFF_FOURCC('T','A','G','S')
+#define AI_LWO_PNTS AI_IFF_FOURCC('P','N','T','S')
+#define AI_LWO_BBOX AI_IFF_FOURCC('B','B','O','X')
+#define AI_LWO_VMAP AI_IFF_FOURCC('V','M','A','P')
+#define AI_LWO_VMAD AI_IFF_FOURCC('V','M','A','D')
+#define AI_LWO_POLS AI_IFF_FOURCC('P','O','L','S')
+#define AI_LWO_PTAG AI_IFF_FOURCC('P','T','A','G')
+#define AI_LWO_ENVL AI_IFF_FOURCC('E','N','V','L')
+#define AI_LWO_CLIP AI_IFF_FOURCC('C','L','I','P')
+#define AI_LWO_SURF AI_IFF_FOURCC('S','U','R','F')
+#define AI_LWO_DESC AI_IFF_FOURCC('D','E','S','C')
+#define AI_LWO_TEXT AI_IFF_FOURCC('T','E','X','T')
+#define AI_LWO_ICON AI_IFF_FOURCC('I','C','O','N')
+
+/* polygon types */
+#define AI_LWO_FACE AI_IFF_FOURCC('F','A','C','E')
+#define AI_LWO_CURV AI_IFF_FOURCC('C','U','R','V')
+#define AI_LWO_PTCH AI_IFF_FOURCC('P','T','C','H')
+#define AI_LWO_MBAL AI_IFF_FOURCC('M','B','A','L')
+#define AI_LWO_BONE AI_IFF_FOURCC('B','O','N','E')
+#define AI_LWO_SUBD AI_IFF_FOURCC('S','U','B','D')
+
+/* polygon tags */
+#define AI_LWO_SURF AI_IFF_FOURCC('S','U','R','F')
+#define AI_LWO_PART AI_IFF_FOURCC('P','A','R','T')
+#define AI_LWO_SMGP AI_IFF_FOURCC('S','M','G','P')
+
+/* envelopes */
+#define AI_LWO_PRE AI_IFF_FOURCC('P','R','E',' ')
+#define AI_LWO_POST AI_IFF_FOURCC('P','O','S','T')
+#define AI_LWO_KEY AI_IFF_FOURCC('K','E','Y',' ')
+#define AI_LWO_SPAN AI_IFF_FOURCC('S','P','A','N')
+#define AI_LWO_TCB AI_IFF_FOURCC('T','C','B',' ')
+#define AI_LWO_HERM AI_IFF_FOURCC('H','E','R','M')
+#define AI_LWO_BEZI AI_IFF_FOURCC('B','E','Z','I')
+#define AI_LWO_BEZ2 AI_IFF_FOURCC('B','E','Z','2')
+#define AI_LWO_LINE AI_IFF_FOURCC('L','I','N','E')
+#define AI_LWO_STEP AI_IFF_FOURCC('S','T','E','P')
+
+/* clips */
+#define AI_LWO_STIL AI_IFF_FOURCC('S','T','I','L')
+#define AI_LWO_ISEQ AI_IFF_FOURCC('I','S','E','Q')
+#define AI_LWO_ANIM AI_IFF_FOURCC('A','N','I','M')
+#define AI_LWO_XREF AI_IFF_FOURCC('X','R','E','F')
+#define AI_LWO_STCC AI_IFF_FOURCC('S','T','C','C')
+#define AI_LWO_TIME AI_IFF_FOURCC('T','I','M','E')
+#define AI_LWO_CONT AI_IFF_FOURCC('C','O','N','T')
+#define AI_LWO_BRIT AI_IFF_FOURCC('B','R','I','T')
+#define AI_LWO_SATR AI_IFF_FOURCC('S','A','T','R')
+#define AI_LWO_HUE AI_IFF_FOURCC('H','U','E',' ')
+#define AI_LWO_GAMM AI_IFF_FOURCC('G','A','M','M')
+#define AI_LWO_NEGA AI_IFF_FOURCC('N','E','G','A')
+#define AI_LWO_IFLT AI_IFF_FOURCC('I','F','L','T')
+#define AI_LWO_PFLT AI_IFF_FOURCC('P','F','L','T')
+
+/* surfaces */
+#define AI_LWO_COLR AI_IFF_FOURCC('C','O','L','R')
+#define AI_LWO_LUMI AI_IFF_FOURCC('L','U','M','I')
+#define AI_LWO_DIFF AI_IFF_FOURCC('D','I','F','F')
+#define AI_LWO_SPEC AI_IFF_FOURCC('S','P','E','C')
+#define AI_LWO_GLOS AI_IFF_FOURCC('G','L','O','S')
+#define AI_LWO_REFL AI_IFF_FOURCC('R','E','F','L')
+#define AI_LWO_RFOP AI_IFF_FOURCC('R','F','O','P')
+#define AI_LWO_RIMG AI_IFF_FOURCC('R','I','M','G')
+#define AI_LWO_RSAN AI_IFF_FOURCC('R','S','A','N')
+#define AI_LWO_TRAN AI_IFF_FOURCC('T','R','A','N')
+#define AI_LWO_TROP AI_IFF_FOURCC('T','R','O','P')
+#define AI_LWO_TIMG AI_IFF_FOURCC('T','I','M','G')
+#define AI_LWO_RIND AI_IFF_FOURCC('R','I','N','D')
+#define AI_LWO_TRNL AI_IFF_FOURCC('T','R','N','L')
+#define AI_LWO_BUMP AI_IFF_FOURCC('B','U','M','P')
+#define AI_LWO_SMAN AI_IFF_FOURCC('S','M','A','N')
+#define AI_LWO_SIDE AI_IFF_FOURCC('S','I','D','E')
+#define AI_LWO_CLRH AI_IFF_FOURCC('C','L','R','H')
+#define AI_LWO_CLRF AI_IFF_FOURCC('C','L','R','F')
+#define AI_LWO_ADTR AI_IFF_FOURCC('A','D','T','R')
+#define AI_LWO_SHRP AI_IFF_FOURCC('S','H','R','P')
+#define AI_LWO_LINE AI_IFF_FOURCC('L','I','N','E')
+#define AI_LWO_LSIZ AI_IFF_FOURCC('L','S','I','Z')
+#define AI_LWO_ALPH AI_IFF_FOURCC('A','L','P','H')
+#define AI_LWO_AVAL AI_IFF_FOURCC('A','V','A','L')
+#define AI_LWO_GVAL AI_IFF_FOURCC('G','V','A','L')
+#define AI_LWO_BLOK AI_IFF_FOURCC('B','L','O','K')
+#define AI_LWO_VCOL AI_IFF_FOURCC('V','C','O','L')
+
+/* texture layer */
+#define AI_LWO_TYPE AI_IFF_FOURCC('T','Y','P','E')
+#define AI_LWO_CHAN AI_IFF_FOURCC('C','H','A','N')
+#define AI_LWO_NAME AI_IFF_FOURCC('N','A','M','E')
+#define AI_LWO_ENAB AI_IFF_FOURCC('E','N','A','B')
+#define AI_LWO_OPAC AI_IFF_FOURCC('O','P','A','C')
+#define AI_LWO_FLAG AI_IFF_FOURCC('F','L','A','G')
+#define AI_LWO_PROJ AI_IFF_FOURCC('P','R','O','J')
+#define AI_LWO_STCK AI_IFF_FOURCC('S','T','C','K')
+#define AI_LWO_TAMP AI_IFF_FOURCC('T','A','M','P')
+
+/* texture coordinates */
+#define AI_LWO_TMAP AI_IFF_FOURCC('T','M','A','P')
+#define AI_LWO_AXIS AI_IFF_FOURCC('A','X','I','S')
+#define AI_LWO_CNTR AI_IFF_FOURCC('C','N','T','R')
+#define AI_LWO_SIZE AI_IFF_FOURCC('S','I','Z','E')
+#define AI_LWO_ROTA AI_IFF_FOURCC('R','O','T','A')
+#define AI_LWO_OREF AI_IFF_FOURCC('O','R','E','F')
+#define AI_LWO_FALL AI_IFF_FOURCC('F','A','L','L')
+#define AI_LWO_CSYS AI_IFF_FOURCC('C','S','Y','S')
+
+/* image map */
+#define AI_LWO_IMAP AI_IFF_FOURCC('I','M','A','P')
+#define AI_LWO_IMAG AI_IFF_FOURCC('I','M','A','G')
+#define AI_LWO_WRAP AI_IFF_FOURCC('W','R','A','P')
+#define AI_LWO_WRPW AI_IFF_FOURCC('W','R','P','W')
+#define AI_LWO_WRPH AI_IFF_FOURCC('W','R','P','H')
+#define AI_LWO_VMAP AI_IFF_FOURCC('V','M','A','P')
+#define AI_LWO_AAST AI_IFF_FOURCC('A','A','S','T')
+#define AI_LWO_PIXB AI_IFF_FOURCC('P','I','X','B')
+
+/* procedural */
+#define AI_LWO_PROC AI_IFF_FOURCC('P','R','O','C')
+#define AI_LWO_COLR AI_IFF_FOURCC('C','O','L','R')
+#define AI_LWO_VALU AI_IFF_FOURCC('V','A','L','U')
+#define AI_LWO_FUNC AI_IFF_FOURCC('F','U','N','C')
+#define AI_LWO_FTPS AI_IFF_FOURCC('F','T','P','S')
+#define AI_LWO_ITPS AI_IFF_FOURCC('I','T','P','S')
+#define AI_LWO_ETPS AI_IFF_FOURCC('E','T','P','S')
+
+/* gradient */
+#define AI_LWO_GRAD AI_IFF_FOURCC('G','R','A','D')
+#define AI_LWO_GRST AI_IFF_FOURCC('G','R','S','T')
+#define AI_LWO_GREN AI_IFF_FOURCC('G','R','E','N')
+#define AI_LWO_PNAM AI_IFF_FOURCC('P','N','A','M')
+#define AI_LWO_INAM AI_IFF_FOURCC('I','N','A','M')
+#define AI_LWO_GRPT AI_IFF_FOURCC('G','R','P','T')
+#define AI_LWO_FKEY AI_IFF_FOURCC('F','K','E','Y')
+#define AI_LWO_IKEY AI_IFF_FOURCC('I','K','E','Y')
+
+/* shader */
+#define AI_LWO_SHDR AI_IFF_FOURCC('S','H','D','R')
+#define AI_LWO_DATA AI_IFF_FOURCC('D','A','T','A')
+
+
+/* VMAP types */
+#define AI_LWO_TXUV AI_IFF_FOURCC('T','X','U','V')
+#define AI_LWO_RGB AI_IFF_FOURCC('R','G','B',' ')
+#define AI_LWO_RGBA AI_IFF_FOURCC('R','G','B','A')
+#define AI_LWO_WGHT AI_IFF_FOURCC('W','G','H','T')
+
+#define AI_LWO_MNVW AI_IFF_FOURCC('M','N','V','W')
+#define AI_LWO_MORF AI_IFF_FOURCC('M','O','R','F')
+#define AI_LWO_SPOT AI_IFF_FOURCC('S','P','O','T')
+#define AI_LWO_PICK AI_IFF_FOURCC('P','I','C','K')
+
+// MODO extension - per-vertex normal vectors
+#define AI_LWO_MODO_NORM AI_IFF_FOURCC('N', 'O', 'R', 'M')
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a face in a LWO file
+ *
+ * \note We can't use the code in SmoothingGroups.inl here - the mesh
+ * structures of 3DS/ASE and LWO are too different.
+ */
+struct Face : public aiFace
+{
+ //! Default construction
+ Face()
+ : surfaceIndex (0)
+ , smoothGroup (0)
+ , type (AI_LWO_FACE)
+ {}
+
+ //! Construction from given type
+ Face(uint32_t _type)
+ : surfaceIndex (0)
+ , smoothGroup (0)
+ , type (_type)
+ {}
+
+ //! Copy construction
+ Face(const Face& f) : aiFace() {
+ *this = f;
+ }
+
+ //! Zero-based index into tags chunk
+ unsigned int surfaceIndex;
+
+ //! Smooth group this face is assigned to
+ unsigned int smoothGroup;
+
+ //! Type of face
+ uint32_t type;
+
+
+ //! Assignment operator
+ Face& operator=(const LWO::Face& f) {
+ aiFace::operator =(f);
+ surfaceIndex = f.surfaceIndex;
+ smoothGroup = f.smoothGroup;
+ type = f.type;
+ return *this;
+ }
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Base structure for all vertex map representations
+ */
+struct VMapEntry
+{
+ VMapEntry(unsigned int _dims)
+ : dims(_dims)
+ {}
+
+ virtual ~VMapEntry() {}
+
+ //! allocates memory for the vertex map
+ virtual void Allocate(unsigned int num)
+ {
+ if (!rawData.empty())
+ return; // return if already allocated
+
+ const unsigned int m = num*dims;
+ rawData.reserve(m + (m>>2u)); // 25% as extra storage for VMADs
+ rawData.resize(m,0.f);
+ abAssigned.resize(num,false);
+ }
+
+ std::string name;
+ unsigned int dims;
+
+ std::vector<float> rawData;
+ std::vector<bool> abAssigned;
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Represents an extra vertex color channel
+ */
+struct VColorChannel : public VMapEntry
+{
+ VColorChannel()
+ : VMapEntry(4)
+ {}
+
+ //! need to overwrite this function - the alpha channel must
+ //! be initialized to 1.0 by default
+ virtual void Allocate(unsigned int num)
+ {
+ if (!rawData.empty())
+ return; // return if already allocated
+
+ register unsigned int m = num*dims;
+ rawData.reserve(m + (m>>2u)); // 25% as extra storage for VMADs
+ rawData.resize(m);
+
+ for (aiColor4D* p = (aiColor4D*)&rawData[0]; p < (aiColor4D*)&rawData[m-1]; ++p)
+ p->a = 1.f;
+
+ abAssigned.resize(num,false);
+ }
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Represents an extra vertex UV channel
+ */
+struct UVChannel : public VMapEntry
+{
+ UVChannel()
+ : VMapEntry(2)
+ {}
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Represents a weight map
+ */
+struct WeightChannel : public VMapEntry
+{
+ WeightChannel()
+ : VMapEntry(1)
+ {}
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Represents a vertex-normals channel (MODO extension)
+ */
+struct NormalChannel : public VMapEntry
+{
+ NormalChannel()
+ : VMapEntry(3)
+ {}
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO file texture
+ */
+struct Texture
+{
+ // we write the enum values out here to make debugging easier ...
+ enum BlendType
+ {
+ Normal = 0,
+ Subtractive = 1,
+ Difference = 2,
+ Multiply = 3,
+ Divide = 4,
+ Alpha = 5,
+ TextureDispl = 6,
+ Additive = 7
+ };
+
+ enum MappingMode
+ {
+ Planar = 0,
+ Cylindrical = 1,
+ Spherical = 2,
+ Cubic = 3,
+ FrontProjection = 4,
+ UV = 5
+ };
+
+ enum Axes
+ {
+ AXIS_X = 0,
+ AXIS_Y = 1,
+ AXIS_Z = 2
+ };
+
+ enum Wrap
+ {
+ RESET = 0,
+ REPEAT = 1,
+ MIRROR = 2,
+ EDGE = 3
+ };
+
+ Texture()
+ : mClipIdx(0xffffffff)
+ , mStrength (1.0f)
+ , mUVChannelIndex ("unknown")
+ , mRealUVIndex (0xffffffff)
+ , enabled (true)
+ , blendType (Additive)
+ , bCanUse (true)
+ , mapMode (UV)
+ , majorAxis (AXIS_X)
+ , wrapAmountH (1.0f)
+ , wrapAmountW (1.0f)
+ , wrapModeWidth (REPEAT)
+ , wrapModeHeight (REPEAT)
+ , ordinal ("\x00")
+ {}
+
+ //! File name of the texture
+ std::string mFileName;
+
+ //! Clip index
+ unsigned int mClipIdx;
+
+ //! Strength of the texture - blend factor
+ float mStrength;
+
+ uint32_t type; // type of the texture
+
+ //! Name of the corresponding UV channel
+ std::string mUVChannelIndex;
+ unsigned int mRealUVIndex;
+
+ //! is the texture enabled?
+ bool enabled;
+
+ //! blend type
+ BlendType blendType;
+
+ //! are we able to use the texture?
+ bool bCanUse;
+
+ //! mapping mode
+ MappingMode mapMode;
+
+ //! major axis for planar, cylindrical, spherical projections
+ Axes majorAxis;
+
+ //! wrap amount for cylindrical and spherical projections
+ float wrapAmountH,wrapAmountW;
+
+ //! wrapping mode for the texture
+ Wrap wrapModeWidth,wrapModeHeight;
+
+ //! ordinal string of the texture
+ std::string ordinal;
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO file clip
+ */
+struct Clip
+{
+ enum Type
+ {
+ STILL, SEQ, REF, UNSUPPORTED
+ } type;
+
+ Clip()
+ : type (UNSUPPORTED)
+ , idx (0)
+ , negate (false)
+ {}
+
+ //! path to the base texture -
+ std::string path;
+
+ //! reference to another CLIP
+ unsigned int clipRef;
+
+ //! index of the clip
+ unsigned int idx;
+
+ //! Negate the clip?
+ bool negate;
+};
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO file shader
+ *
+ * Later
+ */
+struct Shader
+{
+ Shader()
+ : ordinal ("\x00")
+ , functionName ("unknown")
+ , enabled (true)
+ {}
+
+ std::string ordinal;
+ std::string functionName;
+ bool enabled;
+};
+
+typedef std::list < Texture > TextureList;
+typedef std::list < Shader > ShaderList;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a LWO file surface (= material)
+ */
+struct Surface
+{
+ Surface()
+ : mColor (0.78431f,0.78431f,0.78431f)
+ , bDoubleSided (false)
+ , mDiffuseValue (1.f)
+ , mSpecularValue (0.f)
+ , mTransparency (0.f)
+ , mGlossiness (0.4f)
+ , mLuminosity (0.f)
+ , mColorHighlights (0.f)
+ , mMaximumSmoothAngle (0.f) // 0 == not specified, no smoothing
+ , mVCMap ("")
+ , mVCMapType (AI_LWO_RGBA)
+ , mIOR (1.f) // vakuum
+ , mBumpIntensity (1.f)
+ , mWireframe (false)
+ , mAdditiveTransparency (0.f)
+ {}
+
+ //! Name of the surface
+ std::string mName;
+
+ //! Color of the surface
+ aiColor3D mColor;
+
+ //! true for two-sided materials
+ bool bDoubleSided;
+
+ //! Various material parameters
+ float mDiffuseValue,mSpecularValue,mTransparency,mGlossiness,mLuminosity,mColorHighlights;
+
+ //! Maximum angle between two adjacent triangles
+ //! that they can be smoothed - in degrees
+ float mMaximumSmoothAngle;
+
+ //! Vertex color map to be used to color the surface
+ std::string mVCMap;
+ uint32_t mVCMapType;
+
+ //! Names of the special shaders to be applied to the surface
+ ShaderList mShaders;
+
+ //! Textures - the first entry in the list is evaluated first
+ TextureList mColorTextures, // color textures are added to both diffuse and specular texture stacks
+ mDiffuseTextures,
+ mSpecularTextures,
+ mOpacityTextures,
+ mBumpTextures,
+ mGlossinessTextures,
+ mReflectionTextures;
+
+ //! Index of refraction
+ float mIOR;
+
+ //! Bump intensity scaling
+ float mBumpIntensity;
+
+ //! Wireframe flag
+ bool mWireframe;
+
+ //! Intensity of additive blending
+ float mAdditiveTransparency;
+};
+
+// ---------------------------------------------------------------------------
+#define AI_LWO_VALIDATE_CHUNK_LENGTH(length,name,size) \
+ if (length < size) \
+ { \
+ throw DeadlyImportError("LWO: "#name" chunk is too small"); \
+ } \
+
+
+// some typedefs ... to make life with loader monsters like this easier
+typedef std::vector < aiVector3D > PointList;
+typedef std::vector < LWO::Face > FaceList;
+typedef std::vector < LWO::Surface > SurfaceList;
+typedef std::vector < std::string > TagList;
+typedef std::vector < unsigned int > TagMappingTable;
+typedef std::vector < unsigned int > ReferrerList;
+typedef std::vector < WeightChannel > WeightChannelList;
+typedef std::vector < VColorChannel > VColorChannelList;
+typedef std::vector < UVChannel > UVChannelList;
+typedef std::vector < Clip > ClipList;
+typedef std::vector < Envelope > EnvelopeList;
+typedef std::vector < unsigned int > SortedRep;
+
+// ---------------------------------------------------------------------------
+/** \brief Represents a layer in the file
+ */
+struct Layer
+{
+ Layer()
+ : mFaceIDXOfs (0)
+ , mPointIDXOfs (0)
+ , mParent (0x0)
+ , mIndex (0xffff)
+ , skip (false)
+ {}
+
+ /** Temporary point list from the file */
+ PointList mTempPoints;
+
+ /** Lists for every point the index of another point
+ that has been copied from *this* point or 0xffffffff if
+ no copy of the point has been made */
+ ReferrerList mPointReferrers;
+
+ /** Weight channel list from the file */
+ WeightChannelList mWeightChannels;
+
+ /** Subdivision weight channel list from the file */
+ WeightChannelList mSWeightChannels;
+
+ /** Vertex color list from the file */
+ VColorChannelList mVColorChannels;
+
+ /** UV channel list from the file */
+ UVChannelList mUVChannels;
+
+ /** Normal vector channel from the file */
+ NormalChannel mNormals;
+
+ /** Temporary face list from the file*/
+ FaceList mFaces;
+
+ /** Current face indexing offset from the beginning of the buffers*/
+ unsigned int mFaceIDXOfs;
+
+ /** Current point indexing offset from the beginning of the buffers*/
+ unsigned int mPointIDXOfs;
+
+ /** Parent index */
+ uint16_t mParent;
+
+ /** Index of the layer */
+ uint16_t mIndex;
+
+ /** Name of the layer */
+ std::string mName;
+
+ /** Pivot point of the layer */
+ aiVector3D mPivot;
+
+ /** Skip this layer? */
+ bool skip;
+};
+
+typedef std::list<LWO::Layer> LayerList;
+
+
+}}
+
+
+#endif // !! AI_LWO_FILEDATA_INCLUDED
+
diff --git a/3rdparty/assimp/code/LWOLoader.cpp b/3rdparty/assimp/code/LWOLoader.cpp
new file mode 100644
index 000000000..b19ffe698
--- /dev/null
+++ b/3rdparty/assimp/code/LWOLoader.cpp
@@ -0,0 +1,1403 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 LWOLoader.cpp
+ * @brief Implementation of the LWO importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
+
+// internal headers
+#include "LWOLoader.h"
+#include "MaterialSystem.h"
+#include "StringComparison.h"
+#include "SGSpatialSort.h"
+#include "ByteSwap.h"
+#include "ProcessHelper.h"
+#include "ConvertToLHProcess.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+LWOImporter::LWOImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+LWOImporter::~LWOImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool LWOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "lwo" || extension == "lxo")
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens
+ if (!extension.length() || checkSig) {
+ uint32_t tokens[3];
+ tokens[0] = AI_LWO_FOURCC_LWOB;
+ tokens[1] = AI_LWO_FOURCC_LWO2;
+ tokens[2] = AI_LWO_FOURCC_LXOB;
+ return CheckMagicToken(pIOHandler,pFile,tokens,3,8);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void LWOImporter::SetupProperties(const Importer* pImp)
+{
+ configSpeedFlag = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0) ? true : false);
+ configLayerIndex = pImp->GetPropertyInteger (AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,0xffffffff);
+ configLayerName = pImp->GetPropertyString (AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,"");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void LWOImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene,
+ IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open LWO file " + pFile + ".");
+
+ if ((this->fileSize = (unsigned int)file->FileSize()) < 12)
+ throw DeadlyImportError("LWO: The file is too small to contain the IFF header");
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector< uint8_t > mBuffer(fileSize);
+ file->Read( &mBuffer[0], 1, fileSize);
+ this->pScene = pScene;
+
+ // Determine the type of the file
+ uint32_t fileType;
+ const char* sz = IFF::ReadHeader(&mBuffer[0],fileType);
+ if (sz)throw DeadlyImportError(sz);
+
+ mFileBuffer = &mBuffer[0] + 12;
+ fileSize -= 12;
+
+ // Initialize some members with their default values
+ hasNamedLayer = false;
+
+ // Create temporary storage on the stack but store pointers to it in the class
+ // instance. Therefore everything will be destructed properly if an exception
+ // is thrown and we needn't take care of that.
+ LayerList _mLayers;
+ SurfaceList _mSurfaces;
+ TagList _mTags;
+ TagMappingTable _mMapping;
+
+ mLayers = &_mLayers;
+ mTags = &_mTags;
+ mMapping = &_mMapping;
+ mSurfaces = &_mSurfaces;
+
+ // Allocate a default layer (layer indices are 1-based from now)
+ mLayers->push_back(Layer());
+ mCurLayer = &mLayers->back();
+ mCurLayer->mName = "<LWODefault>";
+
+ // old lightwave file format (prior to v6)
+ if (AI_LWO_FOURCC_LWOB == fileType) {
+ DefaultLogger::get()->info("LWO file format: LWOB (<= LightWave 5.5)");
+
+ mIsLWO2 = false;
+ mIsLXOB = false;
+ LoadLWOBFile();
+ }
+ // New lightwave format
+ else if (AI_LWO_FOURCC_LWO2 == fileType) {
+ mIsLXOB = false;
+ DefaultLogger::get()->info("LWO file format: LWO2 (>= LightWave 6)");
+ }
+ // MODO file format
+ else if (AI_LWO_FOURCC_LXOB == fileType) {
+ mIsLXOB = true;
+ DefaultLogger::get()->info("LWO file format: LXOB (Modo)");
+ }
+ // we don't know this format
+ else
+ {
+ char szBuff[5];
+ szBuff[0] = (char)(fileType >> 24u);
+ szBuff[1] = (char)(fileType >> 16u);
+ szBuff[2] = (char)(fileType >> 8u);
+ szBuff[3] = (char)(fileType);
+ szBuff[4] = '\0';
+ throw DeadlyImportError(std::string("Unknown LWO sub format: ") + szBuff);
+ }
+
+ if (AI_LWO_FOURCC_LWOB != fileType) {
+ mIsLWO2 = true;
+ LoadLWO2File();
+
+ // The newer lightwave format allows the user to configure the
+ // loader that just one layer is used. If this is the case
+ // we need to check now whether the requested layer has been found.
+ if (0xffffffff != configLayerIndex && configLayerIndex > mLayers->size())
+ throw DeadlyImportError("LWO2: The requested layer was not found");
+
+ if (configLayerName.length() && !hasNamedLayer) {
+ throw DeadlyImportError("LWO2: Unable to find the requested layer: "
+ + configLayerName);
+ }
+ }
+
+ // now, as we have loaded all data, we can resolve cross-referenced tags and clips
+ ResolveTags();
+ ResolveClips();
+
+ // now process all layers and build meshes and nodes
+ std::vector<aiMesh*> apcMeshes;
+ std::vector<aiNode*> apcNodes;
+ apcNodes. reserve(mLayers->size());
+ apcMeshes.reserve(mLayers->size()*std::min(((unsigned int)mSurfaces->size()/2u), 1u));
+
+ unsigned int iDefaultSurface = 0xffffffff; // index of the default surface
+ for (LayerList::iterator lit = mLayers->begin(), lend = mLayers->end();lit != lend;++lit) {
+ LWO::Layer& layer = *lit;
+ if (layer.skip)
+ continue;
+
+ // I don't know whether there could be dummy layers, but it would be possible
+ const unsigned int meshStart = (unsigned int)apcMeshes.size();
+ if (!layer.mFaces.empty() && !layer.mTempPoints.empty()) {
+
+ // now sort all faces by the surfaces assigned to them
+ std::vector<SortedRep> pSorted(mSurfaces->size()+1);
+
+ unsigned int i = 0;
+ for (FaceList::iterator it = layer.mFaces.begin(), end = layer.mFaces.end();it != end;++it,++i) {
+ // Check whether we support this face's type
+ if ((*it).type != AI_LWO_FACE && (*it).type != AI_LWO_PTCH &&
+ (*it).type != AI_LWO_BONE && (*it).type != AI_LWO_SUBD) {
+ continue;
+ }
+
+ unsigned int idx = (*it).surfaceIndex;
+ if (idx >= mTags->size())
+ {
+ DefaultLogger::get()->warn("LWO: Invalid face surface index");
+ idx = 0xffffffff;
+ }
+ if (0xffffffff == idx || 0xffffffff == (idx = _mMapping[idx])) {
+ if (0xffffffff == iDefaultSurface) {
+ iDefaultSurface = (unsigned int)mSurfaces->size();
+ mSurfaces->push_back(LWO::Surface());
+ LWO::Surface& surf = mSurfaces->back();
+ surf.mColor.r = surf.mColor.g = surf.mColor.b = 0.6f;
+ surf.mName = "LWODefaultSurface";
+ }
+ idx = iDefaultSurface;
+ }
+ pSorted[idx].push_back(i);
+ }
+ if (0xffffffff == iDefaultSurface) {
+ pSorted.erase(pSorted.end()-1);
+ }
+ for (unsigned int p = 0,i = 0;i < mSurfaces->size();++i) {
+ SortedRep& sorted = pSorted[i];
+ if (sorted.empty())
+ continue;
+
+ // generate the mesh
+ aiMesh* mesh = new aiMesh();
+ apcMeshes.push_back(mesh);
+ mesh->mNumFaces = (unsigned int)sorted.size();
+
+ // count the number of vertices
+ SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
+ for (;it != end;++it) {
+ mesh->mNumVertices += layer.mFaces[*it].mNumIndices;
+ }
+
+ aiVector3D *nrm = NULL, * pv = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ aiFace* pf = mesh->mFaces = new aiFace[mesh->mNumFaces];
+ mesh->mMaterialIndex = i;
+
+ // find out which vertex color channels and which texture coordinate
+ // channels are really required by the material attached to this mesh
+ unsigned int vUVChannelIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ unsigned int vVColorIndices[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+#if _DEBUG
+ for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS;++mui )
+ vUVChannelIndices[mui] = 0xffffffff;
+ for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS;++mui )
+ vVColorIndices[mui] = 0xffffffff;
+#endif
+
+ FindUVChannels(_mSurfaces[i],sorted,layer,vUVChannelIndices);
+ FindVCChannels(_mSurfaces[i],sorted,layer,vVColorIndices);
+
+ // allocate storage for UV and CV channels
+ aiVector3D* pvUV[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS;++mui ) {
+ if (0xffffffff == vUVChannelIndices[mui])
+ break;
+
+ pvUV[mui] = mesh->mTextureCoords[mui] = new aiVector3D[mesh->mNumVertices];
+
+ // LightWave doesn't support more than 2 UV components (?)
+ mesh->mNumUVComponents[0] = 2;
+ }
+
+ if (layer.mNormals.name.length())
+ nrm = mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+
+ aiColor4D* pvVC[AI_MAX_NUMBER_OF_COLOR_SETS];
+ for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS;++mui) {
+ if (0xffffffff == vVColorIndices[mui])break;
+ pvVC[mui] = mesh->mColors[mui] = new aiColor4D[mesh->mNumVertices];
+ }
+
+ // we would not need this extra array, but the code is much cleaner if we use it
+ std::vector<unsigned int>& smoothingGroups = layer.mPointReferrers;
+ smoothingGroups.erase (smoothingGroups.begin(),smoothingGroups.end());
+ smoothingGroups.resize(mesh->mNumFaces,0);
+
+ // now convert all faces
+ unsigned int vert = 0;
+ std::vector<unsigned int>::iterator outIt = smoothingGroups.begin();
+ for (it = sorted.begin(); it != end;++it,++outIt) {
+ const LWO::Face& face = layer.mFaces[*it];
+ *outIt = face.smoothGroup;
+
+ // copy all vertices
+ for (unsigned int q = 0; q < face.mNumIndices;++q,++vert) {
+ register unsigned int idx = face.mIndices[q];
+ *pv++ = layer.mTempPoints[idx] /*- layer.mPivot*/;
+
+ // process UV coordinates
+ for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_TEXTURECOORDS;++w) {
+ if (0xffffffff == vUVChannelIndices[w])
+ break;
+ aiVector3D*& pp = pvUV[w];
+ const aiVector2D& src = ((aiVector2D*)&layer.mUVChannels[vUVChannelIndices[w]].rawData[0])[idx];
+ pp->x = src.x;
+ pp->y = src.y;
+ pp++;
+ }
+
+ // process normals (MODO extension)
+ if (nrm) {
+ *nrm = ((aiVector3D*)&layer.mNormals.rawData[0])[idx];
+ nrm->z *= -1.f;
+ ++nrm;
+ }
+
+ // process vertex colors
+ for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_COLOR_SETS;++w) {
+ if (0xffffffff == vVColorIndices[w])
+ break;
+ *pvVC[w] = ((aiColor4D*)&layer.mVColorChannels[vVColorIndices[w]].rawData[0])[idx];
+
+ // If a RGB color map is explicitly requested delete the
+ // alpha channel - it could theoretically be != 1.
+ if (_mSurfaces[i].mVCMapType == AI_LWO_RGB)
+ pvVC[w]->a = 1.f;
+
+ pvVC[w]++;
+ }
+
+#if 0
+ // process vertex weights. We can't properly reconstruct the whole skeleton for now,
+ // but we can create dummy bones for all weight channels which we have.
+ for (unsigned int w = 0; w < layer.mWeightChannels.size();++w)
+ {
+ }
+#endif
+
+ face.mIndices[q] = vert;
+ }
+ pf->mIndices = face.mIndices;
+ pf->mNumIndices = face.mNumIndices;
+ unsigned int** p = (unsigned int**)&face.mIndices;*p = NULL; // HACK: make sure it won't be deleted
+ pf++;
+ }
+
+ if (!mesh->mNormals) {
+ // Compute normal vectors for the mesh - we can't use our GenSmoothNormal-
+ // Step here since it wouldn't handle smoothing groups correctly for LWO.
+ // So we use a separate implementation.
+ ComputeNormals(mesh,smoothingGroups,_mSurfaces[i]);
+ }
+ else DefaultLogger::get()->debug("LWO2: No need to compute normals, they're already there");
+ ++p;
+ }
+ }
+
+ // Generate nodes to render the mesh. Store the source layer in the mParent member of the nodes
+ unsigned int num = apcMeshes.size() - meshStart;
+ if (layer.mName != "<LWODefault>" || num > 0) {
+ aiNode* pcNode = new aiNode();
+ apcNodes.push_back(pcNode);
+ pcNode->mName.Set(layer.mName);
+ pcNode->mParent = (aiNode*)&layer;
+ pcNode->mNumMeshes = num;
+
+ if (pcNode->mNumMeshes) {
+ pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+ for (unsigned int p = 0; p < pcNode->mNumMeshes;++p)
+ pcNode->mMeshes[p] = p + meshStart;
+ }
+ }
+ }
+
+ if (apcNodes.empty() || apcMeshes.empty())
+ throw DeadlyImportError("LWO: No meshes loaded");
+
+ // The RemoveRedundantMaterials step will clean this up later
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = (unsigned int)mSurfaces->size()];
+ for (unsigned int mat = 0; mat < pScene->mNumMaterials;++mat) {
+ MaterialHelper* pcMat = new MaterialHelper();
+ pScene->mMaterials[mat] = pcMat;
+ ConvertMaterial((*mSurfaces)[mat],pcMat);
+ }
+
+ // copy the meshes to the output structure
+ pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = (unsigned int)apcMeshes.size() ];
+ ::memcpy(pScene->mMeshes,&apcMeshes[0],pScene->mNumMeshes*sizeof(void*));
+
+ // generate the final node graph
+ GenerateNodeGraph(apcNodes);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>& smoothingGroups,
+ const LWO::Surface& surface)
+{
+ // Allocate output storage
+ mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+
+ // First generate per-face normals
+ aiVector3D* out;
+ std::vector<aiVector3D> faceNormals;
+
+ // ... in some cases that's already enough
+ if (!surface.mMaximumSmoothAngle)
+ out = mesh->mNormals;
+ else {
+ faceNormals.resize(mesh->mNumVertices);
+ out = &faceNormals[0];
+ }
+
+ aiFace* begin = mesh->mFaces, *const end = mesh->mFaces+mesh->mNumFaces;
+ for (; begin != end; ++begin) {
+ aiFace& face = *begin;
+
+ // LWO doc: "the normal is defined as the cross product of the first and last edges"
+ aiVector3D* pV1 = mesh->mVertices + face.mIndices[0];
+ aiVector3D* pV2 = mesh->mVertices + face.mIndices[1];
+ aiVector3D* pV3 = mesh->mVertices + face.mIndices[face.mNumIndices-1];
+
+ aiVector3D vNor = ((*pV2 - *pV1) ^(*pV3 - *pV1)).Normalize();
+ for (unsigned int i = 0; i < face.mNumIndices;++i)
+ out[face.mIndices[i]] = vNor;
+ }
+ if (!surface.mMaximumSmoothAngle)return;
+ const float posEpsilon = ComputePositionEpsilon(mesh);
+
+ // Now generate the spatial sort tree
+ SGSpatialSort sSort;
+ std::vector<unsigned int>::const_iterator it = smoothingGroups.begin();
+ for ( begin = mesh->mFaces; begin != end; ++begin, ++it)
+ {
+ aiFace& face = *begin;
+ for (unsigned int i = 0; i < face.mNumIndices;++i)
+ {
+ register unsigned int tt = face.mIndices[i];
+ sSort.Add(mesh->mVertices[tt],tt,*it);
+ }
+ }
+ // Sort everything - this takes O(nlogn) time
+ sSort.Prepare();
+ std::vector<unsigned int> poResult;
+ poResult.reserve(20);
+
+ // Generate vertex normals. We have O(logn) for the binary lookup, which we need
+ // for n elements, thus the EXPECTED complexity is O(nlogn)
+ if (surface.mMaximumSmoothAngle < 3.f && !configSpeedFlag) {
+ const float fLimit = cos(surface.mMaximumSmoothAngle);
+
+ for ( begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) {
+ const aiFace& face = *begin;
+ unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices;
+ for (; beginIdx != endIdx; ++beginIdx)
+ {
+ register unsigned int idx = *beginIdx;
+ sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true);
+ std::vector<unsigned int>::const_iterator a, end = poResult.end();
+
+ aiVector3D vNormals;
+ for (a = poResult.begin();a != end;++a) {
+ const aiVector3D& v = faceNormals[*a];
+ if (v * faceNormals[idx] < fLimit)
+ continue;
+ vNormals += v;
+ }
+ mesh->mNormals[idx] = vNormals.Normalize();
+ }
+ }
+ }
+ // faster code path in case there is no smooth angle
+ else {
+ std::vector<bool> vertexDone(mesh->mNumVertices,false);
+ for ( begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) {
+ const aiFace& face = *begin;
+ unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices;
+ for (; beginIdx != endIdx; ++beginIdx)
+ {
+ register unsigned int idx = *beginIdx;
+ if (vertexDone[idx])
+ continue;
+ sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true);
+ std::vector<unsigned int>::const_iterator a, end = poResult.end();
+
+ aiVector3D vNormals;
+ for (a = poResult.begin();a != end;++a) {
+ const aiVector3D& v = faceNormals[*a];
+ vNormals += v;
+ }
+ vNormals.Normalize();
+ for (a = poResult.begin();a != end;++a) {
+ mesh->mNormals[*a] = vNormals;
+ vertexDone[*a] = true;
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::AddChildren(aiNode* node, uint16_t parent, std::vector<aiNode*>& apcNodes)
+{
+ for (std::vector<aiNode*>::iterator it = apcNodes.begin(); it != apcNodes.end(); ++it) {
+ if (*it) {
+ LWO::Layer* layer = (LWO::Layer*)(*it)->mParent;
+ if (layer->mParent == parent && layer->mIndex != parent)
+ ++node->mNumChildren;
+ }
+ }
+
+ if (node->mNumChildren) {
+ unsigned int p = 0;
+
+ node->mChildren = new aiNode* [ node->mNumChildren ];
+ for (std::vector<aiNode*>::iterator it = apcNodes.begin(); it != apcNodes.end(); ++it) {
+ if (*it) {
+ LWO::Layer* layer = (LWO::Layer*)(*it)->mParent;
+ if (layer->mParent == parent && layer->mIndex != parent) {
+ aiNode* nd = node->mChildren[p++] = *it;
+ nd->mParent = node;
+
+ // fixme: ignore pivot points for the moment
+ //nd->mTransformation.a4 = layer->mPivot.x;
+ //nd->mTransformation.b4 = layer->mPivot.y;
+ //nd->mTransformation.c4 = layer->mPivot.z;
+
+ // recursively add more children
+ (*it) = NULL;
+ AddChildren(nd,layer->mIndex,apcNodes);
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::GenerateNodeGraph(std::vector<aiNode*>& apcNodes)
+{
+ // now generate the final nodegraph - generate a root node and attach children
+ aiNode* root = pScene->mRootNode = new aiNode();
+ root->mName.Set("<LWORoot>");
+ AddChildren(root,0,apcNodes);
+
+ // check whether we added all layers with meshes assigned to the output graph.
+ // if not, add them to the root node
+ unsigned int extra = 0;
+ for (std::vector<aiNode*>::iterator it = apcNodes.begin(); it != apcNodes.end(); ++it) {
+ if ((*it) && (*it)->mNumMeshes)
+ ++extra;
+ }
+
+ if (extra) {
+ const unsigned int newSize = extra + pScene->mRootNode->mNumChildren;
+ aiNode** const apcNewNodes = new aiNode*[newSize];
+ if ((extra = root->mNumChildren))
+ ::memcpy(apcNewNodes,root->mChildren,extra*sizeof(void*));
+
+ aiNode** cc = apcNewNodes+extra;
+ for (std::vector<aiNode*>::iterator it = apcNodes.begin(); it != apcNodes.end(); ++it) {
+ if ((*it) && (*it)->mNumMeshes) {
+ aiNode* nd = *cc++ = *it;
+ nd->mParent = pScene->mRootNode;
+
+ // recursively add more children
+ (*it) = NULL;
+ AddChildren(nd,((LWO::Layer*)nd->mParent)->mIndex,apcNodes);
+ }
+ }
+ delete[] root->mChildren;
+ root->mChildren = apcNewNodes;
+ root->mNumChildren = newSize;
+ }
+ if (!pScene->mRootNode->mNumChildren)
+ throw DeadlyImportError("LWO: Unable to build a valid node graph");
+
+ // Remove a single root node with no meshes assigned to it ...
+ if (1 == pScene->mRootNode->mNumChildren) {
+ aiNode* pc = pScene->mRootNode->mChildren[0];
+ pc->mParent = pScene->mRootNode->mChildren[0] = NULL;
+ delete pScene->mRootNode;
+ pScene->mRootNode = pc;
+ }
+
+ // convert the whole stuff to RH with CCW winding
+ MakeLeftHandedProcess maker;
+ maker.Execute(pScene);
+
+ FlipWindingOrderProcess flipper;
+ flipper.Execute(pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::ResolveTags()
+{
+ // --- this function is used for both LWO2 and LWOB
+ mMapping->resize(mTags->size(),0xffffffff);
+ for (unsigned int a = 0; a < mTags->size();++a) {
+
+ const std::string& c = (*mTags)[a];
+ for (unsigned int i = 0; i < mSurfaces->size();++i) {
+
+ const std::string& d = (*mSurfaces)[i].mName;
+ if (!ASSIMP_stricmp(c,d)) {
+
+ (*mMapping)[a] = i;
+ break;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::ResolveClips()
+{
+ for ( unsigned int i = 0; i < mClips.size();++i) {
+
+ Clip& clip = mClips[i];
+ if (Clip::REF == clip.type) {
+
+ if (clip.clipRef >= mClips.size()) {
+ DefaultLogger::get()->error("LWO2: Clip referrer index is out of range");
+ clip.clipRef = 0;
+ }
+
+ Clip& dest = mClips[clip.clipRef];
+ if (Clip::REF == dest.type) {
+ DefaultLogger::get()->error("LWO2: Clip references another clip reference");
+ clip.type = Clip::UNSUPPORTED;
+ }
+
+ else {
+ clip.path = dest.path;
+ clip.type = dest.type;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::AdjustTexturePath(std::string& out)
+{
+ // --- this function is used for both LWO2 and LWOB
+ if (!mIsLWO2 && ::strstr(out.c_str(), "(sequence)")) {
+
+ // remove the (sequence) and append 000
+ DefaultLogger::get()->info("LWOB: Sequence of animated texture found. It will be ignored");
+ out = out.substr(0,out.length()-10) + "000";
+ }
+
+ // format: drive:path/file - we just need to insert a slash after the drive
+ std::string::size_type n = out.find_first_of(':');
+ if (std::string::npos != n) {
+ out.insert(n+1,"/");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOTags(unsigned int size)
+{
+ // --- this function is used for both LWO2 and LWOB
+
+ const char* szCur = (const char*)mFileBuffer, *szLast = szCur;
+ const char* const szEnd = szLast+size;
+ while (szCur < szEnd)
+ {
+ if (!(*szCur))
+ {
+ const size_t len = (size_t)(szCur-szLast);
+ // FIX: skip empty-sized tags
+ if (len)
+ mTags->push_back(std::string(szLast,len));
+ szCur += (len&0x1 ? 1 : 2);
+ szLast = szCur;
+ }
+ szCur++;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWOPoints(unsigned int length)
+{
+ // --- this function is used for both LWO2 and LWOB but for
+ // LWO2 we need to allocate 25% more storage - it could be we'll
+ // need to duplicate some points later.
+ register unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12;
+ if (mIsLWO2)
+ {
+ mCurLayer->mTempPoints.reserve ( regularSize + (regularSize>>2u) );
+ mCurLayer->mTempPoints.resize ( regularSize );
+
+ // initialize all point referrers with the default values
+ mCurLayer->mPointReferrers.reserve ( regularSize + (regularSize>>2u) );
+ mCurLayer->mPointReferrers.resize ( regularSize, 0xffffffff );
+ }
+ else mCurLayer->mTempPoints.resize( regularSize );
+
+ // perform endianess conversions
+#ifndef AI_BUILD_BIG_ENDIAN
+ for (unsigned int i = 0; i < length>>2;++i)
+ ByteSwap::Swap4( mFileBuffer + (i << 2));
+#endif
+ ::memcpy(&mCurLayer->mTempPoints[0],mFileBuffer,length);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2Polygons(unsigned int length)
+{
+ LE_NCONST uint16_t* const end = (LE_NCONST uint16_t*)(mFileBuffer+length);
+ const uint32_t type = GetU4();
+
+ // Determine the type of the polygons
+ switch (type)
+ {
+ // read unsupported stuff too (although we wont process it)
+ case AI_LWO_MBAL:
+ DefaultLogger::get()->warn("LWO2: Encountered unsupported primitive chunk (METABALL)");
+ break;
+ case AI_LWO_CURV:
+ DefaultLogger::get()->warn("LWO2: Encountered unsupported primitive chunk (SPLINE)");;
+ break;
+
+ // These are ok with no restrictions
+ case AI_LWO_PTCH:
+ case AI_LWO_FACE:
+ case AI_LWO_BONE:
+ case AI_LWO_SUBD:
+ break;
+ default:
+
+ // hm!? wtf is this? ok ...
+ DefaultLogger::get()->error("LWO2: Ignoring unknown polygon type.");
+ break;
+ }
+
+ // first find out how many faces and vertices we'll finally need
+ uint16_t* cursor= (uint16_t*)mFileBuffer;
+
+ unsigned int iNumFaces = 0,iNumVertices = 0;
+ CountVertsAndFacesLWO2(iNumVertices,iNumFaces,cursor,end);
+
+ // allocate the output array and copy face indices
+ if (iNumFaces) {
+ cursor = (uint16_t*)mFileBuffer;
+
+ mCurLayer->mFaces.resize(iNumFaces,LWO::Face(type));
+ FaceList::iterator it = mCurLayer->mFaces.begin();
+ CopyFaceIndicesLWO2(it,cursor,end);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::CountVertsAndFacesLWO2(unsigned int& verts, unsigned int& faces,
+ uint16_t*& cursor, const uint16_t* const end, unsigned int max)
+{
+ while (cursor < end && max--)
+ {
+ AI_LSWAP2P(cursor);
+ uint16_t numIndices = *cursor++;
+ numIndices &= 0x03FF;
+ verts += numIndices;++faces;
+
+ for (uint16_t i = 0; i < numIndices; i++)
+ ReadVSizedIntLWO2((uint8_t*&)cursor);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::CopyFaceIndicesLWO2(FaceList::iterator& it,
+ uint16_t*& cursor,
+ const uint16_t* const end)
+{
+ while (cursor < end) {
+
+ LWO::Face& face = *it++;;
+ if ((face.mNumIndices = (*cursor++) & 0x03FF)) /* byte swapping has already been done */ {
+ face.mIndices = new unsigned int[face.mNumIndices];
+ for (unsigned int i = 0; i < face.mNumIndices; i++)
+ {
+ face.mIndices[i] = ReadVSizedIntLWO2((uint8_t*&)cursor) + mCurLayer->mPointIDXOfs;
+ if (face.mIndices[i] > mCurLayer->mTempPoints.size())
+ {
+ DefaultLogger::get()->warn("LWO2: Failure evaluating face record, index is out of range");
+ face.mIndices[i] = (unsigned int)mCurLayer->mTempPoints.size()-1;
+ }
+ }
+ }
+ else throw DeadlyImportError("LWO2: Encountered invalid face record with zero indices");
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2PolygonTags(unsigned int length)
+{
+ LE_NCONST uint8_t* const end = mFileBuffer+length;
+
+ AI_LWO_VALIDATE_CHUNK_LENGTH(length,PTAG,4);
+ uint32_t type = GetU4();
+
+ if (type != AI_LWO_SURF && type != AI_LWO_SMGP)
+ return;
+
+ while (mFileBuffer < end) {
+
+ unsigned int i = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
+ unsigned int j = GetU2();
+
+ if (i >= mCurLayer->mFaces.size()) {
+ DefaultLogger::get()->warn("LWO2: face index in PTAG is out of range");
+ continue;
+ }
+
+ switch (type) {
+
+ case AI_LWO_SURF:
+ mCurLayer->mFaces[i].surfaceIndex = j;
+ break;
+ case AI_LWO_SMGP: /* is that really used? */
+ mCurLayer->mFaces[i].smoothGroup = j;
+ break;
+ };
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+VMapEntry* FindEntry(std::vector< T >& list,const std::string& name, bool perPoly)
+{
+ for (typename std::vector< T >::iterator it = list.begin(), end = list.end();it != end; ++it) {
+ if ((*it).name == name) {
+ if (!perPoly) {
+ DefaultLogger::get()->warn("LWO2: Found two VMAP sections with equal names");
+ }
+ return &(*it);
+ }
+ }
+ list.push_back( T() );
+ VMapEntry* p = &list.back();
+ p->name = name;
+ return p;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline void CreateNewEntry(T& chan, unsigned int srcIdx)
+{
+ if (!chan.name.length())
+ return;
+
+ chan.abAssigned[srcIdx] = true;
+ chan.abAssigned.resize(chan.abAssigned.size()+1,false);
+
+ for (unsigned int a = 0; a < chan.dims;++a)
+ chan.rawData.push_back(chan.rawData[srcIdx*chan.dims+a]);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline void CreateNewEntry(std::vector< T >& list, unsigned int srcIdx)
+{
+ for (typename std::vector< T >::iterator it = list.begin(), end = list.end();it != end;++it) {
+ CreateNewEntry( *it, srcIdx );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+inline void LWOImporter::DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRead,
+ unsigned int idx, float* data)
+{
+ ai_assert(NULL != data);
+ LWO::ReferrerList& refList = mCurLayer->mPointReferrers;
+ unsigned int i;
+
+ base->abAssigned[idx] = true;
+ for (i = 0; i < numRead;++i) {
+ base->rawData[idx*base->dims+i]= data[i];
+ }
+
+ if (0xffffffff != (i = refList[idx])) {
+ DoRecursiveVMAPAssignment(base,numRead,i,data);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+inline void AddToSingleLinkedList(ReferrerList& refList, unsigned int srcIdx, unsigned int destIdx)
+{
+ if (0xffffffff == refList[srcIdx]) {
+ refList[srcIdx] = destIdx;
+ return;
+ }
+ AddToSingleLinkedList(refList,refList[srcIdx],destIdx);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load LWO2 vertex map
+void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
+{
+ LE_NCONST uint8_t* const end = mFileBuffer+length;
+
+ AI_LWO_VALIDATE_CHUNK_LENGTH(length,VMAP,6);
+ unsigned int type = GetU4();
+ unsigned int dims = GetU2();
+
+ VMapEntry* base;
+
+ // read the name of the vertex map
+ std::string name;
+ GetS0(name,length);
+
+ switch (type)
+ {
+ case AI_LWO_TXUV:
+ if (dims != 2) {
+ DefaultLogger::get()->warn("LWO2: Skipping UV channel \'"
+ + name + "\' with !2 components");
+ return;
+ }
+ base = FindEntry(mCurLayer->mUVChannels,name,perPoly);
+ break;
+ case AI_LWO_WGHT:
+ case AI_LWO_MNVW:
+ if (dims != 1) {
+ DefaultLogger::get()->warn("LWO2: Skipping Weight Channel \'"
+ + name + "\' with !1 components");
+ return;
+ }
+ base = FindEntry((type == AI_LWO_WGHT ? mCurLayer->mWeightChannels
+ : mCurLayer->mSWeightChannels),name,perPoly);
+ break;
+ case AI_LWO_RGB:
+ case AI_LWO_RGBA:
+ if (dims != 3 && dims != 4) {
+ DefaultLogger::get()->warn("LWO2: Skipping Color Map \'"
+ + name + "\' with a dimension > 4 or < 3");
+ return;
+ }
+ base = FindEntry(mCurLayer->mVColorChannels,name,perPoly);
+ break;
+
+ case AI_LWO_MODO_NORM:
+ /* This is a non-standard extension chunk used by Luxology's MODO.
+ * It stores per-vertex normals. This VMAP exists just once, has
+ * 3 dimensions and is btw extremely beautiful.
+ */
+ if (name != "vert_normals" || dims != 3 || mCurLayer->mNormals.name.length())
+ return;
+
+ DefaultLogger::get()->info("Processing non-standard extension: MODO VMAP.NORM.vert_normals");
+
+ mCurLayer->mNormals.name = name;
+ base = & mCurLayer->mNormals;
+ break;
+
+ case AI_LWO_PICK: /* these VMAPs are just silently dropped */
+ case AI_LWO_MORF:
+ case AI_LWO_SPOT:
+ return;
+
+ default:
+ if (name == "APS.Level") {
+ // XXX handle this (seems to be subdivision-related).
+ }
+ DefaultLogger::get()->warn("LWO2: Skipping unknown VMAP/VMAD channel \'" + name + "\'");
+ return;
+ };
+ base->Allocate((unsigned int)mCurLayer->mTempPoints.size());
+
+ // now read all entries in the map
+ type = std::min(dims,base->dims);
+ const unsigned int diff = (dims - type)<<2u;
+
+ LWO::FaceList& list = mCurLayer->mFaces;
+ LWO::PointList& pointList = mCurLayer->mTempPoints;
+ LWO::ReferrerList& refList = mCurLayer->mPointReferrers;
+
+ float temp[4];
+
+ const unsigned int numPoints = (unsigned int)pointList.size();
+ const unsigned int numFaces = (unsigned int)list.size();
+
+ while (mFileBuffer < end) {
+
+ unsigned int idx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mPointIDXOfs;
+ if (idx >= numPoints) {
+ DefaultLogger::get()->warn("LWO2: Failure evaluating VMAP/VMAD entry \'" + name + "\', vertex index is out of range");
+ mFileBuffer += base->dims<<2u;
+ continue;
+ }
+ if (perPoly) {
+ unsigned int polyIdx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
+ if (base->abAssigned[idx]) {
+ // we have already a VMAP entry for this vertex - thus
+ // we need to duplicate the corresponding polygon.
+ if (polyIdx >= numFaces) {
+ DefaultLogger::get()->warn("LWO2: Failure evaluating VMAD entry \'" + name + "\', polygon index is out of range");
+ mFileBuffer += base->dims<<2u;
+ continue;
+ }
+
+ LWO::Face& src = list[polyIdx];
+
+ // generate a new unique vertex for the corresponding index - but only
+ // if we can find the index in the face
+ bool had = false;
+ for (unsigned int i = 0; i < src.mNumIndices;++i) {
+
+ unsigned int srcIdx = src.mIndices[i], tmp = idx;
+ do {
+ if (tmp == srcIdx)
+ break;
+ }
+ while ((tmp = refList[tmp]) != 0xffffffff);
+ if (tmp == 0xffffffff)
+ continue;
+
+ had = true;
+ refList.resize(refList.size()+1, 0xffffffff);
+
+ idx = (unsigned int)pointList.size();
+ src.mIndices[i] = (unsigned int)pointList.size();
+
+ // store the index of the new vertex in the old vertex
+ // so we get a single linked list we can traverse in
+ // only one direction
+ AddToSingleLinkedList(refList,srcIdx,src.mIndices[i]);
+ pointList.push_back(pointList[srcIdx]);
+
+ CreateNewEntry(mCurLayer->mVColorChannels, srcIdx );
+ CreateNewEntry(mCurLayer->mUVChannels, srcIdx );
+ CreateNewEntry(mCurLayer->mWeightChannels, srcIdx );
+ CreateNewEntry(mCurLayer->mSWeightChannels, srcIdx );
+ CreateNewEntry(mCurLayer->mNormals, srcIdx );
+ }
+ if (!had) {
+ DefaultLogger::get()->warn("LWO2: Failure evaluating VMAD entry \'" + name + "\', vertex index wasn't found in that polygon");
+ ai_assert(had);
+ }
+ }
+ }
+ for (unsigned int l = 0; l < type;++l)
+ temp[l] = GetF4();
+
+ DoRecursiveVMAPAssignment(base,type,idx, temp);
+ mFileBuffer += diff;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load LWO2 clip
+void LWOImporter::LoadLWO2Clip(unsigned int length)
+{
+ AI_LWO_VALIDATE_CHUNK_LENGTH(length,CLIP,10);
+
+ mClips.push_back(LWO::Clip());
+ LWO::Clip& clip = mClips.back();
+
+ // first - get the index of the clip
+ clip.idx = GetU4();
+
+ IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+ switch (head->type)
+ {
+ case AI_LWO_STIL:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,STIL,1);
+
+ // "Normal" texture
+ GetS0(clip.path,head->length);
+ clip.type = Clip::STILL;
+ break;
+
+ case AI_LWO_ISEQ:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ISEQ,16);
+ // Image sequence. We'll later take the first.
+ {
+ uint8_t digits = GetU1(); mFileBuffer++;
+ int16_t offset = GetU2(); mFileBuffer+=4;
+ int16_t start = GetU2(); mFileBuffer+=4;
+
+ std::string s;std::stringstream ss;
+ GetS0(s,head->length);
+
+ head->length -= (unsigned int)s.length()+1;
+ ss << s;
+ ss << std::setw(digits) << offset + start;
+ GetS0(s,head->length);
+ ss << s;
+ clip.path = ss.str();
+ clip.type = Clip::SEQ;
+ }
+ break;
+
+ case AI_LWO_STCC:
+ DefaultLogger::get()->warn("LWO2: Color shifted images are not supported");
+ break;
+
+ case AI_LWO_ANIM:
+ DefaultLogger::get()->warn("LWO2: Animated textures are not supported");
+ break;
+
+ case AI_LWO_XREF:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,XREF,4);
+
+ // Just a cross-reference to another CLIp
+ clip.type = Clip::REF;
+ clip.clipRef = GetU4();
+ break;
+
+ case AI_LWO_NEGA:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,NEGA,2);
+ clip.negate = (0 != GetU2());
+ break;
+
+ default:
+ DefaultLogger::get()->warn("LWO2: Encountered unknown CLIP subchunk");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load envelope description
+void LWOImporter::LoadLWO2Envelope(unsigned int length)
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + length;
+ AI_LWO_VALIDATE_CHUNK_LENGTH(length,ENVL,4);
+
+ mEnvelopes.push_back(LWO::Envelope());
+ LWO::Envelope& envelope = mEnvelopes.back();
+
+ // Get the index of the envelope
+ envelope.index = ReadVSizedIntLWO2(mFileBuffer);
+
+ // It looks like there might be an extra U4 right after the index,
+ // at least in modo (LXOB) files: we'll ignore it if it's zero,
+ // otherwise it represents the start of a subchunk, so we backtrack.
+ if (mIsLXOB)
+ {
+ uint32_t extra = GetU4();
+ if (extra)
+ {
+ mFileBuffer -= 4;
+ }
+ }
+
+ // ... and read all subchunks
+ while (true)
+ {
+ if (mFileBuffer + 6 >= end)break;
+ LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ throw DeadlyImportError("LWO2: Invalid envelope chunk length");
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ // Type & representation of the envelope
+ case AI_LWO_TYPE:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TYPE,2);
+ mFileBuffer++; // skip user format
+
+ // Determine type of envelope
+ envelope.type = (LWO::EnvelopeType)*mFileBuffer;
+ ++mFileBuffer;
+ break;
+
+ // precondition
+ case AI_LWO_PRE:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,PRE,2);
+ envelope.pre = (LWO::PrePostBehaviour)GetU2();
+ break;
+
+ // postcondition
+ case AI_LWO_POST:
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,POST,2);
+ envelope.post = (LWO::PrePostBehaviour)GetU2();
+ break;
+
+ // keyframe
+ case AI_LWO_KEY:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,KEY,8);
+
+ envelope.keys.push_back(LWO::Key());
+ LWO::Key& key = envelope.keys.back();
+
+ key.time = GetF4();
+ key.value = GetF4();
+ break;
+ }
+
+ // interval interpolation
+ case AI_LWO_SPAN:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPAN,4);
+ if (envelope.keys.size()<2)
+ DefaultLogger::get()->warn("LWO2: Unexpected SPAN chunk");
+ else {
+ LWO::Key& key = envelope.keys.back();
+ switch (GetU4())
+ {
+ case AI_LWO_STEP:
+ key.inter = LWO::IT_STEP;break;
+ case AI_LWO_LINE:
+ key.inter = LWO::IT_LINE;break;
+ case AI_LWO_TCB:
+ key.inter = LWO::IT_TCB;break;
+ case AI_LWO_HERM:
+ key.inter = LWO::IT_HERM;break;
+ case AI_LWO_BEZI:
+ key.inter = LWO::IT_BEZI;break;
+ case AI_LWO_BEZ2:
+ key.inter = LWO::IT_BEZ2;break;
+ default:
+ DefaultLogger::get()->warn("LWO2: Unknown interval interpolation mode");
+ };
+
+ // todo ... read params
+ }
+ break;
+ }
+
+ default:
+ DefaultLogger::get()->warn("LWO2: Encountered unknown ENVL subchunk");
+ }
+ // regardless how much we did actually read, go to the next chunk
+ mFileBuffer = next;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load file - master function
+void LWOImporter::LoadLWO2File()
+{
+ bool skip = false;
+
+ LE_NCONST uint8_t* const end = mFileBuffer + fileSize;
+ while (true)
+ {
+ if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break;
+ IFF::ChunkHeader* const head = IFF::LoadChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ {
+ throw DeadlyImportError("LWO2: Chunk length points behind the file");
+ break;
+ }
+ uint8_t* const next = mFileBuffer+head->length;
+ unsigned int iUnnamed = 0;
+
+ switch (head->type)
+ {
+ // new layer
+ case AI_LWO_LAYR:
+ {
+ // add a new layer to the list ....
+ mLayers->push_back ( LWO::Layer() );
+ LWO::Layer& layer = mLayers->back();
+ mCurLayer = &layer;
+
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LAYR,16);
+
+ // Continue loading this layer or ignore it? Check the layer index property
+ // NOTE: The first layer is the default layer, so the layer index is one-based now
+ if (0xffffffff != configLayerIndex && configLayerIndex != mLayers->size()-1) {
+ skip = true;
+ }
+ else skip = false;
+
+ // layer index. that's just for internal parenting, from the scope of a LWS file
+ // all layers are numbered in the oder in which they appear in the file
+ layer.mIndex = GetU2();
+
+ // pivot point
+ mFileBuffer += 2; /* unknown */
+ mCurLayer->mPivot.x = GetF4();
+ mCurLayer->mPivot.y = GetF4();
+ mCurLayer->mPivot.z = GetF4();
+ GetS0(layer.mName,head->length-16);
+
+ // if the name is empty, generate a default name
+ if (layer.mName.empty()) {
+ char buffer[128]; // should be sufficiently large
+ ::sprintf(buffer,"Layer_%i", iUnnamed++);
+ layer.mName = buffer;
+ }
+
+ // load this layer or ignore it? Check the layer name property
+ if (configLayerName.length() && configLayerName != layer.mName) {
+ skip = true;
+ }
+ else hasNamedLayer = true;
+
+ // optional: parent of this layer
+ if (mFileBuffer + 2 <= next)
+ layer.mParent = GetU2();
+
+ break;
+ }
+
+ // vertex list
+ case AI_LWO_PNTS:
+ {
+ if (skip)
+ break;
+
+ unsigned int old = (unsigned int)mCurLayer->mTempPoints.size();
+ LoadLWOPoints(head->length);
+ mCurLayer->mPointIDXOfs = old;
+ break;
+ }
+ // vertex tags
+ case AI_LWO_VMAD:
+ if (mCurLayer->mFaces.empty())
+ {
+ DefaultLogger::get()->warn("LWO2: Unexpected VMAD chunk");
+ break;
+ }
+ // --- intentionally no break here
+ case AI_LWO_VMAP:
+ {
+ if (skip)
+ break;
+
+ if (mCurLayer->mTempPoints.empty())
+ DefaultLogger::get()->warn("LWO2: Unexpected VMAP chunk");
+ else LoadLWO2VertexMap(head->length,head->type == AI_LWO_VMAD);
+ break;
+ }
+ // face list
+ case AI_LWO_POLS:
+ {
+ if (skip)
+ break;
+
+ unsigned int old = (unsigned int)mCurLayer->mFaces.size();
+ LoadLWO2Polygons(head->length);
+ mCurLayer->mFaceIDXOfs = old;
+ break;
+ }
+ // polygon tags
+ case AI_LWO_PTAG:
+ {
+ if (skip)
+ break;
+
+ if (mCurLayer->mFaces.empty())
+ DefaultLogger::get()->warn("LWO2: Unexpected PTAG");
+ else LoadLWO2PolygonTags(head->length);
+ break;
+ }
+ // list of tags
+ case AI_LWO_TAGS:
+ {
+ if (!mTags->empty())
+ DefaultLogger::get()->warn("LWO2: SRFS chunk encountered twice");
+ else LoadLWOTags(head->length);
+ break;
+ }
+
+ // surface chunk
+ case AI_LWO_SURF:
+ {
+ LoadLWO2Surface(head->length);
+ break;
+ }
+
+ // clip chunk
+ case AI_LWO_CLIP:
+ {
+ LoadLWO2Clip(head->length);
+ break;
+ }
+
+ // envelope chunk
+ case AI_LWO_ENVL:
+ {
+ LoadLWO2Envelope(head->length);
+ break;
+ }
+ }
+ mFileBuffer = next;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER
diff --git a/3rdparty/assimp/code/LWOLoader.h b/3rdparty/assimp/code/LWOLoader.h
new file mode 100644
index 000000000..0c186d087
--- /dev/null
+++ b/3rdparty/assimp/code/LWOLoader.h
@@ -0,0 +1,490 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Declaration of the LWO importer class. */
+#ifndef AI_LWOLOADER_H_INCLUDED
+#define AI_LWOLOADER_H_INCLUDED
+
+#include "../include/aiTypes.h"
+#include "../include/DefaultLogger.h"
+
+#include "LWOFileData.h"
+#include "BaseImporter.h"
+#include "MaterialSystem.h"
+
+struct aiTexture;
+struct aiNode;
+
+namespace Assimp {
+using namespace LWO;
+
+// ---------------------------------------------------------------------------
+/** Class to load LWO files.
+ *
+ * @note Methods named "xxxLWO2[xxx]" are used with the newer LWO2 format.
+ * Methods named "xxxLWOB[xxx]" are used with the older LWOB format.
+ * Methods named "xxxLWO[xxx]" are used with both formats.
+ * Methods named "xxx" are used to preprocess the loaded data -
+ * they aren't specific to one format version
+*/
+// ---------------------------------------------------------------------------
+class LWOImporter : public BaseImporter
+{
+ friend class Importer;
+
+
+protected:
+ /** Constructor to be privately used by Importer */
+ LWOImporter();
+
+ /** Destructor, private as well */
+ ~LWOImporter();
+
+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;
+
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions)
+ {
+ extensions.insert("lxo");
+ extensions.insert("lwo");
+ }
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+ // -------------------------------------------------------------------
+ /** Loads a LWO file in the older LWOB format (LW < 6)
+ */
+ void LoadLWOBFile();
+
+ // -------------------------------------------------------------------
+ /** Loads a LWO file in the newer LWO2 format (LW >= 6)
+ */
+ void LoadLWO2File();
+
+
+ // -------------------------------------------------------------------
+ /** Parsing functions used for all file format versions
+ */
+ inline void GetS0(std::string& out,unsigned int max);
+ inline float GetF4();
+ inline uint32_t GetU4();
+ inline uint16_t GetU2();
+ inline uint8_t GetU1();
+
+
+ // -------------------------------------------------------------------
+ /** Loads a surface chunk from an LWOB file
+ * @param size Maximum size to be read, in bytes.
+ */
+ void LoadLWOBSurface(unsigned int size);
+
+ // -------------------------------------------------------------------
+ /** Loads a surface chunk from an LWO2 file
+ * @param size Maximum size to be read, in bytes.
+ */
+ void LoadLWO2Surface(unsigned int size);
+
+ // -------------------------------------------------------------------
+ /** Loads a texture block from a LWO2 file.
+ * @param size Maximum size to be read, in bytes.
+ * @param head Header of the SUF.BLOK header
+ */
+ void LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader* head,
+ unsigned int size );
+
+ // -------------------------------------------------------------------
+ /** Loads a shader block from a LWO2 file.
+ * @param size Maximum size to be read, in bytes.
+ * @param head Header of the SUF.BLOK header
+ */
+ void LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader* head,
+ unsigned int size );
+
+ // -------------------------------------------------------------------
+ /** Loads an image map from a LWO2 file
+ * @param size Maximum size to be read, in bytes.
+ * @param tex Texture object to be filled
+ */
+ void LoadLWO2ImageMap(unsigned int size, LWO::Texture& tex );
+ void LoadLWO2Gradient(unsigned int size, LWO::Texture& tex );
+ void LoadLWO2Procedural(unsigned int size, LWO::Texture& tex );
+
+ // loads the header - used by thethree functions above
+ void LoadLWO2TextureHeader(unsigned int size, LWO::Texture& tex );
+
+ // -------------------------------------------------------------------
+ /** Loads the LWO tag list from the file
+ * @param size Maximum size to be read, in bytes.
+ */
+ void LoadLWOTags(unsigned int size);
+
+ // -------------------------------------------------------------------
+ /** Load polygons from a POLS chunk
+ * @param length Size of the chunk
+ */
+ void LoadLWO2Polygons(unsigned int length);
+ void LoadLWOBPolygons(unsigned int length);
+
+ // -------------------------------------------------------------------
+ /** Load polygon tags from a PTAG chunk
+ * @param length Size of the chunk
+ */
+ void LoadLWO2PolygonTags(unsigned int length);
+
+ // -------------------------------------------------------------------
+ /** Load a vertex map from a VMAP/VMAD chunk
+ * @param length Size of the chunk
+ * @param perPoly Operate on per-polygon base?
+ */
+ void LoadLWO2VertexMap(unsigned int length, bool perPoly);
+
+ // -------------------------------------------------------------------
+ /** Load polygons from a PNTS chunk
+ * @param length Size of the chunk
+ */
+ void LoadLWOPoints(unsigned int length);
+
+ // -------------------------------------------------------------------
+ /** Load a clip from a CLIP chunk
+ * @param length Size of the chunk
+ */
+ void LoadLWO2Clip(unsigned int length);
+
+ // -------------------------------------------------------------------
+ /** Load an envelope from an EVL chunk
+ * @param length Size of the chunk
+ */
+ void LoadLWO2Envelope(unsigned int length);
+
+ // -------------------------------------------------------------------
+ /** Count vertices and faces in a LWOB/LWO2 file
+ */
+ void CountVertsAndFacesLWO2(unsigned int& verts,
+ unsigned int& faces,
+ uint16_t*& cursor,
+ const uint16_t* const end,
+ unsigned int max = 0xffffffff);
+
+ void CountVertsAndFacesLWOB(unsigned int& verts,
+ unsigned int& faces,
+ LE_NCONST uint16_t*& cursor,
+ const uint16_t* const end,
+ unsigned int max = 0xffffffff);
+
+ // -------------------------------------------------------------------
+ /** Read vertices and faces in a LWOB/LWO2 file
+ */
+ void CopyFaceIndicesLWO2(LWO::FaceList::iterator& it,
+ uint16_t*& cursor,
+ const uint16_t* const end);
+
+ // -------------------------------------------------------------------
+ void CopyFaceIndicesLWOB(LWO::FaceList::iterator& it,
+ LE_NCONST uint16_t*& cursor,
+ const uint16_t* const end,
+ unsigned int max = 0xffffffff);
+
+ // -------------------------------------------------------------------
+ /** Resolve the tag and surface lists that have been loaded.
+ * Generates the mMapping table.
+ */
+ void ResolveTags();
+
+ // -------------------------------------------------------------------
+ /** Resolve the clip list that has been loaded.
+ * Replaces clip references with real clips.
+ */
+ void ResolveClips();
+
+ // -------------------------------------------------------------------
+ /** Add a texture list to an output material description.
+ *
+ * @param pcMat Output material
+ * @param in Input texture list
+ * @param type Type identifier of the texture list
+ */
+ bool HandleTextures(MaterialHelper* pcMat, const TextureList& in,
+ aiTextureType type);
+
+ // -------------------------------------------------------------------
+ /** Adjust a texture path
+ */
+ void AdjustTexturePath(std::string& out);
+
+ // -------------------------------------------------------------------
+ /** Convert a LWO surface description to an ASSIMP material
+ */
+ void ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat);
+
+
+ // -------------------------------------------------------------------
+ /** Get a list of all UV/VC channels required by a specific surface.
+ *
+ * @param surf Working surface
+ * @param layer Working layer
+ * @param out Output list. The members are indices into the
+ * UV/VC channel lists of the layer
+ */
+ void FindUVChannels(/*const*/ LWO::Surface& surf,
+ LWO::SortedRep& sorted,
+ /*const*/ LWO::Layer& layer,
+ unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS]);
+
+ // -------------------------------------------------------------------
+ char FindUVChannels(LWO::TextureList& list,
+ LWO::Layer& layer,LWO::UVChannel& uv, unsigned int next);
+
+ // -------------------------------------------------------------------
+ void FindVCChannels(const LWO::Surface& surf,
+ LWO::SortedRep& sorted,
+ const LWO::Layer& layer,
+ unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS]);
+
+ // -------------------------------------------------------------------
+ /** Generate the final node graph
+ * Unused nodes are deleted.
+ * @param apcNodes Flat list of nodes
+ */
+ void GenerateNodeGraph(std::vector<aiNode*>& apcNodes);
+
+ // -------------------------------------------------------------------
+ /** Add children to a node
+ * @param node Node to become a father
+ * @param parent Index of the node
+ * @param apcNodes Flat list of nodes - used nodes are set to NULL.
+ */
+ void AddChildren(aiNode* node, uint16_t parent,
+ std::vector<aiNode*>& apcNodes);
+
+ // -------------------------------------------------------------------
+ /** Read a variable sized integer
+ * @param inout Input and output buffer
+ */
+ int ReadVSizedIntLWO2(uint8_t*& inout);
+
+ // -------------------------------------------------------------------
+ /** Assign a value from a VMAP to a vertex and all vertices
+ * attached to it.
+ * @param base VMAP destination data
+ * @param numRead Number of float's to be read
+ * @param idx Absolute index of the first vertex
+ * @param data Value of the VMAP to be assigned - read numRead
+ * floats from this array.
+ */
+ void DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRead,
+ unsigned int idx, float* data);
+
+ // -------------------------------------------------------------------
+ /** Compute normal vectors for a mesh
+ * @param mesh Input mesh
+ * @param smoothingGroups Smoothing-groups-per-face array
+ * @param surface Surface for the mesh
+ */
+ void ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>& smoothingGroups,
+ const LWO::Surface& surface);
+
+
+ // -------------------------------------------------------------------
+ /** Setup a new texture after the corresponding chunk was
+ * encountered in the file.
+ * @param list Texture list
+ * @param size Maximum number of bytes to be read
+ * @return Pointer to new texture
+ */
+ LWO::Texture* SetupNewTextureLWOB(LWO::TextureList& list,
+ unsigned int size);
+
+protected:
+
+ /** true if the file is a LWO2 file*/
+ bool mIsLWO2;
+
+ /** true if the file is a LXOB file*/
+ bool mIsLXOB;
+
+ /** Temporary list of layers from the file */
+ LayerList* mLayers;
+
+ /** Pointer to the current layer */
+ LWO::Layer* mCurLayer;
+
+ /** Temporary tag list from the file */
+ TagList* mTags;
+
+ /** Mapping table to convert from tag to surface indices.
+ 0xffffffff indicates that a no corresponding surface is available */
+ TagMappingTable* mMapping;
+
+ /** Temporary surface list from the file */
+ SurfaceList* mSurfaces;
+
+ /** Temporary clip list from the file */
+ ClipList mClips;
+
+ /** Temporary envelope list from the file */
+ EnvelopeList mEnvelopes;
+
+ /** file buffer */
+ uint8_t* mFileBuffer;
+
+ /** Size of the file, in bytes */
+ unsigned int fileSize;
+
+ /** Output scene */
+ aiScene* pScene;
+
+ /** Configuration option: speed flag set? */
+ bool configSpeedFlag;
+
+ /** Configuration option: index of layer to be loaded */
+ unsigned int configLayerIndex;
+
+ /** Configuration option: name of layer to be loaded */
+ std::string configLayerName;
+
+ /** True if we have a named layer */
+ bool hasNamedLayer;
+};
+
+
+// ------------------------------------------------------------------------------------------------
+inline float LWOImporter::GetF4()
+{
+ float f = *((float*)mFileBuffer);mFileBuffer += 4;
+ AI_LSWAP4(f);
+ return f;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline uint32_t LWOImporter::GetU4()
+{
+ uint32_t f = *((uint32_t*)mFileBuffer);mFileBuffer += 4;
+ AI_LSWAP4(f);
+ return f;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline uint16_t LWOImporter::GetU2()
+{
+ uint16_t f = *((uint16_t*)mFileBuffer);mFileBuffer += 2;
+ AI_LSWAP2(f);
+ return f;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline uint8_t LWOImporter::GetU1()
+{
+ return *mFileBuffer++;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline int LWOImporter::ReadVSizedIntLWO2(uint8_t*& inout)
+{
+ int i;
+ int c = *inout;inout++;
+ if (c != 0xFF)
+ {
+ i = c << 8;
+ c = *inout;inout++;
+ i |= c;
+ }
+ else
+ {
+ c = *inout;inout++;
+ i = c << 16;
+ c = *inout;inout++;
+ i |= c << 8;
+ c = *inout;inout++;
+ i |= c;
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+inline void LWOImporter::GetS0(std::string& out,unsigned int max)
+{
+ unsigned int iCursor = 0;
+ const char*sz = (const char*)mFileBuffer;
+ while (*mFileBuffer)
+ {
+ if (++iCursor > max)
+ {
+ DefaultLogger::get()->warn("LWO: Invalid file, string is is too long");
+ break;
+ }
+ ++mFileBuffer;
+ }
+ size_t len = (size_t) ((const char*)mFileBuffer-sz);
+ out = std::string(sz,len);
+ mFileBuffer += (len&0x1 ? 1 : 2);
+}
+
+
+
+} // end of namespace Assimp
+
+#endif // AI_LWOIMPORTER_H_INCLUDED
diff --git a/3rdparty/assimp/code/LWOMaterial.cpp b/3rdparty/assimp/code/LWOMaterial.cpp
new file mode 100644
index 000000000..8af631af6
--- /dev/null
+++ b/3rdparty/assimp/code/LWOMaterial.cpp
@@ -0,0 +1,898 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the material oart of the LWO importer class */
+
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
+
+// internal headers
+#include "LWOLoader.h"
+#include "MaterialSystem.h"
+#include "ByteSwap.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+T lerp(const T& one, const T& two, float val)
+{
+ return one + (two-one)*val;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a lightwave mapping mode to our's
+inline aiTextureMapMode GetMapMode(LWO::Texture::Wrap in)
+{
+ switch (in)
+ {
+ case LWO::Texture::REPEAT:
+ return aiTextureMapMode_Wrap;
+
+ case LWO::Texture::MIRROR:
+ return aiTextureMapMode_Mirror;
+
+ case LWO::Texture::RESET:
+ DefaultLogger::get()->warn("LWO2: Unsupported texture map mode: RESET");
+
+ // fall though here
+ case LWO::Texture::EDGE:
+ return aiTextureMapMode_Clamp;
+ }
+ return (aiTextureMapMode)0;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool LWOImporter::HandleTextures(MaterialHelper* pcMat, const TextureList& in, aiTextureType type)
+{
+ ai_assert(NULL != pcMat);
+
+ unsigned int cur = 0, temp = 0;
+ aiString s;
+ bool ret = false;
+
+ for (TextureList::const_iterator it = in.begin(), end = in.end();it != end;++it) {
+ if (!(*it).enabled || !(*it).bCanUse)
+ continue;
+ ret = true;
+
+ // Convert lightwave's mapping modes to ours. We let them
+ // as they are, the GenUVcoords step will compute UV
+ // channels if they're not there.
+
+ aiTextureMapping mapping;
+ switch ((*it).mapMode)
+ {
+ case LWO::Texture::Planar:
+ mapping = aiTextureMapping_PLANE;
+ break;
+ case LWO::Texture::Cylindrical:
+ mapping = aiTextureMapping_CYLINDER;
+ break;
+ case LWO::Texture::Spherical:
+ mapping = aiTextureMapping_SPHERE;
+ break;
+ case LWO::Texture::Cubic:
+ mapping = aiTextureMapping_BOX;
+ break;
+ case LWO::Texture::FrontProjection:
+ DefaultLogger::get()->error("LWO2: Unsupported texture mapping: FrontProjection");
+ mapping = aiTextureMapping_OTHER;
+ break;
+ case LWO::Texture::UV:
+ {
+ if ( 0xffffffff == (*it).mRealUVIndex ) {
+ // We have no UV index for this texture, so we can't display it
+ continue;
+ }
+
+ // add the UV source index
+ temp = (*it).mRealUVIndex;
+ pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_UVWSRC(type,cur));
+
+ mapping = aiTextureMapping_UV;
+ }
+ break;
+ default:
+ ai_assert(false);
+ };
+
+ if (mapping != aiTextureMapping_UV) {
+ // Setup the main axis
+ aiVector3D v;
+ switch ((*it).majorAxis) {
+ case Texture::AXIS_X:
+ v = aiVector3D(1.f,0.f,0.f);
+ break;
+ case Texture::AXIS_Y:
+ v = aiVector3D(0.f,1.f,0.f);
+ break;
+ default: // case Texture::AXIS_Z:
+ v = aiVector3D(0.f,0.f,1.f);
+ break;
+ }
+
+ pcMat->AddProperty(&v,1,AI_MATKEY_TEXMAP_AXIS(type,cur));
+
+ // Setup UV scalings for cylindric and spherical projections
+ if (mapping == aiTextureMapping_CYLINDER || mapping == aiTextureMapping_SPHERE) {
+ aiUVTransform trafo;
+ trafo.mScaling.x = (*it).wrapAmountW;
+ trafo.mScaling.y = (*it).wrapAmountH;
+
+ BOOST_STATIC_ASSERT(sizeof(aiUVTransform)/sizeof(float) == 5);
+ pcMat->AddProperty(&trafo,1,AI_MATKEY_UVTRANSFORM(type,cur));
+ }
+ DefaultLogger::get()->debug("LWO2: Setting up non-UV mapping");
+ }
+
+ // The older LWOB format does not use indirect references to clips.
+ // The file name of a texture is directly specified in the tex chunk.
+ if (mIsLWO2) {
+ // find the corresponding clip
+ ClipList::iterator clip = mClips.begin();
+ temp = (*it).mClipIdx;
+ for (ClipList::iterator end = mClips.end(); clip != end; ++clip) {
+ if ((*clip).idx == temp)
+ break;
+
+ }
+ if (mClips.end() == clip) {
+ DefaultLogger::get()->error("LWO2: Clip index is out of bounds");
+ temp = 0;
+
+ // fixme: appearently some LWO files shipping with Doom3 don't
+ // have clips at all ... check whether that's true or whether
+ // it's a bug in the loader.
+
+ s.Set("$texture.png");
+
+ //continue;
+ }
+ else {
+ if (Clip::UNSUPPORTED == (*clip).type) {
+ DefaultLogger::get()->error("LWO2: Clip type is not supported");
+ continue;
+ }
+ AdjustTexturePath((*clip).path);
+ s.Set((*clip).path);
+
+ // Additional image settings
+ int flags = 0;
+ if ((*clip).negate) {
+ flags |= aiTextureFlags_Invert;
+ }
+ pcMat->AddProperty(&flags,1,AI_MATKEY_TEXFLAGS(type,cur));
+ }
+ }
+ else
+ {
+ std::string ss = (*it).mFileName;
+ if (!ss.length()) {
+ DefaultLogger::get()->error("LWOB: Empty file name");
+ continue;
+ }
+ AdjustTexturePath(ss);
+ s.Set(ss);
+ }
+ pcMat->AddProperty(&s,AI_MATKEY_TEXTURE(type,cur));
+
+ // add the blend factor
+ pcMat->AddProperty<float>(&(*it).mStrength,1,AI_MATKEY_TEXBLEND(type,cur));
+
+ // add the blend operation
+ switch ((*it).blendType)
+ {
+ case LWO::Texture::Normal:
+ case LWO::Texture::Multiply:
+ temp = (unsigned int)aiTextureOp_Multiply;
+ break;
+
+ case LWO::Texture::Subtractive:
+ case LWO::Texture::Difference:
+ temp = (unsigned int)aiTextureOp_Subtract;
+ break;
+
+ case LWO::Texture::Divide:
+ temp = (unsigned int)aiTextureOp_Divide;
+ break;
+
+ case LWO::Texture::Additive:
+ temp = (unsigned int)aiTextureOp_Add;
+ break;
+
+ default:
+ temp = (unsigned int)aiTextureOp_Multiply;
+ DefaultLogger::get()->warn("LWO2: Unsupported texture blend mode: alpha or displacement");
+
+ }
+ // Setup texture operation
+ pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_TEXOP(type,cur));
+
+ // setup the mapping mode
+ pcMat->AddProperty<int>((int*)&mapping,1,AI_MATKEY_MAPPING(type,cur));
+
+ // add the u-wrapping
+ temp = (unsigned int)GetMapMode((*it).wrapModeWidth);
+ pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_MAPPINGMODE_U(type,cur));
+
+ // add the v-wrapping
+ temp = (unsigned int)GetMapMode((*it).wrapModeHeight);
+ pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_MAPPINGMODE_V(type,cur));
+
+ ++cur;
+ }
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::ConvertMaterial(const LWO::Surface& surf,MaterialHelper* pcMat)
+{
+ // copy the name of the surface
+ aiString st;
+ st.Set(surf.mName);
+ pcMat->AddProperty(&st,AI_MATKEY_NAME);
+
+ const int i = surf.bDoubleSided ? 1 : 0;
+ pcMat->AddProperty(&i,1,AI_MATKEY_TWOSIDED);
+
+ // add the refraction index and the bump intensity
+ pcMat->AddProperty(&surf.mIOR,1,AI_MATKEY_REFRACTI);
+ pcMat->AddProperty(&surf.mBumpIntensity,1,AI_MATKEY_BUMPSCALING);
+
+ aiShadingMode m;
+ if (surf.mSpecularValue && surf.mGlossiness)
+ {
+ float fGloss;
+ if (mIsLWO2) {
+ fGloss = pow( surf.mGlossiness*10.0f+2.0f, 2.0f);
+ }
+ else
+ {
+ if (16.0f >= surf.mGlossiness)
+ fGloss = 6.0f;
+ else if (64.0f >= surf.mGlossiness)
+ fGloss = 20.0f;
+ else if (256.0f >= surf.mGlossiness)
+ fGloss = 50.0f;
+ else fGloss = 80.0f;
+ }
+
+ pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
+ pcMat->AddProperty(&fGloss,1,AI_MATKEY_SHININESS);
+ m = aiShadingMode_Phong;
+ }
+ else m = aiShadingMode_Gouraud;
+
+ // specular color
+ aiColor3D clr = lerp( aiColor3D(1.f,1.f,1.f), surf.mColor, surf.mColorHighlights );
+ pcMat->AddProperty(&clr,1,AI_MATKEY_COLOR_SPECULAR);
+ pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
+
+ // emissive color
+ // luminosity is not really the same but it affects the surface in a similar way. Some scaling looks good.
+ clr.g = clr.b = clr.r = surf.mLuminosity*0.8f;
+ pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
+
+ // opacity ... either additive or default-blended, please
+ if (0.f != surf.mAdditiveTransparency) {
+
+ const int add = aiBlendMode_Additive;
+ pcMat->AddProperty(&surf.mAdditiveTransparency,1,AI_MATKEY_OPACITY);
+ pcMat->AddProperty(&add,1,AI_MATKEY_BLEND_FUNC);
+ }
+
+ else if (10e10f != surf.mTransparency) {
+ const int def = aiBlendMode_Default;
+ const float f = 1.0f-surf.mTransparency;
+ pcMat->AddProperty(&f,1,AI_MATKEY_OPACITY);
+ pcMat->AddProperty(&def,1,AI_MATKEY_BLEND_FUNC);
+ }
+
+
+ // ADD TEXTURES to the material
+ // TODO: find out how we can handle COLOR textures correctly...
+ bool b = HandleTextures(pcMat,surf.mColorTextures,aiTextureType_DIFFUSE);
+ b = (b || HandleTextures(pcMat,surf.mDiffuseTextures,aiTextureType_DIFFUSE));
+ HandleTextures(pcMat,surf.mSpecularTextures,aiTextureType_SPECULAR);
+ HandleTextures(pcMat,surf.mGlossinessTextures,aiTextureType_SHININESS);
+ HandleTextures(pcMat,surf.mBumpTextures,aiTextureType_HEIGHT);
+ HandleTextures(pcMat,surf.mOpacityTextures,aiTextureType_OPACITY);
+ HandleTextures(pcMat,surf.mReflectionTextures,aiTextureType_REFLECTION);
+
+ // Now we need to know which shader to use .. iterate through the shader list of
+ // the surface and search for a name which we know ...
+ for (ShaderList::const_iterator it = surf.mShaders.begin(), end = surf.mShaders.end();it != end;++it) {
+ //if (!(*it).enabled)continue;
+
+ if ((*it).functionName == "LW_SuperCelShader" || (*it).functionName == "AH_CelShader") {
+ DefaultLogger::get()->info("LWO2: Mapping LW_SuperCelShader/AH_CelShader to aiShadingMode_Toon");
+
+ m = aiShadingMode_Toon;
+ break;
+ }
+ else if ((*it).functionName == "LW_RealFresnel" || (*it).functionName == "LW_FastFresnel") {
+ DefaultLogger::get()->info("LWO2: Mapping LW_RealFresnel/LW_FastFresnel to aiShadingMode_Fresnel");
+
+ m = aiShadingMode_Fresnel;
+ break;
+ }
+ else
+ {
+ DefaultLogger::get()->warn("LWO2: Unknown surface shader: " + (*it).functionName);
+ }
+ }
+ if (surf.mMaximumSmoothAngle <= 0.0f)
+ m = aiShadingMode_Flat;
+ pcMat->AddProperty((int*)&m,1,AI_MATKEY_SHADING_MODEL);
+
+ // (the diffuse value is just a scaling factor)
+ // If a diffuse texture is set, we set this value to 1.0
+ clr = (b && false ? aiColor3D(1.f,1.f,1.f) : surf.mColor);
+ clr.r *= surf.mDiffuseValue;
+ clr.g *= surf.mDiffuseValue;
+ clr.b *= surf.mDiffuseValue;
+ pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+}
+
+// ------------------------------------------------------------------------------------------------
+char LWOImporter::FindUVChannels(LWO::TextureList& list,
+ LWO::Layer& /*layer*/,LWO::UVChannel& uv, unsigned int next)
+{
+ char ret = 0;
+ for (TextureList::iterator it = list.begin(), end = list.end();it != end;++it) {
+
+ // Ignore textures with non-UV mappings for the moment.
+ if (!(*it).enabled || !(*it).bCanUse || (*it).mapMode != LWO::Texture::UV) {
+ continue;
+ }
+
+ if ((*it).mUVChannelIndex == uv.name) {
+ ret = 1;
+
+ // got it.
+ if ((*it).mRealUVIndex == 0xffffffff || (*it).mRealUVIndex == next)
+ {
+ (*it).mRealUVIndex = next;
+ }
+ else {
+ // channel mismatch. need to duplicate the material.
+ DefaultLogger::get()->warn("LWO: Channel mismatch, would need to duplicate surface [design bug]");
+
+ // TODO
+ }
+ }
+ }
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::FindUVChannels(LWO::Surface& surf,
+ LWO::SortedRep& sorted,LWO::Layer& layer,
+ unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS])
+{
+ unsigned int next = 0, extra = 0, num_extra = 0;
+
+ // Check whether we have an UV entry != 0 for one of the faces in 'sorted'
+ for (unsigned int i = 0; i < layer.mUVChannels.size();++i) {
+ LWO::UVChannel& uv = layer.mUVChannels[i];
+
+ for (LWO::SortedRep::const_iterator it = sorted.begin(); it != sorted.end(); ++it) {
+
+ LWO::Face& face = layer.mFaces[*it];
+
+ for (unsigned int n = 0; n < face.mNumIndices; ++n) {
+ unsigned int idx = face.mIndices[n];
+
+ if (uv.abAssigned[idx] && ((aiVector2D*)&uv.rawData[0])[idx] != aiVector2D()) {
+
+ if (next >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
+
+ DefaultLogger::get()->error("LWO: Maximum number of UV channels for "
+ "this mesh reached. Skipping channel \'" + uv.name + "\'");
+
+ }
+ else {
+ // Search through all textures assigned to 'surf' and look for this UV channel
+ char had = 0;
+ had |= FindUVChannels(surf.mColorTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mDiffuseTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mSpecularTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mGlossinessTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mOpacityTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mBumpTextures,layer,uv,next);
+ had |= FindUVChannels(surf.mReflectionTextures,layer,uv,next);
+
+ if (had != 0) {
+
+ // We have a texture referencing this UV channel so we have to take special care of it
+ if (num_extra) {
+
+ for (unsigned int a = next; a < std::min( extra, AI_MAX_NUMBER_OF_TEXTURECOORDS-1u ); ++a) {
+ out[a+1] = out[a];
+ }
+ }
+ ++extra;
+ out[next++] = i;
+ }
+ else {
+
+ // Bh ... seems not to be used at all. Push to end if enough space is available.
+ out[extra++] = i;
+ ++num_extra;
+ }
+ }
+ it = sorted.end()-1;
+ break;
+ }
+ }
+ }
+ }
+ if (next != AI_MAX_NUMBER_OF_TEXTURECOORDS) {
+ out[extra] = 0xffffffff;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::FindVCChannels(const LWO::Surface& surf, LWO::SortedRep& sorted, const LWO::Layer& layer,
+ unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS])
+{
+ unsigned int next = 0;
+
+ // Check whether we have an vc entry != 0 for one of the faces in 'sorted'
+ for (unsigned int i = 0; i < layer.mVColorChannels.size();++i) {
+ const LWO::VColorChannel& vc = layer.mVColorChannels[i];
+
+ if (surf.mVCMap == vc.name) {
+ // The vertex color map is explicitely requested by the surface so we need to take special care of it
+ for (unsigned int a = 0; a < std::min(next,AI_MAX_NUMBER_OF_COLOR_SETS-1u); ++a) {
+ out[a+1] = out[a];
+ }
+ out[0] = i;
+ ++next;
+ }
+ else {
+
+ for (LWO::SortedRep::iterator it = sorted.begin(); it != sorted.end(); ++it) {
+ const LWO::Face& face = layer.mFaces[*it];
+
+ for (unsigned int n = 0; n < face.mNumIndices; ++n) {
+ unsigned int idx = face.mIndices[n];
+
+ if (vc.abAssigned[idx] && ((aiColor4D*)&vc.rawData[0])[idx] != aiColor4D(0.f,0.f,0.f,1.f)) {
+ if (next >= AI_MAX_NUMBER_OF_COLOR_SETS) {
+
+ DefaultLogger::get()->error("LWO: Maximum number of vertex color channels for "
+ "this mesh reached. Skipping channel \'" + vc.name + "\'");
+
+ }
+ else {
+ out[next++] = i;
+ }
+ it = sorted.end()-1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (next != AI_MAX_NUMBER_OF_COLOR_SETS) {
+ out[next] = 0xffffffff;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2ImageMap(unsigned int size, LWO::Texture& tex )
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + size;
+ while (true)
+ {
+ if (mFileBuffer + 6 >= end)break;
+ LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ throw DeadlyImportError("LWO2: Invalid SURF.BLOCK chunk length");
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ case AI_LWO_PROJ:
+ tex.mapMode = (Texture::MappingMode)GetU2();
+ break;
+ case AI_LWO_WRAP:
+ tex.wrapModeWidth = (Texture::Wrap)GetU2();
+ tex.wrapModeHeight = (Texture::Wrap)GetU2();
+ break;
+ case AI_LWO_AXIS:
+ tex.majorAxis = (Texture::Axes)GetU2();
+ break;
+ case AI_LWO_IMAG:
+ tex.mClipIdx = GetU2();
+ break;
+ case AI_LWO_VMAP:
+ GetS0(tex.mUVChannelIndex,head->length);
+ break;
+ case AI_LWO_WRPH:
+ tex.wrapAmountH = GetF4();
+ break;
+ case AI_LWO_WRPW:
+ tex.wrapAmountW = GetF4();
+ break;
+ }
+ mFileBuffer = next;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2Procedural(unsigned int /*size*/, LWO::Texture& tex )
+{
+ // --- not supported at the moment
+ DefaultLogger::get()->error("LWO2: Found procedural texture, this is not supported");
+ tex.bCanUse = false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2Gradient(unsigned int /*size*/, LWO::Texture& tex )
+{
+ // --- not supported at the moment
+ DefaultLogger::get()->error("LWO2: Found gradient texture, this is not supported");
+ tex.bCanUse = false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2TextureHeader(unsigned int size, LWO::Texture& tex )
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + size;
+
+ // get the ordinal string
+ GetS0( tex.ordinal, size);
+
+ // we could crash later if this is an empty string ...
+ if (!tex.ordinal.length())
+ {
+ DefaultLogger::get()->error("LWO2: Ill-formed SURF.BLOK ordinal string");
+ tex.ordinal = "\x00";
+ }
+ while (true)
+ {
+ if (mFileBuffer + 6 >= end)break;
+ LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ throw DeadlyImportError("LWO2: Invalid texture header chunk length");
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ case AI_LWO_CHAN:
+ tex.type = GetU4();
+ break;
+ case AI_LWO_ENAB:
+ tex.enabled = GetU2() ? true : false;
+ break;
+ case AI_LWO_OPAC:
+ tex.blendType = (Texture::BlendType)GetU2();
+ tex.mStrength = GetF4();
+ break;
+ }
+ mFileBuffer = next;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader* head, unsigned int size )
+{
+ ai_assert(!mSurfaces->empty());
+ LWO::Surface& surf = mSurfaces->back();
+ LWO::Texture tex;
+
+ // load the texture header
+ LoadLWO2TextureHeader(head->length,tex);
+ size -= head->length + 6;
+
+ // now get the exact type of the texture
+ switch (head->type)
+ {
+ case AI_LWO_PROC:
+ LoadLWO2Procedural(size,tex);
+ break;
+ case AI_LWO_GRAD:
+ LoadLWO2Gradient(size,tex);
+ break;
+ case AI_LWO_IMAP:
+ LoadLWO2ImageMap(size,tex);
+ }
+
+ // get the destination channel
+ TextureList* listRef = NULL;
+ switch (tex.type)
+ {
+ case AI_LWO_COLR:
+ listRef = &surf.mColorTextures;break;
+ case AI_LWO_DIFF:
+ listRef = &surf.mDiffuseTextures;break;
+ case AI_LWO_SPEC:
+ listRef = &surf.mSpecularTextures;break;
+ case AI_LWO_GLOS:
+ listRef = &surf.mGlossinessTextures;break;
+ case AI_LWO_BUMP:
+ listRef = &surf.mBumpTextures;break;
+ case AI_LWO_TRAN:
+ listRef = &surf.mOpacityTextures;break;
+ case AI_LWO_REFL:
+ listRef = &surf.mReflectionTextures;break;
+ default:
+ DefaultLogger::get()->warn("LWO2: Encountered unknown texture type");
+ return;
+ }
+
+ // now attach the texture to the parent surface - sort by ordinal string
+ for (TextureList::iterator it = listRef->begin();it != listRef->end(); ++it) {
+ if (::strcmp(tex.ordinal.c_str(),(*it).ordinal.c_str()) < 0) {
+ listRef->insert(it,tex);
+ return;
+ }
+ }
+ listRef->push_back(tex);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader* /*head*/, unsigned int size )
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + size;
+
+ ai_assert(!mSurfaces->empty());
+ LWO::Surface& surf = mSurfaces->back();
+ LWO::Shader shader;
+
+ // get the ordinal string
+ GetS0( shader.ordinal, size);
+
+ // we could crash later if this is an empty string ...
+ if (!shader.ordinal.length())
+ {
+ DefaultLogger::get()->error("LWO2: Ill-formed SURF.BLOK ordinal string");
+ shader.ordinal = "\x00";
+ }
+
+ // read the header
+ while (true)
+ {
+ if (mFileBuffer + 6 >= end)break;
+ LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ throw DeadlyImportError("LWO2: Invalid shader header chunk length");
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ case AI_LWO_ENAB:
+ shader.enabled = GetU2() ? true : false;
+ break;
+
+ case AI_LWO_FUNC:
+ GetS0( shader.functionName, head->length );
+ }
+ mFileBuffer = next;
+ }
+
+ // now attach the shader to the parent surface - sort by ordinal string
+ for (ShaderList::iterator it = surf.mShaders.begin();it != surf.mShaders.end(); ++it) {
+ if (::strcmp(shader.ordinal.c_str(),(*it).ordinal.c_str()) < 0) {
+ surf.mShaders.insert(it,shader);
+ return;
+ }
+ }
+ surf.mShaders.push_back(shader);
+}
+
+// ------------------------------------------------------------------------------------------------
+void LWOImporter::LoadLWO2Surface(unsigned int size)
+{
+ LE_NCONST uint8_t* const end = mFileBuffer + size;
+
+ mSurfaces->push_back( LWO::Surface () );
+ LWO::Surface& surf = mSurfaces->back();
+
+ GetS0(surf.mName,size);
+
+ // check whether this surface was derived from any other surface
+ std::string derived;
+ GetS0(derived,(unsigned int)(end - mFileBuffer));
+ if (derived.length()) {
+ // yes, find this surface
+ for (SurfaceList::iterator it = mSurfaces->begin(), end = mSurfaces->end()-1; it != end; ++it) {
+ if ((*it).mName == derived) {
+ // we have it ...
+ surf = *it;
+ derived.clear();break;
+ }
+ }
+ if (derived.size())
+ DefaultLogger::get()->warn("LWO2: Unable to find source surface: " + derived);
+ }
+
+ while (true)
+ {
+ if (mFileBuffer + 6 >= end)
+ break;
+ LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+
+ if (mFileBuffer + head->length > end)
+ throw DeadlyImportError("LWO2: Invalid surface chunk length");
+
+ uint8_t* const next = mFileBuffer+head->length;
+ switch (head->type)
+ {
+ // diffuse color
+ case AI_LWO_COLR:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,COLR,12);
+ surf.mColor.r = GetF4();
+ surf.mColor.g = GetF4();
+ surf.mColor.b = GetF4();
+ break;
+ }
+ // diffuse strength ... hopefully
+ case AI_LWO_DIFF:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,DIFF,4);
+ surf.mDiffuseValue = GetF4();
+ break;
+ }
+ // specular strength ... hopefully
+ case AI_LWO_SPEC:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPEC,4);
+ surf.mSpecularValue = GetF4();
+ break;
+ }
+ // transparency
+ case AI_LWO_TRAN:
+ {
+ // transparency explicitly disabled?
+ if (surf.mTransparency == 10e10f)
+ break;
+
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TRAN,4);
+ surf.mTransparency = GetF4();
+ break;
+ }
+ // additive transparency
+ case AI_LWO_ADTR:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ADTR,4);
+ surf.mAdditiveTransparency = GetF4();
+ break;
+ }
+ // wireframe mode
+ case AI_LWO_LINE:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LINE,2);
+ if (GetU2() & 0x1)
+ surf.mWireframe = true;
+ break;
+ }
+ // glossiness
+ case AI_LWO_GLOS:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,GLOS,4);
+ surf.mGlossiness = GetF4();
+ break;
+ }
+ // bump intensity
+ case AI_LWO_BUMP:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,BUMP,4);
+ surf.mBumpIntensity = GetF4();
+ break;
+ }
+ // color highlights
+ case AI_LWO_CLRH:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,CLRH,4);
+ surf.mColorHighlights = GetF4();
+ break;
+ }
+ // index of refraction
+ case AI_LWO_RIND:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,RIND,4);
+ surf.mIOR = GetF4();
+ break;
+ }
+ // polygon sidedness
+ case AI_LWO_SIDE:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SIDE,2);
+ surf.bDoubleSided = (3 == GetU2());
+ break;
+ }
+ // maximum smoothing angle
+ case AI_LWO_SMAN:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
+ surf.mMaximumSmoothAngle = fabs( GetF4() );
+ break;
+ }
+ // vertex color channel to be applied to the surface
+ case AI_LWO_VCOL:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,VCOL,12);
+ surf.mDiffuseValue *= GetF4(); // strength
+ ReadVSizedIntLWO2(mFileBuffer); // skip envelope
+ surf.mVCMapType = GetU4(); // type of the channel
+
+ // name of the channel
+ GetS0(surf.mVCMap, (unsigned int) (next - mFileBuffer ));
+ break;
+ }
+ // surface bock entry
+ case AI_LWO_BLOK:
+ {
+ AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,BLOK,4);
+ LE_NCONST IFF::SubChunkHeader* head2 = IFF::LoadSubChunk(mFileBuffer);
+
+ switch (head2->type)
+ {
+ case AI_LWO_PROC:
+ case AI_LWO_GRAD:
+ case AI_LWO_IMAP:
+ LoadLWO2TextureBlock(head2, head->length);
+ break;
+ case AI_LWO_SHDR:
+ LoadLWO2ShaderBlock(head2, head->length);
+ break;
+
+ default:
+ DefaultLogger::get()->warn("LWO2: Found an unsupported surface BLOK");
+ };
+
+ break;
+ }
+ }
+ mFileBuffer = next;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_X_IMPORTER
diff --git a/3rdparty/assimp/code/LWSLoader.cpp b/3rdparty/assimp/code/LWSLoader.cpp
new file mode 100644
index 000000000..d0034b8cd
--- /dev/null
+++ b/3rdparty/assimp/code/LWSLoader.cpp
@@ -0,0 +1,885 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 LWSLoader.cpp
+ * @brief Implementation of the LWS importer class
+ */
+
+#include "AssimpPCH.h"
+
+#include "LWSLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+#include "SceneCombiner.h"
+#include "GenericProperty.h"
+#include "SkeletonMeshBuilder.h"
+#include "ConvertToLHProcess.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Recursive parsing of LWS files
+void LWS::Element::Parse (const char*& buffer)
+{
+ for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) {
+
+ // begin of a new element with children
+ bool sub = false;
+ if (*buffer == '{') {
+ ++buffer;
+ SkipSpaces(&buffer);
+ sub = true;
+ }
+ else if (*buffer == '}')
+ return;
+
+ children.push_back(Element());
+
+ // copy data line - read token per token
+
+ const char* cur = buffer;
+ while (!IsSpaceOrNewLine(*buffer)) ++buffer;
+ children.back().tokens[0] = std::string(cur,(size_t) (buffer-cur));
+ SkipSpaces(&buffer);
+
+ if (children.back().tokens[0] == "Plugin")
+ {
+ DefaultLogger::get()->debug("LWS: Skipping over plugin-specific data");
+
+ // strange stuff inside Plugin/Endplugin blocks. Needn't
+ // follow LWS syntax, so we skip over it
+ for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) {
+ if (!::strncmp(buffer,"EndPlugin",9)) {
+ //SkipLine(&buffer);
+ break;
+ }
+ }
+ continue;
+ }
+
+ cur = buffer;
+ while (!IsLineEnd(*buffer)) ++buffer;
+ children.back().tokens[1] = std::string(cur,(size_t) (buffer-cur));
+
+ // parse more elements recursively
+ if (sub)
+ children.back().Parse(buffer);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+LWSImporter::LWSImporter()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+LWSImporter::~LWSImporter()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool LWSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler,bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "lws" || extension == "mot")
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens LWSC and LWMO
+ if (!extension.length() || checkSig) {
+ uint32_t tokens[2];
+ tokens[0] = AI_MAKE_MAGIC("LWSC");
+ tokens[1] = AI_MAKE_MAGIC("LWMO");
+ return CheckMagicToken(pIOHandler,pFile,tokens,2);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get list of file extensions
+void LWSImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("lws");
+ extensions.insert("mot");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void LWSImporter::SetupProperties(const Importer* pImp)
+{
+ // AI_CONFIG_FAVOUR_SPEED
+ configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
+
+ // AI_CONFIG_IMPORT_LWS_ANIM_START
+ first = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_START,
+ 150392 /* magic hack */);
+
+ // AI_CONFIG_IMPORT_LWS_ANIM_END
+ last = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_END,
+ 150392 /* magic hack */);
+
+ if (last < first) {
+ std::swap(last,first);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read an envelope description
+void LWSImporter::ReadEnvelope(const LWS::Element& dad, LWO::Envelope& fill )
+{
+ if (dad.children.empty()) {
+ DefaultLogger::get()->error("LWS: Envelope descriptions must not be empty");
+ return;
+ }
+
+ // reserve enough storage
+ std::list< LWS::Element >::const_iterator it = dad.children.begin();;
+ fill.keys.reserve(strtol10(it->tokens[1].c_str()));
+
+ for (++it; it != dad.children.end(); ++it) {
+ const char* c = (*it).tokens[1].c_str();
+
+ if ((*it).tokens[0] == "Key") {
+ fill.keys.push_back(LWO::Key());
+ LWO::Key& key = fill.keys.back();
+
+ float f;
+ SkipSpaces(&c);
+ c = fast_atof_move(c,key.value);
+ SkipSpaces(&c);
+ c = fast_atof_move(c,f);
+
+ key.time = f;
+
+ unsigned int span = strtol10(c,&c), num = 0;
+ switch (span) {
+
+ case 0:
+ key.inter = LWO::IT_TCB;
+ num = 5;
+ break;
+ case 1:
+ case 2:
+ key.inter = LWO::IT_HERM;
+ num = 5;
+ break;
+ case 3:
+ key.inter = LWO::IT_LINE;
+ num = 0;
+ break;
+ case 4:
+ key.inter = LWO::IT_STEP;
+ num = 0;
+ break;
+ case 5:
+ key.inter = LWO::IT_BEZ2;
+ num = 4;
+ break;
+ default:
+ DefaultLogger::get()->error("LWS: Unknown span type");
+ }
+ for (unsigned int i = 0; i < num;++i) {
+ SkipSpaces(&c);
+ c = fast_atof_move(c,key.params[i]);
+ }
+ }
+ else if ((*it).tokens[0] == "Behaviors") {
+ SkipSpaces(&c);
+ fill.pre = (LWO::PrePostBehaviour) strtol10(c,&c);
+ SkipSpaces(&c);
+ fill.post = (LWO::PrePostBehaviour) strtol10(c,&c);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read animation channels in the old LightWave animation format
+void LWSImporter::ReadEnvelope_Old(
+ std::list< LWS::Element >::const_iterator& it,
+ const std::list< LWS::Element >::const_iterator& end,
+ LWS::NodeDesc& nodes,
+ unsigned int /*version*/)
+{
+ unsigned int num,sub_num;
+ if (++it == end)goto unexpected_end;
+
+ num = strtol10((*it).tokens[0].c_str());
+ for (unsigned int i = 0; i < num; ++i) {
+
+ nodes.channels.push_back(LWO::Envelope());
+ LWO::Envelope& envl = nodes.channels.back();
+
+ envl.index = i;
+ envl.type = (LWO::EnvelopeType)(i+1);
+
+ if (++it == end)goto unexpected_end;
+ sub_num = strtol10((*it).tokens[0].c_str());
+
+ for (unsigned int n = 0; n < sub_num;++n) {
+
+ if (++it == end)goto unexpected_end;
+
+ // parse value and time, skip the rest for the moment.
+ LWO::Key key;
+ const char* c = fast_atof_move((*it).tokens[0].c_str(),key.value);
+ SkipSpaces(&c);
+ float f;
+ fast_atof_move((*it).tokens[0].c_str(),f);
+ key.time = f;
+
+ envl.keys.push_back(key);
+ }
+ }
+ return;
+
+unexpected_end:
+ DefaultLogger::get()->error("LWS: Encountered unexpected end of file while parsing object motion");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup a nice name for a node
+void LWSImporter::SetupNodeName(aiNode* nd, LWS::NodeDesc& src)
+{
+ const unsigned int combined = src.number | ((unsigned int)src.type) << 28u;
+
+ // the name depends on the type. We break LWS's strange naming convention
+ // and return human-readable, but still machine-parsable and unique, strings.
+ if (src.type == LWS::NodeDesc::OBJECT) {
+
+ if (src.path.length()) {
+ std::string::size_type s = src.path.find_last_of("\\/");
+ if (s == std::string::npos)
+ s = 0;
+ else ++s;
+
+ nd->mName.length = ::sprintf(nd->mName.data,"%s_(%08X)",src.path.substr(s).c_str(),combined);
+ return;
+ }
+ }
+ nd->mName.length = ::sprintf(nd->mName.data,"%s_(%08X)",src.name,combined);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively build the scenegraph
+void LWSImporter::BuildGraph(aiNode* nd, LWS::NodeDesc& src, std::vector<AttachmentInfo>& attach,
+ BatchLoader& batch,
+ aiCamera**& camOut,
+ aiLight**& lightOut,
+ std::vector<aiNodeAnim*>& animOut)
+{
+ // Setup a very cryptic name for the node, we want the user to be happy
+ SetupNodeName(nd,src);
+
+ // If this is an object from an external file - get the scene and setup proper attachment tags
+ aiScene* obj = NULL;
+ if (src.type == LWS::NodeDesc::OBJECT && src.path.length() ) {
+ obj = batch.GetImport(src.id);
+ if (!obj) {
+ DefaultLogger::get()->error("LWS: Failed to read external file " + src.path);
+ }
+ else {
+ attach.push_back(AttachmentInfo(obj,nd));
+ }
+ }
+
+ // If object is a light source - setup a corresponding ai structure
+ else if (src.type == LWS::NodeDesc::LIGHT) {
+ aiLight* lit = *lightOut++ = new aiLight();
+
+ // compute final light color
+ lit->mColorDiffuse = lit->mColorSpecular = src.lightColor*src.lightIntensity;
+
+ // name to attach light to node -> unique due to LWs indexing system
+ lit->mName = nd->mName;
+
+ // detemine light type and setup additional members
+ if (src.lightType == 2) { /* spot light */
+
+ lit->mType = aiLightSource_SPOT;
+ lit->mAngleInnerCone = (float)AI_DEG_TO_RAD( src.lightConeAngle );
+ lit->mAngleOuterCone = lit->mAngleInnerCone+(float)AI_DEG_TO_RAD( src.lightEdgeAngle );
+
+ }
+ else if (src.lightType == 1) { /* directional light source */
+ lit->mType = aiLightSource_DIRECTIONAL;
+ }
+ else lit->mType = aiLightSource_POINT;
+
+ // fixme: no proper handling of light falloffs yet
+ if (src.lightFalloffType == 1)
+ lit->mAttenuationConstant = 1.f;
+ else if (src.lightFalloffType == 1)
+ lit->mAttenuationLinear = 1.f;
+ else
+ lit->mAttenuationQuadratic = 1.f;
+ }
+
+ // If object is a camera - setup a corresponding ai structure
+ else if (src.type == LWS::NodeDesc::CAMERA) {
+ aiCamera* cam = *camOut++ = new aiCamera();
+
+ // name to attach cam to node -> unique due to LWs indexing system
+ cam->mName = nd->mName;
+ }
+
+ // Get the node transformation from the LWO key
+ LWO::AnimResolver resolver(src.channels,fps);
+ resolver.ExtractBindPose(nd->mTransformation);
+
+ // .. and construct animation channels
+ aiNodeAnim* anim = NULL;
+
+ if (first != last) {
+ resolver.SetAnimationRange(first,last);
+ resolver.ExtractAnimChannel(&anim,AI_LWO_ANIM_FLAG_SAMPLE_ANIMS|AI_LWO_ANIM_FLAG_START_AT_ZERO);
+ if (anim) {
+ anim->mNodeName = nd->mName;
+ animOut.push_back(anim);
+ }
+ }
+
+ // process pivot point, if any
+ if (src.pivotPos != aiVector3D()) {
+ aiMatrix4x4 tmp;
+ aiMatrix4x4::Translation(-src.pivotPos,tmp);
+
+ if (anim) {
+
+ // We have an animation channel for this node. Problem: to combine the pivot
+ // point with the node anims, we'd need to interpolate *all* keys, get
+ // transformation matrices from them, apply the translation and decompose
+ // the resulting matrices again in order to reconstruct the keys. This
+ // solution here is *much* easier ... we're just inserting an extra node
+ // in the hierarchy.
+ // Maybe the final optimization here will be done during postprocessing.
+
+ aiNode* pivot = new aiNode();
+ pivot->mName.length = sprintf( pivot->mName.data, "$Pivot_%s",nd->mName.data);
+ pivot->mTransformation = tmp;
+
+ pivot->mChildren = new aiNode*[pivot->mNumChildren = 1];
+ pivot->mChildren[0] = nd;
+
+ pivot->mParent = nd->mParent;
+ nd->mParent = pivot;
+
+ // swap children and hope the parents wont see a huge difference
+ pivot->mParent->mChildren[pivot->mParent->mNumChildren-1] = pivot;
+ }
+ else {
+ nd->mTransformation = tmp*nd->mTransformation;
+ }
+ }
+
+ // Add children
+ if (src.children.size()) {
+ nd->mChildren = new aiNode*[src.children.size()];
+ for (std::list<LWS::NodeDesc*>::iterator it = src.children.begin(); it != src.children.end(); ++it) {
+ aiNode* ndd = nd->mChildren[nd->mNumChildren++] = new aiNode();
+ ndd->mParent = nd;
+
+ BuildGraph(ndd,**it,attach,batch,camOut,lightOut,animOut);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Determine the exact location of a LWO file
+std::string LWSImporter::FindLWOFile(const std::string& in)
+{
+ // insert missing directory seperator if necessary
+ std::string tmp;
+ if (in.length() > 3 && in[1] == ':'&& in[2] != '\\' && in[2] != '/')
+ {
+ tmp = in[0] + ":\\" + in.substr(2);
+ }
+ else tmp = in;
+
+ if (io->Exists(tmp))
+ return in;
+
+ // file is not accessible for us ... maybe it's packed by
+ // LightWave's 'Package Scene' command?
+
+ // Relevant for us are the following two directories:
+ // <folder>\Objects\<hh>\<*>.lwo
+ // <folder>\Scenes\<hh>\<*>.lws
+ // where <hh> is optional.
+
+ std::string test = ".." + io->getOsSeparator() + tmp;
+ if (io->Exists(test))
+ return test;
+
+ test = ".." + io->getOsSeparator() + test;
+ if (io->Exists(test))
+ return test;
+
+ // return original path, maybe the IOsystem knows better
+ return tmp;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read file into given scene data structure
+void LWSImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler)
+{
+ io = pIOHandler;
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open LWS file " + pFile + ".");
+ }
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector< char > mBuffer;
+ TextFileToBuffer(file.get(),mBuffer);
+
+ // Parse the file structure
+ LWS::Element root; const char* dummy = &mBuffer[0];
+ root.Parse(dummy);
+
+ // Construct a Batchimporter to read more files recursively
+ BatchLoader batch(pIOHandler);
+// batch.SetBasePath(pFile);
+
+ // Construct an array to receive the flat output graph
+ std::list<LWS::NodeDesc> nodes;
+
+ unsigned int cur_light = 0, cur_camera = 0, cur_object = 0;
+ unsigned int num_light = 0, num_camera = 0, num_object = 0;
+
+ // check magic identifier, 'LWSC'
+ bool motion_file = false;
+ std::list< LWS::Element >::const_iterator it = root.children.begin();
+
+ if ((*it).tokens[0] == "LWMO")
+ motion_file = true;
+
+ if ((*it).tokens[0] != "LWSC" && !motion_file)
+ throw DeadlyImportError("LWS: Not a LightWave scene, magic tag LWSC not found");
+
+ // get file format version and print to log
+ ++it;
+ unsigned int version = strtol10((*it).tokens[0].c_str());
+ DefaultLogger::get()->info("LWS file format version is " + (*it).tokens[0]);
+ first = 0.;
+ last = 60.;
+ fps = 25.; /* seems to be a good default frame rate */
+
+ // Now read all elements in a very straghtforward manner
+ for (; it != root.children.end(); ++it) {
+ const char* c = (*it).tokens[1].c_str();
+
+ // 'FirstFrame': begin of animation slice
+ if ((*it).tokens[0] == "FirstFrame") {
+ if (150392. != first /* see SetupProperties() */)
+ first = strtol10(c,&c)-1.; /* we're zero-based */
+ }
+
+ // 'LastFrame': end of animation slice
+ else if ((*it).tokens[0] == "LastFrame") {
+ if (150392. != last /* see SetupProperties() */)
+ last = strtol10(c,&c)-1.; /* we're zero-based */
+ }
+
+ // 'FramesPerSecond': frames per second
+ else if ((*it).tokens[0] == "FramesPerSecond") {
+ fps = strtol10(c,&c);
+ }
+
+ // 'LoadObjectLayer': load a layer of a specific LWO file
+ else if ((*it).tokens[0] == "LoadObjectLayer") {
+
+ // get layer index
+ const int layer = strtol10(c,&c);
+
+ // setup the layer to be loaded
+ BatchLoader::PropertyMap props;
+ SetGenericProperty(props.ints,AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,layer);
+
+ // add node to list
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::OBJECT;
+ if (version >= 4) { // handle LWSC 4 explicit ID
+ SkipSpaces(&c);
+ d.number = strtol16(c,&c) & AI_LWS_MASK;
+ }
+ else d.number = cur_object++;
+
+ // and add the file to the import list
+ SkipSpaces(&c);
+ std::string path = FindLWOFile( c );
+ d.path = path;
+ d.id = batch.AddLoadRequest(path,0,&props);
+
+ nodes.push_back(d);
+ num_object++;
+ }
+ // 'LoadObject': load a LWO file into the scenegraph
+ else if ((*it).tokens[0] == "LoadObject") {
+
+ // add node to list
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::OBJECT;
+
+ if (version >= 4) { // handle LWSC 4 explicit ID
+ d.number = strtol16(c,&c) & AI_LWS_MASK;
+ SkipSpaces(&c);
+ }
+ else d.number = cur_object++;
+ std::string path = FindLWOFile( c );
+ d.id = batch.AddLoadRequest(path,0,NULL);
+
+ d.path = path;
+ nodes.push_back(d);
+ num_object++;
+ }
+ // 'AddNullObject': add a dummy node to the hierarchy
+ else if ((*it).tokens[0] == "AddNullObject") {
+
+ // add node to list
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::OBJECT;
+ d.name = c;
+ if (version >= 4) { // handle LWSC 4 explicit ID
+ d.number = strtol16(c,&c) & AI_LWS_MASK;
+ }
+ else d.number = cur_object++;
+ nodes.push_back(d);
+
+ num_object++;
+ }
+ // 'NumChannels': Number of envelope channels assigned to last layer
+ else if ((*it).tokens[0] == "NumChannels") {
+ // ignore for now
+ }
+ // 'Channel': preceedes any envelope description
+ else if ((*it).tokens[0] == "Channel") {
+ if (nodes.empty()) {
+ if (motion_file) {
+
+ // LightWave motion file. Add dummy node
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::OBJECT;
+ d.name = c;
+ d.number = cur_object++;
+ nodes.push_back(d);
+ }
+ else DefaultLogger::get()->error("LWS: Unexpected keyword: \'Channel\'");
+ }
+
+ // important: index of channel
+ nodes.back().channels.push_back(LWO::Envelope());
+ LWO::Envelope& env = nodes.back().channels.back();
+
+ env.index = strtol10(c);
+
+ // currently we can just interpret the standard channels 0...9
+ // (hack) assume that index-i yields the binary channel type from LWO
+ env.type = (LWO::EnvelopeType)(env.index+1);
+
+ }
+ // 'Envelope': a single animation channel
+ else if ((*it).tokens[0] == "Envelope") {
+ if (nodes.empty() || nodes.back().channels.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'Envelope\'");
+ else {
+ ReadEnvelope((*it),nodes.back().channels.back());
+ }
+ }
+ // 'ObjectMotion': animation information for older lightwave formats
+ else if (version < 3 && ((*it).tokens[0] == "ObjectMotion" ||
+ (*it).tokens[0] == "CameraMotion" ||
+ (*it).tokens[0] == "LightMotion")) {
+
+ if (nodes.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'<Light|Object|Camera>Motion\'");
+ else {
+ ReadEnvelope_Old(it,root.children.end(),nodes.back(),version);
+ }
+ }
+ // 'Pre/PostBehavior': pre/post animation behaviour for LWSC 2
+ else if (version == 2 && (*it).tokens[0] == "Pre/PostBehavior") {
+ if (nodes.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'Pre/PostBehavior'");
+ else {
+ for (std::list<LWO::Envelope>::iterator it = nodes.back().channels.begin(); it != nodes.back().channels.end(); ++it) {
+ // two ints per envelope
+ LWO::Envelope& env = *it;
+ env.pre = (LWO::PrePostBehaviour) strtol10(c,&c); SkipSpaces(&c);
+ env.post = (LWO::PrePostBehaviour) strtol10(c,&c); SkipSpaces(&c);
+ }
+ }
+ }
+ // 'ParentItem': specifies the parent of the current element
+ else if ((*it).tokens[0] == "ParentItem") {
+ if (nodes.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentItem\'");
+
+ else nodes.back().parent = strtol16(c,&c);
+ }
+ // 'ParentObject': deprecated one for older formats
+ else if (version < 3 && (*it).tokens[0] == "ParentObject") {
+ if (nodes.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentObject\'");
+
+ else {
+ nodes.back().parent = strtol10(c,&c) | (1u << 28u);
+ }
+ }
+ // 'AddCamera': add a camera to the scenegraph
+ else if ((*it).tokens[0] == "AddCamera") {
+
+ // add node to list
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::CAMERA;
+
+ if (version >= 4) { // handle LWSC 4 explicit ID
+ d.number = strtol16(c,&c) & AI_LWS_MASK;
+ }
+ else d.number = cur_camera++;
+ nodes.push_back(d);
+
+ num_camera++;
+ }
+ // 'CameraName': set name of currently active camera
+ else if ((*it).tokens[0] == "CameraName") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::CAMERA)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'CameraName\'");
+
+ else nodes.back().name = c;
+ }
+ // 'AddLight': add a light to the scenegraph
+ else if ((*it).tokens[0] == "AddLight") {
+
+ // add node to list
+ LWS::NodeDesc d;
+ d.type = LWS::NodeDesc::LIGHT;
+
+ if (version >= 4) { // handle LWSC 4 explicit ID
+ d.number = strtol16(c,&c) & AI_LWS_MASK;
+ }
+ else d.number = cur_light++;
+ nodes.push_back(d);
+
+ num_light++;
+ }
+ // 'LightName': set name of currently active light
+ else if ((*it).tokens[0] == "LightName") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightName\'");
+
+ else nodes.back().name = c;
+ }
+ // 'LightIntensity': set intensity of currently active light
+ else if ((*it).tokens[0] == "LightIntensity" || (*it).tokens[0] == "LgtIntensity" ) {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightIntensity\'");
+
+ else fast_atof_move(c, nodes.back().lightIntensity );
+
+ }
+ // 'LightType': set type of currently active light
+ else if ((*it).tokens[0] == "LightType") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightType\'");
+
+ else nodes.back().lightType = strtol10(c);
+
+ }
+ // 'LightFalloffType': set falloff type of currently active light
+ else if ((*it).tokens[0] == "LightFalloffType") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightFalloffType\'");
+
+ else nodes.back().lightFalloffType = strtol10(c);
+
+ }
+ // 'LightConeAngle': set cone angle of currently active light
+ else if ((*it).tokens[0] == "LightConeAngle") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightConeAngle\'");
+
+ else nodes.back().lightConeAngle = fast_atof(c);
+
+ }
+ // 'LightEdgeAngle': set area where we're smoothing from min to max intensity
+ else if ((*it).tokens[0] == "LightEdgeAngle") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightEdgeAngle\'");
+
+ else nodes.back().lightEdgeAngle = fast_atof(c);
+
+ }
+ // 'LightColor': set color of currently active light
+ else if ((*it).tokens[0] == "LightColor") {
+ if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightColor\'");
+
+ else {
+ c = fast_atof_move(c, (float&) nodes.back().lightColor.r );
+ SkipSpaces(&c);
+ c = fast_atof_move(c, (float&) nodes.back().lightColor.g );
+ SkipSpaces(&c);
+ c = fast_atof_move(c, (float&) nodes.back().lightColor.b );
+ }
+ }
+
+ // 'PivotPosition': position of local transformation origin
+ else if ((*it).tokens[0] == "PivotPosition" || (*it).tokens[0] == "PivotPoint") {
+ if (nodes.empty())
+ DefaultLogger::get()->error("LWS: Unexpected keyword: \'PivotPosition\'");
+ else {
+ c = fast_atof_move(c, (float&) nodes.back().pivotPos.x );
+ SkipSpaces(&c);
+ c = fast_atof_move(c, (float&) nodes.back().pivotPos.y );
+ SkipSpaces(&c);
+ c = fast_atof_move(c, (float&) nodes.back().pivotPos.z );
+ }
+ }
+ }
+
+ // resolve parenting
+ for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
+
+ // check whether there is another node which calls us a parent
+ for (std::list<LWS::NodeDesc>::iterator dit = nodes.begin(); dit != nodes.end(); ++dit) {
+ if (dit != it && *it == (*dit).parent) {
+ if ((*dit).parent_resolved) {
+ // fixme: it's still possible to produce an overflow due to cross references ..
+ DefaultLogger::get()->error("LWS: Found cross reference in scenegraph");
+ continue;
+ }
+
+ (*it).children.push_back(&*dit);
+ (*dit).parent_resolved = &*it;
+ }
+ }
+ }
+
+ // find out how many nodes have no parent yet
+ unsigned int no_parent = 0;
+ for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
+ if (!(*it).parent_resolved)
+ ++ no_parent;
+ }
+ if (!no_parent)
+ throw DeadlyImportError("LWS: Unable to find scene root node");
+
+
+ // Load all subsequent files
+ batch.LoadAll();
+
+ // and build the final output graph by attaching the loaded external
+ // files to ourselves. first build a master graph
+ aiScene* master = new aiScene();
+ aiNode* nd = master->mRootNode = new aiNode();
+
+ // allocate storage for cameras&lights
+ if (num_camera) {
+ master->mCameras = new aiCamera*[master->mNumCameras = num_camera];
+ }
+ aiCamera** cams = master->mCameras;
+ if (num_light) {
+ master->mLights = new aiLight*[master->mNumLights = num_light];
+ }
+ aiLight** lights = master->mLights;
+
+ std::vector<AttachmentInfo> attach;
+ std::vector<aiNodeAnim*> anims;
+
+ nd->mName.Set("<LWSRoot>");
+ nd->mChildren = new aiNode*[no_parent];
+ for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
+ if (!(*it).parent_resolved) {
+ aiNode* ro = nd->mChildren[ nd->mNumChildren++ ] = new aiNode();
+ ro->mParent = nd;
+
+ // ... and build the scene graph. If we encounter object nodes,
+ // add then to our attachment table.
+ BuildGraph(ro,*it, attach, batch, cams, lights, anims);
+ }
+ }
+
+ // create a master animation channel for us
+ if (anims.size()) {
+ master->mAnimations = new aiAnimation*[master->mNumAnimations = 1];
+ aiAnimation* anim = master->mAnimations[0] = new aiAnimation();
+ anim->mName.Set("LWSMasterAnim");
+
+ // LWS uses seconds as time units, but we convert to frames
+ anim->mTicksPerSecond = fps;
+ anim->mDuration = last-(first-1); /* fixme ... zero or one-based?*/
+
+ anim->mChannels = new aiNodeAnim*[anim->mNumChannels = anims.size()];
+ std::copy(anims.begin(),anims.end(),anim->mChannels);
+ }
+
+ // convert the master scene to RH
+ MakeLeftHandedProcess monster_cheat;
+ monster_cheat.Execute(master);
+
+ // .. ccw
+ FlipWindingOrderProcess flipper;
+ flipper.Execute(pScene);
+
+ // OK ... finally build the output graph
+ SceneCombiner::MergeScenes(&pScene,master,attach,
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) : 0));
+
+ // Check flags
+ if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+
+ if (pScene->mNumAnimations) {
+ // construct skeleton mesh
+ SkeletonMeshBuilder builder(pScene);
+ }
+ }
+
+}
diff --git a/3rdparty/assimp/code/LWSLoader.h b/3rdparty/assimp/code/LWSLoader.h
new file mode 100644
index 000000000..40eb525a1
--- /dev/null
+++ b/3rdparty/assimp/code/LWSLoader.h
@@ -0,0 +1,242 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 LWSLoader.h
+ * @brief Declaration of the LightWave scene importer class.
+ */
+#ifndef AI_LWSLOADER_H_INCLUDED
+#define AI_LWSLOADER_H_INCLUDED
+
+#include "LWOFileData.h"
+#include "SceneCombiner.h"
+
+namespace Assimp {
+ namespace LWS {
+
+// ---------------------------------------------------------------------------
+/** Represents an element in a LWS file.
+ *
+ * This can either be a single data line - <name> <value> or a data
+ * group - { name <data_line0> ... n }
+ */
+class Element
+{
+public:
+ Element()
+ {}
+
+ // first: name, second: rest
+ std::string tokens[2];
+ std::list<Element> children;
+
+ //! Recursive parsing function
+ void Parse (const char*& buffer);
+};
+
+#define AI_LWS_MASK (0xffffffff >> 4u)
+
+// ---------------------------------------------------------------------------
+/** Represents a LWS scenegraph element
+ */
+struct NodeDesc
+{
+ NodeDesc()
+ : number (0)
+ , parent (0)
+ , name ("")
+ , lightColor (1.f,1.f,1.f)
+ , lightIntensity (1.f)
+ , lightType (0)
+ , lightFalloffType (0)
+ , lightConeAngle (45.f)
+ , parent_resolved (NULL)
+ {}
+
+ enum {
+
+ OBJECT = 1,
+ LIGHT = 2,
+ CAMERA = 3,
+ BONE = 4
+ } type; // type of node
+
+ // if object: path
+ std::string path;
+ unsigned int id;
+
+ // number of object
+ unsigned int number;
+
+ // index of parent index
+ unsigned int parent;
+
+ // lights & cameras & dummies: name
+ const char* name;
+
+ // animation channels
+ std::list< LWO::Envelope > channels;
+
+ // position of pivot point
+ aiVector3D pivotPos;
+
+
+
+ // color of light source
+ aiColor3D lightColor;
+
+ // intensity of light source
+ float lightIntensity;
+
+ // type of light source
+ unsigned int lightType;
+
+ // falloff type of light source
+ unsigned int lightFalloffType;
+
+ // cone angle of (spot) light source
+ float lightConeAngle;
+
+ // soft cone angle of (spot) light source
+ float lightEdgeAngle;
+
+
+
+ // list of resolved children
+ std::list< NodeDesc* > children;
+
+ // resolved parent node
+ NodeDesc* parent_resolved;
+
+
+ // for std::find()
+ bool operator == (unsigned int num) const {
+ if (!num)
+ return false;
+ unsigned int _type = num >> 28u;
+
+ return _type == static_cast<unsigned int>(type) && (num & AI_LWS_MASK) == number;
+ }
+};
+
+} // end namespace LWS
+
+// ---------------------------------------------------------------------------
+/** LWS (LightWave Scene Format) importer class.
+ *
+ * This class does heavily depend on the LWO importer class. LWS files
+ * contain mainly descriptions how LWO objects are composed together
+ * in a scene.
+*/
+class LWSImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ LWSImporter();
+
+ /** Destructor, private as well */
+ ~LWSImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ // Check whether we can read a specific file
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ // Get list of supported extensions
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ // Import file into given scene data structure
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ // Setup import properties
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+
+ // -------------------------------------------------------------------
+ // Read an envelope description
+ void ReadEnvelope(const LWS::Element& dad, LWO::Envelope& out );
+
+ // -------------------------------------------------------------------
+ // Read an envelope description for the older LW file format
+ void ReadEnvelope_Old(std::list< LWS::Element >::const_iterator& it,
+ const std::list< LWS::Element >::const_iterator& end,
+ LWS::NodeDesc& nodes,
+ unsigned int version);
+
+ // -------------------------------------------------------------------
+ // Setup a nice name for a node
+ void SetupNodeName(aiNode* nd, LWS::NodeDesc& src);
+
+ // -------------------------------------------------------------------
+ // Recursively build the scenegraph
+ void BuildGraph(aiNode* nd,
+ LWS::NodeDesc& src,
+ std::vector<AttachmentInfo>& attach,
+ BatchLoader& batch,
+ aiCamera**& camOut,
+ aiLight**& lightOut,
+ std::vector<aiNodeAnim*>& animOut);
+
+ // -------------------------------------------------------------------
+ // Try several dirs until we find the right location of a LWS file.
+ std::string FindLWOFile(const std::string& in);
+
+private:
+
+ bool configSpeedFlag;
+ IOSystem* io;
+
+ double first,last,fps;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_LWSIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/LimitBoneWeightsProcess.cpp b/3rdparty/assimp/code/LimitBoneWeightsProcess.cpp
new file mode 100644
index 000000000..e919567de
--- /dev/null
+++ b/3rdparty/assimp/code/LimitBoneWeightsProcess.cpp
@@ -0,0 +1,204 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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.
+
+----------------------------------------------------------------------
+*/
+
+/** Implementation of the LimitBoneWeightsProcess post processing step */
+
+#include "AssimpPCH.h"
+#include "LimitBoneWeightsProcess.h"
+
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+LimitBoneWeightsProcess::LimitBoneWeightsProcess()
+{
+ mMaxWeights = AI_LMW_MAX_WEIGHTS;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+LimitBoneWeightsProcess::~LimitBoneWeightsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool LimitBoneWeightsProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_LimitBoneWeights) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void LimitBoneWeightsProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("LimitBoneWeightsProcess begin");
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ ProcessMesh( pScene->mMeshes[a]);
+
+ DefaultLogger::get()->debug("LimitBoneWeightsProcess end");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp)
+{
+ // get the current value of the property
+ this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unites identical vertices in the given mesh
+void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
+{
+ if ( !pMesh->HasBones())
+ return;
+
+ // collect all bone weights per vertex
+ typedef std::vector< std::vector< Weight > > WeightsPerVertex;
+ WeightsPerVertex vertexWeights( pMesh->mNumVertices);
+
+ // collect all weights per vertex
+ for ( unsigned int a = 0; a < pMesh->mNumBones; a++)
+ {
+ const aiBone* bone = pMesh->mBones[a];
+ for ( unsigned int b = 0; b < bone->mNumWeights; b++)
+ {
+ const aiVertexWeight& w = bone->mWeights[b];
+ vertexWeights[w.mVertexId].push_back( Weight( a, w.mWeight));
+ }
+ }
+
+ unsigned int removed = 0, old_bones = pMesh->mNumBones;
+
+ // now cut the weight count if it exceeds the maximum
+ bool bChanged = false;
+ for ( WeightsPerVertex::iterator vit = vertexWeights.begin(); vit != vertexWeights.end(); ++vit)
+ {
+ if ( vit->size() <= mMaxWeights)
+ continue;
+
+ bChanged = true;
+
+ // more than the defined maximum -> first sort by weight in descending order. That's
+ // why we defined the < operator in such a weird way.
+ std::sort( vit->begin(), vit->end());
+
+ // now kill everything beyond the maximum count
+ unsigned int m = vit->size();
+ vit->erase( vit->begin() + mMaxWeights, vit->end());
+ removed += m-vit->size();
+
+ // and renormalize the weights
+ float sum = 0.0f;
+ for ( std::vector<Weight>::const_iterator it = vit->begin(); it != vit->end(); ++it)
+ sum += it->mWeight;
+ for ( std::vector<Weight>::iterator it = vit->begin(); it != vit->end(); ++it)
+ it->mWeight /= sum;
+ }
+
+ if (bChanged) {
+ // rebuild the vertex weight array for all bones
+ typedef std::vector< std::vector< aiVertexWeight > > WeightsPerBone;
+ WeightsPerBone boneWeights( pMesh->mNumBones);
+ for ( unsigned int a = 0; a < vertexWeights.size(); a++)
+ {
+ const std::vector<Weight>& vw = vertexWeights[a];
+ for ( std::vector<Weight>::const_iterator it = vw.begin(); it != vw.end(); ++it)
+ boneWeights[it->mBone].push_back( aiVertexWeight( a, it->mWeight));
+ }
+
+ // and finally copy the vertex weight list over to the mesh's bones
+ std::vector<bool> abNoNeed(pMesh->mNumBones,false);
+ bChanged = false;
+
+ for ( unsigned int a = 0; a < pMesh->mNumBones; a++)
+ {
+ const std::vector<aiVertexWeight>& bw = boneWeights[a];
+ aiBone* bone = pMesh->mBones[a];
+
+ // ignore the bone if no vertex weights were removed there
+
+ // FIX (Aramis, 07|22|08)
+ // NO! we can't ignore it in this case ... it is possible that
+ // the number of weights did not change, but the weight values did.
+
+ // if ( bw.size() == bone->mNumWeights)
+ // continue;
+
+ // FIX (Aramis, 07|21|08)
+ // It is possible that all weights of a bone have been removed.
+ // This would naturally cause an exception in &bw[0].
+ if ( bw.empty() )
+ {
+ abNoNeed[a] = bChanged = true;
+ continue;
+ }
+
+ // copy the weight list. should always be less weights than before, so we don't need a new allocation
+ ai_assert( bw.size() <= bone->mNumWeights);
+ bone->mNumWeights = (unsigned int) bw.size();
+ ::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight));
+ }
+
+ if (bChanged) {
+ // the number of new bones is smaller than before, so we can reuse the old array
+ aiBone** ppcCur = pMesh->mBones;aiBone** ppcSrc = ppcCur;
+
+ for (std::vector<bool>::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) {
+ if (*iter) {
+ delete *ppcSrc;
+ --pMesh->mNumBones;
+ }
+ else *ppcCur++ = *ppcSrc;
+ ++ppcSrc;
+ }
+ }
+
+ if (!DefaultLogger::isNullLogger()) {
+ char buffer[1024];
+ ::sprintf(buffer,"Removed %i weights. Input bones: %i. Output bones: %i",removed,old_bones,pMesh->mNumBones);
+ DefaultLogger::get()->info(buffer);
+ }
+ }
+}
diff --git a/3rdparty/assimp/code/LimitBoneWeightsProcess.h b/3rdparty/assimp/code/LimitBoneWeightsProcess.h
new file mode 100644
index 000000000..91007d3e2
--- /dev/null
+++ b/3rdparty/assimp/code/LimitBoneWeightsProcess.h
@@ -0,0 +1,146 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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.
+
+----------------------------------------------------------------------
+*/
+
+/** Defines a post processing step to limit the number of bones affecting a single vertex. */
+#ifndef AI_LIMITBONEWEIGHTSPROCESS_H_INC
+#define AI_LIMITBONEWEIGHTSPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+class LimitBoneWeightsTest;
+
+namespace Assimp
+{
+
+// NOTE: If you change these limits, don't forget to change the
+// corresponding values in all Assimp ports
+
+// **********************************************************
+// Java: ConfigProperty.java,
+// ConfigProperty.DEFAULT_BONE_WEIGHT_LIMIT
+// **********************************************************
+
+#if (!defined AI_LMW_MAX_WEIGHTS)
+# define AI_LMW_MAX_WEIGHTS 0x4
+#endif // !! AI_LMW_MAX_WEIGHTS
+
+// ---------------------------------------------------------------------------
+/** This post processing step limits the number of bones affecting a vertex
+* to a certain maximum value. If a vertex is affected by more than that number
+* of bones, the bone weight with the least influence on this vertex are removed.
+* The other weights on this bone are then renormalized to assure the sum weight
+* to be 1.
+*/
+class ASSIMP_API LimitBoneWeightsProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::LimitBoneWeightsTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ LimitBoneWeightsProcess();
+
+ /** Destructor, private as well */
+ ~LimitBoneWeightsProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag.
+ * @param pFlags The processing flags the importer was called with.
+ * A bitwise combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields,
+ * false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ExecuteOnScene().
+ * The function is a request to the process to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Limits the bone weight count for all vertices in the given mesh.
+ * @param pMesh The mesh to process.
+ */
+ void ProcessMesh( aiMesh* pMesh);
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Describes a bone weight on a vertex */
+ struct Weight
+ {
+ unsigned int mBone; ///< Index of the bone
+ float mWeight; ///< Weight of that bone on this vertex
+ Weight() { }
+ Weight( unsigned int pBone, float pWeight)
+ {
+ mBone = pBone;
+ mWeight = pWeight;
+ }
+
+ /** Comparision operator to sort bone weights by descending weight */
+ bool operator < (const Weight& pWeight) const
+ {
+ return mWeight > pWeight.mWeight;
+ }
+ };
+
+public:
+ /** Maximum number of bones influencing any single vertex. */
+ unsigned int mMaxWeights;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_LIMITBONEWEIGHTSPROCESS_H_INC
diff --git a/3rdparty/assimp/code/LineSplitter.h b/3rdparty/assimp/code/LineSplitter.h
new file mode 100644
index 000000000..47f69d827
--- /dev/null
+++ b/3rdparty/assimp/code/LineSplitter.h
@@ -0,0 +1,217 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 LineSplitter.h
+ * @brief LineSplitter, a helper class to iterate through all lines
+ * of a file easily. Works with StreamReader.
+ */
+#ifndef INCLUDED_LINE_SPLITTER_H
+#define INCLUDED_LINE_SPLITTER_H
+
+#include <stdexcept>
+
+#include "StreamReader.h"
+#include "ParsingUtils.h"
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------------------
+/** Usage:
+@code
+for (LineSplitter splitter(stream);splitter;++splitter) {
+
+ if (*splitter == "hi!") {
+ ...
+ }
+ else if (splitter->substr(0,5) == "hello") {
+ ...
+ // access the third token in the line (tokens are space-separated)
+ if (strtol(splitter[2]) > 5) { .. }
+ }
+
+ std::cout << "Current line is: " << splitter.get_index() << std::endl;
+}
+@endcode */
+// ------------------------------------------------------------------------------------------------
+class LineSplitter
+{
+public:
+
+ typedef size_t line_idx;
+
+public:
+
+ // -----------------------------------------
+ /** construct from existing stream reader */
+ LineSplitter(StreamReaderLE& stream)
+ : stream(stream)
+ , swallow()
+ {
+ cur.reserve(1024);
+ operator++();
+
+ idx = 0;
+ }
+
+public:
+
+ // -----------------------------------------
+ /** pseudo-iterator increment */
+ LineSplitter& operator++() {
+ if (swallow) {
+ swallow = false;
+ return *this;
+ }
+
+ if (!*this) {
+ throw std::logic_error("End of file, no more lines to be retrieved.");
+ }
+
+ char s;
+
+ cur.clear(); // I will kill you if you deallocate.
+ while (stream.GetRemainingSize() && (s = stream.GetI1(),1)) {
+ if (s == '\n' || s == '\r') {
+ while (stream.GetRemainingSize() && ((s = stream.GetI1()) == ' ' || s == '\r' || s == '\n')) {};
+ if (stream.GetRemainingSize()) {
+ stream.IncPtr(-1);
+ }
+ break;
+ }
+ cur += s;
+ }
+
+ ++idx;
+ return *this;
+ }
+
+ // -----------------------------------------
+ /** get a pointer to the beginning of a particular token */
+ const char* operator[] (size_t idx) const {
+ const char* s = operator->()->c_str();
+
+ SkipSpaces(&s);
+ for (size_t i = 0; i < idx; ++i) {
+
+ for (;!IsSpace(*s); ++s) {
+ if (IsLineEnd(*s)) {
+ throw std::range_error("Token index out of range, EOL reached");
+ }
+ }
+ SkipSpaces(&s);
+ }
+ return s;
+ }
+
+ // -----------------------------------------
+ /** extract the start positions of N tokens from the current line*/
+ template <size_t N>
+ void get_tokens(const char* (&tokens)[N]) const {
+ const char* s = operator->()->c_str();
+
+ SkipSpaces(&s);
+ for (size_t i = 0; i < N; ++i) {
+ if (IsLineEnd(*s)) {
+ throw std::range_error("Token count out of range, EOL reached");
+ }
+ tokens[i] = s;
+
+ for (;*s && !IsSpace(*s); ++s) {};
+ SkipSpaces(&s);
+ }
+ }
+
+ // -----------------------------------------
+ /** member access */
+ const std::string* operator -> () const {
+ return &cur;
+ }
+
+ const std::string& operator* () const {
+ return cur;
+ }
+
+ // -----------------------------------------
+ /** boolean context */
+ operator bool() const {
+ return stream.GetRemainingSize()>0;
+ }
+
+ // -----------------------------------------
+ /** line indices are zero-based, empty lines are included */
+ operator line_idx() const {
+ return idx;
+ }
+
+ line_idx get_index() const {
+ return idx;
+ }
+
+ // -----------------------------------------
+ /** access the underlying stream object */
+ StreamReaderLE& get_stream() {
+ return stream;
+ }
+
+ // -----------------------------------------
+ /** !strcmp((*this)->substr(0,strlen(check)),check) */
+ bool match_start(const char* check) {
+ const size_t len = strlen(check);
+
+ return len <= cur.length() && std::equal(check,check+len,cur.begin());
+ }
+
+
+ // -----------------------------------------
+ /** swallow the next call to ++, return the previous value. */
+ void swallow_next_increment() {
+ swallow = true;
+ }
+
+private:
+
+ line_idx idx;
+ std::string cur;
+ StreamReaderLE& stream;
+ bool swallow;
+};
+
+}
+#endif // INCLUDED_LINE_SPLITTER_H
diff --git a/3rdparty/assimp/code/MD2FileData.h b/3rdparty/assimp/code/MD2FileData.h
new file mode 100644
index 000000000..661e176ce
--- /dev/null
+++ b/3rdparty/assimp/code/MD2FileData.h
@@ -0,0 +1,163 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MD2FileData.h
+ * @brief Defines helper data structures for importing MD2 files
+ * http://linux.ucla.edu/~phaethon/q3/formats/md2-schoenblum.html
+ */
+#ifndef AI_MD2FILEHELPER_H_INC
+#define AI_MD2FILEHELPER_H_INC
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+#include "./../include/Compiler/pushpack1.h"
+
+namespace Assimp {
+namespace MD2 {
+
+// to make it easier for us, we test the magic word against both "endianesses"
+#define AI_MD2_MAGIC_NUMBER_BE AI_MAKE_MAGIC("IDP2")
+#define AI_MD2_MAGIC_NUMBER_LE AI_MAKE_MAGIC("2PDI")
+
+// common limitations
+#define AI_MD2_VERSION 15
+#define AI_MD2_MAXQPATH 64
+#define AI_MD2_MAX_FRAMES 512
+#define AI_MD2_MAX_SKINS 32
+#define AI_MD2_MAX_VERTS 2048
+#define AI_MD2_MAX_TRIANGLES 4096
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for the MD2 main header
+ */
+struct Header
+{
+ uint32_t magic;
+ uint32_t version;
+ uint32_t skinWidth;
+ uint32_t skinHeight;
+ uint32_t frameSize;
+ uint32_t numSkins;
+ uint32_t numVertices;
+ uint32_t numTexCoords;
+ uint32_t numTriangles;
+ uint32_t numGlCommands;
+ uint32_t numFrames;
+ uint32_t offsetSkins;
+ uint32_t offsetTexCoords;
+ uint32_t offsetTriangles;
+ uint32_t offsetFrames;
+ uint32_t offsetGlCommands;
+ uint32_t offsetEnd;
+
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 OpenGl draw command
+ */
+struct GLCommand
+{
+ float s, t;
+ uint32_t vertexIndex;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 triangle
+ */
+struct Triangle
+{
+ uint16_t vertexIndices[3];
+ uint16_t textureIndices[3];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 vertex
+ */
+struct Vertex
+{
+ uint8_t vertex[3];
+ uint8_t lightNormalIndex;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 frame
+ */
+struct Frame
+{
+ float scale[3];
+ float translate[3];
+ char name[16];
+ Vertex vertices[1];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 texture coordinate
+ */
+struct TexCoord
+{
+ uint16_t s;
+ uint16_t t;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD2 skin
+ */
+struct Skin
+{
+ char name[AI_MD2_MAXQPATH]; /* texture file name */
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+
+// ---------------------------------------------------------------------------
+//! Lookup a normal vector from Quake's normal lookup table
+//! \param index Input index (0-161)
+//! \param vOut Receives the output normal
+void LookupNormalIndex(uint8_t index,aiVector3D& vOut);
+
+
+}
+}
+
+#endif // !! include guard
+
diff --git a/3rdparty/assimp/code/MD2Loader.cpp b/3rdparty/assimp/code/MD2Loader.cpp
new file mode 100644
index 000000000..cb85c9da3
--- /dev/null
+++ b/3rdparty/assimp/code/MD2Loader.cpp
@@ -0,0 +1,414 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER
+
+/** @file Implementation of the MD2 importer class */
+#include "MD2Loader.h"
+#include "MaterialSystem.h"
+#include "ByteSwap.h"
+#include "MD2NormalTable.h" // shouldn't be included by other units
+
+using namespace Assimp;
+using namespace Assimp::MD2;
+
+
+// helper macro to determine the size of an array
+#if (!defined ARRAYSIZE)
+# define ARRAYSIZE(_array) (int(sizeof(_array) / sizeof(_array[0])))
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Helper function to lookup a normal in Quake 2's precalculated table
+void MD2::LookupNormalIndex(uint8_t iNormalIndex,aiVector3D& vOut)
+{
+ // make sure the normal index has a valid value
+ if (iNormalIndex >= ARRAYSIZE(g_avNormals)) {
+ DefaultLogger::get()->warn("Index overflow in Quake II normal vector list");
+ iNormalIndex = ARRAYSIZE(g_avNormals) - 1;
+ }
+ vOut = *((const aiVector3D*)(&g_avNormals[iNormalIndex]));
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MD2Importer::MD2Importer()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MD2Importer::~MD2Importer()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "md2")
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens
+ if (!extension.length() || checkSig) {
+ uint32_t tokens[1];
+ tokens[0] = AI_MD2_MAGIC_NUMBER_LE;
+ return CheckMagicToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all extensions supported by this loader
+void MD2Importer::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("md2");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void MD2Importer::SetupProperties(const Importer* pImp)
+{
+ // The
+ // AI_CONFIG_IMPORT_MD2_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD2_KEYFRAME,0xffffffff);
+ if (0xffffffff == configFrameID){
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+}
+// ------------------------------------------------------------------------------------------------
+// Validate the file header
+void MD2Importer::ValidateHeader( )
+{
+ // check magic number
+ if (m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE &&
+ m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE)
+ {
+ char szBuffer[5];
+ szBuffer[0] = ((char*)&m_pcHeader->magic)[0];
+ szBuffer[1] = ((char*)&m_pcHeader->magic)[1];
+ szBuffer[2] = ((char*)&m_pcHeader->magic)[2];
+ szBuffer[3] = ((char*)&m_pcHeader->magic)[3];
+ szBuffer[4] = '\0';
+
+ throw DeadlyImportError("Invalid MD2 magic word: should be IDP2, the "
+ "magic word found is " + std::string(szBuffer));
+ }
+
+ // check file format version
+ if (m_pcHeader->version != 8)
+ DefaultLogger::get()->warn( "Unsupported md2 file version. Continuing happily ...");
+
+ // check some values whether they are valid
+ if (0 == m_pcHeader->numFrames)
+ throw DeadlyImportError( "Invalid md2 file: NUM_FRAMES is 0");
+
+ if (m_pcHeader->offsetEnd > (uint32_t)fileSize)
+ throw DeadlyImportError( "Invalid md2 file: File is too small");
+
+ if (m_pcHeader->offsetSkins + m_pcHeader->numSkins * sizeof (MD2::Skin) >= fileSize ||
+ m_pcHeader->offsetTexCoords + m_pcHeader->numTexCoords * sizeof (MD2::TexCoord) >= fileSize ||
+ m_pcHeader->offsetTriangles + m_pcHeader->numTriangles * sizeof (MD2::Triangle) >= fileSize ||
+ m_pcHeader->offsetFrames + m_pcHeader->numFrames * sizeof (MD2::Frame) >= fileSize ||
+ m_pcHeader->offsetEnd > fileSize)
+ {
+ throw DeadlyImportError("Invalid MD2 header: some offsets are outside the file");
+ }
+
+ if (m_pcHeader->numSkins > AI_MD2_MAX_SKINS)
+ DefaultLogger::get()->warn("The model contains more skins than Quake 2 supports");
+ if ( m_pcHeader->numFrames > AI_MD2_MAX_FRAMES)
+ DefaultLogger::get()->warn("The model contains more frames than Quake 2 supports");
+ if (m_pcHeader->numVertices > AI_MD2_MAX_VERTS)
+ DefaultLogger::get()->warn("The model contains more vertices than Quake 2 supports");
+
+ if (m_pcHeader->numFrames <= configFrameID )
+ throw DeadlyImportError("The requested frame is not existing the file");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MD2Importer::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open MD2 file " + pFile + "");
+
+ // check whether the md3 file is large enough to contain
+ // at least the file header
+ fileSize = (unsigned int)file->FileSize();
+ if ( fileSize < sizeof(MD2::Header))
+ throw DeadlyImportError( "MD2 File is too small");
+
+ std::vector<uint8_t> mBuffer2(fileSize);
+ file->Read(&mBuffer2[0], 1, fileSize);
+ mBuffer = &mBuffer2[0];
+
+
+ m_pcHeader = (BE_NCONST MD2::Header*)mBuffer;
+
+#ifdef AI_BUILD_BIG_ENDIAN
+
+ ByteSwap::Swap4(&m_pcHeader->frameSize);
+ ByteSwap::Swap4(&m_pcHeader->magic);
+ ByteSwap::Swap4(&m_pcHeader->numFrames);
+ ByteSwap::Swap4(&m_pcHeader->numGlCommands);
+ ByteSwap::Swap4(&m_pcHeader->numSkins);
+ ByteSwap::Swap4(&m_pcHeader->numTexCoords);
+ ByteSwap::Swap4(&m_pcHeader->numTriangles);
+ ByteSwap::Swap4(&m_pcHeader->numVertices);
+ ByteSwap::Swap4(&m_pcHeader->offsetEnd);
+ ByteSwap::Swap4(&m_pcHeader->offsetFrames);
+ ByteSwap::Swap4(&m_pcHeader->offsetGlCommands);
+ ByteSwap::Swap4(&m_pcHeader->offsetSkins);
+ ByteSwap::Swap4(&m_pcHeader->offsetTexCoords);
+ ByteSwap::Swap4(&m_pcHeader->offsetTriangles);
+ ByteSwap::Swap4(&m_pcHeader->skinHeight);
+ ByteSwap::Swap4(&m_pcHeader->skinWidth);
+ ByteSwap::Swap4(&m_pcHeader->version);
+
+#endif
+
+ ValidateHeader();
+
+ // there won't be more than one mesh inside the file
+ pScene->mNumMaterials = 1;
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = new MaterialHelper();
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+
+ aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // navigate to the begin of the frame data
+ BE_NCONST MD2::Frame* pcFrame = (BE_NCONST MD2::Frame*) ((uint8_t*)
+ m_pcHeader + m_pcHeader->offsetFrames);
+
+ pcFrame += configFrameID;
+
+ // navigate to the begin of the triangle data
+ MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*)
+ m_pcHeader + m_pcHeader->offsetTriangles);
+
+ // navigate to the begin of the tex coords data
+ BE_NCONST MD2::TexCoord* pcTexCoords = (BE_NCONST MD2::TexCoord*) ((uint8_t*)
+ m_pcHeader + m_pcHeader->offsetTexCoords);
+
+ // navigate to the begin of the vertex data
+ BE_NCONST MD2::Vertex* pcVerts = (BE_NCONST MD2::Vertex*) (pcFrame->vertices);
+
+#ifdef AI_BUILD_BIG_ENDIAN
+ for (uint32_t i = 0; i< m_pcHeader->numTriangles; ++i)
+ {
+ for (unsigned int p = 0; p < 3;++p)
+ {
+ ByteSwap::Swap2(& pcTriangles[i].textureIndices[p]);
+ ByteSwap::Swap2(& pcTriangles[i].vertexIndices[p]);
+ }
+ }
+ for (uint32_t i = 0; i < m_pcHeader->offsetTexCoords;++i)
+ {
+ ByteSwap::Swap2(& pcTexCoords[i].s);
+ ByteSwap::Swap2(& pcTexCoords[i].t);
+ }
+ ByteSwap::Swap4( & pcFrame->scale[0] );
+ ByteSwap::Swap4( & pcFrame->scale[1] );
+ ByteSwap::Swap4( & pcFrame->scale[2] );
+ ByteSwap::Swap4( & pcFrame->translate[0] );
+ ByteSwap::Swap4( & pcFrame->translate[1] );
+ ByteSwap::Swap4( & pcFrame->translate[2] );
+#endif
+
+ pcMesh->mNumFaces = m_pcHeader->numTriangles;
+ pcMesh->mFaces = new aiFace[m_pcHeader->numTriangles];
+
+ // allocate output storage
+ pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3;
+ pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+
+ // Not sure whether there are MD2 files without texture coordinates
+ // NOTE: texture coordinates can be there without a texture,
+ // but a texture can't be there without a valid UV channel
+ MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
+ const int iMode = (int)aiShadingMode_Gouraud;
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ if (m_pcHeader->numTexCoords && m_pcHeader->numSkins)
+ {
+ // navigate to the first texture associated with the mesh
+ const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)m_pcHeader +
+ m_pcHeader->offsetSkins);
+
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 1.0f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ if (pcSkins->name[0])
+ {
+ aiString szString;
+ const size_t iLen = ::strlen(pcSkins->name);
+ ::memcpy(szString.data,pcSkins->name,iLen);
+ szString.data[iLen] = '\0';
+ szString.length = iLen;
+
+ pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ else{
+ DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped.");
+ }
+ }
+ else {
+ // apply a default material
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.6f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ aiString szName;
+ szName.Set(AI_DEFAULT_TEXTURED_MATERIAL_NAME);
+ pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+
+ aiString sz;
+
+ // TODO: Try to guess the name of the texture file from the model file name
+
+ sz.Set("$texture_dummy.bmp");
+ pcHelper->AddProperty(&sz,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+
+
+ // now read all triangles of the first frame, apply scaling and translation
+ unsigned int iCurrent = 0;
+
+ float fDivisorU = 1.0f,fDivisorV = 1.0f;
+ if (m_pcHeader->numTexCoords) {
+ // allocate storage for texture coordinates, too
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNumUVComponents[0] = 2;
+
+ // check whether the skin width or height are zero (this would
+ // cause a division through zero)
+ if (!m_pcHeader->skinWidth) {
+ DefaultLogger::get()->error("MD2: No valid skin width given");
+ }
+ else fDivisorU = (float)m_pcHeader->skinWidth;
+ if (!m_pcHeader->skinHeight){
+ DefaultLogger::get()->error("MD2: No valid skin height given");
+ }
+ else fDivisorV = (float)m_pcHeader->skinHeight;
+ }
+
+ for (unsigned int i = 0; i < (unsigned int)m_pcHeader->numTriangles;++i) {
+ // Allocate the face
+ pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3];
+ pScene->mMeshes[0]->mFaces[i].mNumIndices = 3;
+
+ // copy texture coordinates
+ // check whether they are different from the previous value at this index.
+ // In this case, create a full separate set of vertices/normals/texcoords
+ for (unsigned int c = 0; c < 3;++c,++iCurrent) {
+
+ // validate vertex indices
+ register unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
+ if (iIndex >= m_pcHeader->numVertices) {
+ DefaultLogger::get()->error("MD2: Vertex index is outside the allowed range");
+ iIndex = m_pcHeader->numVertices-1;
+ }
+
+ // read x,y, and z component of the vertex
+ aiVector3D& vec = pcMesh->mVertices[iCurrent];
+
+ vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
+ vec.x += pcFrame->translate[0];
+
+ vec.y = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1];
+ vec.y += pcFrame->translate[1];
+
+ vec.z = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2];
+ vec.z += pcFrame->translate[2];
+
+ // read the normal vector from the precalculated normal table
+ aiVector3D& vNormal = pcMesh->mNormals[iCurrent];
+ LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal);
+
+ // flip z and y to become right-handed
+ std::swap((float&)vNormal.z,(float&)vNormal.y);
+ std::swap((float&)vec.z,(float&)vec.y);
+
+ if (m_pcHeader->numTexCoords) {
+ // validate texture coordinates
+ iIndex = pcTriangles[i].textureIndices[c];
+ if (iIndex >= m_pcHeader->numTexCoords) {
+ DefaultLogger::get()->error("MD2: UV index is outside the allowed range");
+ iIndex = m_pcHeader->numTexCoords-1;
+ }
+
+ aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent];
+
+ // the texture coordinates are absolute values but we
+ // need relative values between 0 and 1
+ pcOut.x = pcTexCoords[iIndex].s / fDivisorU;
+ pcOut.y = 1.f-pcTexCoords[iIndex].t / fDivisorV;
+ }
+ pScene->mMeshes[0]->mFaces[i].mIndices[c] = iCurrent;
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_MD2_IMPORTER
diff --git a/3rdparty/assimp/code/MD2Loader.h b/3rdparty/assimp/code/MD2Loader.h
new file mode 100644
index 000000000..fe8755800
--- /dev/null
+++ b/3rdparty/assimp/code/MD2Loader.h
@@ -0,0 +1,126 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MD2Loader.h
+ * @brief Declaration of the .MD2 importer class.
+ */
+#ifndef AI_MD2LOADER_H_INCLUDED
+#define AI_MD2LOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+#include "MD2FileData.h"
+
+namespace Assimp {
+class MaterialHelper;
+
+using namespace MD2;
+
+// ---------------------------------------------------------------------------
+/** Importer class for MD2
+*/
+class MD2Importer : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ MD2Importer();
+
+ /** Destructor, private as well */
+ ~MD2Importer();
+
+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;
+
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+
+ // -------------------------------------------------------------------
+ /** Validate the header of the file
+ */
+ void ValidateHeader();
+
+protected:
+
+ /** Configuration option: frame to be loaded */
+ unsigned int configFrameID;
+
+ /** Header of the MD2 file */
+ BE_NCONST MD2::Header* m_pcHeader;
+
+ /** Buffer to hold the loaded file */
+ BE_NCONST uint8_t* mBuffer;
+
+ /** Size of the file, in bytes */
+ unsigned int fileSize;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/MD2NormalTable.h b/3rdparty/assimp/code/MD2NormalTable.h
new file mode 100644
index 000000000..9c795c2d6
--- /dev/null
+++ b/3rdparty/assimp/code/MD2NormalTable.h
@@ -0,0 +1,217 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Slightly modified version of the anorms.h header file
+ * released by ID software with the Quake 2 source code.
+ *
+ * Table of normals used by MD2 models
+ */
+
+#ifndef AI_MDL_NORMALTABLE_H_INC
+#define AI_MDL_NORMALTABLE_H_INC
+
+
+float g_avNormals[162][3] = {
+{ -0.525731f, 0.000000f, 0.850651f },
+{ -0.442863f, 0.238856f, 0.864188f },
+{ -0.295242f, 0.000000f, 0.955423f },
+{ -0.309017f, 0.500000f, 0.809017f },
+{ -0.162460f, 0.262866f, 0.951056f },
+{ 0.000000f, 0.000000f, 1.000000f },
+{ 0.000000f, 0.850651f, 0.525731f },
+{ -0.147621f, 0.716567f, 0.681718f },
+{ 0.147621f, 0.716567f, 0.681718f },
+{ 0.000000f, 0.525731f, 0.850651f },
+{ 0.309017f, 0.500000f, 0.809017f },
+{ 0.525731f, 0.000000f, 0.850651f },
+{ 0.295242f, 0.000000f, 0.955423f },
+{ 0.442863f, 0.238856f, 0.864188f },
+{ 0.162460f, 0.262866f, 0.951056f },
+{ -0.681718f, 0.147621f, 0.716567f },
+{ -0.809017f, 0.309017f, 0.500000f },
+{ -0.587785f, 0.425325f, 0.688191f },
+{ -0.850651f, 0.525731f, 0.000000f },
+{ -0.864188f, 0.442863f, 0.238856f },
+{ -0.716567f, 0.681718f, 0.147621f },
+{ -0.688191f, 0.587785f, 0.425325f },
+{ -0.500000f, 0.809017f, 0.309017f },
+{ -0.238856f, 0.864188f, 0.442863f },
+{ -0.425325f, 0.688191f, 0.587785f },
+{ -0.716567f, 0.681718f, -0.147621f },
+{ -0.500000f, 0.809017f, -0.309017f },
+{ -0.525731f, 0.850651f, 0.000000f },
+{ 0.000000f, 0.850651f, -0.525731f },
+{ -0.238856f, 0.864188f, -0.442863f },
+{ 0.000000f, 0.955423f, -0.295242f },
+{ -0.262866f, 0.951056f, -0.162460f },
+{ 0.000000f, 1.000000f, 0.000000f },
+{ 0.000000f, 0.955423f, 0.295242f },
+{ -0.262866f, 0.951056f, 0.162460f },
+{ 0.238856f, 0.864188f, 0.442863f },
+{ 0.262866f, 0.951056f, 0.162460f },
+{ 0.500000f, 0.809017f, 0.309017f },
+{ 0.238856f, 0.864188f, -0.442863f },
+{ 0.262866f, 0.951056f, -0.162460f },
+{ 0.500000f, 0.809017f, -0.309017f },
+{ 0.850651f, 0.525731f, 0.000000f },
+{ 0.716567f, 0.681718f, 0.147621f },
+{ 0.716567f, 0.681718f, -0.147621f },
+{ 0.525731f, 0.850651f, 0.000000f },
+{ 0.425325f, 0.688191f, 0.587785f },
+{ 0.864188f, 0.442863f, 0.238856f },
+{ 0.688191f, 0.587785f, 0.425325f },
+{ 0.809017f, 0.309017f, 0.500000f },
+{ 0.681718f, 0.147621f, 0.716567f },
+{ 0.587785f, 0.425325f, 0.688191f },
+{ 0.955423f, 0.295242f, 0.000000f },
+{ 1.000000f, 0.000000f, 0.000000f },
+{ 0.951056f, 0.162460f, 0.262866f },
+{ 0.850651f, -0.525731f, 0.000000f },
+{ 0.955423f, -0.295242f, 0.000000f },
+{ 0.864188f, -0.442863f, 0.238856f },
+{ 0.951056f, -0.162460f, 0.262866f },
+{ 0.809017f, -0.309017f, 0.500000f },
+{ 0.681718f, -0.147621f, 0.716567f },
+{ 0.850651f, 0.000000f, 0.525731f },
+{ 0.864188f, 0.442863f, -0.238856f },
+{ 0.809017f, 0.309017f, -0.500000f },
+{ 0.951056f, 0.162460f, -0.262866f },
+{ 0.525731f, 0.000000f, -0.850651f },
+{ 0.681718f, 0.147621f, -0.716567f },
+{ 0.681718f, -0.147621f, -0.716567f },
+{ 0.850651f, 0.000000f, -0.525731f },
+{ 0.809017f, -0.309017f, -0.500000f },
+{ 0.864188f, -0.442863f, -0.238856f },
+{ 0.951056f, -0.162460f, -0.262866f },
+{ 0.147621f, 0.716567f, -0.681718f },
+{ 0.309017f, 0.500000f, -0.809017f },
+{ 0.425325f, 0.688191f, -0.587785f },
+{ 0.442863f, 0.238856f, -0.864188f },
+{ 0.587785f, 0.425325f, -0.688191f },
+{ 0.688191f, 0.587785f, -0.425325f },
+{ -0.147621f, 0.716567f, -0.681718f },
+{ -0.309017f, 0.500000f, -0.809017f },
+{ 0.000000f, 0.525731f, -0.850651f },
+{ -0.525731f, 0.000000f, -0.850651f },
+{ -0.442863f, 0.238856f, -0.864188f },
+{ -0.295242f, 0.000000f, -0.955423f },
+{ -0.162460f, 0.262866f, -0.951056f },
+{ 0.000000f, 0.000000f, -1.000000f },
+{ 0.295242f, 0.000000f, -0.955423f },
+{ 0.162460f, 0.262866f, -0.951056f },
+{ -0.442863f, -0.238856f, -0.864188f },
+{ -0.309017f, -0.500000f, -0.809017f },
+{ -0.162460f, -0.262866f, -0.951056f },
+{ 0.000000f, -0.850651f, -0.525731f },
+{ -0.147621f, -0.716567f, -0.681718f },
+{ 0.147621f, -0.716567f, -0.681718f },
+{ 0.000000f, -0.525731f, -0.850651f },
+{ 0.309017f, -0.500000f, -0.809017f },
+{ 0.442863f, -0.238856f, -0.864188f },
+{ 0.162460f, -0.262866f, -0.951056f },
+{ 0.238856f, -0.864188f, -0.442863f },
+{ 0.500000f, -0.809017f, -0.309017f },
+{ 0.425325f, -0.688191f, -0.587785f },
+{ 0.716567f, -0.681718f, -0.147621f },
+{ 0.688191f, -0.587785f, -0.425325f },
+{ 0.587785f, -0.425325f, -0.688191f },
+{ 0.000000f, -0.955423f, -0.295242f },
+{ 0.000000f, -1.000000f, 0.000000f },
+{ 0.262866f, -0.951056f, -0.162460f },
+{ 0.000000f, -0.850651f, 0.525731f },
+{ 0.000000f, -0.955423f, 0.295242f },
+{ 0.238856f, -0.864188f, 0.442863f },
+{ 0.262866f, -0.951056f, 0.162460f },
+{ 0.500000f, -0.809017f, 0.309017f },
+{ 0.716567f, -0.681718f, 0.147621f },
+{ 0.525731f, -0.850651f, 0.000000f },
+{ -0.238856f, -0.864188f, -0.442863f },
+{ -0.500000f, -0.809017f, -0.309017f },
+{ -0.262866f, -0.951056f, -0.162460f },
+{ -0.850651f, -0.525731f, 0.000000f },
+{ -0.716567f, -0.681718f, -0.147621f },
+{ -0.716567f, -0.681718f, 0.147621f },
+{ -0.525731f, -0.850651f, 0.000000f },
+{ -0.500000f, -0.809017f, 0.309017f },
+{ -0.238856f, -0.864188f, 0.442863f },
+{ -0.262866f, -0.951056f, 0.162460f },
+{ -0.864188f, -0.442863f, 0.238856f },
+{ -0.809017f, -0.309017f, 0.500000f },
+{ -0.688191f, -0.587785f, 0.425325f },
+{ -0.681718f, -0.147621f, 0.716567f },
+{ -0.442863f, -0.238856f, 0.864188f },
+{ -0.587785f, -0.425325f, 0.688191f },
+{ -0.309017f, -0.500000f, 0.809017f },
+{ -0.147621f, -0.716567f, 0.681718f },
+{ -0.425325f, -0.688191f, 0.587785f },
+{ -0.162460f, -0.262866f, 0.951056f },
+{ 0.442863f, -0.238856f, 0.864188f },
+{ 0.162460f, -0.262866f, 0.951056f },
+{ 0.309017f, -0.500000f, 0.809017f },
+{ 0.147621f, -0.716567f, 0.681718f },
+{ 0.000000f, -0.525731f, 0.850651f },
+{ 0.425325f, -0.688191f, 0.587785f },
+{ 0.587785f, -0.425325f, 0.688191f },
+{ 0.688191f, -0.587785f, 0.425325f },
+{ -0.955423f, 0.295242f, 0.000000f },
+{ -0.951056f, 0.162460f, 0.262866f },
+{ -1.000000f, 0.000000f, 0.000000f },
+{ -0.850651f, 0.000000f, 0.525731f },
+{ -0.955423f, -0.295242f, 0.000000f },
+{ -0.951056f, -0.162460f, 0.262866f },
+{ -0.864188f, 0.442863f, -0.238856f },
+{ -0.951056f, 0.162460f, -0.262866f },
+{ -0.809017f, 0.309017f, -0.500000f },
+{ -0.864188f, -0.442863f, -0.238856f },
+{ -0.951056f, -0.162460f, -0.262866f },
+{ -0.809017f, -0.309017f, -0.500000f },
+{ -0.681718f, 0.147621f, -0.716567f },
+{ -0.681718f, -0.147621f, -0.716567f },
+{ -0.850651f, 0.000000f, -0.525731f },
+{ -0.688191f, 0.587785f, -0.425325f },
+{ -0.587785f, 0.425325f, -0.688191f },
+{ -0.425325f, 0.688191f, -0.587785f },
+{ -0.425325f, -0.688191f, -0.587785f },
+{ -0.587785f, -0.425325f, -0.688191f },
+{ -0.688191f, -0.587785f, -0.425325f }
+};
+
+#endif // !! include guard
diff --git a/3rdparty/assimp/code/MD3FileData.h b/3rdparty/assimp/code/MD3FileData.h
new file mode 100644
index 000000000..0c092bcb1
--- /dev/null
+++ b/3rdparty/assimp/code/MD3FileData.h
@@ -0,0 +1,315 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Md3FileData.h
+ *
+ * @brief Defines helper data structures for importing MD3 files.
+ * http://linux.ucla.edu/~phaethon/q3/formats/md3format.html
+ */
+#ifndef AI_MD3FILEHELPER_H_INC
+#define AI_MD3FILEHELPER_H_INC
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+#include "./../include/Compiler/pushpack1.h"
+
+namespace Assimp {
+namespace MD3 {
+
+// to make it easier for us, we test the magic word against both "endianesses"
+#define AI_MD3_MAGIC_NUMBER_BE AI_MAKE_MAGIC("IDP3")
+#define AI_MD3_MAGIC_NUMBER_LE AI_MAKE_MAGIC("3PDI")
+
+// common limitations
+#define AI_MD3_VERSION 15
+#define AI_MD3_MAXQPATH 64
+#define AI_MD3_MAXFRAME 16
+#define AI_MD3_MAX_FRAMES 1024
+#define AI_MD3_MAX_TAGS 16
+#define AI_MD3_MAX_SURFACES 32
+#define AI_MD3_MAX_SHADERS 256
+#define AI_MD3_MAX_VERTS 4096
+#define AI_MD3_MAX_TRIANGLES 8192
+
+// master scale factor for all vertices in a MD3 model
+#define AI_MD3_XYZ_SCALE (1.0f/64.0f)
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for the MD3 main header
+ */
+struct Header
+{
+ //! magic number
+ uint32_t IDENT;
+
+ //! file format version
+ uint32_t VERSION;
+
+ //! original name in .pak archive
+ char NAME[ AI_MD3_MAXQPATH ];
+
+ //! unknown
+ int32_t FLAGS;
+
+ //! number of frames in the file
+ uint32_t NUM_FRAMES;
+
+ //! number of tags in the file
+ uint32_t NUM_TAGS;
+
+ //! number of surfaces in the file
+ uint32_t NUM_SURFACES;
+
+ //! number of skins in the file
+ uint32_t NUM_SKINS;
+
+ //! offset of the first frame
+ uint32_t OFS_FRAMES;
+
+ //! offset of the first tag
+ uint32_t OFS_TAGS;
+
+ //! offset of the first surface
+ uint32_t OFS_SURFACES;
+
+ //! end of file
+ uint32_t OFS_EOF;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for the frame header
+ */
+struct Frame
+{
+ //! minimum bounds
+ aiVector3D min;
+
+ //! maximum bounds
+ aiVector3D max;
+
+ //! local origin for this frame
+ aiVector3D origin;
+
+ //! radius of bounding sphere
+ float radius;
+
+ //! name of frame
+ char name[ AI_MD3_MAXFRAME ];
+
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for the tag header
+ */
+struct Tag
+{
+ //! name of the tag
+ char NAME[ AI_MD3_MAXQPATH ];
+
+ //! Local tag origin and orientation
+ aiVector3D origin;
+ float orientation[3][3];
+
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for the surface header
+ */
+struct Surface
+{
+ //! magic number
+ int32_t IDENT;
+
+ //! original name of the surface
+ char NAME[ AI_MD3_MAXQPATH ];
+
+ //! unknown
+ int32_t FLAGS;
+
+ //! number of frames in the surface
+ uint32_t NUM_FRAMES;
+
+ //! number of shaders in the surface
+ uint32_t NUM_SHADER;
+
+ //! number of vertices in the surface
+ uint32_t NUM_VERTICES;
+
+ //! number of triangles in the surface
+ uint32_t NUM_TRIANGLES;
+
+
+ //! offset to the triangle data
+ uint32_t OFS_TRIANGLES;
+
+ //! offset to the shader data
+ uint32_t OFS_SHADERS;
+
+ //! offset to the texture coordinate data
+ uint32_t OFS_ST;
+
+ //! offset to the vertex/normal data
+ uint32_t OFS_XYZNORMAL;
+
+ //! offset to the end of the Surface object
+ int32_t OFS_END;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for a shader defined in there
+ */
+struct Shader
+{
+ //! filename of the shader
+ char NAME[ AI_MD3_MAXQPATH ];
+
+ //! index of the shader
+ uint32_t SHADER_INDEX;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for a triangle
+ */
+struct Triangle
+{
+ //! triangle indices
+ uint32_t INDEXES[3];
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for an UV coord
+ */
+struct TexCoord
+{
+ //! UV coordinates
+ float U,V;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------
+/** @brief Data structure for a vertex
+ */
+struct Vertex
+{
+ //! X/Y/Z coordinates
+ int16_t X,Y,Z;
+
+ //! encoded normal vector
+ uint16_t NORMAL;
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+// -------------------------------------------------------------------------------
+/** @brief Unpack a Q3 16 bit vector to its full float3 representation
+ *
+ * @param p_iNormal Input normal vector in latitude/longitude form
+ * @param p_afOut Pointer to an array of three floats to receive the result
+ *
+ * @note This has been taken from q3 source (misc_model.c)
+ */
+inline void LatLngNormalToVec3(uint16_t p_iNormal, float* p_afOut)
+{
+ float lat = (float)(( p_iNormal >> 8u ) & 0xff);
+ float lng = (float)(( p_iNormal & 0xff ));
+ lat *= 3.141926f/128.0f;
+ lng *= 3.141926f/128.0f;
+
+ p_afOut[0] = cosf(lat) * sinf(lng);
+ p_afOut[1] = sinf(lat) * sinf(lng);
+ p_afOut[2] = cosf(lng);
+ return;
+}
+
+
+// -------------------------------------------------------------------------------
+/** @brief Pack a Q3 normal into 16bit latitute/longitude representation
+ * @param p_vIn Input vector
+ * @param p_iOut Output normal
+ *
+ * @note This has been taken from q3 source (mathlib.c)
+ */
+inline void Vec3NormalToLatLng( const aiVector3D& p_vIn, uint16_t& p_iOut )
+{
+ // check for singularities
+ if ( 0.0f == p_vIn[0] && 0.0f == p_vIn[1] )
+ {
+ if ( p_vIn[2] > 0.0f )
+ {
+ ((unsigned char*)&p_iOut)[0] = 0;
+ ((unsigned char*)&p_iOut)[1] = 0; // lat = 0, long = 0
+ }
+ else
+ {
+ ((unsigned char*)&p_iOut)[0] = 128;
+ ((unsigned char*)&p_iOut)[1] = 0; // lat = 0, long = 128
+ }
+ }
+ else
+ {
+ int a, b;
+
+ a = int(57.2957795f * ( atan2f( p_vIn[1], p_vIn[0] ) ) * (255.0f / 360.0f ));
+ a &= 0xff;
+
+ b = int(57.2957795f * ( acosf( p_vIn[2] ) ) * ( 255.0f / 360.0f ));
+ b &= 0xff;
+
+ ((unsigned char*)&p_iOut)[0] = b; // longitude
+ ((unsigned char*)&p_iOut)[1] = a; // lattitude
+ }
+}
+
+}
+}
+
+#endif // !! AI_MD3FILEHELPER_H_INC
+
diff --git a/3rdparty/assimp/code/MD3Loader.cpp b/3rdparty/assimp/code/MD3Loader.cpp
new file mode 100644
index 000000000..0e37afc5d
--- /dev/null
+++ b/3rdparty/assimp/code/MD3Loader.cpp
@@ -0,0 +1,1043 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MD3Loader.cpp
+ * @brief Implementation of the MD3 importer class
+ *
+ * Sources:
+ * http://www.gamers.org/dEngine/quake3/UQ3S
+ * http://linux.ucla.edu/~phaethon/q3/formats/md3format.html
+ * http://www.heppler.com/shader/shader/
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
+
+#include "MD3Loader.h"
+#include "ByteSwap.h"
+#include "SceneCombiner.h"
+#include "GenericProperty.h"
+#include "RemoveComments.h"
+#include "ParsingUtils.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Convert a Q3 shader blend function to the appropriate enum value
+Q3Shader::BlendFunc StringToBlendFunc(const std::string& m)
+{
+ if (m == "GL_ONE") {
+ return Q3Shader::BLEND_GL_ONE;
+ }
+ if (m == "GL_ZERO") {
+ return Q3Shader::BLEND_GL_ZERO;
+ }
+ if (m == "GL_SRC_ALPHA") {
+ return Q3Shader::BLEND_GL_SRC_ALPHA;
+ }
+ if (m == "GL_ONE_MINUS_SRC_ALPHA") {
+ return Q3Shader::BLEND_GL_ONE_MINUS_SRC_ALPHA;
+ }
+ if (m == "GL_ONE_MINUS_DST_COLOR") {
+ return Q3Shader::BLEND_GL_ONE_MINUS_DST_COLOR;
+ }
+ DefaultLogger::get()->error("Q3Shader: Unknown blend function: " + m);
+ return Q3Shader::BLEND_NONE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load a Quake 3 shader
+bool Q3Shader::LoadShader(ShaderData& fill, const std::string& pFile,IOSystem* io)
+{
+ boost::scoped_ptr<IOStream> file( io->Open( pFile, "rt"));
+ if (!file.get())
+ return false; // if we can't access the file, don't worry and return
+
+ DefaultLogger::get()->info("Loading Quake3 shader file " + pFile);
+
+ // read file in memory
+ const size_t s = file->FileSize();
+ std::vector<char> _buff(s+1);
+ file->Read(&_buff[0],s,1);
+ _buff[s] = 0;
+
+ // remove comments from it (C++ style)
+ CommentRemover::RemoveLineComments("//",&_buff[0]);
+ const char* buff = &_buff[0];
+
+ Q3Shader::ShaderDataBlock* curData = NULL;
+ Q3Shader::ShaderMapBlock* curMap = NULL;
+
+ // read line per line
+ for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
+
+ if (*buff == '{') {
+ ++buff;
+
+ // append to last section, if any
+ if (!curData) {
+ DefaultLogger::get()->error("Q3Shader: Unexpected shader section token \'{\'");
+ return true; // still no failure, the file is there
+ }
+
+ // read this data section
+ for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
+ if (*buff == '{') {
+ ++buff;
+ // add new map section
+ curData->maps.push_back(Q3Shader::ShaderMapBlock());
+ curMap = &curData->maps.back();
+
+ for (;SkipSpacesAndLineEnd(&buff);SkipLine(&buff)) {
+ // 'map' - Specifies texture file name
+ if (TokenMatchI(buff,"map",3) || TokenMatchI(buff,"clampmap",8)) {
+ curMap->name = GetNextToken(buff);
+ }
+ // 'blendfunc' - Alpha blending mode
+ else if (TokenMatchI(buff,"blendfunc",9)) {
+ const std::string blend_src = GetNextToken(buff);
+ if (blend_src == "add") {
+ curMap->blend_src = Q3Shader::BLEND_GL_ONE;
+ curMap->blend_dest = Q3Shader::BLEND_GL_ONE;
+ }
+ else if (blend_src == "filter") {
+ curMap->blend_src = Q3Shader::BLEND_GL_DST_COLOR;
+ curMap->blend_dest = Q3Shader::BLEND_GL_ZERO;
+ }
+ else if (blend_src == "blend") {
+ curMap->blend_src = Q3Shader::BLEND_GL_SRC_ALPHA;
+ curMap->blend_dest = Q3Shader::BLEND_GL_ONE_MINUS_SRC_ALPHA;
+ }
+ else {
+ curMap->blend_src = StringToBlendFunc(blend_src);
+ curMap->blend_dest = StringToBlendFunc(GetNextToken(buff));
+ }
+ }
+ // 'alphafunc' - Alpha testing mode
+ else if (TokenMatchI(buff,"alphafunc",9)) {
+ const std::string at = GetNextToken(buff);
+ if (at == "GT0") {
+ curMap->alpha_test = Q3Shader::AT_GT0;
+ }
+ else if (at == "LT128") {
+ curMap->alpha_test = Q3Shader::AT_LT128;
+ }
+ else if (at == "GE128") {
+ curMap->alpha_test = Q3Shader::AT_GE128;
+ }
+ }
+ else if (*buff == '}') {
+ ++buff;
+ // close this map section
+ curMap = NULL;
+ break;
+ }
+ }
+
+ }
+ else if (*buff == '}') {
+ ++buff;
+ curData = NULL;
+ break;
+ }
+
+ // 'cull' specifies culling behaviour for the model
+ else if (TokenMatchI(buff,"cull",4)) {
+ SkipSpaces(&buff);
+ if (!ASSIMP_strincmp(buff,"back",4)) {
+ curData->cull = Q3Shader::CULL_CCW;
+ }
+ else if (!ASSIMP_strincmp(buff,"front",5)) {
+ curData->cull = Q3Shader::CULL_CW;
+ }
+ else if (!ASSIMP_strincmp(buff,"none",4) || !ASSIMP_strincmp(buff,"disable",7)) {
+ curData->cull = Q3Shader::CULL_NONE;
+ }
+ else DefaultLogger::get()->error("Q3Shader: Unrecognized cull mode");
+ }
+ }
+ }
+
+ else {
+ // add new section
+ fill.blocks.push_back(Q3Shader::ShaderDataBlock());
+ curData = &fill.blocks.back();
+
+ // get the name of this section
+ curData->name = GetNextToken(buff);
+ }
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load a Quake 3 skin
+bool Q3Shader::LoadSkin(SkinData& fill, const std::string& pFile,IOSystem* io)
+{
+ boost::scoped_ptr<IOStream> file( io->Open( pFile, "rt"));
+ if (!file.get())
+ return false; // if we can't access the file, don't worry and return
+
+ DefaultLogger::get()->info("Loading Quake3 skin file " + pFile);
+
+ // read file in memory
+ const size_t s = file->FileSize();
+ std::vector<char> _buff(s+1);const char* buff = &_buff[0];
+ file->Read(&_buff[0],s,1);
+ _buff[s] = 0;
+
+ // remove commas
+ std::replace(_buff.begin(),_buff.end(),',',' ');
+
+ // read token by token and fill output table
+ for (;*buff;) {
+ SkipSpacesAndLineEnd(&buff);
+
+ // get first identifier
+ std::string ss = GetNextToken(buff);
+
+ // ignore tokens starting with tag_
+ if (!::strncmp(&ss[0],"tag_",std::min((size_t)4, ss.length())))
+ continue;
+
+ fill.textures.push_back(SkinData::TextureEntry());
+ SkinData::TextureEntry& s = fill.textures.back();
+
+ s.first = ss;
+ s.second = GetNextToken(buff);
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert Q3Shader to material
+void Q3Shader::ConvertShaderToMaterial(MaterialHelper* out, const ShaderDataBlock& shader)
+{
+ ai_assert(NULL != out);
+
+ /* IMPORTANT: This is not a real conversion. Actually we're just guessing and
+ * hacking around to build an aiMaterial that looks nearly equal to the
+ * original Quake 3 shader. We're missing some important features like
+ * animatable material properties in our material system, but at least
+ * multiple textures should be handled correctly.
+ */
+
+ // Two-sided material?
+ if (shader.cull == Q3Shader::CULL_NONE) {
+ const int twosided = 1;
+ out->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED);
+ }
+
+ unsigned int cur_emissive = 0, cur_diffuse = 0, cur_lm =0;
+
+ // Iterate through all textures
+ for (std::list< Q3Shader::ShaderMapBlock >::const_iterator it = shader.maps.begin(); it != shader.maps.end();++it) {
+
+ // CONVERSION BEHAVIOUR:
+ //
+ //
+ // If the texture is additive
+ // - if it is the first texture, assume additive blending for the whole material
+ // - otherwise register it as emissive texture.
+ //
+ // If the texture is using standard blend (or if the blend mode is unknown)
+ // - if first texture: assume default blending for material
+ // - in any case: set it as diffuse texture
+ //
+ // If the texture is using 'filter' blending
+ // - take as lightmap
+ //
+ // Textures with alpha funcs
+ // - aiTextureFlags_UseAlpha is set (otherwise aiTextureFlags_NoAlpha is explicitly set)
+ aiString s((*it).name);
+ aiTextureType type; unsigned int index;
+
+ if ((*it).blend_src == Q3Shader::BLEND_GL_ONE && (*it).blend_dest == Q3Shader::BLEND_GL_ONE) {
+ if (it == shader.maps.begin()) {
+ const int additive = aiBlendMode_Additive;
+ out->AddProperty(&additive,1,AI_MATKEY_BLEND_FUNC);
+
+ index = cur_diffuse++;
+ type = aiTextureType_DIFFUSE;
+ }
+ else {
+ index = cur_emissive++;
+ type = aiTextureType_EMISSIVE;
+ }
+ }
+ else if ((*it).blend_src == Q3Shader::BLEND_GL_DST_COLOR && (*it).blend_dest == Q3Shader::BLEND_GL_ZERO) {
+ index = cur_lm++;
+ type = aiTextureType_LIGHTMAP;
+ }
+ else {
+ const int blend = aiBlendMode_Default;
+ out->AddProperty(&blend,1,AI_MATKEY_BLEND_FUNC);
+
+ index = cur_diffuse++;
+ type = aiTextureType_DIFFUSE;
+ }
+
+ // setup texture
+ out->AddProperty(&s,AI_MATKEY_TEXTURE(type,index));
+
+ // setup texture flags
+ const int use_alpha = ((*it).alpha_test != Q3Shader::AT_NONE ? aiTextureFlags_UseAlpha : aiTextureFlags_IgnoreAlpha);
+ out->AddProperty(&use_alpha,1,AI_MATKEY_TEXFLAGS(type,index));
+ }
+ // If at least one emissive texture was set, set the emissive base color to 1 to ensure
+ // the texture is actually displayed.
+ if (0 != cur_emissive) {
+ aiColor3D one(1.f,1.f,1.f);
+ out->AddProperty(&one,1,AI_MATKEY_COLOR_EMISSIVE);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MD3Importer::MD3Importer()
+: configFrameID (0)
+, configHandleMP (true)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MD3Importer::~MD3Importer()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "md3")
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens
+ if (!extension.length() || checkSig) {
+ uint32_t tokens[1];
+ tokens[0] = AI_MD3_MAGIC_NUMBER_LE;
+ return CheckMagicToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MD3Importer::ValidateHeaderOffsets()
+{
+ // Check magic number
+ if (pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE &&
+ pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
+ throw DeadlyImportError( "Invalid MD3 file: Magic bytes not found");
+
+ // Check file format version
+ if (pcHeader->VERSION > 15)
+ DefaultLogger::get()->warn( "Unsupported MD3 file version. Continuing happily ...");
+
+ // Check some offset values whether they are valid
+ if (!pcHeader->NUM_SURFACES)
+ throw DeadlyImportError( "Invalid md3 file: NUM_SURFACES is 0");
+
+ if (pcHeader->OFS_FRAMES >= fileSize || pcHeader->OFS_SURFACES >= fileSize ||
+ pcHeader->OFS_EOF > fileSize) {
+ throw DeadlyImportError("Invalid MD3 header: some offsets are outside the file");
+ }
+
+ if (pcHeader->NUM_FRAMES <= configFrameID )
+ throw DeadlyImportError("The requested frame is not existing the file");
+}
+
+// ------------------------------------------------------------------------------------------------
+void MD3Importer::ValidateSurfaceHeaderOffsets(const MD3::Surface* pcSurf)
+{
+ // Calculate the relative offset of the surface
+ const int32_t ofs = int32_t((const unsigned char*)pcSurf-this->mBuffer);
+
+ // Check whether all data chunks are inside the valid range
+ if (pcSurf->OFS_TRIANGLES + ofs + pcSurf->NUM_TRIANGLES * sizeof(MD3::Triangle) > fileSize ||
+ pcSurf->OFS_SHADERS + ofs + pcSurf->NUM_SHADER * sizeof(MD3::Shader) > fileSize ||
+ pcSurf->OFS_ST + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord) > fileSize ||
+ pcSurf->OFS_XYZNORMAL + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::Vertex) > fileSize) {
+
+ throw DeadlyImportError("Invalid MD3 surface header: some offsets are outside the file");
+ }
+
+ // Check whether all requirements for Q3 files are met. We don't
+ // care, but probably someone does.
+ if (pcSurf->NUM_TRIANGLES > AI_MD3_MAX_TRIANGLES) {
+ DefaultLogger::get()->warn("MD3: Quake III triangle limit exceeded");
+ }
+
+ if (pcSurf->NUM_SHADER > AI_MD3_MAX_SHADERS) {
+ DefaultLogger::get()->warn("MD3: Quake III shader limit exceeded");
+ }
+
+ if (pcSurf->NUM_VERTICES > AI_MD3_MAX_VERTS) {
+ DefaultLogger::get()->warn("MD3: Quake III vertex limit exceeded");
+ }
+
+ if (pcSurf->NUM_FRAMES > AI_MD3_MAX_FRAMES) {
+ DefaultLogger::get()->warn("MD3: Quake III frame limit exceeded");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void MD3Importer::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("md3");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void MD3Importer::SetupProperties(const Importer* pImp)
+{
+ // The
+ // AI_CONFIG_IMPORT_MD3_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD3_KEYFRAME,0xffffffff);
+ if (0xffffffff == configFrameID) {
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+
+ // AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART
+ configHandleMP = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART,1));
+
+ // AI_CONFIG_IMPORT_MD3_SKIN_NAME
+ configSkinFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SKIN_NAME,"default"));
+
+ // AI_CONFIG_IMPORT_MD3_SHADER_SRC
+ configShaderFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SHADER_SRC,""));
+
+ // AI_CONFIG_FAVOUR_SPEED
+ configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to read the skin for a MD3 file
+void MD3Importer::ReadSkin(Q3Shader::SkinData& fill) const
+{
+ // skip any postfixes (e.g. lower_1.md3)
+ std::string::size_type s = filename.find_last_of('_');
+ if (s == std::string::npos) {
+ s = filename.find_last_of('.');
+ }
+ ai_assert(s != std::string::npos);
+
+ const std::string skin_file = path + filename.substr(0,s) + "_" + configSkinFile + ".skin";
+ Q3Shader::LoadSkin(fill,skin_file,mIOHandler);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to read the shader for a MD3 file
+void MD3Importer::ReadShader(Q3Shader::ShaderData& fill) const
+{
+ // Determine Q3 model name from given path
+ std::string::size_type s = path.find_last_of("\\/",path.length()-2);
+ const std::string model_file = path.substr(s+1,path.length()-(s+2));
+
+ // If no specific dir or file is given, use our default search behaviour
+ if (!configShaderFile.length()) {
+ if (!Q3Shader::LoadShader(fill,path + "..\\..\\..\\scripts\\" + model_file + ".shader",mIOHandler)) {
+ Q3Shader::LoadShader(fill,path + "..\\..\\..\\scripts\\" + filename + ".shader",mIOHandler);
+ }
+ }
+ else {
+ // If the given string specifies a file, load this file.
+ // Otherwise it's a directory.
+ std::string::size_type st = configShaderFile.find_last_of('.');
+ if (st == std::string::npos) {
+
+ if (!Q3Shader::LoadShader(fill,configShaderFile + model_file + ".shader",mIOHandler)) {
+ Q3Shader::LoadShader(fill,configShaderFile + filename + ".shader",mIOHandler);
+ }
+ }
+ else {
+ Q3Shader::LoadShader(fill,configShaderFile,mIOHandler);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Tiny helper to remove a single node from its parent' list
+void RemoveSingleNodeFromList(aiNode* nd)
+{
+ if (!nd || nd->mNumChildren || !nd->mParent)return;
+ aiNode* par = nd->mParent;
+ for (unsigned int i = 0; i < par->mNumChildren;++i) {
+ if (par->mChildren[i] == nd) {
+ --par->mNumChildren;
+ for (;i < par->mNumChildren;++i) {
+ par->mChildren[i] = par->mChildren[i+1];
+ }
+ delete nd;
+ break;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a multi-part Q3 player model
+bool MD3Importer::ReadMultipartFile()
+{
+ // check whether the file name contains a common postfix, e.g lower_2.md3
+ std::string::size_type s = filename.find_last_of('_'), t = filename.find_last_of('.');
+ ai_assert(t != std::string::npos);
+ if (s == std::string::npos)
+ s = t;
+
+ const std::string mod_filename = filename.substr(0,s);
+ const std::string suffix = filename.substr(s,t-s);
+
+ if (mod_filename == "lower" || mod_filename == "upper" || mod_filename == "head"){
+ const std::string lower = path + "lower" + suffix + ".md3";
+ const std::string upper = path + "upper" + suffix + ".md3";
+ const std::string head = path + "head" + suffix + ".md3";
+
+ aiScene* scene_upper = NULL;
+ aiScene* scene_lower = NULL;
+ aiScene* scene_head = NULL;
+ std::string failure;
+
+ aiNode* tag_torso, *tag_head;
+ std::vector<AttachmentInfo> attach;
+
+ DefaultLogger::get()->info("Multi part MD3 player model: lower, upper and head parts are joined");
+
+ // ensure we won't try to load ourselves recursively
+ BatchLoader::PropertyMap props;
+ SetGenericProperty( props.ints, AI_CONFIG_IMPORT_MD3_HANDLE_MULTIPART, 0, NULL);
+
+ // now read these three files
+ BatchLoader batch(mIOHandler);
+ const unsigned int _lower = batch.AddLoadRequest(lower,0,&props);
+ const unsigned int _upper = batch.AddLoadRequest(upper,0,&props);
+ const unsigned int _head = batch.AddLoadRequest(head,0,&props);
+ batch.LoadAll();
+
+ // now construct a dummy scene to place these three parts in
+ aiScene* master = new aiScene();
+ aiNode* nd = master->mRootNode = new aiNode();
+ nd->mName.Set("<MD3_Player>");
+
+ // ... and get them. We need all of them.
+ scene_lower = batch.GetImport(_lower);
+ if (!scene_lower) {
+ DefaultLogger::get()->error("M3D: Failed to read multi part model, lower.md3 fails to load");
+ failure = "lower";
+ goto error_cleanup;
+ }
+
+ scene_upper = batch.GetImport(_upper);
+ if (!scene_upper) {
+ DefaultLogger::get()->error("M3D: Failed to read multi part model, upper.md3 fails to load");
+ failure = "upper";
+ goto error_cleanup;
+ }
+
+ scene_head = batch.GetImport(_head);
+ if (!scene_head) {
+ DefaultLogger::get()->error("M3D: Failed to read multi part model, head.md3 fails to load");
+ failure = "head";
+ goto error_cleanup;
+ }
+
+ // build attachment infos. search for typical Q3 tags
+
+ // original root
+ scene_lower->mRootNode->mName.Set("lower");
+ attach.push_back(AttachmentInfo(scene_lower, nd));
+
+ // tag_torso
+ tag_torso = scene_lower->mRootNode->FindNode("tag_torso");
+ if (!tag_torso) {
+ DefaultLogger::get()->error("M3D: Failed to find attachment tag for multi part model: tag_torso expected");
+ goto error_cleanup;
+ }
+ scene_upper->mRootNode->mName.Set("upper");
+ attach.push_back(AttachmentInfo(scene_upper,tag_torso));
+
+ // tag_head
+ tag_head = scene_upper->mRootNode->FindNode("tag_head");
+ if (!tag_head) {
+ DefaultLogger::get()->error("M3D: Failed to find attachment tag for multi part model: tag_head expected");
+ goto error_cleanup;
+ }
+ scene_head->mRootNode->mName.Set("head");
+ attach.push_back(AttachmentInfo(scene_head,tag_head));
+
+ // Remove tag_head and tag_torso from all other model parts ...
+ // this ensures (together with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY)
+ // that tag_torso/tag_head is also the name of the (unique) output node
+ RemoveSingleNodeFromList (scene_upper->mRootNode->FindNode("tag_torso"));
+ RemoveSingleNodeFromList (scene_head-> mRootNode->FindNode("tag_head" ));
+
+ // Undo the rotations which we applied to the coordinate systems. We're
+ // working in global Quake space here
+ scene_head->mRootNode->mTransformation = aiMatrix4x4();
+ scene_lower->mRootNode->mTransformation = aiMatrix4x4();
+ scene_upper->mRootNode->mTransformation = aiMatrix4x4();
+
+ // and merge the scenes
+ SceneCombiner::MergeScenes(&mScene,master, attach,
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES |
+ AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES |
+ AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS |
+ (!configSpeedFlag ? AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY : 0));
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ mScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
+
+ return true;
+
+error_cleanup:
+ delete scene_upper;
+ delete scene_lower;
+ delete scene_head;
+ delete master;
+
+ if (failure == mod_filename) {
+ throw DeadlyImportError("MD3: failure to read multipart host file");
+ }
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a MD3 path to a proper value
+void MD3Importer::ConvertPath(const char* texture_name, const char* header_name, std::string& out) const
+{
+ // If the MD3's internal path itself and the given path are using
+ // the same directory, remove it completely to get right output paths.
+ const char* end1 = ::strrchr(header_name,'\\');
+ if (!end1)end1 = ::strrchr(header_name,'/');
+
+ const char* end2 = ::strrchr(texture_name,'\\');
+ if (!end2)end2 = ::strrchr(texture_name,'/');
+
+ // HACK: If the paths starts with "models", ignore the
+ // next two hierarchy levels, it specifies just the model name.
+ // Ignored by Q3, it might be not equal to the real model location.
+ if (end2) {
+
+ size_t len2;
+ const size_t len1 = (size_t)(end1 - header_name);
+ if (!ASSIMP_strincmp(texture_name,"models",6) && (texture_name[6] == '/' || texture_name[6] == '\\')) {
+ len2 = 6; // ignore the seventh - could be slash or backslash
+
+ if (!header_name[0]) {
+ // Use the file name only
+ out = end2+1;
+ return;
+ }
+ }
+ else len2 = std::min (len1, (size_t)(end2 - texture_name ));
+ if (!ASSIMP_strincmp(texture_name,header_name,len2)) {
+ // Use the file name only
+ out = end2+1;
+ return;
+ }
+ }
+ // Use the full path
+ out = texture_name;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MD3Importer::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ mFile = pFile;
+ mScene = pScene;
+ mIOHandler = pIOHandler;
+
+ // get base path and file name
+ // todo ... move to PathConverter
+ std::string::size_type s = mFile.find_last_of("/\\");
+ if (s == std::string::npos) {
+ s = 0;
+ }
+ else ++s;
+ filename = mFile.substr(s), path = mFile.substr(0,s);
+ for ( std::string::iterator it = filename .begin(); it != filename.end(); ++it)
+ *it = tolower( *it);
+
+ // Load multi-part model file, if necessary
+ if (configHandleMP) {
+ if (ReadMultipartFile())
+ return;
+ }
+
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open MD3 file " + pFile + ".");
+
+ // Check whether the md3 file is large enough to contain the header
+ fileSize = (unsigned int)file->FileSize();
+ if ( fileSize < sizeof(MD3::Header))
+ throw DeadlyImportError( "MD3 File is too small.");
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector<unsigned char> mBuffer2 (fileSize);
+ file->Read( &mBuffer2[0], 1, fileSize);
+ mBuffer = &mBuffer2[0];
+
+ pcHeader = (BE_NCONST MD3::Header*)mBuffer;
+
+ // Ensure correct endianess
+#ifdef AI_BUILD_BIG_ENDIAN
+
+ AI_SWAP4(pcHeader->VERSION);
+ AI_SWAP4(pcHeader->FLAGS);
+ AI_SWAP4(pcHeader->IDENT);
+ AI_SWAP4(pcHeader->NUM_FRAMES);
+ AI_SWAP4(pcHeader->NUM_SKINS);
+ AI_SWAP4(pcHeader->NUM_SURFACES);
+ AI_SWAP4(pcHeader->NUM_TAGS);
+ AI_SWAP4(pcHeader->OFS_EOF);
+ AI_SWAP4(pcHeader->OFS_FRAMES);
+ AI_SWAP4(pcHeader->OFS_SURFACES);
+ AI_SWAP4(pcHeader->OFS_TAGS);
+
+#endif
+
+ // Validate the file header
+ ValidateHeaderOffsets();
+
+ // Navigate to the list of surfaces
+ BE_NCONST MD3::Surface* pcSurfaces = (BE_NCONST MD3::Surface*)(mBuffer + pcHeader->OFS_SURFACES);
+
+ // Navigate to the list of tags
+ BE_NCONST MD3::Tag* pcTags = (BE_NCONST MD3::Tag*)(mBuffer + pcHeader->OFS_TAGS);
+
+ // Allocate output storage
+ pScene->mNumMeshes = pcHeader->NUM_SURFACES;
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+
+ pScene->mNumMaterials = pcHeader->NUM_SURFACES;
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
+
+ // Set arrays to zero to ensue proper destruction if an exception is raised
+ ::memset(pScene->mMeshes,0,pScene->mNumMeshes*sizeof(aiMesh*));
+ ::memset(pScene->mMaterials,0,pScene->mNumMaterials*sizeof(aiMaterial*));
+
+ // Now read possible skins from .skin file
+ Q3Shader::SkinData skins;
+ ReadSkin(skins);
+
+ // And check whether we can locate a shader file for this model
+ Q3Shader::ShaderData shaders;
+ ReadShader(shaders);
+
+ // Adjust all texture paths in the shader
+ const char* header_name = pcHeader->NAME;
+ if (shaders.blocks.size()) {
+ for (std::list< Q3Shader::ShaderDataBlock >::iterator dit = shaders.blocks.begin(); dit != shaders.blocks.end(); ++dit) {
+ ConvertPath((*dit).name.c_str(),header_name,(*dit).name);
+
+ for (std::list< Q3Shader::ShaderMapBlock >::iterator mit = (*dit).maps.begin(); mit != (*dit).maps.end(); ++mit) {
+ ConvertPath((*mit).name.c_str(),header_name,(*mit).name);
+ }
+ }
+ }
+
+ // Read all surfaces from the file
+ unsigned int iNum = pcHeader->NUM_SURFACES;
+ unsigned int iNumMaterials = 0;
+ while (iNum-- > 0) {
+
+ // Ensure correct endianess
+#ifdef AI_BUILD_BIG_ENDIAN
+
+ AI_SWAP4(pcSurfaces->FLAGS);
+ AI_SWAP4(pcSurfaces->IDENT);
+ AI_SWAP4(pcSurfaces->NUM_FRAMES);
+ AI_SWAP4(pcSurfaces->NUM_SHADER);
+ AI_SWAP4(pcSurfaces->NUM_TRIANGLES);
+ AI_SWAP4(pcSurfaces->NUM_VERTICES);
+ AI_SWAP4(pcSurfaces->OFS_END);
+ AI_SWAP4(pcSurfaces->OFS_SHADERS);
+ AI_SWAP4(pcSurfaces->OFS_ST);
+ AI_SWAP4(pcSurfaces->OFS_TRIANGLES);
+ AI_SWAP4(pcSurfaces->OFS_XYZNORMAL);
+
+#endif
+
+ // Validate the surface header
+ ValidateSurfaceHeaderOffsets(pcSurfaces);
+
+ // Navigate to the vertex list of the surface
+ BE_NCONST MD3::Vertex* pcVertices = (BE_NCONST MD3::Vertex*)
+ (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL);
+
+ // Navigate to the triangle list of the surface
+ BE_NCONST MD3::Triangle* pcTriangles = (BE_NCONST MD3::Triangle*)
+ (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES);
+
+ // Navigate to the texture coordinate list of the surface
+ BE_NCONST MD3::TexCoord* pcUVs = (BE_NCONST MD3::TexCoord*)
+ (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_ST);
+
+ // Navigate to the shader list of the surface
+ BE_NCONST MD3::Shader* pcShaders = (BE_NCONST MD3::Shader*)
+ (((uint8_t*)pcSurfaces) + pcSurfaces->OFS_SHADERS);
+
+ // If the submesh is empty ignore it
+ if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES)
+ {
+ pcSurfaces = (BE_NCONST MD3::Surface*)(((uint8_t*)pcSurfaces) + pcSurfaces->OFS_END);
+ pScene->mNumMeshes--;
+ continue;
+ }
+
+ // Allocate output mesh
+ pScene->mMeshes[iNum] = new aiMesh();
+ aiMesh* pcMesh = pScene->mMeshes[iNum];
+
+ std::string _texture_name;
+ const char* texture_name = NULL;
+
+ // Check whether we have a texture record for this surface in the .skin file
+ std::list< Q3Shader::SkinData::TextureEntry >::iterator it = std::find(
+ skins.textures.begin(), skins.textures.end(), pcSurfaces->NAME );
+
+ if (it != skins.textures.end()) {
+ texture_name = &*( _texture_name = (*it).second).begin();
+ DefaultLogger::get()->debug("MD3: Assigning skin texture " + (*it).second + " to surface " + pcSurfaces->NAME);
+ (*it).resolved = true; // mark entry as resolved
+ }
+
+ // Get the first shader (= texture?) assigned to the surface
+ if (!texture_name && pcSurfaces->NUM_SHADER) {
+ texture_name = pcShaders->NAME;
+ }
+
+ std::string convertedPath;
+ if (texture_name) {
+ ConvertPath(texture_name,header_name,convertedPath);
+ }
+
+ const Q3Shader::ShaderDataBlock* shader = NULL;
+
+ // Now search the current shader for a record with this name (
+ // excluding texture file extension)
+ if (shaders.blocks.size()) {
+
+ std::string::size_type s = convertedPath.find_last_of('.');
+ if (s == std::string::npos)
+ s = convertedPath.length();
+
+ const std::string without_ext = convertedPath.substr(0,s);
+ std::list< Q3Shader::ShaderDataBlock >::const_iterator dit = std::find(shaders.blocks.begin(),shaders.blocks.end(),without_ext);
+ if (dit != shaders.blocks.end()) {
+ // Hurra, wir haben einen. Tolle Sache.
+ shader = &*dit;
+ DefaultLogger::get()->info("Found shader record for " +without_ext );
+ }
+ else DefaultLogger::get()->warn("Unable to find shader record for " +without_ext );
+ }
+
+ MaterialHelper* pcHelper = new MaterialHelper();
+
+ const int iMode = (int)aiShadingMode_Gouraud;
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ // Add a small ambient color value - Quake 3 seems to have one
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ clr.b = clr.g = clr.r = 1.0f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ // use surface name + skin_name as material name
+ aiString name;
+ name.Set("MD3_[" + configSkinFile + "][" + pcSurfaces->NAME + "]");
+ pcHelper->AddProperty(&name,AI_MATKEY_NAME);
+
+ if (!shader) {
+ // Setup dummy texture file name to ensure UV coordinates are kept during postprocessing
+ aiString szString;
+ if (convertedPath.length()) {
+ szString.Set(convertedPath);
+ }
+ else {
+ DefaultLogger::get()->warn("Texture file name has zero length. Using default name");
+ szString.Set("dummy_texture.bmp");
+ }
+ pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ // prevent transparency by default
+ int no_alpha = aiTextureFlags_IgnoreAlpha;
+ pcHelper->AddProperty(&no_alpha,1,AI_MATKEY_TEXFLAGS_DIFFUSE(0));
+ }
+ else {
+ Q3Shader::ConvertShaderToMaterial(pcHelper,*shader);
+ }
+
+ pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
+ pcMesh->mMaterialIndex = iNumMaterials++;
+
+ // Ensure correct endianess
+#ifdef AI_BUILD_BIG_ENDIAN
+
+ for (uint32_t i = 0; i < pcSurfaces->NUM_VERTICES;++i) {
+ AI_SWAP2( pcVertices[i].NORMAL );
+ AI_SWAP2( pcVertices[i].X );
+ AI_SWAP2( pcVertices[i].Y );
+ AI_SWAP2( pcVertices[i].Z );
+
+ AI_SWAP4( pcUVs[i].U );
+ AI_SWAP4( pcUVs[i].U );
+ }
+ for (uint32_t i = 0; i < pcSurfaces->NUM_TRIANGLES;++i) {
+ AI_SWAP4(pcTriangles[i].INDEXES[0]);
+ AI_SWAP4(pcTriangles[i].INDEXES[1]);
+ AI_SWAP4(pcTriangles[i].INDEXES[2]);
+ }
+
+#endif
+
+ // Fill mesh information
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ pcMesh->mNumVertices = pcSurfaces->NUM_TRIANGLES*3;
+ pcMesh->mNumFaces = pcSurfaces->NUM_TRIANGLES;
+ pcMesh->mFaces = new aiFace[pcSurfaces->NUM_TRIANGLES];
+ pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNumUVComponents[0] = 2;
+
+ // Fill in all triangles
+ unsigned int iCurrent = 0;
+ for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i) {
+ pcMesh->mFaces[i].mIndices = new unsigned int[3];
+ pcMesh->mFaces[i].mNumIndices = 3;
+
+ //unsigned int iTemp = iCurrent;
+ for (unsigned int c = 0; c < 3;++c,++iCurrent) {
+ pcMesh->mFaces[i].mIndices[c] = iCurrent;
+
+ // Read vertices
+ aiVector3D& vec = pcMesh->mVertices[iCurrent];
+ vec.x = pcVertices[ pcTriangles->INDEXES[c]].X*AI_MD3_XYZ_SCALE;
+ vec.y = pcVertices[ pcTriangles->INDEXES[c]].Y*AI_MD3_XYZ_SCALE;
+ vec.z = pcVertices[ pcTriangles->INDEXES[c]].Z*AI_MD3_XYZ_SCALE;
+
+ // Convert the normal vector to uncompressed float3 format
+ aiVector3D& nor = pcMesh->mNormals[iCurrent];
+ LatLngNormalToVec3(pcVertices[pcTriangles->INDEXES[c]].NORMAL,(float*)&nor);
+
+ // Read texture coordinates
+ pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U;
+ pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V;
+ }
+ // Flip face order if necessary
+ if (!shader || shader->cull == Q3Shader::CULL_CW) {
+ std::swap(pcMesh->mFaces[i].mIndices[2],pcMesh->mFaces[i].mIndices[1]);
+ }
+ pcTriangles++;
+ }
+
+ // Go to the next surface
+ pcSurfaces = (BE_NCONST MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
+ }
+
+ // For debugging purposes: check whether we found matches for all entries in the skins file
+ if (!DefaultLogger::isNullLogger()) {
+ for (std::list< Q3Shader::SkinData::TextureEntry>::const_iterator it = skins.textures.begin();it != skins.textures.end(); ++it) {
+ if (!(*it).resolved) {
+ DefaultLogger::get()->error("MD3: Failed to match skin " + (*it).first + " to surface " + (*it).second);
+ }
+ }
+ }
+
+ if (!pScene->mNumMeshes)
+ throw DeadlyImportError( "MD3: File contains no valid mesh");
+ pScene->mNumMaterials = iNumMaterials;
+
+ // Now we need to generate an empty node graph
+ pScene->mRootNode = new aiNode("<MD3Root>");
+ pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+ pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+
+ // Attach tiny children for all tags
+ if (pcHeader->NUM_TAGS) {
+ pScene->mRootNode->mNumChildren = pcHeader->NUM_TAGS;
+ pScene->mRootNode->mChildren = new aiNode*[pcHeader->NUM_TAGS];
+
+ for (unsigned int i = 0; i < pcHeader->NUM_TAGS; ++i, ++pcTags) {
+
+ aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode();
+ nd->mName.Set((const char*)pcTags->NAME);
+ nd->mParent = pScene->mRootNode;
+
+ AI_SWAP4(pcTags->origin.x);
+ AI_SWAP4(pcTags->origin.y);
+ AI_SWAP4(pcTags->origin.z);
+
+ // Copy local origin, again flip z,y
+ nd->mTransformation.a4 = pcTags->origin.x;
+ nd->mTransformation.b4 = pcTags->origin.y;
+ nd->mTransformation.c4 = pcTags->origin.z;
+
+ // Copy rest of transformation (need to transpose to match row-order matrix)
+ for (unsigned int a = 0; a < 3;++a) {
+ for (unsigned int m = 0; m < 3;++m) {
+ nd->mTransformation[m][a] = pcTags->orientation[a][m];
+ AI_SWAP4(nd->mTransformation[m][a]);
+ }
+ }
+ }
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mRootNode->mMeshes[i] = i;
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
+}
+
+#endif // !! ASSIMP_BUILD_NO_MD3_IMPORTER
diff --git a/3rdparty/assimp/code/MD3Loader.h b/3rdparty/assimp/code/MD3Loader.h
new file mode 100644
index 000000000..e4a65a529
--- /dev/null
+++ b/3rdparty/assimp/code/MD3Loader.h
@@ -0,0 +1,331 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Md3Loader.h
+ * @brief Declaration of the .MD3 importer class.
+ */
+#ifndef AI_MD3LOADER_H_INCLUDED
+#define AI_MD3LOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "ByteSwap.h"
+
+#include "../include/aiTypes.h"
+
+#include "MD3FileData.h"
+namespace Assimp {
+class MaterialHelper;
+
+using namespace MD3;
+namespace Q3Shader {
+
+// ---------------------------------------------------------------------------
+/** @brief Tiny utility data structure to hold the data of a .skin file
+ */
+struct SkinData
+{
+ //! A single entryin texture list
+ struct TextureEntry : public std::pair<std::string,std::string>
+ {
+ // did we resolve this texture entry?
+ bool resolved;
+
+ // for std::find()
+ bool operator == (const std::string& f) const {
+ return f == first;
+ }
+ };
+
+ //! List of textures
+ std::list<TextureEntry> textures;
+
+ // rest is ignored for the moment
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies cull modi for Quake shader files.
+ */
+enum ShaderCullMode
+{
+ CULL_NONE,
+ CULL_CW,
+ CULL_CCW
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies alpha blend modi (src + dest) for Quake shader files
+ */
+enum BlendFunc
+{
+ BLEND_NONE,
+ BLEND_GL_ONE,
+ BLEND_GL_ZERO,
+ BLEND_GL_DST_COLOR,
+ BLEND_GL_ONE_MINUS_DST_COLOR,
+ BLEND_GL_SRC_ALPHA,
+ BLEND_GL_ONE_MINUS_SRC_ALPHA
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Specifies alpha test modi for Quake texture maps
+ */
+enum AlphaTestFunc
+{
+ AT_NONE,
+ AT_GT0,
+ AT_LT128,
+ AT_GE128
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Tiny utility data structure to hold a .shader map data block
+ */
+struct ShaderMapBlock
+{
+ ShaderMapBlock()
+ : blend_src (BLEND_NONE)
+ , blend_dest (BLEND_NONE)
+ , alpha_test (AT_NONE)
+ {}
+
+ //! Name of referenced map
+ std::string name;
+
+ //! Blend and alpha test settings for texture
+ BlendFunc blend_src,blend_dest;
+ AlphaTestFunc alpha_test;
+
+
+ //! For std::find()
+ bool operator== (const std::string& o) const {
+ return !ASSIMP_stricmp(o,name);
+ }
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Tiny utility data structure to hold a .shader data block
+ */
+struct ShaderDataBlock
+{
+ ShaderDataBlock()
+ : cull (CULL_CW)
+ {}
+
+ //! Name of referenced data element
+ std::string name;
+
+ //! Cull mode for the element
+ ShaderCullMode cull;
+
+ //! Maps defined in the shader
+ std::list<ShaderMapBlock> maps;
+
+
+ //! For std::find()
+ bool operator== (const std::string& o) const {
+ return !ASSIMP_stricmp(o,name);
+ }
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Tiny utility data structure to hold the data of a .shader file
+ */
+struct ShaderData
+{
+ //! Shader data blocks
+ std::list<ShaderDataBlock> blocks;
+};
+
+// ---------------------------------------------------------------------------
+/** @brief Load a shader file
+ *
+ * Generally, parsing is error tolerant. There's no failure.
+ * @param fill Receives output data
+ * @param file File to be read.
+ * @param io IOSystem to be used for reading
+ * @return false if file is not accessible
+ */
+bool LoadShader(ShaderData& fill, const std::string& file,IOSystem* io);
+
+
+// ---------------------------------------------------------------------------
+/** @brief Convert a Q3Shader to an aiMaterial
+ *
+ * @param[out] out Material structure to be filled.
+ * @param[in] shader Input shader
+ */
+void ConvertShaderToMaterial(MaterialHelper* out, const ShaderDataBlock& shader);
+
+// ---------------------------------------------------------------------------
+/** @brief Load a skin file
+ *
+ * Generally, parsing is error tolerant. There's no failure.
+ * @param fill Receives output data
+ * @param file File to be read.
+ * @param io IOSystem to be used for reading
+ * @return false if file is not accessible
+ */
+bool LoadSkin(SkinData& fill, const std::string& file,IOSystem* io);
+
+} // ! namespace Q3SHader
+
+// ---------------------------------------------------------------------------
+/** @brief Importer class to load MD3 files
+*/
+class MD3Importer : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ MD3Importer();
+
+ /** Destructor, private as well */
+ ~MD3Importer();
+
+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;
+
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Validate offsets in the header
+ */
+ void ValidateHeaderOffsets();
+ void ValidateSurfaceHeaderOffsets(const MD3::Surface* pcSurfHeader);
+
+ // -------------------------------------------------------------------
+ /** Read a Q3 multipart file
+ * @return true if multi part has been processed
+ */
+ bool ReadMultipartFile();
+
+ // -------------------------------------------------------------------
+ /** Try to read the skin for a MD3 file
+ * @param fill Receives output information
+ */
+ void ReadSkin(Q3Shader::SkinData& fill) const;
+
+ // -------------------------------------------------------------------
+ /** Try to read the shader for a MD3 file
+ * @param fill Receives output information
+ */
+ void ReadShader(Q3Shader::ShaderData& fill) const;
+
+ // -------------------------------------------------------------------
+ /** Convert a texture path in a MD3 file to a proper value
+ * @param[in] texture_name Path to be converted
+ * @param[in] header_path Base path specified in MD3 header
+ * @param[out] out Receives the converted output string
+ */
+ void ConvertPath(const char* texture_name, const char* header_path,
+ std::string& out) const;
+
+protected:
+
+ /** Configuration option: frame to be loaded */
+ unsigned int configFrameID;
+
+ /** Configuration option: process multi-part files */
+ bool configHandleMP;
+
+ /** Configuration option: name of skin file to be read */
+ std::string configSkinFile;
+
+ /** Configuration option: name or path of shader */
+ std::string configShaderFile;
+
+ /** Configuration option: speed flag was set? */
+ bool configSpeedFlag;
+
+ /** Header of the MD3 file */
+ BE_NCONST MD3::Header* pcHeader;
+
+ /** File buffer */
+ BE_NCONST unsigned char* mBuffer;
+
+ /** Size of the file, in bytes */
+ unsigned int fileSize;
+
+ /** Current file name */
+ std::string mFile;
+
+ /** Current base directory */
+ std::string path;
+
+ /** Pure file we're currently reading */
+ std::string filename;
+
+ /** Output scene to be filled */
+ aiScene* mScene;
+
+ /** IO system to be used to access the data*/
+ IOSystem* mIOHandler;
+ };
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/MD4FileData.h b/3rdparty/assimp/code/MD4FileData.h
new file mode 100644
index 000000000..4e97e92b3
--- /dev/null
+++ b/3rdparty/assimp/code/MD4FileData.h
@@ -0,0 +1,218 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines the helper data structures for importing MD4 files */
+#ifndef AI_MD4FILEHELPER_H_INC
+#define AI_MD4FILEHELPER_H_INC
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
+# pragma pack(push,1)
+# define PACK_STRUCT
+#elif defined( __GNUC__ )
+# define PACK_STRUCT __attribute__((packed))
+#else
+# error Compiler not supported
+#endif
+
+
+namespace Assimp
+{
+// http://gongo.quakedev.com/md4.html
+namespace MD4
+{
+
+#define AI_MD4_MAGIC_NUMBER_BE 'IDP4'
+#define AI_MD4_MAGIC_NUMBER_LE '4PDI'
+
+// common limitations
+#define AI_MD4_VERSION 4
+#define AI_MD4_MAXQPATH 64
+#define AI_MD4_MAX_FRAMES 2028
+#define AI_MD4_MAX_SURFACES 32
+#define AI_MD4_MAX_BONES 256
+#define AI_MD4_MAX_VERTS 4096
+#define AI_MD4_MAX_TRIANGLES 8192
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for the MD4 main header
+ */
+// ---------------------------------------------------------------------------
+struct Header
+{
+ //! magic number
+ int32_t magic;
+
+ //! file format version
+ int32_t version;
+
+ //! original name in .pak archive
+ unsigned char name[ AI_MD4_MAXQPATH ];
+
+ //! number of frames in the file
+ int32_t NUM_FRAMES;
+
+ //! number of bones in the file
+ int32_t NUM_BONES;
+
+ //! number of surfaces in the file
+ int32_t NUM_SURFACES;
+
+ //! offset of the first frame
+ int32_t OFS_FRAMES;
+
+ //! offset of the first bone
+ int32_t OFS_BONES;
+
+ //! offset of the first surface
+ int32_t OFS_SURFACES;
+
+ //! end of file
+ int32_t OFS_EOF;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Stores the local transformation matrix of a bone
+ */
+// ---------------------------------------------------------------------------
+struct BoneFrame
+{
+ float matrix[3][4];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Stores the name / parent index / flag of a node
+ */
+// ---------------------------------------------------------------------------
+struct BoneName
+{
+ char name[32] ;
+ int parent ;
+ int flags ;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a surface in a MD4 file
+ */
+// ---------------------------------------------------------------------------
+struct Surface
+{
+ int32_t ident;
+ char name[64];
+ char shader[64];
+ int32_t shaderIndex;
+ int32_t lodBias;
+ int32_t minLod;
+ int32_t ofsHeader;
+ int32_t numVerts;
+ int32_t ofsVerts;
+ int32_t numTris;
+ int32_t ofsTris;
+ int32_t numBoneRefs;
+ int32_t ofsBoneRefs;
+ int32_t ofsCollapseMap;
+ int32_t ofsEnd;
+} PACK_STRUCT;
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD4 vertex' weight
+ */
+// ---------------------------------------------------------------------------
+struct Weight
+{
+ int32_t boneIndex;
+ float boneWeight;
+ float offset[3];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a vertex in a MD4 file
+ */
+// ---------------------------------------------------------------------------
+struct Vertex
+{
+ float vertex[3];
+ float normal[3];
+ float texCoords[2];
+ int32_t numWeights;
+ Weight weights[1];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a triangle in a MD4 file
+ */
+// ---------------------------------------------------------------------------
+struct Triangle
+{
+ int32_t indexes[3];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MD4 frame
+ */
+// ---------------------------------------------------------------------------
+struct Frame
+{
+ float bounds[3][2];
+ float localOrigin[3];
+ float radius;
+ BoneFrame bones[1];
+} PACK_STRUCT;
+
+
+// reset packing to the original value
+#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
+# pragma pack( pop )
+#endif
+#undef PACK_STRUCT
+
+
+};
+};
+
+#endif // !! AI_MD4FILEHELPER_H_INC
diff --git a/3rdparty/assimp/code/MD5Loader.cpp b/3rdparty/assimp/code/MD5Loader.cpp
new file mode 100644
index 000000000..d18077421
--- /dev/null
+++ b/3rdparty/assimp/code/MD5Loader.cpp
@@ -0,0 +1,727 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MD5Loader.cpp
+ * @brief Implementation of the MD5 importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
+
+// internal headers
+#include "MaterialSystem.h"
+#include "RemoveComments.h"
+#include "MD5Loader.h"
+#include "StringComparison.h"
+#include "fast_atof.h"
+#include "SkeletonMeshBuilder.h"
+
+using namespace Assimp;
+
+// Minimum weight value. Weights inside [-n ... n] are ignored
+#define AI_MD5_WEIGHT_EPSILON 1e-5f
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MD5Importer::MD5Importer()
+: configNoAutoLoad (false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MD5Importer::~MD5Importer()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "md5anim" || extension == "md5mesh" || extension == "md5camera")
+ return true;
+ else if (!extension.length() || checkSig) {
+ if (!pIOHandler)
+ return true;
+ const char* tokens[] = {"MD5Version"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get list of all supported extensions
+void MD5Importer::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("md5anim");
+ extensions.insert("md5mesh");
+ extensions.insert("md5camera");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import properties
+void MD5Importer::SetupProperties(const Importer* pImp)
+{
+ // AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD
+ configNoAutoLoad = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MD5Importer::InternReadFile( const std::string& pFile,
+ aiScene* _pScene, IOSystem* _pIOHandler)
+{
+ pIOHandler = _pIOHandler;
+ pScene = _pScene;
+ bHadMD5Mesh = bHadMD5Anim = bHadMD5Camera = false;
+
+ // remove the file extension
+ std::string::size_type pos = pFile.find_last_of('.');
+ mFile = (std::string::npos == pos ? pFile : pFile.substr(0,pos+1));
+
+ const std::string extension = GetExtension(pFile);
+ try {
+ if (extension == "md5camera") {
+ LoadMD5CameraFile();
+ }
+ else if (configNoAutoLoad || extension == "md5anim") {
+ // determine file extension and process just *one* file
+ if (extension.length() == 0) {
+ /* fixme */
+ }
+ if (extension == "md5anim") {
+ LoadMD5AnimFile();
+ }
+ else if (extension == "md5mesh") {
+ LoadMD5MeshFile();
+ }
+ }
+ else {
+ LoadMD5MeshFile();
+ LoadMD5AnimFile();
+ }
+ }
+ catch ( std::exception&) {
+ // XXX use more idiomatic RAII solution
+ UnloadFileFromMemory();
+ throw;
+ }
+
+ // make sure we have at least one file
+ if (!bHadMD5Mesh && !bHadMD5Anim && !bHadMD5Camera)
+ throw DeadlyImportError("Failed to read valid contents from this MD5* file");
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
+
+ // the output scene wouldn't pass the validation without this flag
+ if (!bHadMD5Mesh)
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load a file into a memory buffer
+void MD5Importer::LoadFileIntoMemory (IOStream* file)
+{
+ ai_assert(NULL != file);
+ fileSize = (unsigned int)file->FileSize();
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ pScene = pScene;
+ mBuffer = new char[fileSize+1];
+ file->Read( (void*)mBuffer, 1, fileSize);
+ iLineNumber = 1;
+
+ // append a terminal 0
+ mBuffer[fileSize] = '\0';
+
+ // now remove all line comments from the file
+ CommentRemover::RemoveLineComments("//",mBuffer,' ');
+}
+
+// ------------------------------------------------------------------------------------------------
+// Unload the current memory buffer
+void MD5Importer::UnloadFileFromMemory ()
+{
+ // delete the file buffer
+ delete[] mBuffer;
+ mBuffer = NULL;
+ fileSize = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build unique vertices
+void MD5Importer::MakeDataUnique (MD5::MeshDesc& meshSrc)
+{
+ std::vector<bool> abHad(meshSrc.mVertices.size(),false);
+
+ // allocate enough storage to keep the output structures
+ const unsigned int iNewNum = meshSrc.mFaces.size()*3;
+ unsigned int iNewIndex = meshSrc.mVertices.size();
+ meshSrc.mVertices.resize(iNewNum);
+
+ // try to guess how much storage we'll need for new weights
+ const float fWeightsPerVert = meshSrc.mWeights.size() / (float)iNewIndex;
+ const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum);
+ meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer
+
+ for (FaceList::const_iterator iter = meshSrc.mFaces.begin(),iterEnd = meshSrc.mFaces.end();iter != iterEnd;++iter){
+ const aiFace& face = *iter;
+ for (unsigned int i = 0; i < 3;++i) {
+ if (face.mIndices[0] >= meshSrc.mVertices.size())
+ throw DeadlyImportError("MD5MESH: Invalid vertex index");
+
+ if (abHad[face.mIndices[i]]) {
+ // generate a new vertex
+ meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]];
+ face.mIndices[i] = iNewIndex++;
+ }
+ else abHad[face.mIndices[i]] = true;
+ }
+ // swap face order
+ std::swap(face.mIndices[0],face.mIndices[2]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursive node graph construction from a MD5MESH
+void MD5Importer::AttachChilds_Mesh(int iParentID,aiNode* piParent, BoneList& bones)
+{
+ ai_assert(NULL != piParent && !piParent->mNumChildren);
+
+ // First find out how many children we'll have
+ for (int i = 0; i < (int)bones.size();++i) {
+ if (iParentID != i && bones[i].mParentIndex == iParentID) {
+ ++piParent->mNumChildren;
+ }
+ }
+ if (piParent->mNumChildren) {
+ piParent->mChildren = new aiNode*[piParent->mNumChildren];
+ for (int i = 0; i < (int)bones.size();++i) {
+ // (avoid infinite recursion)
+ if (iParentID != i && bones[i].mParentIndex == iParentID) {
+ aiNode* pc;
+ // setup a new node
+ *piParent->mChildren++ = pc = new aiNode();
+ pc->mName = aiString(bones[i].mName);
+ pc->mParent = piParent;
+
+ // get the transformation matrix from rotation and translational components
+ aiQuaternion quat;
+ MD5::ConvertQuaternion ( bones[i].mRotationQuat, quat );
+
+ // FIX to get to Assimp's quaternion conventions
+ quat.w *= -1.f;
+
+ bones[i].mTransform = aiMatrix4x4 ( quat.GetMatrix());
+ bones[i].mTransform.a4 = bones[i].mPositionXYZ.x;
+ bones[i].mTransform.b4 = bones[i].mPositionXYZ.y;
+ bones[i].mTransform.c4 = bones[i].mPositionXYZ.z;
+
+ // store it for later use
+ pc->mTransformation = bones[i].mInvTransform = bones[i].mTransform;
+ bones[i].mInvTransform.Inverse();
+
+ // the transformations for each bone are absolute, so we need to multiply them
+ // with the inverse of the absolute matrix of the parent joint
+ if (-1 != iParentID) {
+ pc->mTransformation = bones[iParentID].mInvTransform * pc->mTransformation;
+ }
+
+ // add children to this node, too
+ AttachChilds_Mesh( i, pc, bones);
+ }
+ }
+ // undo offset computations
+ piParent->mChildren -= piParent->mNumChildren;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursive node graph construction from a MD5ANIM
+void MD5Importer::AttachChilds_Anim(int iParentID,aiNode* piParent, AnimBoneList& bones,const aiNodeAnim** node_anims)
+{
+ ai_assert(NULL != piParent && !piParent->mNumChildren);
+
+ // First find out how many children we'll have
+ for (int i = 0; i < (int)bones.size();++i) {
+ if (iParentID != i && bones[i].mParentIndex == iParentID) {
+ ++piParent->mNumChildren;
+ }
+ }
+ if (piParent->mNumChildren) {
+ piParent->mChildren = new aiNode*[piParent->mNumChildren];
+ for (int i = 0; i < (int)bones.size();++i) {
+ // (avoid infinite recursion)
+ if (iParentID != i && bones[i].mParentIndex == iParentID)
+ {
+ aiNode* pc;
+ // setup a new node
+ *piParent->mChildren++ = pc = new aiNode();
+ pc->mName = aiString(bones[i].mName);
+ pc->mParent = piParent;
+
+ // get the corresponding animation channel and its first frame
+ const aiNodeAnim** cur = node_anims;
+ while ((**cur).mNodeName != pc->mName)++cur;
+
+ aiMatrix4x4::Translation((**cur).mPositionKeys[0].mValue,pc->mTransformation);
+ pc->mTransformation = pc->mTransformation * aiMatrix4x4((**cur).mRotationKeys[0].mValue.GetMatrix()) ;
+
+ // add children to this node, too
+ AttachChilds_Anim( i, pc, bones,node_anims);
+ }
+ }
+ // undo offset computations
+ piParent->mChildren -= piParent->mNumChildren;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load a MD5MESH file
+void MD5Importer::LoadMD5MeshFile ()
+{
+ std::string pFile = mFile + "md5mesh";
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ DefaultLogger::get()->warn("Failed to read MD5MESH file: " + pFile);
+ return;
+ }
+ bHadMD5Mesh = true;
+ LoadFileIntoMemory(file.get());
+
+ // now construct a parser and parse the file
+ MD5::MD5Parser parser(mBuffer,fileSize);
+
+ // load the mesh information from it
+ MD5::MD5MeshParser meshParser(parser.mSections);
+
+ // create the bone hierarchy - first the root node and dummy nodes for all meshes
+ pScene->mRootNode = new aiNode("<MD5_Root>");
+ pScene->mRootNode->mNumChildren = 2;
+ pScene->mRootNode->mChildren = new aiNode*[2];
+
+ // build the hierarchy from the MD5MESH file
+ aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
+ pcNode->mName.Set("<MD5_Hierarchy>");
+ pcNode->mParent = pScene->mRootNode;
+ AttachChilds_Mesh(-1,pcNode,meshParser.mJoints);
+
+ pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
+ pcNode->mName.Set("<MD5_Mesh>");
+ pcNode->mParent = pScene->mRootNode;
+
+#if 0
+ if (pScene->mRootNode->mChildren[1]->mNumChildren) /* start at the right hierarchy level */
+ SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[1]->mChildren[0]);
+#else
+ std::vector<MD5::MeshDesc>::const_iterator end = meshParser.mMeshes.end();
+
+ // FIX: MD5 files exported from Blender can have empty meshes
+ for (std::vector<MD5::MeshDesc>::const_iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) {
+ if (!(*it).mFaces.empty() && !(*it).mVertices.empty())
+ ++pScene->mNumMaterials;
+ }
+
+ // generate all meshes
+ pScene->mNumMeshes = pScene->mNumMaterials;
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
+
+ // storage for node mesh indices
+ pcNode->mNumMeshes = pScene->mNumMeshes;
+ pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+ for (unsigned int m = 0; m < pcNode->mNumMeshes;++m)
+ pcNode->mMeshes[m] = m;
+
+ unsigned int n = 0;
+ for (std::vector<MD5::MeshDesc>::iterator it = meshParser.mMeshes.begin(),end = meshParser.mMeshes.end(); it != end;++it) {
+ MD5::MeshDesc& meshSrc = *it;
+ if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty())
+ continue;
+
+ aiMesh* mesh = pScene->mMeshes[n] = new aiMesh();
+ mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // generate unique vertices in our internal verbose format
+ MakeDataUnique(meshSrc);
+
+ mesh->mNumVertices = (unsigned int) meshSrc.mVertices.size();
+ mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+ mesh->mNumUVComponents[0] = 2;
+
+ // copy texture coordinates
+ aiVector3D* pv = mesh->mTextureCoords[0];
+ for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
+ pv->x = (*iter).mUV.x;
+ pv->y = 1.0f-(*iter).mUV.y; // D3D to OpenGL
+ pv->z = 0.0f;
+ }
+
+ // sort all bone weights - per bone
+ unsigned int* piCount = new unsigned int[meshParser.mJoints.size()];
+ ::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size());
+
+ for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
+ for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
+ {
+ MD5::WeightDesc& desc = meshSrc.mWeights[w];
+ /* FIX for some invalid exporters */
+ if (!(desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON ))
+ ++piCount[desc.mBone];
+ }
+ }
+
+ // check how many we will need
+ for (unsigned int p = 0; p < meshParser.mJoints.size();++p)
+ if (piCount[p])mesh->mNumBones++;
+
+ if (mesh->mNumBones) // just for safety
+ {
+ mesh->mBones = new aiBone*[mesh->mNumBones];
+ for (unsigned int q = 0,h = 0; q < meshParser.mJoints.size();++q)
+ {
+ if (!piCount[q])continue;
+ aiBone* p = mesh->mBones[h] = new aiBone();
+ p->mNumWeights = piCount[q];
+ p->mWeights = new aiVertexWeight[p->mNumWeights];
+ p->mName = aiString(meshParser.mJoints[q].mName);
+ p->mOffsetMatrix = meshParser.mJoints[q].mInvTransform;
+
+ // store the index for later use
+ MD5::BoneDesc& boneSrc = meshParser.mJoints[q];
+ boneSrc.mMap = h++;
+
+ // compute w-component of quaternion
+ MD5::ConvertQuaternion( boneSrc.mRotationQuat, boneSrc.mRotationQuatConverted );
+ }
+
+ //unsigned int g = 0;
+ pv = mesh->mVertices;
+ for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
+ // compute the final vertex position from all single weights
+ *pv = aiVector3D();
+
+ // there are models which have weights which don't sum to 1 ...
+ float fSum = 0.0f;
+ for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
+ fSum += meshSrc.mWeights[w].mWeight;
+ if (!fSum) {
+ DefaultLogger::get()->error("MD5MESH: The sum of all vertex bone weights is 0");
+ continue;
+ }
+
+ // process bone weights
+ for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) {
+ if (w >= meshSrc.mWeights.size())
+ throw DeadlyImportError("MD5MESH: Invalid weight index");
+
+ MD5::WeightDesc& desc = meshSrc.mWeights[w];
+ if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON)
+ continue;
+
+ const float fNewWeight = desc.mWeight / fSum;
+
+ // transform the local position into worldspace
+ MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone];
+ const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (desc.vOffsetPosition);
+
+ // use the original weight to compute the vertex position
+ // (some MD5s seem to depend on the invalid weight values ...)
+ *pv += ((boneSrc.mPositionXYZ+v)* desc.mWeight);
+
+ aiBone* bone = mesh->mBones[boneSrc.mMap];
+ *bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight);
+ }
+ }
+
+ // undo our nice offset tricks ...
+ for (unsigned int p = 0; p < mesh->mNumBones;++p)
+ mesh->mBones[p]->mWeights -= mesh->mBones[p]->mNumWeights;
+ }
+
+ delete[] piCount;
+
+ // now setup all faces - we can directly copy the list
+ // (however, take care that the aiFace destructor doesn't delete the mIndices array)
+ mesh->mNumFaces = (unsigned int)meshSrc.mFaces.size();
+ mesh->mFaces = new aiFace[mesh->mNumFaces];
+ for (unsigned int c = 0; c < mesh->mNumFaces;++c) {
+ mesh->mFaces[c].mNumIndices = 3;
+ mesh->mFaces[c].mIndices = meshSrc.mFaces[c].mIndices;
+ meshSrc.mFaces[c].mIndices = NULL;
+ }
+
+ // generate a material for the mesh
+ MaterialHelper* mat = new MaterialHelper();
+ pScene->mMaterials[n] = mat;
+
+ // insert the typical doom3 textures:
+ // nnn_local.tga - normal map
+ // nnn_h.tga - height map
+ // nnn_s.tga - specular map
+ // nnn_d.tga - diffuse map
+ if (meshSrc.mShader.length && !strchr(meshSrc.mShader.data,'.')) {
+
+ aiString temp(meshSrc.mShader);
+ temp.Append("_local.tga");
+ mat->AddProperty(&temp,AI_MATKEY_TEXTURE_NORMALS(0));
+
+ temp = aiString(meshSrc.mShader);
+ temp.Append("_s.tga");
+ mat->AddProperty(&temp,AI_MATKEY_TEXTURE_SPECULAR(0));
+
+ temp = aiString(meshSrc.mShader);
+ temp.Append("_d.tga");
+ mat->AddProperty(&temp,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ temp = aiString(meshSrc.mShader);
+ temp.Append("_h.tga");
+ mat->AddProperty(&temp,AI_MATKEY_TEXTURE_HEIGHT(0));
+
+ // set this also as material name
+ mat->AddProperty(&meshSrc.mShader,AI_MATKEY_NAME);
+ }
+ else mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ mesh->mMaterialIndex = n++;
+ }
+#endif
+ // delete the file again
+ UnloadFileFromMemory();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load an MD5ANIM file
+void MD5Importer::LoadMD5AnimFile ()
+{
+ std::string pFile = mFile + "md5anim";
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile);
+ return;
+ }
+ LoadFileIntoMemory(file.get());
+
+ // parse the basic file structure
+ MD5::MD5Parser parser(mBuffer,fileSize);
+
+ // load the animation information from the parse tree
+ MD5::MD5AnimParser animParser(parser.mSections);
+
+ // generate and fill the output animation
+ if (animParser.mAnimatedBones.empty() || animParser.mFrames.empty() ||
+ animParser.mBaseFrames.size() != animParser.mAnimatedBones.size()) {
+
+ DefaultLogger::get()->error("MD5ANIM: No frames or animated bones loaded");
+ }
+ else {
+ bHadMD5Anim = true;
+
+ pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations = 1];
+ aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation();
+ anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size();
+ anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
+ for (unsigned int i = 0; i < anim->mNumChannels;++i) {
+ aiNodeAnim* node = anim->mChannels[i] = new aiNodeAnim();
+ node->mNodeName = aiString( animParser.mAnimatedBones[i].mName );
+
+ // allocate storage for the keyframes
+ node->mPositionKeys = new aiVectorKey[animParser.mFrames.size()];
+ node->mRotationKeys = new aiQuatKey[animParser.mFrames.size()];
+ }
+
+ // 1 tick == 1 frame
+ anim->mTicksPerSecond = animParser.fFrameRate;
+
+ for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end();iter != iterEnd;++iter){
+ double dTime = (double)(*iter).iIndex;
+ aiNodeAnim** pcAnimNode = anim->mChannels;
+ if (!(*iter).mValues.empty() || iter == animParser.mFrames.begin()) /* be sure we have at least one frame */
+ {
+ // now process all values in there ... read all joints
+ MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0];
+ for (AnimBoneList::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2,
+ ++pcAnimNode,++pcBaseFrame)
+ {
+ if ((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) {
+
+ // Allow for empty frames
+ if ((*iter2).iFlags != 0) {
+ throw DeadlyImportError("MD5: Keyframe index is out of range");
+
+ }
+ continue;
+ }
+ const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex];
+ aiNodeAnim* pcCurAnimBone = *pcAnimNode;
+
+ aiVectorKey* vKey = &pcCurAnimBone->mPositionKeys[pcCurAnimBone->mNumPositionKeys++];
+ aiQuatKey* qKey = &pcCurAnimBone->mRotationKeys [pcCurAnimBone->mNumRotationKeys++];
+ aiVector3D vTemp;
+
+ // translational component
+ for (unsigned int i = 0; i < 3; ++i) {
+ if ((*iter2).iFlags & (1u << i))
+ vKey->mValue[i] = *fpCur++;
+ else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i];
+ }
+
+ // orientation component
+ for (unsigned int i = 0; i < 3; ++i) {
+ if ((*iter2).iFlags & (8u << i))
+ vTemp[i] = *fpCur++;
+ else vTemp[i] = pcBaseFrame->vRotationQuat[i];
+ }
+
+ MD5::ConvertQuaternion(vTemp, qKey->mValue);
+ qKey->mTime = vKey->mTime = dTime;
+
+ // we need this to get to Assimp quaternion conventions
+ qKey->mValue.w *= -1.f;
+ }
+ }
+
+ // compute the duration of the animation
+ anim->mDuration = std::max(dTime,anim->mDuration);
+ }
+
+ // If we didn't build the hierarchy yet (== we didn't load a MD5MESH),
+ // construct it now from the data given in the MD5ANIM.
+ if (!pScene->mRootNode) {
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<MD5_Hierarchy>");
+
+ AttachChilds_Anim(-1,pScene->mRootNode,animParser.mAnimatedBones,(const aiNodeAnim**)anim->mChannels);
+
+ // Call SkeletonMeshBuilder to construct a mesh to represent the shape
+ if (pScene->mRootNode->mNumChildren) {
+ SkeletonMeshBuilder skeleton_maker(pScene,pScene->mRootNode->mChildren[0]);
+ }
+ }
+ }
+ // delete the file again
+ UnloadFileFromMemory();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load an MD5CAMERA file
+void MD5Importer::LoadMD5CameraFile ()
+{
+ std::string pFile = mFile + "md5camera";
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError("Failed to read MD5CAMERA file: " + pFile);
+ }
+ bHadMD5Camera = true;
+ LoadFileIntoMemory(file.get());
+
+ // parse the basic file structure
+ MD5::MD5Parser parser(mBuffer,fileSize);
+
+ // load the camera animation data from the parse tree
+ MD5::MD5CameraParser cameraParser(parser.mSections);
+
+ if (cameraParser.frames.empty())
+ throw DeadlyImportError("MD5CAMERA: No frames parsed");
+
+ std::vector<unsigned int>& cuts = cameraParser.cuts;
+ std::vector<MD5::CameraAnimFrameDesc>& frames = cameraParser.frames;
+
+ // Construct output graph - a simple root with a dummy child.
+ // The root node performs the coordinate system conversion
+ aiNode* root = pScene->mRootNode = new aiNode("<MD5CameraRoot>");
+ root->mChildren = new aiNode*[root->mNumChildren = 1];
+ root->mChildren[0] = new aiNode("<MD5Camera>");
+ root->mChildren[0]->mParent = root;
+
+ // ... but with one camera assigned to it
+ pScene->mCameras = new aiCamera*[pScene->mNumCameras = 1];
+ aiCamera* cam = pScene->mCameras[0] = new aiCamera();
+ cam->mName = "<MD5Camera>";
+
+ // FIXME: Fov is currently set to the first frame's value
+ cam->mHorizontalFOV = AI_DEG_TO_RAD( frames.front().fFOV );
+
+ // every cut is written to a separate aiAnimation
+ if (!cuts.size()) {
+ cuts.push_back(0);
+ cuts.push_back(frames.size()-1);
+ }
+ else {
+ cuts.insert(cuts.begin(),0);
+
+ if (cuts.back() < frames.size()-1)
+ cuts.push_back(frames.size()-1);
+ }
+
+ pScene->mNumAnimations = cuts.size()-1;
+ aiAnimation** tmp = pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations];
+ for (std::vector<unsigned int>::const_iterator it = cuts.begin(); it != cuts.end()-1; ++it) {
+
+ aiAnimation* anim = *tmp++ = new aiAnimation();
+ anim->mName.length = ::sprintf(anim->mName.data,"anim%u_from_%u_to_%u",(unsigned int)(it-cuts.begin()),(*it),*(it+1));
+
+ anim->mTicksPerSecond = cameraParser.fFrameRate;
+ anim->mChannels = new aiNodeAnim*[anim->mNumChannels = 1];
+ aiNodeAnim* nd = anim->mChannels[0] = new aiNodeAnim();
+ nd->mNodeName.Set("<MD5Camera>");
+
+ nd->mNumPositionKeys = nd->mNumRotationKeys = *(it+1) - (*it);
+ nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
+ nd->mRotationKeys = new aiQuatKey [nd->mNumRotationKeys];
+ for (unsigned int i = 0; i < nd->mNumPositionKeys; ++i) {
+
+ nd->mPositionKeys[i].mValue = frames[*it+i].vPositionXYZ;
+ MD5::ConvertQuaternion(frames[*it+i].vRotationQuat,nd->mRotationKeys[i].mValue);
+ nd->mRotationKeys[i].mTime = nd->mPositionKeys[i].mTime = *it+i;
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_MD5_IMPORTER
diff --git a/3rdparty/assimp/code/MD5Loader.h b/3rdparty/assimp/code/MD5Loader.h
new file mode 100644
index 000000000..0677eef97
--- /dev/null
+++ b/3rdparty/assimp/code/MD5Loader.h
@@ -0,0 +1,194 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MD5Loader.h
+ * @brief Definition of the .MD5 importer class.
+ * http://www.modwiki.net/wiki/MD5_(file_format)
+*/
+#ifndef AI_MD5LOADER_H_INCLUDED
+#define AI_MD5LOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "MD5Parser.h"
+
+#include "../include/aiTypes.h"
+
+namespace Assimp {
+
+class IOStream;
+using namespace Assimp::MD5;
+
+// ---------------------------------------------------------------------------
+/** Importer class for the MD5 file format
+*/
+class MD5Importer : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ MD5Importer();
+
+ /** Destructor, private as well */
+ ~MD5Importer();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Load a *.MD5MESH file.
+ */
+ void LoadMD5MeshFile ();
+
+ // -------------------------------------------------------------------
+ /** Load a *.MD5ANIM file.
+ */
+ void LoadMD5AnimFile ();
+
+ // -------------------------------------------------------------------
+ /** Load a *.MD5CAMERA file.
+ */
+ void LoadMD5CameraFile ();
+
+ // -------------------------------------------------------------------
+ /** Construct node hierarchy from a given MD5ANIM
+ * @param iParentID Current parent ID
+ * @param piParent Parent node to attach to
+ * @param bones Input bones
+ * @param node_anims Generated node animations
+ */
+ void AttachChilds_Anim(int iParentID,aiNode* piParent,
+ AnimBoneList& bones,const aiNodeAnim** node_anims);
+
+ // -------------------------------------------------------------------
+ /** Construct node hierarchy from a given MD5MESH
+ * @param iParentID Current parent ID
+ * @param piParent Parent node to attach to
+ * @param bones Input bones
+ */
+ void AttachChilds_Mesh(int iParentID,aiNode* piParent,BoneList& bones);
+
+ // -------------------------------------------------------------------
+ /** Build unique vertex buffers from a given MD5ANIM
+ * @param meshSrc Input data
+ */
+ void MakeDataUnique (MD5::MeshDesc& meshSrc);
+
+ // -------------------------------------------------------------------
+ /** Load the contents of a specific file into memory and
+ * alocates a buffer to keep it.
+ *
+ * mBuffer is modified to point to this buffer.
+ * @param pFile File stream to be read
+ */
+ void LoadFileIntoMemory (IOStream* pFile);
+ void UnloadFileFromMemory ();
+
+
+ /** IOSystem to be used to access files */
+ IOSystem* mIOHandler;
+
+ /** Path to the file, excluding the file extension but
+ with the dot */
+ std::string mFile;
+
+ /** Buffer to hold the loaded file */
+ char* mBuffer;
+
+ /** Size of the file */
+ unsigned int fileSize;
+
+ /** Current line number. For debugging purposes */
+ unsigned int iLineNumber;
+
+ /** Scene to be filled */
+ aiScene* pScene;
+
+ /** (Custom) I/O handler implementation */
+ IOSystem* pIOHandler;
+
+ /** true if a MD5MESH file has already been parsed */
+ bool bHadMD5Mesh;
+
+ /** true if a MD5ANIM file has already been parsed */
+ bool bHadMD5Anim;
+
+ /** true if a MD5CAMERA file has already been parsed */
+ bool bHadMD5Camera;
+
+ /** configuration option: prevent anim autoload */
+ bool configNoAutoLoad;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/MD5Parser.cpp b/3rdparty/assimp/code/MD5Parser.cpp
new file mode 100644
index 000000000..5541b2994
--- /dev/null
+++ b/3rdparty/assimp/code/MD5Parser.cpp
@@ -0,0 +1,471 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MD5Parser.cpp
+ * @brief Implementation of the MD5 parser class
+ */
+#include "AssimpPCH.h"
+
+// internal headers
+#include "MD5Loader.h"
+#include "MaterialSystem.h"
+#include "fast_atof.h"
+#include "ParsingUtils.h"
+#include "StringComparison.h"
+
+using namespace Assimp;
+using namespace Assimp::MD5;
+
+// ------------------------------------------------------------------------------------------------
+// Parse the segment structure fo a MD5 file
+MD5Parser::MD5Parser(char* _buffer, unsigned int _fileSize )
+{
+ ai_assert(NULL != _buffer && 0 != _fileSize);
+
+ buffer = _buffer;
+ fileSize = _fileSize;
+ lineNumber = 0;
+
+ DefaultLogger::get()->debug("MD5Parser begin");
+
+ // parse the file header
+ ParseHeader();
+
+ // and read all sections until we're finished
+ bool running = true;
+ while (running) {
+ mSections.push_back(Section());
+ Section& sec = mSections.back();
+ if (!ParseSection(sec)) {
+ break;
+ }
+ }
+
+ if ( !DefaultLogger::isNullLogger()) {
+ char szBuffer[128]; // should be sufficiently large
+ ::sprintf(szBuffer,"MD5Parser end. Parsed %i sections",(int)mSections.size());
+ DefaultLogger::get()->debug(szBuffer);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Report error to the log stream
+/*static*/ void MD5Parser::ReportError (const char* error, unsigned int line)
+{
+ char szBuffer[1024];
+ ::sprintf(szBuffer,"[MD5] Line %i: %s",line,error);
+ throw DeadlyImportError(szBuffer);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Report warning to the log stream
+/*static*/ void MD5Parser::ReportWarning (const char* warn, unsigned int line)
+{
+ char szBuffer[1024];
+ ::sprintf(szBuffer,"[MD5] Line %i: %s",line,warn);
+ DefaultLogger::get()->warn(szBuffer);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse and validate the MD5 header
+void MD5Parser::ParseHeader()
+{
+ // parse and validate the file version
+ SkipSpaces();
+ if (!TokenMatch(buffer,"MD5Version",10)) {
+ ReportError("Invalid MD5 file: MD5Version tag has not been found");
+ }
+ SkipSpaces();
+ unsigned int iVer = ::strtol10(buffer,(const char**)&buffer);
+ if (10 != iVer) {
+ ReportError("MD5 version tag is unknown (10 is expected)");
+ }
+ SkipLine();
+
+ // print the command line options to the console
+ // FIX: can break the log length limit, so we need to be careful
+ char* sz = buffer;
+ while (!IsLineEnd( *buffer++)) {};
+ DefaultLogger::get()->info(std::string(sz,std::min((uintptr_t)MAX_LOG_MESSAGE_LENGTH, (uintptr_t)(buffer-sz))));
+ SkipSpacesAndLineEnd();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursive MD5 parsing function
+bool MD5Parser::ParseSection(Section& out)
+{
+ // store the current line number for use in error messages
+ out.iLineNumber = lineNumber;
+
+ // first parse the name of the section
+ char* sz = buffer;
+ while (!IsSpaceOrNewLine( *buffer))buffer++;
+ out.mName = std::string(sz,(uintptr_t)(buffer-sz));
+ SkipSpaces();
+
+ bool running = true;
+ while (running) {
+ if ('{' == *buffer) {
+ // it is a normal section so read all lines
+ buffer++;
+ bool run = true;
+ while (run)
+ {
+ if (!SkipSpacesAndLineEnd()) {
+ return false; // seems this was the last section
+ }
+ if ('}' == *buffer) {
+ buffer++;
+ break;
+ }
+
+ out.mElements.push_back(Element());
+ Element& elem = out.mElements.back();
+
+ elem.iLineNumber = lineNumber;
+ elem.szStart = buffer;
+
+ // terminate the line with zero
+ while (!IsLineEnd( *buffer))buffer++;
+ if (*buffer) {
+ ++lineNumber;
+ *buffer++ = '\0';
+ }
+ }
+ break;
+ }
+ else if (!IsSpaceOrNewLine(*buffer)) {
+ // it is an element at global scope. Parse its value and go on
+ sz = buffer;
+ while (!IsSpaceOrNewLine( *buffer++)) {};
+ out.mGlobalValue = std::string(sz,(uintptr_t)(buffer-sz));
+ continue;
+ }
+ break;
+ }
+ return SkipSpacesAndLineEnd();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Some dirty macros just because they're so funny and easy to debug
+
+// skip all spaces ... handle EOL correctly
+#define AI_MD5_SKIP_SPACES() if (!SkipSpaces(&sz)) \
+ MD5Parser::ReportWarning("Unexpected end of line",(*eit).iLineNumber);
+
+ // read a triple float in brackets: (1.0 1.0 1.0)
+#define AI_MD5_READ_TRIPLE(vec) \
+ AI_MD5_SKIP_SPACES(); \
+ if ('(' != *sz++) \
+ MD5Parser::ReportWarning("Unexpected token: ( was expected",(*eit).iLineNumber); \
+ AI_MD5_SKIP_SPACES(); \
+ sz = fast_atof_move(sz,(float&)vec.x); \
+ AI_MD5_SKIP_SPACES(); \
+ sz = fast_atof_move(sz,(float&)vec.y); \
+ AI_MD5_SKIP_SPACES(); \
+ sz = fast_atof_move(sz,(float&)vec.z); \
+ AI_MD5_SKIP_SPACES(); \
+ if (')' != *sz++) \
+ MD5Parser::ReportWarning("Unexpected token: ) was expected",(*eit).iLineNumber);
+
+ // parse a string, enclosed in quotation marks or not
+#define AI_MD5_PARSE_STRING(out) \
+ bool bQuota = (*sz == '\"'); \
+ const char* szStart = sz; \
+ while (!IsSpaceOrNewLine(*sz))++sz; \
+ const char* szEnd = sz; \
+ if (bQuota) { \
+ szStart++; \
+ if ('\"' != *(szEnd-=1)) { \
+ MD5Parser::ReportWarning("Expected closing quotation marks in string", \
+ (*eit).iLineNumber); \
+ continue; \
+ } \
+ } \
+ out.length = (size_t)(szEnd - szStart); \
+ ::memcpy(out.data,szStart,out.length); \
+ out.data[out.length] = '\0';
+
+// ------------------------------------------------------------------------------------------------
+// .MD5MESH parsing function
+MD5MeshParser::MD5MeshParser(SectionList& mSections)
+{
+ DefaultLogger::get()->debug("MD5MeshParser begin");
+
+ // now parse all sections
+ for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter){
+ if ( (*iter).mName == "numMeshes") {
+ mMeshes.reserve(::strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ( (*iter).mName == "numJoints") {
+ mJoints.reserve(::strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ((*iter).mName == "joints") {
+ // "origin" -1 ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000000 0.707107 )
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit){
+ mJoints.push_back(BoneDesc());
+ BoneDesc& desc = mJoints.back();
+
+ const char* sz = (*eit).szStart;
+ AI_MD5_PARSE_STRING(desc.mName);
+ AI_MD5_SKIP_SPACES();
+
+ // negative values, at least -1, is allowed here
+ desc.mParentIndex = (int)strtol10s(sz,&sz);
+
+ AI_MD5_READ_TRIPLE(desc.mPositionXYZ);
+ AI_MD5_READ_TRIPLE(desc.mRotationQuat); // normalized quaternion, so w is not there
+ }
+ }
+ else if ((*iter).mName == "mesh") {
+ mMeshes.push_back(MeshDesc());
+ MeshDesc& desc = mMeshes.back();
+
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit){
+ const char* sz = (*eit).szStart;
+
+ // shader attribute
+ if (TokenMatch(sz,"shader",6)) {
+ AI_MD5_SKIP_SPACES();
+ AI_MD5_PARSE_STRING(desc.mShader);
+ }
+ // numverts attribute
+ else if (TokenMatch(sz,"numverts",8)) {
+ AI_MD5_SKIP_SPACES();
+ desc.mVertices.resize(strtol10(sz));
+ }
+ // numtris attribute
+ else if (TokenMatch(sz,"numtris",7)) {
+ AI_MD5_SKIP_SPACES();
+ desc.mFaces.resize(strtol10(sz));
+ }
+ // numweights attribute
+ else if (TokenMatch(sz,"numweights",10)) {
+ AI_MD5_SKIP_SPACES();
+ desc.mWeights.resize(strtol10(sz));
+ }
+ // vert attribute
+ // "vert 0 ( 0.394531 0.513672 ) 0 1"
+ else if (TokenMatch(sz,"vert",4)) {
+ AI_MD5_SKIP_SPACES();
+ const unsigned int idx = ::strtol10(sz,&sz);
+ AI_MD5_SKIP_SPACES();
+ if (idx >= desc.mVertices.size())
+ desc.mVertices.resize(idx+1);
+
+ VertexDesc& vert = desc.mVertices[idx];
+ if ('(' != *sz++)
+ MD5Parser::ReportWarning("Unexpected token: ( was expected",(*eit).iLineNumber);
+ AI_MD5_SKIP_SPACES();
+ sz = fast_atof_move(sz,(float&)vert.mUV.x);
+ AI_MD5_SKIP_SPACES();
+ sz = fast_atof_move(sz,(float&)vert.mUV.y);
+ AI_MD5_SKIP_SPACES();
+ if (')' != *sz++)
+ MD5Parser::ReportWarning("Unexpected token: ) was expected",(*eit).iLineNumber);
+ AI_MD5_SKIP_SPACES();
+ vert.mFirstWeight = ::strtol10(sz,&sz);
+ AI_MD5_SKIP_SPACES();
+ vert.mNumWeights = ::strtol10(sz,&sz);
+ }
+ // tri attribute
+ // "tri 0 15 13 12"
+ else if (TokenMatch(sz,"tri",3)) {
+ AI_MD5_SKIP_SPACES();
+ const unsigned int idx = strtol10(sz,&sz);
+ if (idx >= desc.mFaces.size())
+ desc.mFaces.resize(idx+1);
+
+ aiFace& face = desc.mFaces[idx];
+ face.mIndices = new unsigned int[face.mNumIndices = 3];
+ for (unsigned int i = 0; i < 3;++i) {
+ AI_MD5_SKIP_SPACES();
+ face.mIndices[i] = strtol10(sz,&sz);
+ }
+ }
+ // weight attribute
+ // "weight 362 5 0.500000 ( -3.553583 11.893474 9.719339 )"
+ else if (TokenMatch(sz,"weight",6)) {
+ AI_MD5_SKIP_SPACES();
+ const unsigned int idx = strtol10(sz,&sz);
+ AI_MD5_SKIP_SPACES();
+ if (idx >= desc.mWeights.size())
+ desc.mWeights.resize(idx+1);
+
+ WeightDesc& weight = desc.mWeights[idx];
+ weight.mBone = strtol10(sz,&sz);
+ AI_MD5_SKIP_SPACES();
+ sz = fast_atof_move(sz,weight.mWeight);
+ AI_MD5_READ_TRIPLE(weight.vOffsetPosition);
+ }
+ }
+ }
+ }
+ DefaultLogger::get()->debug("MD5MeshParser end");
+}
+
+// ------------------------------------------------------------------------------------------------
+// .MD5ANIM parsing function
+MD5AnimParser::MD5AnimParser(SectionList& mSections)
+{
+ DefaultLogger::get()->debug("MD5AnimParser begin");
+
+ fFrameRate = 24.0f;
+ mNumAnimatedComponents = 0xffffffff;
+ for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter) {
+ if ((*iter).mName == "hierarchy") {
+ // "sheath" 0 63 6
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();eit != eitEnd; ++eit) {
+ mAnimatedBones.push_back ( AnimBoneDesc () );
+ AnimBoneDesc& desc = mAnimatedBones.back();
+
+ const char* sz = (*eit).szStart;
+ AI_MD5_PARSE_STRING(desc.mName);
+ AI_MD5_SKIP_SPACES();
+
+ // parent index - negative values are allowed (at least -1)
+ desc.mParentIndex = ::strtol10s(sz,&sz);
+
+ // flags (highest is 2^6-1)
+ AI_MD5_SKIP_SPACES();
+ if (63 < (desc.iFlags = ::strtol10(sz,&sz))){
+ MD5Parser::ReportWarning("Invalid flag combination in hierarchy section",(*eit).iLineNumber);
+ }
+ AI_MD5_SKIP_SPACES();
+
+ // index of the first animation keyframe component for this joint
+ desc.iFirstKeyIndex = ::strtol10(sz,&sz);
+ }
+ }
+ else if ((*iter).mName == "baseframe") {
+ // ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000242 0.707107 )
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit) {
+ const char* sz = (*eit).szStart;
+
+ mBaseFrames.push_back ( BaseFrameDesc () );
+ BaseFrameDesc& desc = mBaseFrames.back();
+
+ AI_MD5_READ_TRIPLE(desc.vPositionXYZ);
+ AI_MD5_READ_TRIPLE(desc.vRotationQuat);
+ }
+ }
+ else if ((*iter).mName == "frame") {
+ if (!(*iter).mGlobalValue.length()) {
+ MD5Parser::ReportWarning("A frame section must have a frame index",(*iter).iLineNumber);
+ continue;
+ }
+
+ mFrames.push_back ( FrameDesc () );
+ FrameDesc& desc = mFrames.back();
+ desc.iIndex = strtol10((*iter).mGlobalValue.c_str());
+
+ // we do already know how much storage we will presumably need
+ if (0xffffffff != mNumAnimatedComponents)
+ desc.mValues.reserve(mNumAnimatedComponents);
+
+ // now read all elements (continous list of floats)
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit){
+ const char* sz = (*eit).szStart;
+ while (SkipSpacesAndLineEnd(&sz)) {
+ float f;sz = fast_atof_move(sz,f);
+ desc.mValues.push_back(f);
+ }
+ }
+ }
+ else if ((*iter).mName == "numFrames") {
+ mFrames.reserve(strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ((*iter).mName == "numJoints") {
+ const unsigned int num = strtol10((*iter).mGlobalValue.c_str());
+ mAnimatedBones.reserve(num);
+
+ // try to guess the number of animated components if that element is not given
+ if (0xffffffff == mNumAnimatedComponents)
+ mNumAnimatedComponents = num * 6;
+ }
+ else if ((*iter).mName == "numAnimatedComponents") {
+ mAnimatedBones.reserve( strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ((*iter).mName == "frameRate") {
+ fast_atof_move((*iter).mGlobalValue.c_str(),fFrameRate);
+ }
+ }
+ DefaultLogger::get()->debug("MD5AnimParser end");
+}
+
+// ------------------------------------------------------------------------------------------------
+// .MD5CAMERA parsing function
+MD5CameraParser::MD5CameraParser(SectionList& mSections)
+{
+ DefaultLogger::get()->debug("MD5CameraParser begin");
+ fFrameRate = 24.0f;
+
+ for (SectionList::const_iterator iter = mSections.begin(), iterEnd = mSections.end();iter != iterEnd;++iter) {
+ if ((*iter).mName == "numFrames") {
+ frames.reserve(strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ((*iter).mName == "frameRate") {
+ fFrameRate = fast_atof ((*iter).mGlobalValue.c_str());
+ }
+ else if ((*iter).mName == "numCuts") {
+ cuts.reserve(strtol10((*iter).mGlobalValue.c_str()));
+ }
+ else if ((*iter).mName == "cuts") {
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit){
+ cuts.push_back(strtol10((*eit).szStart)+1);
+ }
+ }
+ else if ((*iter).mName == "camera") {
+ for (ElementList::const_iterator eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end(); eit != eitEnd; ++eit){
+ const char* sz = (*eit).szStart;
+
+ frames.push_back(CameraAnimFrameDesc());
+ CameraAnimFrameDesc& cur = frames.back();
+ AI_MD5_READ_TRIPLE(cur.vPositionXYZ);
+ AI_MD5_READ_TRIPLE(cur.vRotationQuat);
+ AI_MD5_SKIP_SPACES();
+ cur.fFOV = fast_atof(sz);
+ }
+ }
+ }
+ DefaultLogger::get()->debug("MD5CameraParser end");
+}
+
diff --git a/3rdparty/assimp/code/MD5Parser.h b/3rdparty/assimp/code/MD5Parser.h
new file mode 100644
index 000000000..33a0e4d87
--- /dev/null
+++ b/3rdparty/assimp/code/MD5Parser.h
@@ -0,0 +1,460 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MD5Parser.h
+ * @brief Definition of the .MD5 parser class.
+ * http://www.modwiki.net/wiki/MD5_(file_format)
+ */
+#ifndef AI_MD5PARSER_H_INCLUDED
+#define AI_MD5PARSER_H_INCLUDED
+
+#include "../include/aiTypes.h"
+#include "ParsingUtils.h"
+
+struct aiFace;
+
+namespace Assimp {
+namespace MD5 {
+
+// ---------------------------------------------------------------------------
+/** Represents a single element in a MD5 file
+ *
+ * Elements are always contained in sections.
+*/
+struct Element
+{
+ //! Points to the starting point of the element
+ //! Whitespace at the beginning and at the end have been removed,
+ //! Elements are terminated with \0
+ char* szStart;
+
+ //! Original line number (can be used in error messages
+ //! if a parsing error occurs)
+ unsigned int iLineNumber;
+};
+
+typedef std::vector< Element > ElementList;
+
+// ---------------------------------------------------------------------------
+/** Represents a section of a MD5 file (such as the mesh or the joints section)
+ *
+ * A section is always enclosed in { and } brackets.
+*/
+struct Section
+{
+ //! Original line number (can be used in error messages
+ //! if a parsing error occurs)
+ unsigned int iLineNumber;
+
+ //! List of all elements which have been parsed in this section.
+ ElementList mElements;
+
+ //! Name of the section
+ std::string mName;
+
+ //! For global elements: the value of the element as string
+ //! Iif !length() the section is not a global element
+ std::string mGlobalValue;
+};
+
+typedef std::vector< Section> SectionList;
+
+// ---------------------------------------------------------------------------
+/** Basic information about a joint
+*/
+struct BaseJointDescription
+{
+ //! Name of the bone
+ aiString mName;
+
+ //! Parent index of the bone
+ int mParentIndex;
+};
+
+// ---------------------------------------------------------------------------
+/** Represents a bone (joint) descriptor in a MD5Mesh file
+*/
+struct BoneDesc : BaseJointDescription
+{
+ //! Absolute position of the bone
+ aiVector3D mPositionXYZ;
+
+ //! Absolute rotation of the bone
+ aiVector3D mRotationQuat;
+ aiQuaternion mRotationQuatConverted;
+
+ //! Absolute transformation of the bone
+ //! (temporary)
+ aiMatrix4x4 mTransform;
+
+ //! Inverse transformation of the bone
+ //! (temporary)
+ aiMatrix4x4 mInvTransform;
+
+ //! Internal
+ unsigned int mMap;
+};
+
+typedef std::vector< BoneDesc > BoneList;
+
+// ---------------------------------------------------------------------------
+/** Represents a bone (joint) descriptor in a MD5Anim file
+*/
+struct AnimBoneDesc : BaseJointDescription
+{
+ //! Flags (AI_MD5_ANIMATION_FLAG_xxx)
+ unsigned int iFlags;
+
+ //! Index of the first key that corresponds to this anim bone
+ unsigned int iFirstKeyIndex;
+};
+
+typedef std::vector< AnimBoneDesc > AnimBoneList;
+
+
+// ---------------------------------------------------------------------------
+/** Represents a base frame descriptor in a MD5Anim file
+*/
+struct BaseFrameDesc
+{
+ aiVector3D vPositionXYZ;
+ aiVector3D vRotationQuat;
+};
+
+typedef std::vector< BaseFrameDesc > BaseFrameList;
+
+// ---------------------------------------------------------------------------
+/** Represents a camera animation frame in a MDCamera file
+*/
+struct CameraAnimFrameDesc : BaseFrameDesc
+{
+ float fFOV;
+};
+
+typedef std::vector< CameraAnimFrameDesc > CameraFrameList;
+
+// ---------------------------------------------------------------------------
+/** Represents a frame descriptor in a MD5Anim file
+*/
+struct FrameDesc
+{
+ //! Index of the frame
+ unsigned int iIndex;
+
+ //! Animation keyframes - a large blob of data at first
+ std::vector< float > mValues;
+};
+
+typedef std::vector< FrameDesc > FrameList;
+
+// ---------------------------------------------------------------------------
+/** Represents a vertex descriptor in a MD5 file
+*/
+struct VertexDesc
+{
+ VertexDesc()
+ : mFirstWeight (0)
+ , mNumWeights (0)
+ {}
+
+ //! UV cordinate of the vertex
+ aiVector2D mUV;
+
+ //! Index of the first weight of the vertex in
+ //! the vertex weight list
+ unsigned int mFirstWeight;
+
+ //! Number of weights assigned to this vertex
+ unsigned int mNumWeights;
+};
+
+typedef std::vector< VertexDesc > VertexList;
+
+// ---------------------------------------------------------------------------
+/** Represents a vertex weight descriptor in a MD5 file
+*/
+struct WeightDesc
+{
+ //! Index of the bone to which this weight refers
+ unsigned int mBone;
+
+ //! The weight value
+ float mWeight;
+
+ //! The offset position of this weight
+ // ! (in the coordinate system defined by the parent bone)
+ aiVector3D vOffsetPosition;
+};
+
+typedef std::vector< WeightDesc > WeightList;
+typedef std::vector< aiFace > FaceList;
+
+// ---------------------------------------------------------------------------
+/** Represents a mesh in a MD5 file
+*/
+struct MeshDesc
+{
+ //! Weights of the mesh
+ WeightList mWeights;
+
+ //! Vertices of the mesh
+ VertexList mVertices;
+
+ //! Faces of the mesh
+ FaceList mFaces;
+
+ //! Name of the shader (=texture) to be assigned to the mesh
+ aiString mShader;
+};
+
+typedef std::vector< MeshDesc > MeshList;
+
+// ---------------------------------------------------------------------------
+// Convert a quaternion to its usual representation
+inline void ConvertQuaternion (const aiVector3D& in, aiQuaternion& out) {
+
+ out.x = in.x;
+ out.y = in.y;
+ out.z = in.z;
+
+ const float t = 1.0f - (in.x*in.x) - (in.y*in.y) - (in.z*in.z);
+
+ if (t < 0.0f)
+ out.w = 0.0f;
+ else out.w = sqrt (t);
+}
+
+// ---------------------------------------------------------------------------
+/** Parses the data sections of a MD5 mesh file
+*/
+class MD5MeshParser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5MeshParser instance from an existing
+ * preparsed list of file sections.
+ *
+ * @param mSections List of file sections (output of MD5Parser)
+ */
+ MD5MeshParser(SectionList& mSections);
+
+ //! List of all meshes
+ MeshList mMeshes;
+
+ //! List of all joints
+ BoneList mJoints;
+};
+
+// remove this flag if you need to the bounding box data
+#define AI_MD5_PARSE_NO_BOUNDS
+
+// ---------------------------------------------------------------------------
+/** Parses the data sections of a MD5 animation file
+*/
+class MD5AnimParser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5AnimParser instance from an existing
+ * preparsed list of file sections.
+ *
+ * @param mSections List of file sections (output of MD5Parser)
+ */
+ MD5AnimParser(SectionList& mSections);
+
+
+ //! Output frame rate
+ float fFrameRate;
+
+ //! List of animation bones
+ AnimBoneList mAnimatedBones;
+
+ //! List of base frames
+ BaseFrameList mBaseFrames;
+
+ //! List of animation frames
+ FrameList mFrames;
+
+ //! Number of animated components
+ unsigned int mNumAnimatedComponents;
+};
+
+// ---------------------------------------------------------------------------
+/** Parses the data sections of a MD5 camera animation file
+*/
+class MD5CameraParser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5CameraParser instance from an existing
+ * preparsed list of file sections.
+ *
+ * @param mSections List of file sections (output of MD5Parser)
+ */
+ MD5CameraParser(SectionList& mSections);
+
+
+ //! Output frame rate
+ float fFrameRate;
+
+ //! List of cuts
+ std::vector<unsigned int> cuts;
+
+ //! Frames
+ CameraFrameList frames;
+};
+
+// ---------------------------------------------------------------------------
+/** Parses the block structure of MD5MESH and MD5ANIM files (but does no
+ * further processing)
+*/
+class MD5Parser
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** Constructs a new MD5Parser instance from an existing buffer.
+ *
+ * @param buffer File buffer
+ * @param fileSize Length of the file in bytes (excluding a terminal 0)
+ */
+ MD5Parser(char* buffer, unsigned int fileSize);
+
+
+ // -------------------------------------------------------------------
+ /** Report a specific error message and throw an exception
+ * @param error Error message to be reported
+ * @param line Index of the line where the error occured
+ */
+ static void ReportError (const char* error, unsigned int line);
+
+ // -------------------------------------------------------------------
+ /** Report a specific warning
+ * @param warn Warn message to be reported
+ * @param line Index of the line where the error occured
+ */
+ static void ReportWarning (const char* warn, unsigned int line);
+
+
+ void ReportError (const char* error) {
+ return ReportError(error, lineNumber);
+ }
+
+ void ReportWarning (const char* warn) {
+ return ReportWarning(warn, lineNumber);
+ }
+
+public:
+
+ //! List of all sections which have been read
+ SectionList mSections;
+
+private:
+
+ // -------------------------------------------------------------------
+ /** Parses a file section. The current file pointer must be outside
+ * of a section.
+ * @param out Receives the section data
+ * @return true if the end of the file has been reached
+ * @throws ImportErrorException if an error occurs
+ */
+ bool ParseSection(Section& out);
+
+ // -------------------------------------------------------------------
+ /** Parses the file header
+ * @throws ImportErrorException if an error occurs
+ */
+ void ParseHeader();
+
+
+ // override these functions to make sure the line counter gets incremented
+ // -------------------------------------------------------------------
+ bool SkipLine( const char* in, const char** out)
+ {
+ ++lineNumber;
+ return Assimp::SkipLine(in,out);
+ }
+ // -------------------------------------------------------------------
+ bool SkipLine( )
+ {
+ return SkipLine(buffer,(const char**)&buffer);
+ }
+ // -------------------------------------------------------------------
+ bool SkipSpacesAndLineEnd( const char* in, const char** out)
+ {
+ bool bHad = false;
+ bool running = true;
+ while (running) {
+ if ( *in == '\r' || *in == '\n') {
+ // we open files in binary mode, so there could be \r\n sequences ...
+ if (!bHad) {
+ bHad = true;
+ ++lineNumber;
+ }
+ }
+ else if (*in == '\t' || *in == ' ')bHad = false;
+ else break;
+ in++;
+ }
+ *out = in;
+ return *in != '\0';
+ }
+ // -------------------------------------------------------------------
+ bool SkipSpacesAndLineEnd( )
+ {
+ return SkipSpacesAndLineEnd(buffer,(const char**)&buffer);
+ }
+ // -------------------------------------------------------------------
+ bool SkipSpaces( )
+ {
+ return Assimp::SkipSpaces((const char**)&buffer);
+ }
+
+ char* buffer;
+ unsigned int fileSize;
+ unsigned int lineNumber;
+};
+}}
+
+#endif // AI_MD5PARSER_H_INCLUDED
diff --git a/3rdparty/assimp/code/MDCFileData.h b/3rdparty/assimp/code/MDCFileData.h
new file mode 100644
index 000000000..3208c3fa9
--- /dev/null
+++ b/3rdparty/assimp/code/MDCFileData.h
@@ -0,0 +1,199 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines the helper data structures for importing MDC files
+
+**********************************************************************
+File format specification:
+http://themdcfile.planetwolfenstein.gamespy.com/MDC_File_Format.pdf
+**********************************************************************
+
+*/
+#ifndef AI_MDCFILEHELPER_H_INC
+#define AI_MDCFILEHELPER_H_INC
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+#include "./../include/Compiler/pushpack1.h"
+
+
+namespace Assimp {
+namespace MDC {
+
+
+// to make it easier for us, we test the magic word against both "endianesses"
+#define AI_MDC_MAGIC_NUMBER_BE AI_MAKE_MAGIC("CPDI")
+#define AI_MDC_MAGIC_NUMBER_LE AI_MAKE_MAGIC("IDPC")
+
+// common limitations
+#define AI_MDC_VERSION 2
+#define AI_MDC_MAXQPATH 64
+#define AI_MDC_MAX_BONES 128
+
+#define AI_MDC_CVERT_BIAS 127.0f
+#define AI_MDC_DELTA_SCALING 4.0f
+#define AI_MDC_BASE_SCALING (1.0f / 64.0f)
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC file's main header
+ */
+struct Header
+{
+ uint32_t ulIdent ;
+ uint32_t ulVersion ;
+ char ucName [ AI_MDC_MAXQPATH ] ;
+ uint32_t ulFlags ;
+ uint32_t ulNumFrames ;
+ uint32_t ulNumTags ;
+ uint32_t ulNumSurfaces ;
+ uint32_t ulNumSkins ;
+ uint32_t ulOffsetBorderFrames ;
+ uint32_t ulOffsetTagNames ;
+ uint32_t ulOffsetTagFrames ;
+ uint32_t ulOffsetSurfaces ;
+ uint32_t ulOffsetEnd ;
+} PACK_STRUCT ;
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC file's surface header
+ */
+struct Surface
+{
+ uint32_t ulIdent ;
+ char ucName [ AI_MDC_MAXQPATH ] ;
+ uint32_t ulFlags ;
+ uint32_t ulNumCompFrames ;
+ uint32_t ulNumBaseFrames ;
+ uint32_t ulNumShaders ;
+ uint32_t ulNumVertices ;
+ uint32_t ulNumTriangles ;
+ uint32_t ulOffsetTriangles ;
+ uint32_t ulOffsetShaders ;
+ uint32_t ulOffsetTexCoords ;
+ uint32_t ulOffsetBaseVerts ;
+ uint32_t ulOffsetCompVerts ;
+ uint32_t ulOffsetFrameBaseFrames ;
+ uint32_t ulOffsetFrameCompFrames ;
+ uint32_t ulOffsetEnd ;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC frame
+ */
+struct Frame
+{
+ //! bounding box minimum coords
+ aiVector3D bboxMin ;
+
+ //! bounding box maximum coords
+ aiVector3D bboxMax ;
+
+ //! local origin of the frame
+ aiVector3D localOrigin ;
+
+ //! radius of the BB
+ float radius ;
+
+ //! Name of the frame
+ char name [ 16 ] ;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC triangle
+ */
+struct Triangle
+{
+ uint32_t aiIndices[3];
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC texture coordinate
+ */
+struct TexturCoord
+{
+ float u,v;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC base vertex
+ */
+struct BaseVertex
+{
+ int16_t x,y,z;
+ uint16_t normal;
+} PACK_STRUCT;
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC compressed vertex
+ */
+struct CompressedVertex
+{
+ uint8_t xd,yd,zd,nd;
+} PACK_STRUCT;
+
+
+// ---------------------------------------------------------------------------
+/** \brief Data structure for a MDC shader
+ */
+struct Shader
+{
+ char ucName [ AI_MDC_MAXQPATH ] ;
+ uint32_t ulPath;
+
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+
+// ---------------------------------------------------------------------------
+/** Build a floating point vertex from the compressed data in MDC files
+ */
+void BuildVertex(const Frame& frame,
+ const BaseVertex& bvert,
+ const CompressedVertex& cvert,
+ aiVector3D& vXYZOut,
+ aiVector3D& vNorOut);
+}}
+
+#endif // !! AI_MDCFILEHELPER_H_INC
diff --git a/3rdparty/assimp/code/MDCLoader.cpp b/3rdparty/assimp/code/MDCLoader.cpp
new file mode 100644
index 000000000..077f3b0a0
--- /dev/null
+++ b/3rdparty/assimp/code/MDCLoader.cpp
@@ -0,0 +1,472 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the MDC importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
+
+// internal headers
+#include "MDCLoader.h"
+#include "MD3FileData.h"
+#include "MDCNormalTable.h" // shouldn't be included by other units
+
+using namespace Assimp;
+using namespace Assimp::MDC;
+
+
+// ------------------------------------------------------------------------------------------------
+void MDC::BuildVertex(const Frame& frame,
+ const BaseVertex& bvert,
+ const CompressedVertex& cvert,
+ aiVector3D& vXYZOut,
+ aiVector3D& vNorOut)
+{
+ // compute the position
+ const float xd = (cvert.xd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
+ const float yd = (cvert.yd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
+ const float zd = (cvert.zd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
+ vXYZOut.x = frame.localOrigin.x + AI_MDC_BASE_SCALING * (bvert.x + xd);
+ vXYZOut.y = frame.localOrigin.y + AI_MDC_BASE_SCALING * (bvert.y + yd);
+ vXYZOut.z = frame.localOrigin.z + AI_MDC_BASE_SCALING * (bvert.z + zd);
+
+ // compute the normal vector .. ehm ... lookup it in the table :-)
+ vNorOut.x = mdcNormals[cvert.nd][0];
+ vNorOut.y = mdcNormals[cvert.nd][1];
+ vNorOut.z = mdcNormals[cvert.nd][2];
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MDCImporter::MDCImporter()
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MDCImporter::~MDCImporter()
+{
+}
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MDCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+ if (extension == "mdc")
+ return true;
+
+ // if check for extension is not enough, check for the magic tokens
+ if (!extension.length() || checkSig) {
+ uint32_t tokens[1];
+ tokens[0] = AI_MDC_MAGIC_NUMBER_LE;
+ return CheckMagicToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MDCImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("mdc");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Validate the header of the given MDC file
+void MDCImporter::ValidateHeader()
+{
+ AI_SWAP4( this->pcHeader->ulVersion );
+ AI_SWAP4( this->pcHeader->ulFlags );
+ AI_SWAP4( this->pcHeader->ulNumFrames );
+ AI_SWAP4( this->pcHeader->ulNumTags );
+ AI_SWAP4( this->pcHeader->ulNumSurfaces );
+ AI_SWAP4( this->pcHeader->ulNumSkins );
+ AI_SWAP4( this->pcHeader->ulOffsetBorderFrames );
+
+ if (pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_BE &&
+ pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_LE)
+ {
+ char szBuffer[5];
+ szBuffer[0] = ((char*)&pcHeader->ulIdent)[0];
+ szBuffer[1] = ((char*)&pcHeader->ulIdent)[1];
+ szBuffer[2] = ((char*)&pcHeader->ulIdent)[2];
+ szBuffer[3] = ((char*)&pcHeader->ulIdent)[3];
+ szBuffer[4] = '\0';
+
+ throw DeadlyImportError("Invalid MDC magic word: should be IDPC, the "
+ "magic word found is " + std::string( szBuffer ));
+ }
+
+ if (pcHeader->ulVersion != AI_MDC_VERSION)
+ DefaultLogger::get()->warn("Unsupported MDC file version (2 (AI_MDC_VERSION) was expected)");
+
+ if (pcHeader->ulOffsetBorderFrames + pcHeader->ulNumFrames * sizeof(MDC::Frame) > this->fileSize ||
+ pcHeader->ulOffsetSurfaces + pcHeader->ulNumSurfaces * sizeof(MDC::Surface) > this->fileSize)
+ {
+ throw DeadlyImportError("Some of the offset values in the MDC header are invalid "
+ "and point to something behind the file.");
+ }
+
+ if (this->configFrameID >= this->pcHeader->ulNumFrames)
+ throw DeadlyImportError("The requested frame is not available");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Validate the header of a given MDC file surface
+void MDCImporter::ValidateSurfaceHeader(BE_NCONST MDC::Surface* pcSurf)
+{
+ AI_SWAP4(pcSurf->ulFlags);
+ AI_SWAP4(pcSurf->ulNumCompFrames);
+ AI_SWAP4(pcSurf->ulNumBaseFrames);
+ AI_SWAP4(pcSurf->ulNumShaders);
+ AI_SWAP4(pcSurf->ulNumVertices);
+ AI_SWAP4(pcSurf->ulNumTriangles);
+ AI_SWAP4(pcSurf->ulOffsetTriangles);
+ AI_SWAP4(pcSurf->ulOffsetTexCoords);
+ AI_SWAP4(pcSurf->ulOffsetBaseVerts);
+ AI_SWAP4(pcSurf->ulOffsetCompVerts);
+ AI_SWAP4(pcSurf->ulOffsetFrameBaseFrames);
+ AI_SWAP4(pcSurf->ulOffsetFrameCompFrames);
+ AI_SWAP4(pcSurf->ulOffsetEnd);
+
+ const unsigned int iMax = this->fileSize - (unsigned int)((int8_t*)pcSurf-(int8_t*)pcHeader);
+
+ if (pcSurf->ulOffsetBaseVerts + pcSurf->ulNumVertices * sizeof(MDC::BaseVertex) > iMax ||
+ (pcSurf->ulNumCompFrames && pcSurf->ulOffsetCompVerts + pcSurf->ulNumVertices * sizeof(MDC::CompressedVertex) > iMax) ||
+ pcSurf->ulOffsetTriangles + pcSurf->ulNumTriangles * sizeof(MDC::Triangle) > iMax ||
+ pcSurf->ulOffsetTexCoords + pcSurf->ulNumVertices * sizeof(MDC::TexturCoord) > iMax ||
+ pcSurf->ulOffsetShaders + pcSurf->ulNumShaders * sizeof(MDC::Shader) > iMax ||
+ pcSurf->ulOffsetFrameBaseFrames + pcSurf->ulNumBaseFrames * 2 > iMax ||
+ (pcSurf->ulNumCompFrames && pcSurf->ulOffsetFrameCompFrames + pcSurf->ulNumCompFrames * 2 > iMax))
+ {
+ throw DeadlyImportError("Some of the offset values in the MDC surface header "
+ "are invalid and point somewhere behind the file.");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void MDCImporter::SetupProperties(const Importer* pImp)
+{
+ // The AI_CONFIG_IMPORT_MDC_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ if (0xffffffff == (this->configFrameID = pImp->GetPropertyInteger(
+ AI_CONFIG_IMPORT_MDC_KEYFRAME,0xffffffff)))
+ {
+ this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MDCImporter::InternReadFile(
+ const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open MDC file " + pFile + ".");
+
+ // check whether the mdc file is large enough to contain the file header
+ fileSize = (unsigned int)file->FileSize();
+ if ( fileSize < sizeof(MDC::Header))
+ throw DeadlyImportError( "MDC File is too small.");
+
+ std::vector<unsigned char> mBuffer2(fileSize);
+ file->Read( &mBuffer2[0], 1, fileSize);
+ mBuffer = &mBuffer2[0];
+
+ // validate the file header
+ this->pcHeader = (BE_NCONST MDC::Header*)this->mBuffer;
+ this->ValidateHeader();
+
+ std::vector<std::string> aszShaders;
+
+ // get a pointer to the frame we want to read
+ BE_NCONST MDC::Frame* pcFrame = (BE_NCONST MDC::Frame*)(this->mBuffer+
+ this->pcHeader->ulOffsetBorderFrames);
+
+ // no need to swap the other members, we won't need them
+ pcFrame += configFrameID;
+ AI_SWAP4( pcFrame->localOrigin[0] );
+ AI_SWAP4( pcFrame->localOrigin[1] );
+ AI_SWAP4( pcFrame->localOrigin[2] );
+
+ // get the number of valid surfaces
+ BE_NCONST MDC::Surface* pcSurface, *pcSurface2;
+ pcSurface = pcSurface2 = (BE_NCONST MDC::Surface*)(mBuffer + pcHeader->ulOffsetSurfaces);
+ unsigned int iNumShaders = 0;
+ for (unsigned int i = 0; i < pcHeader->ulNumSurfaces;++i)
+ {
+ // validate the surface header
+ this->ValidateSurfaceHeader(pcSurface2);
+
+ if (pcSurface2->ulNumVertices && pcSurface2->ulNumTriangles)++pScene->mNumMeshes;
+ iNumShaders += pcSurface2->ulNumShaders;
+ pcSurface2 = (BE_NCONST MDC::Surface*)((int8_t*)pcSurface2 + pcSurface2->ulOffsetEnd);
+ }
+ aszShaders.reserve(iNumShaders);
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+
+ // necessary that we don't crash if an exception occurs
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mMeshes[i] = NULL;
+
+ // now read all surfaces
+ unsigned int iDefaultMatIndex = 0xffffffff;
+ for (unsigned int i = 0, iNum = 0; i < pcHeader->ulNumSurfaces;++i)
+ {
+ if (!pcSurface->ulNumVertices || !pcSurface->ulNumTriangles)continue;
+ aiMesh* pcMesh = pScene->mMeshes[iNum++] = new aiMesh();
+
+ pcMesh->mNumFaces = pcSurface->ulNumTriangles;
+ pcMesh->mNumVertices = pcMesh->mNumFaces * 3;
+
+ // store the name of the surface for use as node name.
+ // FIX: make sure there is a 0 termination
+ const_cast<char&>(pcSurface->ucName[AI_MDC_MAXQPATH-1]) = '\0';
+ pcMesh->mTextureCoords[3] = (aiVector3D*)pcSurface->ucName;
+
+ // go to the first shader in the file. ignore the others.
+ if (pcSurface->ulNumShaders)
+ {
+ const MDC::Shader* pcShader = (const MDC::Shader*)((int8_t*)pcSurface + pcSurface->ulOffsetShaders);
+ pcMesh->mMaterialIndex = (unsigned int)aszShaders.size();
+
+ // create a new shader
+ aszShaders.push_back(std::string( pcShader->ucName, std::min(
+ ::strlen(pcShader->ucName),sizeof(pcShader->ucName)) ));
+ }
+ // need to create a default material
+ else if (0xffffffff == iDefaultMatIndex)
+ {
+ pcMesh->mMaterialIndex = iDefaultMatIndex = (unsigned int)aszShaders.size();
+ aszShaders.push_back(std::string());
+ }
+ // otherwise assign a reference to the default material
+ else pcMesh->mMaterialIndex = iDefaultMatIndex;
+
+ // allocate output storage for the mesh
+ aiVector3D* pcVertCur = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ aiVector3D* pcNorCur = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+ aiVector3D* pcUVCur = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ aiFace* pcFaceCur = pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+ // create all vertices/faces
+ BE_NCONST MDC::Triangle* pcTriangle = (BE_NCONST MDC::Triangle*)
+ ((int8_t*)pcSurface+pcSurface->ulOffsetTriangles);
+
+ BE_NCONST MDC::TexturCoord* const pcUVs = (BE_NCONST MDC::TexturCoord*)
+ ((int8_t*)pcSurface+pcSurface->ulOffsetTexCoords);
+
+ // get a pointer to the uncompressed vertices
+ int16_t iOfs = *((int16_t*) ((int8_t*) pcSurface +
+ pcSurface->ulOffsetFrameBaseFrames) + this->configFrameID);
+
+ AI_SWAP2(iOfs);
+
+ BE_NCONST MDC::BaseVertex* const pcVerts = (BE_NCONST MDC::BaseVertex*)
+ ((int8_t*)pcSurface+pcSurface->ulOffsetBaseVerts) +
+ ((int)iOfs * pcSurface->ulNumVertices * 4);
+
+ // do the main swapping stuff ...
+#if (defined AI_BUILD_BIG_ENDIAN)
+
+ // swap all triangles
+ for (unsigned int i = 0; i < pcSurface->ulNumTriangles;++i)
+ {
+ AI_SWAP4( pcTriangle[i].aiIndices[0] );
+ AI_SWAP4( pcTriangle[i].aiIndices[1] );
+ AI_SWAP4( pcTriangle[i].aiIndices[2] );
+ }
+
+ // swap all vertices
+ for (unsigned int i = 0; i < pcSurface->ulNumVertices*pcSurface->ulNumBaseFrames;++i)
+ {
+ AI_SWAP2( pcVerts->normal );
+ AI_SWAP2( pcVerts->x );
+ AI_SWAP2( pcVerts->y );
+ AI_SWAP2( pcVerts->z );
+ }
+
+ // swap all texture coordinates
+ for (unsigned int i = 0; i < pcSurface->ulNumVertices;++i)
+ {
+ AI_SWAP4( pcUVs->v );
+ AI_SWAP4( pcUVs->v );
+ }
+
+#endif
+
+ const MDC::CompressedVertex* pcCVerts = NULL;
+ int16_t* mdcCompVert = NULL;
+
+ // access compressed frames for large frame numbers, but never for the first
+ if ( this->configFrameID && pcSurface->ulNumCompFrames > 0 )
+ {
+ mdcCompVert = (int16_t*) ((int8_t*)pcSurface+pcSurface->ulOffsetFrameCompFrames) + this->configFrameID;
+ AI_SWAP2P(mdcCompVert);
+ if ( *mdcCompVert >= 0 )
+ {
+ pcCVerts = (const MDC::CompressedVertex*)((int8_t*)pcSurface +
+ pcSurface->ulOffsetCompVerts) + *mdcCompVert * pcSurface->ulNumVertices;
+ }
+ else mdcCompVert = NULL;
+ }
+
+ // copy all faces
+ for (unsigned int iFace = 0; iFace < pcSurface->ulNumTriangles;++iFace,
+ ++pcTriangle,++pcFaceCur)
+ {
+ const unsigned int iOutIndex = iFace*3;
+ pcFaceCur->mNumIndices = 3;
+ pcFaceCur->mIndices = new unsigned int[3];
+
+ for (unsigned int iIndex = 0; iIndex < 3;++iIndex,
+ ++pcVertCur,++pcUVCur,++pcNorCur)
+ {
+ uint32_t quak = pcTriangle->aiIndices[iIndex];
+ if (quak >= pcSurface->ulNumVertices)
+ {
+ DefaultLogger::get()->error("MDC vertex index is out of range");
+ quak = pcSurface->ulNumVertices-1;
+ }
+
+ // compressed vertices?
+ if (mdcCompVert)
+ {
+ MDC::BuildVertex(*pcFrame,pcVerts[quak],pcCVerts[quak],
+ *pcVertCur,*pcNorCur);
+ }
+ else
+ {
+ // copy position
+ pcVertCur->x = pcVerts[quak].x * AI_MDC_BASE_SCALING;
+ pcVertCur->y = pcVerts[quak].y * AI_MDC_BASE_SCALING;
+ pcVertCur->z = pcVerts[quak].z * AI_MDC_BASE_SCALING;
+
+ // copy normals
+ MD3::LatLngNormalToVec3( pcVerts[quak].normal, &pcNorCur->x );
+
+ // copy texture coordinates
+ pcUVCur->x = pcUVs[quak].u;
+ pcUVCur->y = 1.0f-pcUVs[quak].v; // DX to OGL
+ }
+ pcVertCur->x += pcFrame->localOrigin[0] ;
+ pcVertCur->y += pcFrame->localOrigin[1] ;
+ pcVertCur->z += pcFrame->localOrigin[2] ;
+ }
+
+ // swap the face order - DX to OGL
+ pcFaceCur->mIndices[0] = iOutIndex + 2;
+ pcFaceCur->mIndices[1] = iOutIndex + 1;
+ pcFaceCur->mIndices[2] = iOutIndex + 0;
+ }
+
+ pcSurface = (BE_NCONST MDC::Surface*)((int8_t*)pcSurface + pcSurface->ulOffsetEnd);
+ }
+
+ // create a flat node graph with a root node and one child for each surface
+ if (!pScene->mNumMeshes)
+ throw DeadlyImportError( "Invalid MDC file: File contains no valid mesh");
+ else if (1 == pScene->mNumMeshes)
+ {
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set(std::string((const char*)pScene->mMeshes[0]->mTextureCoords[3]));
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+ }
+ else
+ {
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumChildren = pScene->mNumMeshes;
+ pScene->mRootNode->mChildren = new aiNode*[pScene->mNumMeshes];
+ pScene->mRootNode->mName.Set("<root>");
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ {
+ aiNode* pcNode = pScene->mRootNode->mChildren[i] = new aiNode();
+ pcNode->mParent = pScene->mRootNode;
+ pcNode->mName.Set(std::string((const char*)pScene->mMeshes[i]->mTextureCoords[3]));
+ pcNode->mNumMeshes = 1;
+ pcNode->mMeshes = new unsigned int[1];
+ pcNode->mMeshes[0] = i;
+ }
+ }
+
+ // make sure we invalidate the pointer to the mesh name
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mMeshes[i]->mTextureCoords[3] = NULL;
+
+ // create materials
+ pScene->mNumMaterials = (unsigned int)aszShaders.size();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ {
+ MaterialHelper* pcMat = new MaterialHelper();
+ pScene->mMaterials[i] = pcMat;
+
+ const std::string& name = aszShaders[i];
+
+ int iMode = (int)aiShadingMode_Gouraud;
+ pcMat->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ // add a small ambient color value - RtCW seems to have one
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.05f;
+ pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ if (name.length())clr.b = clr.g = clr.r = 1.0f;
+ else clr.b = clr.g = clr.r = 0.6f;
+
+ pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ if (name.length())
+ {
+ aiString path;
+ path.Set(name);
+ pcMat->AddProperty(&path,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_MDC_IMPORTER
diff --git a/3rdparty/assimp/code/MDCLoader.h b/3rdparty/assimp/code/MDCLoader.h
new file mode 100644
index 000000000..4cabf09f9
--- /dev/null
+++ b/3rdparty/assimp/code/MDCLoader.h
@@ -0,0 +1,132 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MDCLoader.h
+ * @brief Definition of the MDC importer class.
+ */
+#ifndef AI_MDCLOADER_H_INCLUDED
+#define AI_MDCLOADER_H_INCLUDED
+
+#include "../include/aiTypes.h"
+
+#include "BaseImporter.h"
+#include "MDCFileData.h"
+#include "ByteSwap.h"
+
+namespace Assimp {
+using namespace MDC;
+
+// ---------------------------------------------------------------------------
+/** Importer class to load the RtCW MDC file format
+*/
+class MDCImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ MDCImporter();
+
+ /** Destructor, private as well */
+ ~MDCImporter();
+
+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;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Validate the header of the file
+ */
+ void ValidateHeader();
+
+ // -------------------------------------------------------------------
+ /** Validate the header of a MDC surface
+ */
+ void ValidateSurfaceHeader(BE_NCONST MDC::Surface* pcSurf);
+
+protected:
+
+
+ /** Configuration option: frame to be loaded */
+ unsigned int configFrameID;
+
+ /** Header of the MDC file */
+ BE_NCONST MDC::Header* pcHeader;
+
+ /** Buffer to hold the loaded file */
+ unsigned char* mBuffer;
+
+ /** size of the file, in bytes */
+ unsigned int fileSize;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
+
diff --git a/3rdparty/assimp/code/MDCNormalTable.h b/3rdparty/assimp/code/MDCNormalTable.h
new file mode 100644
index 000000000..f47e97f33
--- /dev/null
+++ b/3rdparty/assimp/code/MDCNormalTable.h
@@ -0,0 +1,299 @@
+/* -----------------------------------------------------------------------------
+
+PicoModel Library
+
+Copyright (c) 2002, Randy Reddig & seaw0lf
+All rights reserved.
+
+Redistribution and use 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 names of the copyright holders nor the names of its contributors may
+be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+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.
+
+----------------------------------------------------------------------------- */
+
+#if (!defined MDC_NORMAL_TABLE_INCLUDED)
+#define MDC_NORMAL_TABLE_INCLUDED
+
+/* mdc decoding normal table */
+float mdcNormals[ 256 ][ 3 ] =
+{
+ { 1.000000f, 0.000000f, 0.000000f },
+ { 0.980785f, 0.195090f, 0.000000f },
+ { 0.923880f, 0.382683f, 0.000000f },
+ { 0.831470f, 0.555570f, 0.000000f },
+ { 0.707107f, 0.707107f, 0.000000f },
+ { 0.555570f, 0.831470f, 0.000000f },
+ { 0.382683f, 0.923880f, 0.000000f },
+ { 0.195090f, 0.980785f, 0.000000f },
+ { -0.000000f, 1.000000f, 0.000000f },
+ { -0.195090f, 0.980785f, 0.000000f },
+ { -0.382683f, 0.923880f, 0.000000f },
+ { -0.555570f, 0.831470f, 0.000000f },
+ { -0.707107f, 0.707107f, 0.000000f },
+ { -0.831470f, 0.555570f, 0.000000f },
+ { -0.923880f, 0.382683f, 0.000000f },
+ { -0.980785f, 0.195090f, 0.000000f },
+ { -1.000000f, -0.000000f, 0.000000f },
+ { -0.980785f, -0.195090f, 0.000000f },
+ { -0.923880f, -0.382683f, 0.000000f },
+ { -0.831470f, -0.555570f, 0.000000f },
+ { -0.707107f, -0.707107f, 0.000000f },
+ { -0.555570f, -0.831469f, 0.000000f },
+ { -0.382684f, -0.923880f, 0.000000f },
+ { -0.195090f, -0.980785f, 0.000000f },
+ { 0.000000f, -1.000000f, 0.000000f },
+ { 0.195090f, -0.980785f, 0.000000f },
+ { 0.382684f, -0.923879f, 0.000000f },
+ { 0.555570f, -0.831470f, 0.000000f },
+ { 0.707107f, -0.707107f, 0.000000f },
+ { 0.831470f, -0.555570f, 0.000000f },
+ { 0.923880f, -0.382683f, 0.000000f },
+ { 0.980785f, -0.195090f, 0.000000f },
+ { 0.980785f, 0.000000f, -0.195090f },
+ { 0.956195f, 0.218245f, -0.195090f },
+ { 0.883657f, 0.425547f, -0.195090f },
+ { 0.766809f, 0.611510f, -0.195090f },
+ { 0.611510f, 0.766809f, -0.195090f },
+ { 0.425547f, 0.883657f, -0.195090f },
+ { 0.218245f, 0.956195f, -0.195090f },
+ { -0.000000f, 0.980785f, -0.195090f },
+ { -0.218245f, 0.956195f, -0.195090f },
+ { -0.425547f, 0.883657f, -0.195090f },
+ { -0.611510f, 0.766809f, -0.195090f },
+ { -0.766809f, 0.611510f, -0.195090f },
+ { -0.883657f, 0.425547f, -0.195090f },
+ { -0.956195f, 0.218245f, -0.195090f },
+ { -0.980785f, -0.000000f, -0.195090f },
+ { -0.956195f, -0.218245f, -0.195090f },
+ { -0.883657f, -0.425547f, -0.195090f },
+ { -0.766809f, -0.611510f, -0.195090f },
+ { -0.611510f, -0.766809f, -0.195090f },
+ { -0.425547f, -0.883657f, -0.195090f },
+ { -0.218245f, -0.956195f, -0.195090f },
+ { 0.000000f, -0.980785f, -0.195090f },
+ { 0.218245f, -0.956195f, -0.195090f },
+ { 0.425547f, -0.883657f, -0.195090f },
+ { 0.611510f, -0.766809f, -0.195090f },
+ { 0.766809f, -0.611510f, -0.195090f },
+ { 0.883657f, -0.425547f, -0.195090f },
+ { 0.956195f, -0.218245f, -0.195090f },
+ { 0.923880f, 0.000000f, -0.382683f },
+ { 0.892399f, 0.239118f, -0.382683f },
+ { 0.800103f, 0.461940f, -0.382683f },
+ { 0.653281f, 0.653281f, -0.382683f },
+ { 0.461940f, 0.800103f, -0.382683f },
+ { 0.239118f, 0.892399f, -0.382683f },
+ { -0.000000f, 0.923880f, -0.382683f },
+ { -0.239118f, 0.892399f, -0.382683f },
+ { -0.461940f, 0.800103f, -0.382683f },
+ { -0.653281f, 0.653281f, -0.382683f },
+ { -0.800103f, 0.461940f, -0.382683f },
+ { -0.892399f, 0.239118f, -0.382683f },
+ { -0.923880f, -0.000000f, -0.382683f },
+ { -0.892399f, -0.239118f, -0.382683f },
+ { -0.800103f, -0.461940f, -0.382683f },
+ { -0.653282f, -0.653281f, -0.382683f },
+ { -0.461940f, -0.800103f, -0.382683f },
+ { -0.239118f, -0.892399f, -0.382683f },
+ { 0.000000f, -0.923880f, -0.382683f },
+ { 0.239118f, -0.892399f, -0.382683f },
+ { 0.461940f, -0.800103f, -0.382683f },
+ { 0.653281f, -0.653282f, -0.382683f },
+ { 0.800103f, -0.461940f, -0.382683f },
+ { 0.892399f, -0.239117f, -0.382683f },
+ { 0.831470f, 0.000000f, -0.555570f },
+ { 0.790775f, 0.256938f, -0.555570f },
+ { 0.672673f, 0.488726f, -0.555570f },
+ { 0.488726f, 0.672673f, -0.555570f },
+ { 0.256938f, 0.790775f, -0.555570f },
+ { -0.000000f, 0.831470f, -0.555570f },
+ { -0.256938f, 0.790775f, -0.555570f },
+ { -0.488726f, 0.672673f, -0.555570f },
+ { -0.672673f, 0.488726f, -0.555570f },
+ { -0.790775f, 0.256938f, -0.555570f },
+ { -0.831470f, -0.000000f, -0.555570f },
+ { -0.790775f, -0.256938f, -0.555570f },
+ { -0.672673f, -0.488726f, -0.555570f },
+ { -0.488725f, -0.672673f, -0.555570f },
+ { -0.256938f, -0.790775f, -0.555570f },
+ { 0.000000f, -0.831470f, -0.555570f },
+ { 0.256938f, -0.790775f, -0.555570f },
+ { 0.488725f, -0.672673f, -0.555570f },
+ { 0.672673f, -0.488726f, -0.555570f },
+ { 0.790775f, -0.256938f, -0.555570f },
+ { 0.707107f, 0.000000f, -0.707107f },
+ { 0.653281f, 0.270598f, -0.707107f },
+ { 0.500000f, 0.500000f, -0.707107f },
+ { 0.270598f, 0.653281f, -0.707107f },
+ { -0.000000f, 0.707107f, -0.707107f },
+ { -0.270598f, 0.653282f, -0.707107f },
+ { -0.500000f, 0.500000f, -0.707107f },
+ { -0.653281f, 0.270598f, -0.707107f },
+ { -0.707107f, -0.000000f, -0.707107f },
+ { -0.653281f, -0.270598f, -0.707107f },
+ { -0.500000f, -0.500000f, -0.707107f },
+ { -0.270598f, -0.653281f, -0.707107f },
+ { 0.000000f, -0.707107f, -0.707107f },
+ { 0.270598f, -0.653281f, -0.707107f },
+ { 0.500000f, -0.500000f, -0.707107f },
+ { 0.653282f, -0.270598f, -0.707107f },
+ { 0.555570f, 0.000000f, -0.831470f },
+ { 0.481138f, 0.277785f, -0.831470f },
+ { 0.277785f, 0.481138f, -0.831470f },
+ { -0.000000f, 0.555570f, -0.831470f },
+ { -0.277785f, 0.481138f, -0.831470f },
+ { -0.481138f, 0.277785f, -0.831470f },
+ { -0.555570f, -0.000000f, -0.831470f },
+ { -0.481138f, -0.277785f, -0.831470f },
+ { -0.277785f, -0.481138f, -0.831470f },
+ { 0.000000f, -0.555570f, -0.831470f },
+ { 0.277785f, -0.481138f, -0.831470f },
+ { 0.481138f, -0.277785f, -0.831470f },
+ { 0.382683f, 0.000000f, -0.923880f },
+ { 0.270598f, 0.270598f, -0.923880f },
+ { -0.000000f, 0.382683f, -0.923880f },
+ { -0.270598f, 0.270598f, -0.923880f },
+ { -0.382683f, -0.000000f, -0.923880f },
+ { -0.270598f, -0.270598f, -0.923880f },
+ { 0.000000f, -0.382683f, -0.923880f },
+ { 0.270598f, -0.270598f, -0.923880f },
+ { 0.195090f, 0.000000f, -0.980785f },
+ { -0.000000f, 0.195090f, -0.980785f },
+ { -0.195090f, -0.000000f, -0.980785f },
+ { 0.000000f, -0.195090f, -0.980785f },
+ { 0.980785f, 0.000000f, 0.195090f },
+ { 0.956195f, 0.218245f, 0.195090f },
+ { 0.883657f, 0.425547f, 0.195090f },
+ { 0.766809f, 0.611510f, 0.195090f },
+ { 0.611510f, 0.766809f, 0.195090f },
+ { 0.425547f, 0.883657f, 0.195090f },
+ { 0.218245f, 0.956195f, 0.195090f },
+ { -0.000000f, 0.980785f, 0.195090f },
+ { -0.218245f, 0.956195f, 0.195090f },
+ { -0.425547f, 0.883657f, 0.195090f },
+ { -0.611510f, 0.766809f, 0.195090f },
+ { -0.766809f, 0.611510f, 0.195090f },
+ { -0.883657f, 0.425547f, 0.195090f },
+ { -0.956195f, 0.218245f, 0.195090f },
+ { -0.980785f, -0.000000f, 0.195090f },
+ { -0.956195f, -0.218245f, 0.195090f },
+ { -0.883657f, -0.425547f, 0.195090f },
+ { -0.766809f, -0.611510f, 0.195090f },
+ { -0.611510f, -0.766809f, 0.195090f },
+ { -0.425547f, -0.883657f, 0.195090f },
+ { -0.218245f, -0.956195f, 0.195090f },
+ { 0.000000f, -0.980785f, 0.195090f },
+ { 0.218245f, -0.956195f, 0.195090f },
+ { 0.425547f, -0.883657f, 0.195090f },
+ { 0.611510f, -0.766809f, 0.195090f },
+ { 0.766809f, -0.611510f, 0.195090f },
+ { 0.883657f, -0.425547f, 0.195090f },
+ { 0.956195f, -0.218245f, 0.195090f },
+ { 0.923880f, 0.000000f, 0.382683f },
+ { 0.892399f, 0.239118f, 0.382683f },
+ { 0.800103f, 0.461940f, 0.382683f },
+ { 0.653281f, 0.653281f, 0.382683f },
+ { 0.461940f, 0.800103f, 0.382683f },
+ { 0.239118f, 0.892399f, 0.382683f },
+ { -0.000000f, 0.923880f, 0.382683f },
+ { -0.239118f, 0.892399f, 0.382683f },
+ { -0.461940f, 0.800103f, 0.382683f },
+ { -0.653281f, 0.653281f, 0.382683f },
+ { -0.800103f, 0.461940f, 0.382683f },
+ { -0.892399f, 0.239118f, 0.382683f },
+ { -0.923880f, -0.000000f, 0.382683f },
+ { -0.892399f, -0.239118f, 0.382683f },
+ { -0.800103f, -0.461940f, 0.382683f },
+ { -0.653282f, -0.653281f, 0.382683f },
+ { -0.461940f, -0.800103f, 0.382683f },
+ { -0.239118f, -0.892399f, 0.382683f },
+ { 0.000000f, -0.923880f, 0.382683f },
+ { 0.239118f, -0.892399f, 0.382683f },
+ { 0.461940f, -0.800103f, 0.382683f },
+ { 0.653281f, -0.653282f, 0.382683f },
+ { 0.800103f, -0.461940f, 0.382683f },
+ { 0.892399f, -0.239117f, 0.382683f },
+ { 0.831470f, 0.000000f, 0.555570f },
+ { 0.790775f, 0.256938f, 0.555570f },
+ { 0.672673f, 0.488726f, 0.555570f },
+ { 0.488726f, 0.672673f, 0.555570f },
+ { 0.256938f, 0.790775f, 0.555570f },
+ { -0.000000f, 0.831470f, 0.555570f },
+ { -0.256938f, 0.790775f, 0.555570f },
+ { -0.488726f, 0.672673f, 0.555570f },
+ { -0.672673f, 0.488726f, 0.555570f },
+ { -0.790775f, 0.256938f, 0.555570f },
+ { -0.831470f, -0.000000f, 0.555570f },
+ { -0.790775f, -0.256938f, 0.555570f },
+ { -0.672673f, -0.488726f, 0.555570f },
+ { -0.488725f, -0.672673f, 0.555570f },
+ { -0.256938f, -0.790775f, 0.555570f },
+ { 0.000000f, -0.831470f, 0.555570f },
+ { 0.256938f, -0.790775f, 0.555570f },
+ { 0.488725f, -0.672673f, 0.555570f },
+ { 0.672673f, -0.488726f, 0.555570f },
+ { 0.790775f, -0.256938f, 0.555570f },
+ { 0.707107f, 0.000000f, 0.707107f },
+ { 0.653281f, 0.270598f, 0.707107f },
+ { 0.500000f, 0.500000f, 0.707107f },
+ { 0.270598f, 0.653281f, 0.707107f },
+ { -0.000000f, 0.707107f, 0.707107f },
+ { -0.270598f, 0.653282f, 0.707107f },
+ { -0.500000f, 0.500000f, 0.707107f },
+ { -0.653281f, 0.270598f, 0.707107f },
+ { -0.707107f, -0.000000f, 0.707107f },
+ { -0.653281f, -0.270598f, 0.707107f },
+ { -0.500000f, -0.500000f, 0.707107f },
+ { -0.270598f, -0.653281f, 0.707107f },
+ { 0.000000f, -0.707107f, 0.707107f },
+ { 0.270598f, -0.653281f, 0.707107f },
+ { 0.500000f, -0.500000f, 0.707107f },
+ { 0.653282f, -0.270598f, 0.707107f },
+ { 0.555570f, 0.000000f, 0.831470f },
+ { 0.481138f, 0.277785f, 0.831470f },
+ { 0.277785f, 0.481138f, 0.831470f },
+ { -0.000000f, 0.555570f, 0.831470f },
+ { -0.277785f, 0.481138f, 0.831470f },
+ { -0.481138f, 0.277785f, 0.831470f },
+ { -0.555570f, -0.000000f, 0.831470f },
+ { -0.481138f, -0.277785f, 0.831470f },
+ { -0.277785f, -0.481138f, 0.831470f },
+ { 0.000000f, -0.555570f, 0.831470f },
+ { 0.277785f, -0.481138f, 0.831470f },
+ { 0.481138f, -0.277785f, 0.831470f },
+ { 0.382683f, 0.000000f, 0.923880f },
+ { 0.270598f, 0.270598f, 0.923880f },
+ { -0.000000f, 0.382683f, 0.923880f },
+ { -0.270598f, 0.270598f, 0.923880f },
+ { -0.382683f, -0.000000f, 0.923880f },
+ { -0.270598f, -0.270598f, 0.923880f },
+ { 0.000000f, -0.382683f, 0.923880f },
+ { 0.270598f, -0.270598f, 0.923880f },
+ { 0.195090f, 0.000000f, 0.980785f },
+ { -0.000000f, 0.195090f, 0.980785f },
+ { -0.195090f, -0.000000f, 0.980785f },
+ { 0.000000f, -0.195090f, 0.980785f }
+};
+
+#endif // !! MDC_NORMAL_TABLE_INCLUDED
diff --git a/3rdparty/assimp/code/MDLDefaultColorMap.h b/3rdparty/assimp/code/MDLDefaultColorMap.h
new file mode 100644
index 000000000..a40da826c
--- /dev/null
+++ b/3rdparty/assimp/code/MDLDefaultColorMap.h
@@ -0,0 +1,118 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines the default color map used for Quake 1 model textures
+ *
+ * The lib tries to load colormap.lmp from the model's directory.
+ * This table is only used when required.
+ */
+
+#ifndef AI_MDL_DEFAULTLMP_H_INC
+#define AI_MDL_DEFAULTLMP_H_INC
+
+const unsigned char g_aclrDefaultColorMap[256][3] = {
+{ 0, 0, 0}, { 15, 15, 15}, { 31, 31, 31}, { 47, 47, 47},
+{ 63, 63, 63}, { 75, 75, 75}, { 91, 91, 91}, {107, 107, 107},
+{123, 123, 123}, {139, 139, 139}, {155, 155, 155}, {171, 171, 171},
+{187, 187, 187}, {203, 203, 203}, {219, 219, 219}, {235, 235, 235},
+{ 15, 11, 7}, { 23, 15, 11}, { 31, 23, 11}, { 39, 27, 15},
+{ 47, 35, 19}, { 55, 43, 23}, { 63, 47, 23}, { 75, 55, 27},
+{ 83, 59, 27}, { 91, 67, 31}, { 99, 75, 31}, {107, 83, 31},
+{115, 87, 31}, {123, 95, 35}, {131, 103, 35}, {143, 111, 35},
+{ 11, 11, 15}, { 19, 19, 27}, { 27, 27, 39}, { 39, 39, 51},
+{ 47, 47, 63}, { 55, 55, 75}, { 63, 63, 87}, { 71, 71, 103},
+{ 79, 79, 115}, { 91, 91, 127}, { 99, 99, 139}, {107, 107, 151},
+{115, 115, 163}, {123, 123, 175}, {131, 131, 187}, {139, 139, 203},
+{ 0, 0, 0}, { 7, 7, 0}, { 11, 11, 0}, { 19, 19, 0},
+{ 27, 27, 0}, { 35, 35, 0}, { 43, 43, 7}, { 47, 47, 7},
+{ 55, 55, 7}, { 63, 63, 7}, { 71, 71, 7}, { 75, 75, 11},
+{ 83, 83, 11}, { 91, 91, 11}, { 99, 99, 11}, {107, 107, 15},
+{ 7, 0, 0}, { 15, 0, 0}, { 23, 0, 0}, { 31, 0, 0},
+{ 39, 0, 0}, { 47, 0, 0}, { 55, 0, 0}, { 63, 0, 0},
+{ 71, 0, 0}, { 79, 0, 0}, { 87, 0, 0}, { 95, 0, 0},
+{103, 0, 0}, {111, 0, 0}, {119, 0, 0}, {127, 0, 0},
+{ 19, 19, 0}, { 27, 27, 0}, { 35, 35, 0}, { 47, 43, 0},
+{ 55, 47, 0}, { 67, 55, 0}, { 75, 59, 7}, { 87, 67, 7},
+{ 95, 71, 7}, {107, 75, 11}, {119, 83, 15}, {131, 87, 19},
+{139, 91, 19}, {151, 95, 27}, {163, 99, 31}, {175, 103, 35},
+{ 35, 19, 7}, { 47, 23, 11}, { 59, 31, 15}, { 75, 35, 19},
+{ 87, 43, 23}, { 99, 47, 31}, {115, 55, 35}, {127, 59, 43},
+{143, 67, 51}, {159, 79, 51}, {175, 99, 47}, {191, 119, 47},
+{207, 143, 43}, {223, 171, 39}, {239, 203, 31}, {255, 243, 27},
+{ 11, 7, 0}, { 27, 19, 0}, { 43, 35, 15}, { 55, 43, 19},
+{ 71, 51, 27}, { 83, 55, 35}, { 99, 63, 43}, {111, 71, 51},
+{127, 83, 63}, {139, 95, 71}, {155, 107, 83}, {167, 123, 95},
+{183, 135, 107}, {195, 147, 123}, {211, 163, 139}, {227, 179, 151},
+{171, 139, 163}, {159, 127, 151}, {147, 115, 135}, {139, 103, 123},
+{127, 91, 111}, {119, 83, 99}, {107, 75, 87}, { 95, 63, 75},
+{ 87, 55, 67}, { 75, 47, 55}, { 67, 39, 47}, { 55, 31, 35},
+{ 43, 23, 27}, { 35, 19, 19}, { 23, 11, 11}, { 15, 7, 7},
+{187, 115, 159}, {175, 107, 143}, {163, 95, 131}, {151, 87, 119},
+{139, 79, 107}, {127, 75, 95}, {115, 67, 83}, {107, 59, 75},
+{ 95, 51, 63}, { 83, 43, 55}, { 71, 35, 43}, { 59, 31, 35},
+{ 47, 23, 27}, { 35, 19, 19}, { 23, 11, 11}, { 15, 7, 7},
+{219, 195, 187}, {203, 179, 167}, {191, 163, 155}, {175, 151, 139},
+{163, 135, 123}, {151, 123, 111}, {135, 111, 95}, {123, 99, 83},
+{107, 87, 71}, { 95, 75, 59}, { 83, 63, 51}, { 67, 51, 39},
+{ 55, 43, 31}, { 39, 31, 23}, { 27, 19, 15}, { 15, 11, 7},
+{111, 131, 123}, {103, 123, 111}, { 95, 115, 103}, { 87, 107, 95},
+{ 79, 99, 87}, { 71, 91, 79}, { 63, 83, 71}, { 55, 75, 63},
+{ 47, 67, 55}, { 43, 59, 47}, { 35, 51, 39}, { 31, 43, 31},
+{ 23, 35, 23}, { 15, 27, 19}, { 11, 19, 11}, { 7, 11, 7},
+{255, 243, 27}, {239, 223, 23}, {219, 203, 19}, {203, 183, 15},
+{187, 167, 15}, {171, 151, 11}, {155, 131, 7}, {139, 115, 7},
+{123, 99, 7}, {107, 83, 0}, { 91, 71, 0}, { 75, 55, 0},
+{ 59, 43, 0}, { 43, 31, 0}, { 27, 15, 0}, { 11, 7, 0},
+{ 0, 0, 255}, { 11, 11, 239}, { 19, 19, 223}, { 27, 27, 207},
+{ 35, 35, 191}, { 43, 43, 175}, { 47, 47, 159}, { 47, 47, 143},
+{ 47, 47, 127}, { 47, 47, 111}, { 47, 47, 95}, { 43, 43, 79},
+{ 35, 35, 63}, { 27, 27, 47}, { 19, 19, 31}, { 11, 11, 15},
+{ 43, 0, 0}, { 59, 0, 0}, { 75, 7, 0}, { 95, 7, 0},
+{111, 15, 0}, {127, 23, 7}, {147, 31, 7}, {163, 39, 11},
+{183, 51, 15}, {195, 75, 27}, {207, 99, 43}, {219, 127, 59},
+{227, 151, 79}, {231, 171, 95}, {239, 191, 119}, {247, 211, 139},
+{167, 123, 59}, {183, 155, 55}, {199, 195, 55}, {231, 227, 87},
+{127, 191, 255}, {171, 231, 255}, {215, 255, 255}, {103, 0, 0},
+{139, 0, 0}, {179, 0, 0}, {215, 0, 0}, {255, 0, 0},
+{255, 243, 147}, {255, 247, 199}, {255, 255, 255}, {159, 91, 83} };
+
+
+#endif // !! AI_MDL_DEFAULTLMP_H_INC
diff --git a/3rdparty/assimp/code/MDLFileData.h b/3rdparty/assimp/code/MDLFileData.h
new file mode 100644
index 000000000..098cb8dce
--- /dev/null
+++ b/3rdparty/assimp/code/MDLFileData.h
@@ -0,0 +1,959 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MDLFileData.h
+ * @brief Definition of in-memory structures for the MDL file format.
+ *
+ * The specification has been taken from various sources on the internet.
+ * - http://tfc.duke.free.fr/coding/mdl-specs-en.html
+ * - Conitec's MED SDK
+ * - Many quite long HEX-editor sessions
+ */
+
+#ifndef AI_MDLFILEHELPER_H_INC
+#define AI_MDLFILEHELPER_H_INC
+
+#include "./../include/Compiler/pushpack1.h"
+
+namespace Assimp {
+namespace MDL {
+
+// -------------------------------------------------------------------------------------
+// to make it easier for us, we test the magic word against both "endianesses"
+
+// magic bytes used in Quake 1 MDL meshes
+#define AI_MDL_MAGIC_NUMBER_BE AI_MAKE_MAGIC("IDPO")
+#define AI_MDL_MAGIC_NUMBER_LE AI_MAKE_MAGIC("OPDI")
+
+// magic bytes used in GameStudio A<very low> MDL meshes
+#define AI_MDL_MAGIC_NUMBER_BE_GS3 AI_MAKE_MAGIC("MDL2")
+#define AI_MDL_MAGIC_NUMBER_LE_GS3 AI_MAKE_MAGIC("2LDM")
+
+// magic bytes used in GameStudio A4 MDL meshes
+#define AI_MDL_MAGIC_NUMBER_BE_GS4 AI_MAKE_MAGIC("MDL3")
+#define AI_MDL_MAGIC_NUMBER_LE_GS4 AI_MAKE_MAGIC("3LDM")
+
+// magic bytes used in GameStudio A5+ MDL meshes
+#define AI_MDL_MAGIC_NUMBER_BE_GS5a AI_MAKE_MAGIC("MDL4")
+#define AI_MDL_MAGIC_NUMBER_LE_GS5a AI_MAKE_MAGIC("4LDM")
+#define AI_MDL_MAGIC_NUMBER_BE_GS5b AI_MAKE_MAGIC("MDL5")
+#define AI_MDL_MAGIC_NUMBER_LE_GS5b AI_MAKE_MAGIC("5LDM")
+
+// magic bytes used in GameStudio A7+ MDL meshes
+#define AI_MDL_MAGIC_NUMBER_BE_GS7 AI_MAKE_MAGIC("MDL7")
+#define AI_MDL_MAGIC_NUMBER_LE_GS7 AI_MAKE_MAGIC("7LDM")
+
+
+// common limitations for Quake1 meshes. The loader does not check them,
+// (however it warns) but models should not exceed these limits.
+#if (!defined AI_MDL_VERSION)
+# define AI_MDL_VERSION 6
+#endif
+#if (!defined AI_MDL_MAX_FRAMES)
+# define AI_MDL_MAX_FRAMES 256
+#endif
+#if (!defined AI_MDL_MAX_UVS)
+# define AI_MDL_MAX_UVS 1024
+#endif
+#if (!defined AI_MDL_MAX_VERTS)
+# define AI_MDL_MAX_VERTS 1024
+#endif
+#if (!defined AI_MDL_MAX_TRIANGLES)
+# define AI_MDL_MAX_TRIANGLES 2048
+#endif
+
+// material key that is set for dummy materials that are
+// just referencing another material
+#if (!defined AI_MDL7_REFERRER_MATERIAL)
+# define AI_MDL7_REFERRER_MATERIAL "&&&referrer&&&",0,0
+#endif
+
+// -------------------------------------------------------------------------------------
+/** \struct Header
+ * \brief Data structure for the MDL main header
+ */
+struct Header
+{
+ //! magic number: "IDPO"
+ uint32_t ident;
+
+ //! version number: 6
+ int32_t version;
+
+ //! scale factors for each axis
+ aiVector3D scale;
+
+ //! translation factors for each axis
+ aiVector3D translate;
+
+ //! bounding radius of the mesh
+ float boundingradius;
+
+ //! Position of the viewer's exe. Ignored
+ aiVector3D vEyePos;
+
+ //! Number of textures
+ int32_t num_skins;
+
+ //! Texture width in pixels
+ int32_t skinwidth;
+
+ //! Texture height in pixels
+ int32_t skinheight;
+
+ //! Number of vertices contained in the file
+ int32_t num_verts;
+
+ //! Number of triangles contained in the file
+ int32_t num_tris;
+
+ //! Number of frames contained in the file
+ int32_t num_frames;
+
+ //! 0 = synchron, 1 = random . Ignored
+ //! (MDLn formats: number of texture coordinates)
+ int32_t synctype;
+
+ //! State flag
+ int32_t flags;
+
+ //! Could be the total size of the file (and not a float)
+ float size;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct Header_MDL7
+ * \brief Data structure for the MDL 7 main header
+ */
+struct Header_MDL7
+{
+ //! magic number: "MDL7"
+ char ident[4];
+
+ //! Version number. Ignored
+ int32_t version;
+
+ //! Number of bones in file
+ uint32_t bones_num;
+
+ //! Number of groups in file
+ uint32_t groups_num;
+
+ //! Size of data in the file
+ uint32_t data_size;
+
+ //! Ignored. Used to store entity specific information
+ int32_t entlump_size;
+
+ //! Ignored. Used to store MED related data
+ int32_t medlump_size;
+
+ //! Size of the Bone_MDL7 data structure used in the file
+ uint16_t bone_stc_size;
+
+ //! Size of the Skin_MDL 7 data structure used in the file
+ uint16_t skin_stc_size;
+
+ //! Size of a single color (e.g. in a material)
+ uint16_t colorvalue_stc_size;
+
+ //! Size of the Material_MDL7 data structure used in the file
+ uint16_t material_stc_size;
+
+ //! Size of a texture coordinate set in the file
+ uint16_t skinpoint_stc_size;
+
+ //! Size of a triangle in the file
+ uint16_t triangle_stc_size;
+
+ //! Size of a normal vertex in the file
+ uint16_t mainvertex_stc_size;
+
+ //! Size of a per-frame animated vertex in the file
+ //! (this is not supported)
+ uint16_t framevertex_stc_size;
+
+ //! Size of a bone animation matrix
+ uint16_t bonetrans_stc_size;
+
+ //! Size of the Frame_MDL7 data structure used in the file
+ uint16_t frame_stc_size;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct Bone_MDL7
+ * \brief Data structure for a bone in a MDL7 file
+ */
+struct Bone_MDL7
+{
+ //! Index of the parent bone of *this* bone. 0xffff means:
+ //! "hey, I have no parent, I'm an orphan"
+ uint16_t parent_index;
+ uint8_t _unused_[2];
+
+ //! Relative position of the bone (relative to the
+ //! parent bone)
+ float x,y,z;
+
+ //! Optional name of the bone
+ char name[1 /* DUMMY SIZE */];
+} PACK_STRUCT;
+
+#if (!defined AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS)
+# define AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS (16 + 20)
+#endif
+
+#if (!defined AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_32_CHARS)
+# define AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_32_CHARS (16 + 32)
+#endif
+
+#if (!defined AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE)
+# define AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE (16)
+#endif
+
+#if (!defined AI_MDL7_MAX_GROUPNAMESIZE)
+# define AI_MDL7_MAX_GROUPNAMESIZE 16
+#endif // ! AI_MDL7_MAX_GROUPNAMESIZE
+
+// -------------------------------------------------------------------------------------
+/** \struct Group_MDL7
+ * \brief Group in a MDL7 file
+ */
+struct Group_MDL7
+{
+ //! = '1' -> triangle based Mesh
+ unsigned char typ;
+
+ int8_t deformers;
+ int8_t max_weights;
+ int8_t _unused_;
+
+ //! size of data for this group in bytes ( MD7_GROUP stc. included).
+ int32_t groupdata_size;
+ char name[AI_MDL7_MAX_GROUPNAMESIZE];
+
+ //! Number of skins
+ int32_t numskins;
+
+ //! Number of texture coordinates
+ int32_t num_stpts;
+
+ //! Number of triangles
+ int32_t numtris;
+
+ //! Number of vertices
+ int32_t numverts;
+
+ //! Number of frames
+ int32_t numframes;
+} PACK_STRUCT;
+
+#define AI_MDL7_SKINTYPE_MIPFLAG 0x08
+#define AI_MDL7_SKINTYPE_MATERIAL 0x10
+#define AI_MDL7_SKINTYPE_MATERIAL_ASCDEF 0x20
+#define AI_MDL7_SKINTYPE_RGBFLAG 0x80
+
+#if (!defined AI_MDL7_MAX_BONENAMESIZE)
+# define AI_MDL7_MAX_BONENAMESIZE 20
+#endif // !! AI_MDL7_MAX_BONENAMESIZE
+
+// -------------------------------------------------------------------------------------
+/** \struct Deformer_MDL7
+ * \brief Deformer in a MDL7 file
+ */
+struct Deformer_MDL7
+{
+ int8_t deformer_version; // 0
+ int8_t deformer_typ; // 0 - bones
+ int8_t _unused_[2];
+ int32_t group_index;
+ int32_t elements;
+ int32_t deformerdata_size;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct DeformerElement_MDL7
+ * \brief Deformer element in a MDL7 file
+ */
+struct DeformerElement_MDL7
+{
+ //! bei deformer_typ==0 (==bones) element_index == bone index
+ int32_t element_index;
+ char element_name[AI_MDL7_MAX_BONENAMESIZE];
+ int32_t weights;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct DeformerWeight_MDL7
+ * \brief Deformer weight in a MDL7 file
+ */
+struct DeformerWeight_MDL7
+{
+ //! for deformer_typ==0 (==bones) index == vertex index
+ int32_t index;
+ float weight;
+} PACK_STRUCT;
+
+
+// don't know why this was in the original headers ...
+typedef int32_t MD7_MATERIAL_ASCDEFSIZE;
+
+// -------------------------------------------------------------------------------------
+/** \struct ColorValue_MDL7
+ * \brief Data structure for a color value in a MDL7 file
+ */
+struct ColorValue_MDL7
+{
+ float r,g,b,a;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct Material_MDL7
+ * \brief Data structure for a Material in a MDL7 file
+ */
+struct Material_MDL7
+{
+ //! Diffuse base color of the material
+ ColorValue_MDL7 Diffuse;
+
+ //! Ambient base color of the material
+ ColorValue_MDL7 Ambient;
+
+ //! Specular base color of the material
+ ColorValue_MDL7 Specular;
+
+ //! Emissive base color of the material
+ ColorValue_MDL7 Emissive;
+
+ //! Phong power
+ float Power;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct Skin
+ * \brief Skin data structure #1 - used by Quake1, MDL2, MDL3 and MDL4
+ */
+struct Skin
+{
+ //! 0 = single (Skin), 1 = group (GroupSkin)
+ //! For MDL3-5: Defines the type of the skin and there
+ //! fore the size of the data to skip:
+ //-------------------------------------------------------
+ //! 2 for 565 RGB,
+ //! 3 for 4444 ARGB,
+ //! 10 for 565 mipmapped,
+ //! 11 for 4444 mipmapped (bpp = 2),
+ //! 12 for 888 RGB mipmapped (bpp = 3),
+ //! 13 for 8888 ARGB mipmapped (bpp = 4)
+ //-------------------------------------------------------
+ int32_t group;
+
+ //! Texture data
+ uint8_t *data;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct Skin
+ * \brief Skin data structure #2 - used by MDL5, MDL6 and MDL7
+ * \see Skin
+ */
+struct Skin_MDL5
+{
+ int32_t size, width, height;
+ uint8_t *data;
+} PACK_STRUCT;
+
+// maximum length of texture file name
+#if (!defined AI_MDL7_MAX_TEXNAMESIZE)
+# define AI_MDL7_MAX_TEXNAMESIZE 0x10
+#endif
+
+// ---------------------------------------------------------------------------
+/** \struct Skin_MDL7
+ * \brief Skin data structure #3 - used by MDL7 and HMP7
+ */
+struct Skin_MDL7
+{
+ uint8_t typ;
+ int8_t _unused_[3];
+ int32_t width;
+ int32_t height;
+ char texture_name[AI_MDL7_MAX_TEXNAMESIZE];
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct RGB565
+ * \brief Data structure for a RGB565 pixel in a texture
+ */
+struct RGB565
+{
+ uint16_t r : 5;
+ uint16_t g : 6;
+ uint16_t b : 5;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct ARGB4
+ * \brief Data structure for a ARGB4444 pixel in a texture
+ */
+struct ARGB4
+{
+ uint16_t a : 4;
+ uint16_t r : 4;
+ uint16_t g : 4;
+ uint16_t b : 4;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct GroupSkin
+ * \brief Skin data structure #2 (group of pictures)
+ */
+struct GroupSkin
+{
+ //! 0 = single (Skin), 1 = group (GroupSkin)
+ int32_t group;
+
+ //! Number of images
+ int32_t nb;
+
+ //! Time for each image
+ float *time;
+
+ //! Data of each image
+ uint8_t **data;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct TexCoord
+ * \brief Texture coordinate data structure used by the Quake1 MDL format
+ */
+struct TexCoord
+{
+ //! Is the vertex on the noundary between front and back piece?
+ int32_t onseam;
+
+ //! Texture coordinate in the tx direction
+ int32_t s;
+
+ //! Texture coordinate in the ty direction
+ int32_t t;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct TexCoord_MDL3
+ * \brief Data structure for an UV coordinate in the 3DGS MDL3 format
+ */
+struct TexCoord_MDL3
+{
+ //! position, horizontally in range 0..skinwidth-1
+ int16_t u;
+
+ //! position, vertically in range 0..skinheight-1
+ int16_t v;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct TexCoord_MDL7
+ * \brief Data structure for an UV coordinate in the 3DGS MDL7 format
+ */
+struct TexCoord_MDL7
+{
+ //! position, horizontally in range 0..1
+ float u;
+
+ //! position, vertically in range 0..1
+ float v;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct SkinSet_MDL7
+ * \brief Skin set data structure for the 3DGS MDL7 format
+ * MDL7 references UV coordinates per face via an index list.
+ * This allows the use of multiple skins per face with just one
+ * UV coordinate set.
+ */
+struct SkinSet_MDL7
+{
+ //! Index into the UV coordinate list
+ uint16_t st_index[3]; // size 6
+
+ //! Material index
+ int32_t material; // size 4
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct Triangle
+ * \brief Triangle data structure for the Quake1 MDL format
+ */
+struct Triangle
+{
+ //! 0 = backface, 1 = frontface
+ int32_t facesfront;
+
+ //! Vertex indices
+ int32_t vertex[3];
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct Triangle_MDL3
+ * \brief Triangle data structure for the 3DGS MDL3 format
+ */
+struct Triangle_MDL3
+{
+ //! Index of 3 3D vertices in range 0..numverts
+ uint16_t index_xyz[3];
+
+ //! Index of 3 skin vertices in range 0..numskinverts
+ uint16_t index_uv[3];
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct Triangle_MDL7
+ * \brief Triangle data structure for the 3DGS MDL7 format
+ */
+struct Triangle_MDL7
+{
+ //! Vertex indices
+ uint16_t v_index[3]; // size 6
+
+ //! Two skinsets. The second will be used for multi-texturing
+ SkinSet_MDL7 skinsets[2];
+} PACK_STRUCT;
+
+#if (!defined AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV)
+# define AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV (6+sizeof(SkinSet_MDL7)-sizeof(uint32_t))
+#endif
+#if (!defined AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV_WITH_MATINDEX)
+# define AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV_WITH_MATINDEX (6+sizeof(SkinSet_MDL7))
+#endif
+#if (!defined AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV)
+# define AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV (6+2*sizeof(SkinSet_MDL7))
+#endif
+
+// Helper constants for Triangle::facesfront
+#if (!defined AI_MDL_BACKFACE)
+# define AI_MDL_BACKFACE 0x0
+#endif
+#if (!defined AI_MDL_FRONTFACE)
+# define AI_MDL_FRONTFACE 0x1
+#endif
+
+// -------------------------------------------------------------------------------------
+/** \struct Vertex
+ * \brief Vertex data structure
+ */
+struct Vertex
+{
+ uint8_t v[3];
+ uint8_t normalIndex;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+struct Vertex_MDL4
+{
+ uint16_t v[3];
+ uint8_t normalIndex;
+ uint8_t unused;
+} PACK_STRUCT;
+
+#define AI_MDL7_FRAMEVERTEX120503_STCSIZE 16
+#define AI_MDL7_FRAMEVERTEX030305_STCSIZE 26
+
+// -------------------------------------------------------------------------------------
+/** \struct Vertex_MDL7
+ * \brief Vertex data structure used in MDL7 files
+ */
+struct Vertex_MDL7
+{
+ float x,y,z;
+ uint16_t vertindex; // = bone index
+ union {
+ uint8_t norm162index;
+ float norm[3];
+ };
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+/** \struct BoneTransform_MDL7
+ * \brief bone transformation matrix structure used in MDL7 files
+ */
+struct BoneTransform_MDL7
+{
+ //! 4*3
+ float m [4*4];
+
+ //! the index of this vertex, 0.. header::bones_num - 1
+ uint16_t bone_index;
+
+ //! I HATE 3DGS AND THE SILLY DEVELOPER WHO DESIGNED
+ //! THIS STUPID FILE FORMAT!
+ int8_t _unused_[2];
+} PACK_STRUCT;
+
+
+#define AI_MDL7_MAX_FRAMENAMESIZE 16
+
+
+// -------------------------------------------------------------------------------------
+/** \struct Frame_MDL7
+ * \brief Frame data structure used by MDL7 files
+ */
+struct Frame_MDL7
+{
+ char frame_name[AI_MDL7_MAX_FRAMENAMESIZE];
+ uint32_t vertices_count;
+ uint32_t transmatrix_count;
+};
+
+
+// -------------------------------------------------------------------------------------
+/** \struct SimpleFrame
+ * \brief Data structure for a simple frame
+ */
+struct SimpleFrame
+{
+ //! Minimum vertex of the bounding box
+ Vertex bboxmin;
+
+ //! Maximum vertex of the bounding box
+ Vertex bboxmax;
+
+ //! Name of the frame
+ char name[16];
+
+ //! Vertex list of the frame
+ Vertex *verts;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct Frame
+ * \brief Model frame data structure
+ */
+struct Frame
+{
+ //! 0 = simple frame, !0 = group frame
+ int32_t type;
+
+ //! Frame data
+ SimpleFrame frame;
+} PACK_STRUCT;
+
+
+// -------------------------------------------------------------------------------------
+struct SimpleFrame_MDLn_SP
+{
+ //! Minimum vertex of the bounding box
+ Vertex_MDL4 bboxmin;
+
+ //! Maximum vertex of the bounding box
+ Vertex_MDL4 bboxmax;
+
+ //! Name of the frame
+ char name[16];
+
+ //! Vertex list of the frame
+ Vertex_MDL4 *verts;
+} PACK_STRUCT;
+
+// -------------------------------------------------------------------------------------
+/** \struct GroupFrame
+ * \brief Data structure for a group of frames
+ */
+struct GroupFrame
+{
+ //! 0 = simple frame, !0 = group frame
+ int32_t type;
+
+ //! Minimum vertex for all single frames
+ Vertex min;
+
+ //! Maximum vertex for all single frames
+ Vertex max;
+
+ //! Time for all single frames
+ float *time;
+
+ //! List of single frames
+ SimpleFrame *frames;
+} PACK_STRUCT;
+
+#include "./../include/Compiler/poppack1.h"
+
+// -------------------------------------------------------------------------------------
+/** \struct IntFace_MDL7
+ * \brief Internal data structure to temporarily represent a face
+ */
+struct IntFace_MDL7
+{
+ // provide a constructor for our own convenience
+ IntFace_MDL7()
+ {
+ // set everything to zero
+ mIndices[0] = mIndices[1] = mIndices[2] = 0;
+ iMatIndex[0] = iMatIndex[1] = 0;
+ }
+
+ //! Vertex indices
+ uint32_t mIndices[3];
+
+ //! Material index (maximally two channels, which are joined later)
+ unsigned int iMatIndex[2];
+};
+
+// -------------------------------------------------------------------------------------
+/** \struct IntMaterial_MDL7
+ * \brief Internal data structure to temporarily represent a material
+ * which has been created from two single materials along with the
+ * original material indices.
+ */
+struct IntMaterial_MDL7
+{
+ // provide a constructor for our own convenience
+ IntMaterial_MDL7()
+ {
+ pcMat = NULL;
+ iOldMatIndices[0] = iOldMatIndices[1] = 0;
+ }
+
+ //! Material instance
+ MaterialHelper* pcMat;
+
+ //! Old material indices
+ unsigned int iOldMatIndices[2];
+};
+
+// -------------------------------------------------------------------------------------
+/** \struct IntBone_MDL7
+ * \brief Internal data structure to represent a bone in a MDL7 file with
+ * all of its animation channels assigned to it.
+ */
+struct IntBone_MDL7 : aiBone
+{
+ //! Default constructor
+ IntBone_MDL7() : iParent (0xffff)
+ {
+ pkeyPositions.reserve(30);
+ pkeyScalings.reserve(30);
+ pkeyRotations.reserve(30);
+ }
+
+ //! Parent bone of the bone
+ uint64_t iParent;
+
+ //! Relative position of the bone
+ aiVector3D vPosition;
+
+ //! Array of position keys
+ std::vector<aiVectorKey> pkeyPositions;
+
+ //! Array of scaling keys
+ std::vector<aiVectorKey> pkeyScalings;
+
+ //! Array of rotation keys
+ std::vector<aiQuatKey> pkeyRotations;
+};
+
+// -------------------------------------------------------------------------------------
+//! Describes a MDL7 frame
+struct IntFrameInfo_MDL7
+{
+ //! Construction from an existing frame header
+ IntFrameInfo_MDL7(BE_NCONST MDL::Frame_MDL7* _pcFrame,unsigned int _iIndex)
+ : iIndex(_iIndex)
+ , pcFrame(_pcFrame)
+ {}
+
+ //! Index of the frame
+ unsigned int iIndex;
+
+ //! Points to the header of the frame
+ BE_NCONST MDL::Frame_MDL7* pcFrame;
+};
+
+// -------------------------------------------------------------------------------------
+//! Describes a MDL7 mesh group
+struct IntGroupInfo_MDL7
+{
+ //! Default constructor
+ IntGroupInfo_MDL7()
+ : iIndex(0)
+ , pcGroup(NULL)
+ , pcGroupUVs(NULL)
+ , pcGroupTris(NULL)
+ , pcGroupVerts(NULL)
+ {}
+
+ //! Construction from an existing group header
+ IntGroupInfo_MDL7(BE_NCONST MDL::Group_MDL7* _pcGroup, unsigned int _iIndex)
+ : iIndex(_iIndex)
+ , pcGroup(_pcGroup)
+ {}
+
+ //! Index of the group
+ unsigned int iIndex;
+
+ //! Points to the header of the group
+ BE_NCONST MDL::Group_MDL7* pcGroup;
+
+ //! Points to the beginning of the uv coordinate section
+ BE_NCONST MDL::TexCoord_MDL7* pcGroupUVs;
+
+ //! Points to the beginning of the triangle section
+ BE_NCONST MDL::Triangle_MDL7* pcGroupTris;
+
+ //! Points to the beginning of the vertex section
+ BE_NCONST MDL::Vertex_MDL7* pcGroupVerts;
+};
+
+// -------------------------------------------------------------------------------------
+//! Holds the data that belongs to a MDL7 mesh group
+struct IntGroupData_MDL7
+{
+ IntGroupData_MDL7()
+ : pcFaces(NULL), bNeed2UV(false)
+ {}
+
+ //! Array of faces that belong to the group
+ MDL::IntFace_MDL7* pcFaces;
+
+ //! Array of vertex positions
+ std::vector<aiVector3D> vPositions;
+
+ //! Array of vertex normals
+ std::vector<aiVector3D> vNormals;
+
+ //! Array of bones indices
+ std::vector<unsigned int> aiBones;
+
+ //! First UV coordinate set
+ std::vector<aiVector3D> vTextureCoords1;
+
+ //! Optional second UV coordinate set
+ std::vector<aiVector3D> vTextureCoords2;
+
+ //! Specifies whether there are two texture
+ //! coordinate sets required
+ bool bNeed2UV;
+};
+
+// -------------------------------------------------------------------------------------
+//! Holds data from an MDL7 file that is shared by all mesh groups
+struct IntSharedData_MDL7
+{
+ //! Default constructor
+ IntSharedData_MDL7()
+ {
+ abNeedMaterials.reserve(10);
+ }
+
+ //! Destruction: properly delete all allocated resources
+ ~IntSharedData_MDL7()
+ {
+ // kill all bones
+ if (this->apcOutBones)
+ {
+ for (unsigned int m = 0; m < iNum;++m)
+ delete this->apcOutBones[m];
+ delete[] this->apcOutBones;
+ }
+ }
+
+ //! Specifies which materials are used
+ std::vector<bool> abNeedMaterials;
+
+ //! List of all materials
+ std::vector<MaterialHelper*> pcMats;
+
+ //! List of all bones
+ IntBone_MDL7** apcOutBones;
+
+ //! number of bones
+ unsigned int iNum;
+};
+
+// -------------------------------------------------------------------------------------
+//! Contains input data for GenerateOutputMeshes_3DGS_MDL7
+struct IntSplittedGroupData_MDL7
+{
+ //! Construction from a given shared data set
+ IntSplittedGroupData_MDL7(IntSharedData_MDL7& _shared,
+ std::vector<aiMesh*>& _avOutList)
+
+ : shared(_shared), avOutList(_avOutList)
+ {
+ }
+
+ //! Destruction: properly delete all allocated resources
+ ~IntSplittedGroupData_MDL7()
+ {
+ // kill all face lists
+ if (this->aiSplit)
+ {
+ for (unsigned int m = 0; m < shared.pcMats.size();++m)
+ delete this->aiSplit[m];
+ delete[] this->aiSplit;
+ }
+ }
+
+ //! Contains a list of all faces per material
+ std::vector<unsigned int>** aiSplit;
+
+ //! Shared data for all groups of the model
+ IntSharedData_MDL7& shared;
+
+ //! List of meshes
+ std::vector<aiMesh*>& avOutList;
+};
+
+
+}
+} // end namespaces
+
+#endif // !! AI_MDLFILEHELPER_H_INC
diff --git a/3rdparty/assimp/code/MDLLoader.cpp b/3rdparty/assimp/code/MDLLoader.cpp
new file mode 100644
index 000000000..5ee9d8509
--- /dev/null
+++ b/3rdparty/assimp/code/MDLLoader.cpp
@@ -0,0 +1,1929 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MDLLoader.cpp
+ * @brief Implementation of the main parts of the MDL importer class
+ * *TODO* Cleanup and further testing of some parts necessary
+ */
+
+// internal headers
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
+
+#include "MDLLoader.h"
+#include "MDLDefaultColorMap.h"
+#include "MD2FileData.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Ugly stuff ... nevermind
+#define _AI_MDL7_ACCESS(_data, _index, _limit, _type) \
+ (*((const _type*)(((const char*)_data) + _index * _limit)))
+
+#define _AI_MDL7_ACCESS_PTR(_data, _index, _limit, _type) \
+ ((BE_NCONST _type*)(((const char*)_data) + _index * _limit))
+
+#define _AI_MDL7_ACCESS_VERT(_data, _index, _limit) \
+ _AI_MDL7_ACCESS(_data,_index,_limit,MDL::Vertex_MDL7)
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MDLImporter::MDLImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MDLImporter::~MDLImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MDLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ // if check for extension is not enough, check for the magic tokens
+ if (extension == "mdl" || !extension.length() || checkSig) {
+ uint32_t tokens[8];
+ tokens[0] = AI_MDL_MAGIC_NUMBER_LE_HL2a;
+ tokens[1] = AI_MDL_MAGIC_NUMBER_LE_HL2b;
+ tokens[2] = AI_MDL_MAGIC_NUMBER_LE_GS7;
+ tokens[3] = AI_MDL_MAGIC_NUMBER_LE_GS5b;
+ tokens[4] = AI_MDL_MAGIC_NUMBER_LE_GS5a;
+ tokens[5] = AI_MDL_MAGIC_NUMBER_LE_GS4;
+ tokens[6] = AI_MDL_MAGIC_NUMBER_LE_GS3;
+ tokens[7] = AI_MDL_MAGIC_NUMBER_LE;
+ return CheckMagicToken(pIOHandler,pFile,tokens,8,0);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void MDLImporter::SetupProperties(const Importer* pImp)
+{
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MDL_KEYFRAME,0xffffffff);
+
+ // The
+ // AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ if (0xffffffff == configFrameID) {
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+
+ // AI_CONFIG_IMPORT_MDL_COLORMAP - pallette file
+ configPalette = pImp->GetPropertyString(AI_CONFIG_IMPORT_MDL_COLORMAP,"colormap.lmp");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all supported extensions
+void MDLImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert( "mdl" );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MDLImporter::InternReadFile( const std::string& pFile,
+ aiScene* _pScene, IOSystem* _pIOHandler)
+{
+ pScene = _pScene;
+ pIOHandler = _pIOHandler;
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open MDL file " + pFile + ".");
+ }
+
+ // This should work for all other types of MDL files, too ...
+ // the quake header is one of the smallest, afaik
+ iFileSize = (unsigned int)file->FileSize();
+ if ( iFileSize < sizeof(MDL::Header)) {
+ throw DeadlyImportError( "MDL File is too small.");
+ }
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ std::vector<unsigned char> buffer(iFileSize+1);
+ mBuffer = &buffer[0];
+ file->Read( (void*)mBuffer, 1, iFileSize);
+
+ // Append a binary zero to the end of the buffer.
+ // this is just for safety that string parsing routines
+ // find the end of the buffer ...
+ mBuffer[iFileSize] = '\0';
+ const uint32_t iMagicWord = *((uint32_t*)mBuffer);
+
+ // Determine the file subtype and call the appropriate member function
+
+ // Original Quake1 format
+ if (AI_MDL_MAGIC_NUMBER_BE == iMagicWord || AI_MDL_MAGIC_NUMBER_LE == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: Quake 1, magic word is IDPO");
+ iGSFileVersion = 0;
+ InternReadFile_Quake1();
+ }
+ // GameStudio A<old> MDL2 format - used by some test models that come with 3DGS
+ else if (AI_MDL_MAGIC_NUMBER_BE_GS3 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS3 == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A2, magic word is MDL2");
+ iGSFileVersion = 2;
+ InternReadFile_Quake1();
+ }
+ // GameStudio A4 MDL3 format
+ else if (AI_MDL_MAGIC_NUMBER_BE_GS4 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS4 == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A4, magic word is MDL3");
+ iGSFileVersion = 3;
+ InternReadFile_3DGS_MDL345();
+ }
+ // GameStudio A5+ MDL4 format
+ else if (AI_MDL_MAGIC_NUMBER_BE_GS5a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS5a == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A4, magic word is MDL4");
+ iGSFileVersion = 4;
+ InternReadFile_3DGS_MDL345();
+ }
+ // GameStudio A5+ MDL5 format
+ else if (AI_MDL_MAGIC_NUMBER_BE_GS5b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS5b == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A5, magic word is MDL5");
+ iGSFileVersion = 5;
+ InternReadFile_3DGS_MDL345();
+ }
+ // GameStudio A7 MDL7 format
+ else if (AI_MDL_MAGIC_NUMBER_BE_GS7 == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_GS7 == iMagicWord) {
+ DefaultLogger::get()->debug("MDL subtype: 3D GameStudio A7, magic word is MDL7");
+ iGSFileVersion = 7;
+ InternReadFile_3DGS_MDL7();
+ }
+ // IDST/IDSQ Format (CS:S/HL, etc ...)
+ else if (AI_MDL_MAGIC_NUMBER_BE_HL2a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2a == iMagicWord ||
+ AI_MDL_MAGIC_NUMBER_BE_HL2b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2b == iMagicWord)
+ {
+ DefaultLogger::get()->debug("MDL subtype: Source(tm) Engine, magic word is IDST/IDSQ");
+ iGSFileVersion = 0;
+ InternReadFile_HL2();
+ }
+ else {
+ // print the magic word to the log file
+ throw DeadlyImportError( "Unknown MDL subformat " + pFile +
+ ". Magic word (" + std::string((char*)&iMagicWord,4) + ") is not known");
+ }
+
+ // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
+ pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
+ 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
+
+ // delete the file buffer and cleanup
+ AI_DEBUG_INVALIDATE_PTR(mBuffer);
+ AI_DEBUG_INVALIDATE_PTR(pIOHandler);
+ AI_DEBUG_INVALIDATE_PTR(pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether we're still inside the valid file range
+void MDLImporter::SizeCheck(const void* szPos)
+{
+ if (!szPos || (const unsigned char*)szPos > this->mBuffer + this->iFileSize)
+ {
+ throw DeadlyImportError("Invalid MDL file. The file is too small "
+ "or contains invalid data.");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Just for debgging purposes
+void MDLImporter::SizeCheck(const void* szPos, const char* szFile, unsigned int iLine)
+{
+ ai_assert(NULL != szFile);
+ if (!szPos || (const unsigned char*)szPos > mBuffer + iFileSize)
+ {
+ // remove a directory if there is one
+ const char* szFilePtr = ::strrchr(szFile,'\\');
+ if (!szFilePtr) {
+ if (!(szFilePtr = ::strrchr(szFile,'/')))
+ szFilePtr = szFile;
+ }
+ if (szFilePtr)++szFilePtr;
+
+ char szBuffer[1024];
+ ::sprintf(szBuffer,"Invalid MDL file. The file is too small "
+ "or contains invalid data (File: %s Line: %i)",szFilePtr,iLine);
+
+ throw DeadlyImportError(szBuffer);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Validate a quake file header
+void MDLImporter::ValidateHeader_Quake1(const MDL::Header* pcHeader)
+{
+ // some values may not be NULL
+ if (!pcHeader->num_frames)
+ throw DeadlyImportError( "[Quake 1 MDL] There are no frames in the file");
+
+ if (!pcHeader->num_verts)
+ throw DeadlyImportError( "[Quake 1 MDL] There are no vertices in the file");
+
+ if (!pcHeader->num_tris)
+ throw DeadlyImportError( "[Quake 1 MDL] There are no triangles in the file");
+
+ // check whether the maxima are exceeded ...however, this applies for Quake 1 MDLs only
+ if (!this->iGSFileVersion)
+ {
+ if (pcHeader->num_verts > AI_MDL_MAX_VERTS)
+ DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_VERTS vertices");
+
+ if (pcHeader->num_tris > AI_MDL_MAX_TRIANGLES)
+ DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_TRIANGLES triangles");
+
+ if (pcHeader->num_frames > AI_MDL_MAX_FRAMES)
+ DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_FRAMES frames");
+
+ // (this does not apply for 3DGS MDLs)
+ if (!this->iGSFileVersion && pcHeader->version != AI_MDL_VERSION)
+ DefaultLogger::get()->warn("Quake 1 MDL model has an unknown version: AI_MDL_VERSION (=6) is "
+ "the expected file format version");
+ if (pcHeader->num_skins && (!pcHeader->skinwidth || !pcHeader->skinheight))
+ DefaultLogger::get()->warn("Skin width or height are 0");
+ }
+}
+
+#ifdef AI_BUILD_BIG_ENDIAN
+// ------------------------------------------------------------------------------------------------
+void FlipQuakeHeader(BE_NCONST MDL::Header* pcHeader)
+{
+ AI_SWAP4( pcHeader->ident);
+ AI_SWAP4( pcHeader->version);
+ AI_SWAP4( pcHeader->boundingradius);
+ AI_SWAP4( pcHeader->flags);
+ AI_SWAP4( pcHeader->num_frames);
+ AI_SWAP4( pcHeader->num_skins);
+ AI_SWAP4( pcHeader->num_tris);
+ AI_SWAP4( pcHeader->num_verts);
+ for (unsigned int i = 0; i < 3;++i)
+ {
+ AI_SWAP4( pcHeader->scale[i]);
+ AI_SWAP4( pcHeader->translate[i]);
+ }
+ AI_SWAP4( pcHeader->size);
+ AI_SWAP4( pcHeader->skinheight);
+ AI_SWAP4( pcHeader->skinwidth);
+ AI_SWAP4( pcHeader->synctype);
+}
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Read a Quake 1 file
+void MDLImporter::InternReadFile_Quake1( )
+{
+ ai_assert(NULL != pScene);
+ BE_NCONST MDL::Header *pcHeader = (BE_NCONST MDL::Header*)this->mBuffer;
+
+#ifdef AI_BUILD_BIG_ENDIAN
+ FlipQuakeHeader(pcHeader);
+#endif
+
+ ValidateHeader_Quake1(pcHeader);
+
+ // current cursor position in the file
+ const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
+
+ // need to read all textures
+ for (unsigned int i = 0; i < (unsigned int)pcHeader->num_skins;++i)
+ {
+ union{BE_NCONST MDL::Skin* pcSkin;BE_NCONST MDL::GroupSkin* pcGroupSkin;};
+ pcSkin = (BE_NCONST MDL::Skin*)szCurrent;
+
+ AI_SWAP4( pcSkin->group );
+
+ // Quake 1 groupskins
+ if (1 == pcSkin->group)
+ {
+ AI_SWAP4( pcGroupSkin->nb );
+
+ // need to skip multiple images
+ const unsigned int iNumImages = (unsigned int)pcGroupSkin->nb;
+ szCurrent += sizeof(uint32_t) * 2;
+
+ if (0 != iNumImages)
+ {
+ if (!i) {
+ // however, create only one output image (the first)
+ this->CreateTextureARGB8_3DGS_MDL3(szCurrent + iNumImages * sizeof(float));
+ }
+ // go to the end of the skin section / the beginning of the next skin
+ szCurrent += pcHeader->skinheight * pcHeader->skinwidth +
+ sizeof(float) * iNumImages;
+ }
+ }
+ // 3DGS has a few files that are using other 3DGS like texture formats here
+ else
+ {
+ szCurrent += sizeof(uint32_t);
+ unsigned int iSkip = i ? 0xffffffff : 0;
+ this->CreateTexture_3DGS_MDL4(szCurrent,pcSkin->group,&iSkip);
+ szCurrent += iSkip;
+ }
+ }
+ // get a pointer to the texture coordinates
+ BE_NCONST MDL::TexCoord* pcTexCoords = (BE_NCONST MDL::TexCoord*)szCurrent;
+ szCurrent += sizeof(MDL::TexCoord) * pcHeader->num_verts;
+
+ // get a pointer to the triangles
+ BE_NCONST MDL::Triangle* pcTriangles = (BE_NCONST MDL::Triangle*)szCurrent;
+ szCurrent += sizeof(MDL::Triangle) * pcHeader->num_tris;
+ VALIDATE_FILE_SIZE(szCurrent);
+
+ // now get a pointer to the first frame in the file
+ BE_NCONST MDL::Frame* pcFrames = (BE_NCONST MDL::Frame*)szCurrent;
+ BE_NCONST MDL::SimpleFrame* pcFirstFrame;
+
+ if (0 == pcFrames->type)
+ {
+ // get address of single frame
+ pcFirstFrame = &pcFrames->frame;
+ }
+ else
+ {
+ // get the first frame in the group
+ BE_NCONST MDL::GroupFrame* pcFrames2 = (BE_NCONST MDL::GroupFrame*)pcFrames;
+ pcFirstFrame = (BE_NCONST MDL::SimpleFrame*)(&pcFrames2->time + pcFrames->type);
+ }
+ BE_NCONST MDL::Vertex* pcVertices = (BE_NCONST MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name));
+ VALIDATE_FILE_SIZE((const unsigned char*)(pcVertices + pcHeader->num_verts));
+
+#ifdef AI_BUILD_BIG_ENDIAN
+ for (int i = 0; i<pcHeader->num_verts;++i)
+ {
+ AI_SWAP4( pcTexCoords[i].onseam );
+ AI_SWAP4( pcTexCoords[i].s );
+ AI_SWAP4( pcTexCoords[i].t );
+ }
+
+ for (int i = 0; i<pcHeader->num_tris;++i)
+ {
+ AI_SWAP4( pcTriangles[i].facesfront);
+ AI_SWAP4( pcTriangles[i].vertex[0]);
+ AI_SWAP4( pcTriangles[i].vertex[1]);
+ AI_SWAP4( pcTriangles[i].vertex[2]);
+ }
+#endif
+
+ // setup materials
+ SetupMaterialProperties_3DGS_MDL5_Quake1();
+
+ // allocate enough storage to hold all vertices and triangles
+ aiMesh* pcMesh = new aiMesh();
+
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+ pcMesh->mNumVertices = pcHeader->num_tris * 3;
+ pcMesh->mNumFaces = pcHeader->num_tris;
+ pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+ pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNumUVComponents[0] = 2;
+
+ // there won't be more than one mesh inside the file
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+ pScene->mMeshes[0] = pcMesh;
+
+ // now iterate through all triangles
+ unsigned int iCurrent = 0;
+ for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i)
+ {
+ pcMesh->mFaces[i].mIndices = new unsigned int[3];
+ pcMesh->mFaces[i].mNumIndices = 3;
+
+ unsigned int iTemp = iCurrent;
+ for (unsigned int c = 0; c < 3;++c,++iCurrent)
+ {
+ pcMesh->mFaces[i].mIndices[c] = iCurrent;
+
+ // read vertices
+ unsigned int iIndex = pcTriangles->vertex[c];
+ if (iIndex >= (unsigned int)pcHeader->num_verts)
+ {
+ iIndex = pcHeader->num_verts-1;
+ DefaultLogger::get()->warn("Index overflow in Q1-MDL vertex list.");
+ }
+
+ aiVector3D& vec = pcMesh->mVertices[iCurrent];
+ vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
+ vec.x += pcHeader->translate[0];
+
+ vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
+ vec.y += pcHeader->translate[1];
+ //vec.y *= -1.0f;
+
+ vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
+ vec.z += pcHeader->translate[2];
+
+ // read the normal vector from the precalculated normal table
+ MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
+ //pcMesh->mNormals[iCurrent].y *= -1.0f;
+
+ // read texture coordinates
+ float s = (float)pcTexCoords[iIndex].s;
+ float t = (float)pcTexCoords[iIndex].t;
+
+ // translate texture coordinates
+ if (0 == pcTriangles->facesfront && 0 != pcTexCoords[iIndex].onseam) {
+ s += pcHeader->skinwidth * 0.5f;
+ }
+
+ // Scale s and t to range from 0.0 to 1.0
+ pcMesh->mTextureCoords[0][iCurrent].x = (s + 0.5f) / pcHeader->skinwidth;
+ pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-(t + 0.5f) / pcHeader->skinheight;
+
+ }
+ pcMesh->mFaces[i].mIndices[0] = iTemp+2;
+ pcMesh->mFaces[i].mIndices[1] = iTemp+1;
+ pcMesh->mFaces[i].mIndices[2] = iTemp+0;
+ pcTriangles++;
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup material properties for Quake and older GameStudio files
+void MDLImporter::SetupMaterialProperties_3DGS_MDL5_Quake1( )
+{
+ const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
+
+ // allocate ONE material
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = new MaterialHelper();
+ pScene->mNumMaterials = 1;
+
+ // setup the material's properties
+ const int iMode = (int)aiShadingMode_Gouraud;
+ MaterialHelper* const pcHelper = (MaterialHelper*)pScene->mMaterials[0];
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ aiColor4D clr;
+ if (0 != pcHeader->num_skins && pScene->mNumTextures) {
+ // can we replace the texture with a single color?
+ clr = this->ReplaceTextureWithColor(pScene->mTextures[0]);
+ if (is_not_qnan(clr.r)) {
+ delete pScene->mTextures[0];
+ delete[] pScene->mTextures;
+
+ pScene->mTextures = NULL;
+ pScene->mNumTextures = 0;
+ }
+ else {
+ clr.b = clr.a = clr.g = clr.r = 1.0f;
+ aiString szString;
+ ::memcpy(szString.data,AI_MAKE_EMBEDDED_TEXNAME(0),3);
+ szString.length = 2;
+ pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ }
+
+ pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.r *= 0.05f;clr.g *= 0.05f;
+ clr.b *= 0.05f;clr.a = 1.0f;
+ pcHelper->AddProperty<aiColor4D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a MDL 3,4,5 file
+void MDLImporter::InternReadFile_3DGS_MDL345( )
+{
+ ai_assert(NULL != pScene);
+
+ // the header of MDL 3/4/5 is nearly identical to the original Quake1 header
+ BE_NCONST MDL::Header *pcHeader = (BE_NCONST MDL::Header*)this->mBuffer;
+#ifdef AI_BUILD_BIG_ENDIAN
+ FlipQuakeHeader(pcHeader);
+#endif
+ ValidateHeader_Quake1(pcHeader);
+
+ // current cursor position in the file
+ const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
+
+ // need to read all textures
+ for (unsigned int i = 0; i < (unsigned int)pcHeader->num_skins;++i) {
+ BE_NCONST MDL::Skin* pcSkin;
+ pcSkin = (BE_NCONST MDL::Skin*)szCurrent;
+ AI_SWAP4( pcSkin->group);
+ // create one output image
+ unsigned int iSkip = i ? 0xffffffff : 0;
+ if (5 <= iGSFileVersion)
+ {
+ // MDL5 format could contain MIPmaps
+ CreateTexture_3DGS_MDL5((unsigned char*)pcSkin + sizeof(uint32_t),
+ pcSkin->group,&iSkip);
+ }
+ else {
+ CreateTexture_3DGS_MDL4((unsigned char*)pcSkin + sizeof(uint32_t),
+ pcSkin->group,&iSkip);
+ }
+ // need to skip one image
+ szCurrent += iSkip + sizeof(uint32_t);
+
+ }
+ // get a pointer to the texture coordinates
+ BE_NCONST MDL::TexCoord_MDL3* pcTexCoords = (BE_NCONST MDL::TexCoord_MDL3*)szCurrent;
+ szCurrent += sizeof(MDL::TexCoord_MDL3) * pcHeader->synctype;
+
+ // NOTE: for MDLn formats "synctype" corresponds to the number of UV coords
+
+ // get a pointer to the triangles
+ BE_NCONST MDL::Triangle_MDL3* pcTriangles = (BE_NCONST MDL::Triangle_MDL3*)szCurrent;
+ szCurrent += sizeof(MDL::Triangle_MDL3) * pcHeader->num_tris;
+
+#ifdef AI_BUILD_BIG_ENDIAN
+
+ for (int i = 0; i<pcHeader->synctype;++i) {
+ AI_SWAP2( pcTexCoords[i].u );
+ AI_SWAP2( pcTexCoords[i].v );
+ }
+
+ for (int i = 0; i<pcHeader->num_tris;++i) {
+ AI_SWAP2( pcTriangles[i].index_xyz[0]);
+ AI_SWAP2( pcTriangles[i].index_xyz[1]);
+ AI_SWAP2( pcTriangles[i].index_xyz[2]);
+ AI_SWAP2( pcTriangles[i].index_uv[0]);
+ AI_SWAP2( pcTriangles[i].index_uv[1]);
+ AI_SWAP2( pcTriangles[i].index_uv[2]);
+ }
+
+#endif
+
+ VALIDATE_FILE_SIZE(szCurrent);
+
+ // setup materials
+ SetupMaterialProperties_3DGS_MDL5_Quake1();
+
+ // allocate enough storage to hold all vertices and triangles
+ aiMesh* pcMesh = new aiMesh();
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ pcMesh->mNumVertices = pcHeader->num_tris * 3;
+ pcMesh->mNumFaces = pcHeader->num_tris;
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+ // there won't be more than one mesh inside the file
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+ pScene->mMeshes[0] = pcMesh;
+
+ // allocate output storage
+ pcMesh->mNumVertices = (unsigned int)pcHeader->num_tris*3;
+ pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+
+ if (pcHeader->synctype) {
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNumUVComponents[0] = 2;
+ }
+
+ // now get a pointer to the first frame in the file
+ BE_NCONST MDL::Frame* pcFrames = (BE_NCONST MDL::Frame*)szCurrent;
+ AI_SWAP4(pcFrames->type);
+
+ // byte packed vertices
+ // FIXME: these two snippets below are almost identical ... join them?
+ /////////////////////////////////////////////////////////////////////////////////////
+ if (0 == pcFrames->type || 3 >= this->iGSFileVersion) {
+
+ const MDL::SimpleFrame* pcFirstFrame = (const MDL::SimpleFrame*)(szCurrent + sizeof(uint32_t));
+ const MDL::Vertex* pcVertices = (const MDL::Vertex*) ((pcFirstFrame->name) + sizeof(pcFirstFrame->name));
+
+ VALIDATE_FILE_SIZE(pcVertices + pcHeader->num_verts);
+
+ // now iterate through all triangles
+ unsigned int iCurrent = 0;
+ for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i) {
+ pcMesh->mFaces[i].mIndices = new unsigned int[3];
+ pcMesh->mFaces[i].mNumIndices = 3;
+
+ unsigned int iTemp = iCurrent;
+ for (unsigned int c = 0; c < 3;++c,++iCurrent) {
+ // read vertices
+ unsigned int iIndex = pcTriangles->index_xyz[c];
+ if (iIndex >= (unsigned int)pcHeader->num_verts) {
+ iIndex = pcHeader->num_verts-1;
+ DefaultLogger::get()->warn("Index overflow in MDLn vertex list");
+ }
+
+ aiVector3D& vec = pcMesh->mVertices[iCurrent];
+ vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
+ vec.x += pcHeader->translate[0];
+
+ vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
+ vec.y += pcHeader->translate[1];
+ // vec.y *= -1.0f;
+
+ vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
+ vec.z += pcHeader->translate[2];
+
+ // read the normal vector from the precalculated normal table
+ MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
+ // pcMesh->mNormals[iCurrent].y *= -1.0f;
+
+ // read texture coordinates
+ if (pcHeader->synctype) {
+ ImportUVCoordinate_3DGS_MDL345(pcMesh->mTextureCoords[0][iCurrent],
+ pcTexCoords,pcTriangles->index_uv[c]);
+ }
+ }
+ pcMesh->mFaces[i].mIndices[0] = iTemp+2;
+ pcMesh->mFaces[i].mIndices[1] = iTemp+1;
+ pcMesh->mFaces[i].mIndices[2] = iTemp+0;
+ pcTriangles++;
+ }
+
+ }
+ // short packed vertices
+ /////////////////////////////////////////////////////////////////////////////////////
+ else {
+ // now get a pointer to the first frame in the file
+ const MDL::SimpleFrame_MDLn_SP* pcFirstFrame = (const MDL::SimpleFrame_MDLn_SP*) (szCurrent + sizeof(uint32_t));
+
+ // get a pointer to the vertices
+ const MDL::Vertex_MDL4* pcVertices = (const MDL::Vertex_MDL4*) ((pcFirstFrame->name) +
+ sizeof(pcFirstFrame->name));
+
+ VALIDATE_FILE_SIZE(pcVertices + pcHeader->num_verts);
+
+ // now iterate through all triangles
+ unsigned int iCurrent = 0;
+ for (unsigned int i = 0; i < (unsigned int) pcHeader->num_tris;++i) {
+ pcMesh->mFaces[i].mIndices = new unsigned int[3];
+ pcMesh->mFaces[i].mNumIndices = 3;
+
+ unsigned int iTemp = iCurrent;
+ for (unsigned int c = 0; c < 3;++c,++iCurrent) {
+ // read vertices
+ unsigned int iIndex = pcTriangles->index_xyz[c];
+ if (iIndex >= (unsigned int)pcHeader->num_verts) {
+ iIndex = pcHeader->num_verts-1;
+ DefaultLogger::get()->warn("Index overflow in MDLn vertex list");
+ }
+
+ aiVector3D& vec = pcMesh->mVertices[iCurrent];
+ vec.x = (float)pcVertices[iIndex].v[0] * pcHeader->scale[0];
+ vec.x += pcHeader->translate[0];
+
+ vec.y = (float)pcVertices[iIndex].v[1] * pcHeader->scale[1];
+ vec.y += pcHeader->translate[1];
+ // vec.y *= -1.0f;
+
+ vec.z = (float)pcVertices[iIndex].v[2] * pcHeader->scale[2];
+ vec.z += pcHeader->translate[2];
+
+ // read the normal vector from the precalculated normal table
+ MD2::LookupNormalIndex(pcVertices[iIndex].normalIndex,pcMesh->mNormals[iCurrent]);
+ // pcMesh->mNormals[iCurrent].y *= -1.0f;
+
+ // read texture coordinates
+ if (pcHeader->synctype) {
+ ImportUVCoordinate_3DGS_MDL345(pcMesh->mTextureCoords[0][iCurrent],
+ pcTexCoords,pcTriangles->index_uv[c]);
+ }
+ }
+ pcMesh->mFaces[i].mIndices[0] = iTemp+2;
+ pcMesh->mFaces[i].mIndices[1] = iTemp+1;
+ pcMesh->mFaces[i].mIndices[2] = iTemp+0;
+ pcTriangles++;
+ }
+ }
+
+ // For MDL5 we will need to build valid texture coordinates
+ // basing upon the file loaded (only support one file as skin)
+ if (0x5 == iGSFileVersion)
+ CalculateUVCoordinates_MDL5();
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a single UV coordinate for Quake and older GameStudio files
+void MDLImporter::ImportUVCoordinate_3DGS_MDL345(
+ aiVector3D& vOut,
+ const MDL::TexCoord_MDL3* pcSrc,
+ unsigned int iIndex)
+{
+ ai_assert(NULL != pcSrc);
+ const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
+
+ // validate UV indices
+ if (iIndex >= (unsigned int) pcHeader->synctype) {
+ iIndex = pcHeader->synctype-1;
+ DefaultLogger::get()->warn("Index overflow in MDLn UV coord list");
+ }
+
+ float s = (float)pcSrc[iIndex].u;
+ float t = (float)pcSrc[iIndex].v;
+
+ // Scale s and t to range from 0.0 to 1.0
+ if (0x5 != iGSFileVersion) {
+ s = (s + 0.5f) / pcHeader->skinwidth;
+ t = 1.0f-(t + 0.5f) / pcHeader->skinheight;
+ }
+
+ vOut.x = s;
+ vOut.y = t;
+ vOut.z = 0.0f;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Compute UV coordinates for a MDL5 file
+void MDLImporter::CalculateUVCoordinates_MDL5()
+{
+ const MDL::Header* const pcHeader = (const MDL::Header*)this->mBuffer;
+ if (pcHeader->num_skins && this->pScene->mNumTextures) {
+ const aiTexture* pcTex = this->pScene->mTextures[0];
+
+ // if the file is loaded in DDS format: get the size of the
+ // texture from the header of the DDS file
+ // skip three DWORDs and read first height, then the width
+ unsigned int iWidth, iHeight;
+ if (!pcTex->mHeight) {
+ const uint32_t* piPtr = (uint32_t*)pcTex->pcData;
+
+ piPtr += 3;
+ iHeight = (unsigned int)*piPtr++;
+ iWidth = (unsigned int)*piPtr;
+ if (!iHeight || !iWidth)
+ {
+ DefaultLogger::get()->warn("Either the width or the height of the "
+ "embedded DDS texture is zero. Unable to compute final texture "
+ "coordinates. The texture coordinates remain in their original "
+ "0-x/0-y (x,y = texture size) range.");
+ iWidth = 1;
+ iHeight = 1;
+ }
+ }
+ else {
+ iWidth = pcTex->mWidth;
+ iHeight = pcTex->mHeight;
+ }
+
+ if (1 != iWidth || 1 != iHeight) {
+ const float fWidth = (float)iWidth;
+ const float fHeight = (float)iHeight;
+ aiMesh* pcMesh = this->pScene->mMeshes[0];
+ for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
+ {
+ pcMesh->mTextureCoords[0][i].x /= fWidth;
+ pcMesh->mTextureCoords[0][i].y /= fHeight;
+ pcMesh->mTextureCoords[0][i].y = 1.0f - pcMesh->mTextureCoords[0][i].y; // DX to OGL
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Validate the header of a MDL7 file
+void MDLImporter::ValidateHeader_3DGS_MDL7(const MDL::Header_MDL7* pcHeader)
+{
+ ai_assert(NULL != pcHeader);
+
+ // There are some fixed sizes ...
+ if (sizeof(MDL::ColorValue_MDL7) != pcHeader->colorvalue_stc_size) {
+ throw DeadlyImportError(
+ "[3DGS MDL7] sizeof(MDL::ColorValue_MDL7) != pcHeader->colorvalue_stc_size");
+ }
+ if (sizeof(MDL::TexCoord_MDL7) != pcHeader->skinpoint_stc_size) {
+ throw DeadlyImportError(
+ "[3DGS MDL7] sizeof(MDL::TexCoord_MDL7) != pcHeader->skinpoint_stc_size");
+ }
+ if (sizeof(MDL::Skin_MDL7) != pcHeader->skin_stc_size) {
+ throw DeadlyImportError(
+ "sizeof(MDL::Skin_MDL7) != pcHeader->skin_stc_size");
+ }
+
+ // if there are no groups ... how should we load such a file?
+ if (!pcHeader->groups_num) {
+ throw DeadlyImportError( "[3DGS MDL7] No frames found");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// resolve bone animation matrices
+void MDLImporter::CalcAbsBoneMatrices_3DGS_MDL7(MDL::IntBone_MDL7** apcOutBones)
+{
+ const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+ const MDL::Bone_MDL7* pcBones = (const MDL::Bone_MDL7*)(pcHeader+1);
+ ai_assert(NULL != apcOutBones);
+
+ // first find the bone that has NO parent, calculate the
+ // animation matrix for it, then go on and search for the next parent
+ // index (0) and so on until we can't find a new node.
+ uint16_t iParent = 0xffff;
+ uint32_t iIterations = 0;
+ while (iIterations++ < pcHeader->bones_num) {
+ for (uint32_t iBone = 0; iBone < pcHeader->bones_num;++iBone) {
+ BE_NCONST MDL::Bone_MDL7* pcBone = _AI_MDL7_ACCESS_PTR(pcBones,iBone,
+ pcHeader->bone_stc_size,MDL::Bone_MDL7);
+
+ AI_SWAP2(pcBone->parent_index);
+ AI_SWAP4(pcBone->x);
+ AI_SWAP4(pcBone->y);
+ AI_SWAP4(pcBone->z);
+
+ if (iParent == pcBone->parent_index) {
+ // MDL7 readme
+ ////////////////////////////////////////////////////////////////
+ /*
+ The animation matrix is then calculated the following way:
+
+ vector3 bPos = <absolute bone position>
+ matrix44 laM; // local animation matrix
+ sphrvector key_rotate = <bone rotation>
+
+ matrix44 m1,m2;
+ create_trans_matrix(m1, -bPos.x, -bPos.y, -bPos.z);
+ create_trans_matrix(m2, -bPos.x, -bPos.y, -bPos.z);
+
+ create_rotation_matrix(laM,key_rotate);
+
+ laM = sm1 * laM;
+ laM = laM * sm2;
+ */
+ /////////////////////////////////////////////////////////////////
+
+ MDL::IntBone_MDL7* const pcOutBone = apcOutBones[iBone];
+
+ // store the parent index of the bone
+ pcOutBone->iParent = pcBone->parent_index;
+ if (0xffff != iParent) {
+ const MDL::IntBone_MDL7* pcParentBone = apcOutBones[iParent];
+ pcOutBone->mOffsetMatrix.a4 = -pcParentBone->vPosition.x;
+ pcOutBone->mOffsetMatrix.b4 = -pcParentBone->vPosition.y;
+ pcOutBone->mOffsetMatrix.c4 = -pcParentBone->vPosition.z;
+ }
+ pcOutBone->vPosition.x = pcBone->x;
+ pcOutBone->vPosition.y = pcBone->y;
+ pcOutBone->vPosition.z = pcBone->z;
+ pcOutBone->mOffsetMatrix.a4 -= pcBone->x;
+ pcOutBone->mOffsetMatrix.b4 -= pcBone->y;
+ pcOutBone->mOffsetMatrix.c4 -= pcBone->z;
+
+ if (AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE == pcHeader->bone_stc_size) {
+ // no real name for our poor bone is specified :-(
+ pcOutBone->mName.length = ::sprintf(pcOutBone->mName.data,
+ "UnnamedBone_%i",iBone);
+ }
+ else {
+ // Make sure we won't run over the buffer's end if there is no
+ // terminal 0 character (however the documentation says there
+ // should be one)
+ uint32_t iMaxLen = pcHeader->bone_stc_size-16;
+ for (uint32_t qq = 0; qq < iMaxLen;++qq) {
+ if (!pcBone->name[qq]) {
+ iMaxLen = qq;
+ break;
+ }
+ }
+
+ // store the name of the bone
+ pcOutBone->mName.length = (size_t)iMaxLen;
+ ::memcpy(pcOutBone->mName.data,pcBone->name,pcOutBone->mName.length);
+ pcOutBone->mName.data[pcOutBone->mName.length] = '\0';
+ }
+ }
+ }
+ ++iParent;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read bones from a MDL7 file
+MDL::IntBone_MDL7** MDLImporter::LoadBones_3DGS_MDL7()
+{
+ const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+ if (pcHeader->bones_num) {
+ // validate the size of the bone data structure in the file
+ if (AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_20_CHARS != pcHeader->bone_stc_size &&
+ AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_32_CHARS != pcHeader->bone_stc_size &&
+ AI_MDL7_BONE_STRUCT_SIZE__NAME_IS_NOT_THERE != pcHeader->bone_stc_size)
+ {
+ DefaultLogger::get()->warn("Unknown size of bone data structure");
+ return NULL;
+ }
+
+ MDL::IntBone_MDL7** apcBonesOut = new MDL::IntBone_MDL7*[pcHeader->bones_num];
+ for (uint32_t crank = 0; crank < pcHeader->bones_num;++crank)
+ apcBonesOut[crank] = new MDL::IntBone_MDL7();
+
+ // and calculate absolute bone offset matrices ...
+ CalcAbsBoneMatrices_3DGS_MDL7(apcBonesOut);
+ return apcBonesOut;
+ }
+ return NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+// read faces from a MDL7 file
+void MDLImporter::ReadFaces_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData)
+{
+ const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+ BE_NCONST MDL::Triangle_MDL7* pcGroupTris = groupInfo.pcGroupTris;
+
+ // iterate through all triangles and build valid display lists
+ unsigned int iOutIndex = 0;
+ for (unsigned int iTriangle = 0; iTriangle < (unsigned int)groupInfo.pcGroup->numtris; ++iTriangle) {
+ AI_SWAP2(pcGroupTris->v_index[0]);
+ AI_SWAP2(pcGroupTris->v_index[1]);
+ AI_SWAP2(pcGroupTris->v_index[2]);
+
+ // iterate through all indices of the current triangle
+ for (unsigned int c = 0; c < 3;++c,++iOutIndex) {
+
+ // validate the vertex index
+ unsigned int iIndex = pcGroupTris->v_index[c];
+ if (iIndex > (unsigned int)groupInfo.pcGroup->numverts) {
+ // (we might need to read this section a second time - to process frame vertices correctly)
+ const_cast<MDL::Triangle_MDL7*>(pcGroupTris)->v_index[c] = iIndex = groupInfo.pcGroup->numverts-1;
+ DefaultLogger::get()->warn("Index overflow in MDL7 vertex list");
+ }
+
+ // write the output face index
+ groupData.pcFaces[iTriangle].mIndices[2-c] = iOutIndex;
+
+ aiVector3D& vPosition = groupData.vPositions[ iOutIndex ];
+ vPosition.x = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex, pcHeader->mainvertex_stc_size) .x;
+ vPosition.y = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .y;
+ vPosition.z = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .z;
+
+ // if we have bones, save the index
+ if (!groupData.aiBones.empty()) {
+ groupData.aiBones[iOutIndex] = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,
+ iIndex,pcHeader->mainvertex_stc_size).vertindex;
+ }
+
+ // now read the normal vector
+ if (AI_MDL7_FRAMEVERTEX030305_STCSIZE <= pcHeader->mainvertex_stc_size) {
+ // read the full normal vector
+ aiVector3D& vNormal = groupData.vNormals[ iOutIndex ];
+ vNormal.x = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[0];
+ AI_SWAP4(vNormal.x);
+ vNormal.y = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[1];
+ AI_SWAP4(vNormal.y);
+ vNormal.z = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[2];
+ AI_SWAP4(vNormal.z);
+ }
+ else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size) {
+ // read the normal vector from Quake2's smart table
+ aiVector3D& vNormal = groupData.vNormals[ iOutIndex ];
+ MD2::LookupNormalIndex(_AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,
+ pcHeader->mainvertex_stc_size) .norm162index,vNormal);
+ }
+ // validate and process the first uv coordinate set
+ if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV) {
+
+ if (groupInfo.pcGroup->num_stpts) {
+ AI_SWAP2(pcGroupTris->skinsets[0].st_index[0]);
+ AI_SWAP2(pcGroupTris->skinsets[0].st_index[1]);
+ AI_SWAP2(pcGroupTris->skinsets[0].st_index[2]);
+
+ iIndex = pcGroupTris->skinsets[0].st_index[c];
+ if (iIndex > (unsigned int)groupInfo.pcGroup->num_stpts) {
+ iIndex = groupInfo.pcGroup->num_stpts-1;
+ DefaultLogger::get()->warn("Index overflow in MDL7 UV coordinate list (#1)");
+ }
+
+ float u = groupInfo.pcGroupUVs[iIndex].u;
+ float v = 1.0f-groupInfo.pcGroupUVs[iIndex].v; // DX to OGL
+
+ groupData.vTextureCoords1[iOutIndex].x = u;
+ groupData.vTextureCoords1[iOutIndex].y = v;
+ }
+ // assign the material index, but only if it is existing
+ if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_ONE_UV_WITH_MATINDEX){
+ AI_SWAP4(pcGroupTris->skinsets[0].material);
+ groupData.pcFaces[iTriangle].iMatIndex[0] = pcGroupTris->skinsets[0].material;
+ }
+ }
+ // validate and process the second uv coordinate set
+ if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV) {
+
+ if (groupInfo.pcGroup->num_stpts) {
+ AI_SWAP2(pcGroupTris->skinsets[1].st_index[0]);
+ AI_SWAP2(pcGroupTris->skinsets[1].st_index[1]);
+ AI_SWAP2(pcGroupTris->skinsets[1].st_index[2]);
+ AI_SWAP4(pcGroupTris->skinsets[1].material);
+
+ iIndex = pcGroupTris->skinsets[1].st_index[c];
+ if (iIndex > (unsigned int)groupInfo.pcGroup->num_stpts) {
+ iIndex = groupInfo.pcGroup->num_stpts-1;
+ DefaultLogger::get()->warn("Index overflow in MDL7 UV coordinate list (#2)");
+ }
+
+ float u = groupInfo.pcGroupUVs[ iIndex ].u;
+ float v = 1.0f-groupInfo.pcGroupUVs[ iIndex ].v;
+
+ groupData.vTextureCoords2[ iOutIndex ].x = u;
+ groupData.vTextureCoords2[ iOutIndex ].y = v; // DX to OGL
+
+ // check whether we do really need the second texture
+ // coordinate set ... wastes memory and loading time
+ if (0 != iIndex && (u != groupData.vTextureCoords1[ iOutIndex ].x ||
+ v != groupData.vTextureCoords1[ iOutIndex ].y ) )
+ groupData.bNeed2UV = true;
+
+ // if the material differs, we need a second skin, too
+ if (pcGroupTris->skinsets[ 1 ].material != pcGroupTris->skinsets[ 0 ].material)
+ groupData.bNeed2UV = true;
+ }
+ // assign the material index
+ groupData.pcFaces[ iTriangle ].iMatIndex[ 1 ] = pcGroupTris->skinsets[ 1 ].material;
+ }
+ }
+ // get the next triangle in the list
+ pcGroupTris = (BE_NCONST MDL::Triangle_MDL7*)((const char*)pcGroupTris + pcHeader->triangle_stc_size);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// handle frames in a MDL7 file
+bool MDLImporter::ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSharedData_MDL7& shared,
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut)
+{
+ ai_assert(NULL != szCurrent && NULL != szCurrentOut);
+ const MDL::Header_MDL7 *pcHeader = (const MDL::Header_MDL7*)mBuffer;
+
+ // if we have no bones we can simply skip all frames,
+ // otherwise we'll need to process them.
+ // FIX: If we need another frame than the first we must apply frame vertex replacements ...
+ for (unsigned int iFrame = 0; iFrame < (unsigned int)groupInfo.pcGroup->numframes;++iFrame) {
+ MDL::IntFrameInfo_MDL7 frame ((BE_NCONST MDL::Frame_MDL7*)szCurrent,iFrame);
+
+ AI_SWAP4(frame.pcFrame->vertices_count);
+ AI_SWAP4(frame.pcFrame->transmatrix_count);
+
+ const unsigned int iAdd = pcHeader->frame_stc_size +
+ frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size +
+ frame.pcFrame->transmatrix_count * pcHeader->bonetrans_stc_size;
+
+ if (((const char*)szCurrent - (const char*)pcHeader) + iAdd > (unsigned int)pcHeader->data_size) {
+ DefaultLogger::get()->warn("Index overflow in frame area. "
+ "Ignoring all frames and all further mesh groups, too.");
+
+ // don't parse more groups if we can't even read one
+ // FIXME: sometimes this seems to occur even for valid files ...
+ *szCurrentOut = szCurrent;
+ return false;
+ }
+ // our output frame?
+ if (configFrameID == iFrame) {
+ BE_NCONST MDL::Vertex_MDL7* pcFrameVertices = (BE_NCONST MDL::Vertex_MDL7*)(szCurrent+pcHeader->frame_stc_size);
+
+ for (unsigned int qq = 0; qq < frame.pcFrame->vertices_count;++qq) {
+ // I assume this are simple replacements for normal vertices, the bone index serving
+ // as the index of the vertex to be replaced.
+ uint16_t iIndex = _AI_MDL7_ACCESS(pcFrameVertices,qq,pcHeader->framevertex_stc_size,MDL::Vertex_MDL7).vertindex;
+ AI_SWAP2(iIndex);
+ if (iIndex >= groupInfo.pcGroup->numverts) {
+ DefaultLogger::get()->warn("Invalid vertex index in frame vertex section");
+ continue;
+ }
+
+ aiVector3D vPosition,vNormal;
+
+ vPosition.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .x;
+ AI_SWAP4(vPosition.x);
+ vPosition.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .y;
+ AI_SWAP4(vPosition.y);
+ vPosition.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .z;
+ AI_SWAP4(vPosition.z);
+
+ // now read the normal vector
+ if (AI_MDL7_FRAMEVERTEX030305_STCSIZE <= pcHeader->mainvertex_stc_size) {
+ // read the full normal vector
+ vNormal.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[0];
+ AI_SWAP4(vNormal.x);
+ vNormal.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[1];
+ AI_SWAP4(vNormal.y);
+ vNormal.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[2];
+ AI_SWAP4(vNormal.z);
+ }
+ else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size) {
+ // read the normal vector from Quake2's smart table
+ MD2::LookupNormalIndex(_AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,
+ pcHeader->framevertex_stc_size) .norm162index,vNormal);
+ }
+
+ // FIXME: O(n^2) at the moment ...
+ BE_NCONST MDL::Triangle_MDL7* pcGroupTris = groupInfo.pcGroupTris;
+ unsigned int iOutIndex = 0;
+ for (unsigned int iTriangle = 0; iTriangle < (unsigned int)groupInfo.pcGroup->numtris; ++iTriangle) {
+ // iterate through all indices of the current triangle
+ for (unsigned int c = 0; c < 3;++c,++iOutIndex) {
+ // replace the vertex with the new data
+ const unsigned int iCurIndex = pcGroupTris->v_index[c];
+ if (iCurIndex == iIndex) {
+ groupData.vPositions[iOutIndex] = vPosition;
+ groupData.vNormals[iOutIndex] = vNormal;
+ }
+ }
+ // get the next triangle in the list
+ pcGroupTris = (BE_NCONST MDL::Triangle_MDL7*)((const char*)
+ pcGroupTris + pcHeader->triangle_stc_size);
+ }
+ }
+ }
+ // parse bone trafo matrix keys (only if there are bones ...)
+ if (shared.apcOutBones) {
+ ParseBoneTrafoKeys_3DGS_MDL7(groupInfo,frame,shared);
+ }
+ szCurrent += iAdd;
+ }
+ *szCurrentOut = szCurrent;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Sort faces by material, handle multiple UVs correctly
+void MDLImporter::SortByMaterials_3DGS_MDL7(
+ const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSplittedGroupData_MDL7& splittedGroupData)
+{
+ const unsigned int iNumMaterials = (unsigned int)splittedGroupData.shared.pcMats.size();
+ if (!groupData.bNeed2UV) {
+ // if we don't need a second set of texture coordinates there is no reason to keep it in memory ...
+ groupData.vTextureCoords2.clear();
+
+ // allocate the array
+ splittedGroupData.aiSplit = new std::vector<unsigned int>*[iNumMaterials];
+
+ for (unsigned int m = 0; m < iNumMaterials;++m)
+ splittedGroupData.aiSplit[m] = new std::vector<unsigned int>();
+
+ // iterate through all faces and sort by material
+ for (unsigned int iFace = 0; iFace < (unsigned int)groupInfo.pcGroup->numtris;++iFace) {
+ // check range
+ if (groupData.pcFaces[iFace].iMatIndex[0] >= iNumMaterials) {
+ // use the last material instead
+ splittedGroupData.aiSplit[iNumMaterials-1]->push_back(iFace);
+
+ // sometimes MED writes -1, but normally only if there is only
+ // one skin assigned. No warning in this case
+ if (0xFFFFFFFF != groupData.pcFaces[iFace].iMatIndex[0])
+ DefaultLogger::get()->warn("Index overflow in MDL7 material list [#0]");
+ }
+ else splittedGroupData.aiSplit[groupData.pcFaces[iFace].
+ iMatIndex[0]]->push_back(iFace);
+ }
+ }
+ else
+ {
+ // we need to build combined materials for each combination of
+ std::vector<MDL::IntMaterial_MDL7> avMats;
+ avMats.reserve(iNumMaterials*2);
+
+ // fixme: why on the heap?
+ std::vector<std::vector<unsigned int>* > aiTempSplit(iNumMaterials*2);
+ for (unsigned int m = 0; m < iNumMaterials;++m)
+ aiTempSplit[m] = new std::vector<unsigned int>();
+
+ // iterate through all faces and sort by material
+ for (unsigned int iFace = 0; iFace < (unsigned int)groupInfo.pcGroup->numtris;++iFace) {
+ // check range
+ unsigned int iMatIndex = groupData.pcFaces[iFace].iMatIndex[0];
+ if (iMatIndex >= iNumMaterials) {
+ // sometimes MED writes -1, but normally only if there is only
+ // one skin assigned. No warning in this case
+ if (0xffffffff != iMatIndex)
+ DefaultLogger::get()->warn("Index overflow in MDL7 material list [#1]");
+ iMatIndex = iNumMaterials-1;
+ }
+ unsigned int iMatIndex2 = groupData.pcFaces[iFace].iMatIndex[1];
+
+ unsigned int iNum = iMatIndex;
+ if (0xffffffff != iMatIndex2 && iMatIndex != iMatIndex2) {
+ if (iMatIndex2 >= iNumMaterials) {
+ // sometimes MED writes -1, but normally only if there is only
+ // one skin assigned. No warning in this case
+ DefaultLogger::get()->warn("Index overflow in MDL7 material list [#2]");
+ iMatIndex2 = iNumMaterials-1;
+ }
+
+ // do a slow seach in the list ...
+ iNum = 0;
+ bool bFound = false;
+ for (std::vector<MDL::IntMaterial_MDL7>::iterator i = avMats.begin();i != avMats.end();++i,++iNum){
+ if ((*i).iOldMatIndices[0] == iMatIndex && (*i).iOldMatIndices[1] == iMatIndex2) {
+ // reuse this material
+ bFound = true;
+ break;
+ }
+ }
+ if (!bFound) {
+ // build a new material ...
+ MDL::IntMaterial_MDL7 sHelper;
+ sHelper.pcMat = new MaterialHelper();
+ sHelper.iOldMatIndices[0] = iMatIndex;
+ sHelper.iOldMatIndices[1] = iMatIndex2;
+ JoinSkins_3DGS_MDL7(splittedGroupData.shared.pcMats[iMatIndex],
+ splittedGroupData.shared.pcMats[iMatIndex2],sHelper.pcMat);
+
+ // and add it to the list
+ avMats.push_back(sHelper);
+ iNum = (unsigned int)avMats.size()-1;
+ }
+ // adjust the size of the file array
+ if (iNum == aiTempSplit.size()) {
+ aiTempSplit.push_back(new std::vector<unsigned int>());
+ }
+ }
+ aiTempSplit[iNum]->push_back(iFace);
+ }
+
+ // now add the newly created materials to the old list
+ if (0 == groupInfo.iIndex) {
+ splittedGroupData.shared.pcMats.resize(avMats.size());
+ for (unsigned int o = 0; o < avMats.size();++o)
+ splittedGroupData.shared.pcMats[o] = avMats[o].pcMat;
+ }
+ else {
+ // This might result in redundant materials ...
+ splittedGroupData.shared.pcMats.resize(iNumMaterials + avMats.size());
+ for (unsigned int o = iNumMaterials; o < avMats.size();++o)
+ splittedGroupData.shared.pcMats[o] = avMats[o].pcMat;
+ }
+
+ // and build the final face-to-material array
+ splittedGroupData.aiSplit = new std::vector<unsigned int>*[aiTempSplit.size()];
+ for (unsigned int m = 0; m < iNumMaterials;++m)
+ splittedGroupData.aiSplit[m] = aiTempSplit[m];
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a MDL7 file
+void MDLImporter::InternReadFile_3DGS_MDL7( )
+{
+ ai_assert(NULL != pScene);
+
+ MDL::IntSharedData_MDL7 sharedData;
+
+ // current cursor position in the file
+ BE_NCONST MDL::Header_MDL7 *pcHeader = (BE_NCONST MDL::Header_MDL7*)this->mBuffer;
+ const unsigned char* szCurrent = (const unsigned char*)(pcHeader+1);
+
+ AI_SWAP4(pcHeader->version);
+ AI_SWAP4(pcHeader->bones_num);
+ AI_SWAP4(pcHeader->groups_num);
+ AI_SWAP4(pcHeader->data_size);
+ AI_SWAP4(pcHeader->entlump_size);
+ AI_SWAP4(pcHeader->medlump_size);
+ AI_SWAP2(pcHeader->bone_stc_size);
+ AI_SWAP2(pcHeader->skin_stc_size);
+ AI_SWAP2(pcHeader->colorvalue_stc_size);
+ AI_SWAP2(pcHeader->material_stc_size);
+ AI_SWAP2(pcHeader->skinpoint_stc_size);
+ AI_SWAP2(pcHeader->triangle_stc_size);
+ AI_SWAP2(pcHeader->mainvertex_stc_size);
+ AI_SWAP2(pcHeader->framevertex_stc_size);
+ AI_SWAP2(pcHeader->bonetrans_stc_size);
+ AI_SWAP2(pcHeader->frame_stc_size);
+
+ // validate the header of the file. There are some structure
+ // sizes that are expected by the loader to be constant
+ this->ValidateHeader_3DGS_MDL7(pcHeader);
+
+ // load all bones (they are shared by all groups, so
+ // we'll need to add them to all groups/meshes later)
+ // apcBonesOut is a list of all bones or NULL if they could not been loaded
+ szCurrent += pcHeader->bones_num * pcHeader->bone_stc_size;
+ sharedData.apcOutBones = this->LoadBones_3DGS_MDL7();
+
+ // vector to held all created meshes
+ std::vector<aiMesh*>* avOutList;
+
+ // 3 meshes per group - that should be OK for most models
+ avOutList = new std::vector<aiMesh*>[pcHeader->groups_num];
+ for (uint32_t i = 0; i < pcHeader->groups_num;++i)
+ avOutList[i].reserve(3);
+
+ // buffer to held the names of all groups in the file
+ char* aszGroupNameBuffer = new char[AI_MDL7_MAX_GROUPNAMESIZE*pcHeader->groups_num];
+
+ // read all groups
+ for (unsigned int iGroup = 0; iGroup < (unsigned int)pcHeader->groups_num;++iGroup) {
+ MDL::IntGroupInfo_MDL7 groupInfo((BE_NCONST MDL::Group_MDL7*)szCurrent,iGroup);
+ szCurrent = (const unsigned char*)(groupInfo.pcGroup+1);
+
+ VALIDATE_FILE_SIZE(szCurrent);
+
+ AI_SWAP4(groupInfo.pcGroup->groupdata_size);
+ AI_SWAP4(groupInfo.pcGroup->numskins);
+ AI_SWAP4(groupInfo.pcGroup->num_stpts);
+ AI_SWAP4(groupInfo.pcGroup->numtris);
+ AI_SWAP4(groupInfo.pcGroup->numverts);
+ AI_SWAP4(groupInfo.pcGroup->numframes);
+
+ if (1 != groupInfo.pcGroup->typ) {
+ // Not a triangle-based mesh
+ DefaultLogger::get()->warn("[3DGS MDL7] Not a triangle mesh group. Continuing happily");
+ }
+
+ // store the name of the group
+ const unsigned int ofs = iGroup*AI_MDL7_MAX_GROUPNAMESIZE;
+ ::memcpy(&aszGroupNameBuffer[ofs],
+ groupInfo.pcGroup->name,AI_MDL7_MAX_GROUPNAMESIZE);
+
+ // make sure '\0' is at the end
+ aszGroupNameBuffer[ofs+AI_MDL7_MAX_GROUPNAMESIZE-1] = '\0';
+
+ // read all skins
+ sharedData.pcMats.reserve(sharedData.pcMats.size() + groupInfo.pcGroup->numskins);
+ sharedData.abNeedMaterials.resize(sharedData.abNeedMaterials.size() +
+ groupInfo.pcGroup->numskins,false);
+
+ for (unsigned int iSkin = 0; iSkin < (unsigned int)groupInfo.pcGroup->numskins;++iSkin) {
+ ParseSkinLump_3DGS_MDL7(szCurrent,&szCurrent,sharedData.pcMats);
+ }
+ // if we have absolutely no skin loaded we need to generate a default material
+ if (sharedData.pcMats.empty()) {
+ const int iMode = (int)aiShadingMode_Gouraud;
+ sharedData.pcMats.push_back(new MaterialHelper());
+ MaterialHelper* pcHelper = (MaterialHelper*)sharedData.pcMats[0];
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.6f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ aiString szName;
+ szName.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+
+ sharedData.abNeedMaterials.resize(1,false);
+ }
+
+ // now get a pointer to all texture coords in the group
+ groupInfo.pcGroupUVs = (BE_NCONST MDL::TexCoord_MDL7*)szCurrent;
+ for (int i = 0; i < groupInfo.pcGroup->num_stpts; ++i){
+ AI_SWAP4(groupInfo.pcGroupUVs[i].u);
+ AI_SWAP4(groupInfo.pcGroupUVs[i].v);
+ }
+ szCurrent += pcHeader->skinpoint_stc_size * groupInfo.pcGroup->num_stpts;
+
+ // now get a pointer to all triangle in the group
+ groupInfo.pcGroupTris = (BE_NCONST MDL::Triangle_MDL7*)szCurrent;
+ szCurrent += pcHeader->triangle_stc_size * groupInfo.pcGroup->numtris;
+
+ // now get a pointer to all vertices in the group
+ groupInfo.pcGroupVerts = (BE_NCONST MDL::Vertex_MDL7*)szCurrent;
+ for (int i = 0; i < groupInfo.pcGroup->numverts; ++i){
+ AI_SWAP4(groupInfo.pcGroupVerts[i].x);
+ AI_SWAP4(groupInfo.pcGroupVerts[i].y);
+ AI_SWAP4(groupInfo.pcGroupVerts[i].z);
+
+ AI_SWAP2(groupInfo.pcGroupVerts[i].vertindex);
+ //We can not swap the normal information now as we don't know which of the two kinds it is
+ }
+ szCurrent += pcHeader->mainvertex_stc_size * groupInfo.pcGroup->numverts;
+ VALIDATE_FILE_SIZE(szCurrent);
+
+ MDL::IntSplittedGroupData_MDL7 splittedGroupData(sharedData,avOutList[iGroup]);
+ MDL::IntGroupData_MDL7 groupData;
+ if (groupInfo.pcGroup->numtris && groupInfo.pcGroup->numverts)
+ {
+ // build output vectors
+ const unsigned int iNumVertices = groupInfo.pcGroup->numtris*3;
+ groupData.vPositions.resize(iNumVertices);
+ groupData.vNormals.resize(iNumVertices);
+
+ if (sharedData.apcOutBones)groupData.aiBones.resize(iNumVertices,0xffffffff);
+
+ // it is also possible that there are 0 UV coordinate sets
+ if (groupInfo.pcGroup->num_stpts){
+ groupData.vTextureCoords1.resize(iNumVertices,aiVector3D());
+
+ // check whether the triangle data structure is large enough
+ // to contain a second UV coodinate set
+ if (pcHeader->triangle_stc_size >= AI_MDL7_TRIANGLE_STD_SIZE_TWO_UV) {
+ groupData.vTextureCoords2.resize(iNumVertices,aiVector3D());
+ groupData.bNeed2UV = true;
+ }
+ }
+ groupData.pcFaces = new MDL::IntFace_MDL7[groupInfo.pcGroup->numtris];
+
+ // read all faces into the preallocated arrays
+ ReadFaces_3DGS_MDL7(groupInfo, groupData);
+
+ // sort by materials
+ SortByMaterials_3DGS_MDL7(groupInfo, groupData,
+ splittedGroupData);
+
+ for (unsigned int qq = 0; qq < sharedData.pcMats.size();++qq) {
+ if (!splittedGroupData.aiSplit[qq]->empty())
+ sharedData.abNeedMaterials[qq] = true;
+ }
+ }
+ else DefaultLogger::get()->warn("[3DGS MDL7] Mesh group consists of 0 "
+ "vertices or faces. It will be skipped.");
+
+ // process all frames and generate output meshes
+ ProcessFrames_3DGS_MDL7(groupInfo,groupData, sharedData,szCurrent,&szCurrent);
+ GenerateOutputMeshes_3DGS_MDL7(groupData,splittedGroupData);
+ }
+
+ // generate a nodegraph and subnodes for each group
+ pScene->mRootNode = new aiNode();
+
+ // now we need to build a final mesh list
+ for (uint32_t i = 0; i < pcHeader->groups_num;++i)
+ pScene->mNumMeshes += (unsigned int)avOutList[i].size();
+
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; {
+ unsigned int p = 0,q = 0;
+ for (uint32_t i = 0; i < pcHeader->groups_num;++i) {
+ for (unsigned int a = 0; a < avOutList[i].size();++a) {
+ pScene->mMeshes[p++] = avOutList[i][a];
+ }
+ if (!avOutList[i].empty())++pScene->mRootNode->mNumChildren;
+ }
+ // we will later need an extra node to serve as parent for all bones
+ if (sharedData.apcOutBones)++pScene->mRootNode->mNumChildren;
+ this->pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
+ p = 0;
+ for (uint32_t i = 0; i < pcHeader->groups_num;++i) {
+ if (avOutList[i].empty())continue;
+
+ aiNode* const pcNode = pScene->mRootNode->mChildren[p] = new aiNode();
+ pcNode->mNumMeshes = (unsigned int)avOutList[i].size();
+ pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
+ pcNode->mParent = this->pScene->mRootNode;
+ for (unsigned int a = 0; a < pcNode->mNumMeshes;++a)
+ pcNode->mMeshes[a] = q + a;
+ q += (unsigned int)avOutList[i].size();
+
+ // setup the name of the node
+ char* const szBuffer = &aszGroupNameBuffer[i*AI_MDL7_MAX_GROUPNAMESIZE];
+ if ('\0' == *szBuffer)
+ pcNode->mName.length = ::sprintf(szBuffer,"Group_%i",p);
+ else pcNode->mName.length = ::strlen(szBuffer);
+ ::strcpy(pcNode->mName.data,szBuffer);
+ ++p;
+ }
+ }
+
+ // if there is only one root node with a single child we can optimize it a bit ...
+ if (1 == pScene->mRootNode->mNumChildren && !sharedData.apcOutBones) {
+ aiNode* pcOldRoot = this->pScene->mRootNode;
+ pScene->mRootNode = pcOldRoot->mChildren[0];
+ pcOldRoot->mChildren[0] = NULL;
+ delete pcOldRoot;
+ pScene->mRootNode->mParent = NULL;
+ }
+ else pScene->mRootNode->mName.Set("<mesh_root>");
+
+ delete[] avOutList;
+ delete[] aszGroupNameBuffer;
+ AI_DEBUG_INVALIDATE_PTR(avOutList);
+ AI_DEBUG_INVALIDATE_PTR(aszGroupNameBuffer);
+
+ // build a final material list.
+ CopyMaterials_3DGS_MDL7(sharedData);
+ HandleMaterialReferences_3DGS_MDL7();
+
+ // generate output bone animations and add all bones to the scenegraph
+ if (sharedData.apcOutBones) {
+ // this step adds empty dummy bones to the nodegraph
+ // insert another dummy node to avoid name conflicts
+ aiNode* const pc = pScene->mRootNode->mChildren[pScene->mRootNode->mNumChildren-1] = new aiNode();
+
+ pc->mName.Set("<skeleton_root>");
+
+ // add bones to the nodegraph
+ AddBonesToNodeGraph_3DGS_MDL7((const Assimp::MDL::IntBone_MDL7 **)
+ sharedData.apcOutBones,pc,0xffff);
+
+ // this steps build a valid output animation
+ BuildOutputAnims_3DGS_MDL7((const Assimp::MDL::IntBone_MDL7 **)
+ sharedData.apcOutBones);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Copy materials
+void MDLImporter::CopyMaterials_3DGS_MDL7(MDL::IntSharedData_MDL7 &shared)
+{
+ pScene->mNumMaterials = (unsigned int)shared.pcMats.size();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ pScene->mMaterials[i] = shared.pcMats[i];
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Process material references
+void MDLImporter::HandleMaterialReferences_3DGS_MDL7()
+{
+ // search for referrer materials
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
+ int iIndex = 0;
+ if (AI_SUCCESS == aiGetMaterialInteger(pScene->mMaterials[i],AI_MDL7_REFERRER_MATERIAL, &iIndex) ) {
+ for (unsigned int a = 0; a < pScene->mNumMeshes;++a) {
+ aiMesh* const pcMesh = pScene->mMeshes[a];
+ if (i == pcMesh->mMaterialIndex) {
+ pcMesh->mMaterialIndex = iIndex;
+ }
+ }
+ // collapse the rest of the array
+ delete pScene->mMaterials[i];
+ for (unsigned int pp = i; pp < pScene->mNumMaterials-1;++pp) {
+
+ pScene->mMaterials[pp] = pScene->mMaterials[pp+1];
+ for (unsigned int a = 0; a < pScene->mNumMeshes;++a) {
+ aiMesh* const pcMesh = pScene->mMeshes[a];
+ if (pcMesh->mMaterialIndex > i)--pcMesh->mMaterialIndex;
+ }
+ }
+ --pScene->mNumMaterials;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read bone transformation keys
+void MDLImporter::ParseBoneTrafoKeys_3DGS_MDL7(
+ const MDL::IntGroupInfo_MDL7& groupInfo,
+ IntFrameInfo_MDL7& frame,
+ MDL::IntSharedData_MDL7& shared)
+{
+ const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+
+ // only the first group contains bone animation keys
+ if (frame.pcFrame->transmatrix_count) {
+ if (!groupInfo.iIndex) {
+ // skip all frames vertices. We can't support them
+ const MDL::BoneTransform_MDL7* pcBoneTransforms = (const MDL::BoneTransform_MDL7*)
+ (((const char*)frame.pcFrame) + pcHeader->frame_stc_size +
+ frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size);
+
+ // read all transformation matrices
+ for (unsigned int iTrafo = 0; iTrafo < frame.pcFrame->transmatrix_count;++iTrafo) {
+ if (pcBoneTransforms->bone_index >= pcHeader->bones_num) {
+ DefaultLogger::get()->warn("Index overflow in frame area. "
+ "Unable to parse this bone transformation");
+ }
+ else {
+ AddAnimationBoneTrafoKey_3DGS_MDL7(frame.iIndex,
+ pcBoneTransforms,shared.apcOutBones);
+ }
+ pcBoneTransforms = (const MDL::BoneTransform_MDL7*)(
+ (const char*)pcBoneTransforms + pcHeader->bonetrans_stc_size);
+ }
+ }
+ else {
+ DefaultLogger::get()->warn("Ignoring animation keyframes in groups != 0");
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Attach bones to the output nodegraph
+void MDLImporter::AddBonesToNodeGraph_3DGS_MDL7(const MDL::IntBone_MDL7** apcBones,
+ aiNode* pcParent,uint16_t iParentIndex)
+{
+ ai_assert(NULL != apcBones && NULL != pcParent);
+
+ // get a pointer to the header ...
+ const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+
+ const MDL::IntBone_MDL7** apcBones2 = apcBones;
+ for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
+
+ const MDL::IntBone_MDL7* const pcBone = *apcBones2++;
+ if (pcBone->iParent == iParentIndex) {
+ ++pcParent->mNumChildren;
+ }
+ }
+ pcParent->mChildren = new aiNode*[pcParent->mNumChildren];
+ unsigned int qq = 0;
+ for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
+
+ const MDL::IntBone_MDL7* const pcBone = *apcBones++;
+ if (pcBone->iParent != iParentIndex)continue;
+
+ aiNode* pcNode = pcParent->mChildren[qq++] = new aiNode();
+ pcNode->mName = aiString( pcBone->mName );
+
+ AddBonesToNodeGraph_3DGS_MDL7(apcBones,pcNode,(uint16_t)i);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build output animations
+void MDLImporter::BuildOutputAnims_3DGS_MDL7(
+ const MDL::IntBone_MDL7** apcBonesOut)
+{
+ ai_assert(NULL != apcBonesOut);
+ const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)mBuffer;
+
+ // one animation ...
+ aiAnimation* pcAnim = new aiAnimation();
+ for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
+ if (!apcBonesOut[i]->pkeyPositions.empty()) {
+
+ // get the last frame ... (needn't be equal to pcHeader->frames_num)
+ for (size_t qq = 0; qq < apcBonesOut[i]->pkeyPositions.size();++qq) {
+ pcAnim->mDuration = std::max(pcAnim->mDuration, (double)
+ apcBonesOut[i]->pkeyPositions[qq].mTime);
+ }
+ ++pcAnim->mNumChannels;
+ }
+ }
+ if (pcAnim->mDuration) {
+ pcAnim->mChannels = new aiNodeAnim*[pcAnim->mNumChannels];
+
+ unsigned int iCnt = 0;
+ for (uint32_t i = 0; i < pcHeader->bones_num;++i) {
+ if (!apcBonesOut[i]->pkeyPositions.empty()) {
+ const MDL::IntBone_MDL7* const intBone = apcBonesOut[i];
+
+ aiNodeAnim* const pcNodeAnim = pcAnim->mChannels[iCnt++] = new aiNodeAnim();
+ pcNodeAnim->mNodeName = aiString( intBone->mName );
+
+ // allocate enough storage for all keys
+ pcNodeAnim->mNumPositionKeys = (unsigned int)intBone->pkeyPositions.size();
+ pcNodeAnim->mNumScalingKeys = (unsigned int)intBone->pkeyPositions.size();
+ pcNodeAnim->mNumRotationKeys = (unsigned int)intBone->pkeyPositions.size();
+
+ pcNodeAnim->mPositionKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys];
+ pcNodeAnim->mScalingKeys = new aiVectorKey[pcNodeAnim->mNumPositionKeys];
+ pcNodeAnim->mRotationKeys = new aiQuatKey[pcNodeAnim->mNumPositionKeys];
+
+ // copy all keys
+ for (unsigned int qq = 0; qq < pcNodeAnim->mNumPositionKeys;++qq) {
+ pcNodeAnim->mPositionKeys[qq] = intBone->pkeyPositions[qq];
+ pcNodeAnim->mScalingKeys[qq] = intBone->pkeyScalings[qq];
+ pcNodeAnim->mRotationKeys[qq] = intBone->pkeyRotations[qq];
+ }
+ }
+ }
+
+ // store the output animation
+ pScene->mNumAnimations = 1;
+ pScene->mAnimations = new aiAnimation*[1];
+ pScene->mAnimations[0] = pcAnim;
+ }
+ else delete pcAnim;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MDLImporter::AddAnimationBoneTrafoKey_3DGS_MDL7(unsigned int iTrafo,
+ const MDL::BoneTransform_MDL7* pcBoneTransforms,
+ MDL::IntBone_MDL7** apcBonesOut)
+{
+ ai_assert(NULL != pcBoneTransforms);
+ ai_assert(NULL != apcBonesOut);
+
+ // first .. get the transformation matrix
+ aiMatrix4x4 mTransform;
+ mTransform.a1 = pcBoneTransforms->m[0];
+ mTransform.b1 = pcBoneTransforms->m[1];
+ mTransform.c1 = pcBoneTransforms->m[2];
+ mTransform.d1 = pcBoneTransforms->m[3];
+
+ mTransform.a2 = pcBoneTransforms->m[4];
+ mTransform.b2 = pcBoneTransforms->m[5];
+ mTransform.c2 = pcBoneTransforms->m[6];
+ mTransform.d2 = pcBoneTransforms->m[7];
+
+ mTransform.a3 = pcBoneTransforms->m[8];
+ mTransform.b3 = pcBoneTransforms->m[9];
+ mTransform.c3 = pcBoneTransforms->m[10];
+ mTransform.d3 = pcBoneTransforms->m[11];
+
+ // now decompose the transformation matrix into separate
+ // scaling, rotation and translation
+ aiVectorKey vScaling,vPosition;
+ aiQuatKey qRotation;
+
+ // FIXME: Decompose will assert in debug builds if the matrix is invalid ...
+ mTransform.Decompose(vScaling.mValue,qRotation.mValue,vPosition.mValue);
+
+ // now generate keys
+ vScaling.mTime = qRotation.mTime = vPosition.mTime = (double)iTrafo;
+
+ // add the keys to the bone
+ MDL::IntBone_MDL7* const pcBoneOut = apcBonesOut[pcBoneTransforms->bone_index];
+ pcBoneOut->pkeyPositions.push_back ( vPosition );
+ pcBoneOut->pkeyScalings.push_back ( vScaling );
+ pcBoneOut->pkeyRotations.push_back ( qRotation );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Construct output meshes
+void MDLImporter::GenerateOutputMeshes_3DGS_MDL7(
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSplittedGroupData_MDL7& splittedGroupData)
+{
+ const MDL::IntSharedData_MDL7& shared = splittedGroupData.shared;
+
+ // get a pointer to the header ...
+ const MDL::Header_MDL7* const pcHeader = (const MDL::Header_MDL7*)this->mBuffer;
+ const unsigned int iNumOutBones = pcHeader->bones_num;
+
+ for (std::vector<MaterialHelper*>::size_type i = 0; i < shared.pcMats.size();++i) {
+ if (!splittedGroupData.aiSplit[i]->empty()) {
+
+ // allocate the output mesh
+ aiMesh* pcMesh = new aiMesh();
+
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+ pcMesh->mMaterialIndex = (unsigned int)i;
+
+ // allocate output storage
+ pcMesh->mNumFaces = (unsigned int)splittedGroupData.aiSplit[i]->size();
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+ pcMesh->mNumVertices = pcMesh->mNumFaces*3;
+ pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+
+ if (!groupData.vTextureCoords1.empty()) {
+ pcMesh->mNumUVComponents[0] = 2;
+ pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ if (!groupData.vTextureCoords2.empty()) {
+ pcMesh->mNumUVComponents[1] = 2;
+ pcMesh->mTextureCoords[1] = new aiVector3D[pcMesh->mNumVertices];
+ }
+ }
+
+ // iterate through all faces and build an unique set of vertices
+ unsigned int iCurrent = 0;
+ for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace) {
+ pcMesh->mFaces[iFace].mNumIndices = 3;
+ pcMesh->mFaces[iFace].mIndices = new unsigned int[3];
+
+ unsigned int iSrcFace = splittedGroupData.aiSplit[i]->operator[](iFace);
+ const MDL::IntFace_MDL7& oldFace = groupData.pcFaces[iSrcFace];
+
+ // iterate through all face indices
+ for (unsigned int c = 0; c < 3;++c) {
+ const uint32_t iIndex = oldFace.mIndices[c];
+ pcMesh->mVertices[iCurrent] = groupData.vPositions[iIndex];
+ pcMesh->mNormals[iCurrent] = groupData.vNormals[iIndex];
+
+ if (!groupData.vTextureCoords1.empty()) {
+
+ pcMesh->mTextureCoords[0][iCurrent] = groupData.vTextureCoords1[iIndex];
+ if (!groupData.vTextureCoords2.empty()) {
+ pcMesh->mTextureCoords[1][iCurrent] = groupData.vTextureCoords2[iIndex];
+ }
+ }
+ pcMesh->mFaces[iFace].mIndices[c] = iCurrent++;
+ }
+ }
+
+ // if we have bones in the mesh we'll need to generate
+ // proper vertex weights for them
+ if (!groupData.aiBones.empty()) {
+ std::vector<std::vector<unsigned int> > aaiVWeightList;
+ aaiVWeightList.resize(iNumOutBones);
+
+ int iCurrent = 0;
+ for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace) {
+ unsigned int iSrcFace = splittedGroupData.aiSplit[i]->operator[](iFace);
+ const MDL::IntFace_MDL7& oldFace = groupData.pcFaces[iSrcFace];
+
+ // iterate through all face indices
+ for (unsigned int c = 0; c < 3;++c) {
+ unsigned int iBone = groupData.aiBones[ oldFace.mIndices[c] ];
+ if (0xffffffff != iBone) {
+ if (iBone >= iNumOutBones) {
+ DefaultLogger::get()->error("Bone index overflow. "
+ "The bone index of a vertex exceeds the allowed range. ");
+ iBone = iNumOutBones-1;
+ }
+ aaiVWeightList[ iBone ].push_back ( iCurrent );
+ }
+ ++iCurrent;
+ }
+ }
+ // now check which bones are required ...
+ for (std::vector<std::vector<unsigned int> >::const_iterator k = aaiVWeightList.begin();k != aaiVWeightList.end();++k) {
+ if (!(*k).empty()) {
+ ++pcMesh->mNumBones;
+ }
+ }
+ pcMesh->mBones = new aiBone*[pcMesh->mNumBones];
+ iCurrent = 0;
+ for (std::vector<std::vector<unsigned int> >::const_iterator k = aaiVWeightList.begin();k!= aaiVWeightList.end();++k,++iCurrent)
+ {
+ if ((*k).empty())
+ continue;
+
+ // seems we'll need this node
+ aiBone* pcBone = pcMesh->mBones[ iCurrent ] = new aiBone();
+ pcBone->mName = aiString(shared.apcOutBones[ iCurrent ]->mName);
+ pcBone->mOffsetMatrix = shared.apcOutBones[ iCurrent ]->mOffsetMatrix;
+
+ // setup vertex weights
+ pcBone->mNumWeights = (unsigned int)(*k).size();
+ pcBone->mWeights = new aiVertexWeight[pcBone->mNumWeights];
+
+ for (unsigned int weight = 0; weight < pcBone->mNumWeights;++weight) {
+ pcBone->mWeights[weight].mVertexId = (*k)[weight];
+ pcBone->mWeights[weight].mWeight = 1.0f;
+ }
+ }
+ }
+ // add the mesh to the list of output meshes
+ splittedGroupData.avOutList.push_back(pcMesh);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Join to materials
+void MDLImporter::JoinSkins_3DGS_MDL7(
+ MaterialHelper* pcMat1,
+ MaterialHelper* pcMat2,
+ MaterialHelper* pcMatOut)
+{
+ ai_assert(NULL != pcMat1 && NULL != pcMat2 && NULL != pcMatOut);
+
+ // first create a full copy of the first skin property set
+ // and assign it to the output material
+ MaterialHelper::CopyPropertyList(pcMatOut,pcMat1);
+
+ int iVal = 0;
+ pcMatOut->AddProperty<int>(&iVal,1,AI_MATKEY_UVWSRC_DIFFUSE(0));
+
+ // then extract the diffuse texture from the second skin,
+ // setup 1 as UV source and we have it
+ aiString sString;
+ if (AI_SUCCESS == aiGetMaterialString ( pcMat2, AI_MATKEY_TEXTURE_DIFFUSE(0),&sString )) {
+ iVal = 1;
+ pcMatOut->AddProperty<int>(&iVal,1,AI_MATKEY_UVWSRC_DIFFUSE(1));
+ pcMatOut->AddProperty(&sString,AI_MATKEY_TEXTURE_DIFFUSE(1));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a half-life 2 MDL
+void MDLImporter::InternReadFile_HL2( )
+{
+ //const MDL::Header_HL2* pcHeader = (const MDL::Header_HL2*)this->mBuffer;
+ throw DeadlyImportError("HL2 MDLs are not implemented");
+}
+
+#endif // !! ASSIMP_BUILD_NO_MDL_IMPORTER
diff --git a/3rdparty/assimp/code/MDLLoader.h b/3rdparty/assimp/code/MDLLoader.h
new file mode 100644
index 000000000..4d0654648
--- /dev/null
+++ b/3rdparty/assimp/code/MDLLoader.h
@@ -0,0 +1,460 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MDLLoader.h
+ * @brief Declaration of the loader for MDL files
+ */
+
+#ifndef AI_MDLLOADER_H_INCLUDED
+#define AI_MDLLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+
+struct aiNode;
+#include "MDLFileData.h"
+#include "HalfLifeFileData.h"
+
+namespace Assimp {
+class MaterialHelper;
+
+using namespace MDL;
+
+// --------------------------------------------------------------------------------------
+// Include file/line information in debug builds
+#ifdef ASSIMP_BUILD_DEBUG
+# define VALIDATE_FILE_SIZE(msg) SizeCheck(msg,__FILE__,__LINE__)
+#else
+# define VALIDATE_FILE_SIZE(msg) SizeCheck(msg)
+#endif
+
+// --------------------------------------------------------------------------------------
+/** @brief Class to load MDL files.
+ *
+ * Several subformats exist:
+ * <ul>
+ * <li>Quake I</li>
+ * <li>3D Game Studio MDL3, MDL4</li>
+ * <li>3D Game Studio MDL5</li>
+ * <li>3D Game Studio MDL7</li>
+ * <li>Halflife 2</li>
+ * </ul>
+ * These formats are partially identical and it would be possible to load
+ * them all with a single 1000-line function-beast. However, it has been
+ * splitted to several code paths to make the code easier to read and maintain.
+*/
+class MDLImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ MDLImporter();
+
+ /** Destructor, private as well */
+ ~MDLImporter();
+
+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;
+
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Import a quake 1 MDL file (IDPO)
+ */
+ void InternReadFile_Quake1( );
+
+ // -------------------------------------------------------------------
+ /** Import a GameStudio A4/A5 file (MDL 3,4,5)
+ */
+ void InternReadFile_3DGS_MDL345( );
+
+ // -------------------------------------------------------------------
+ /** Import a GameStudio A7 file (MDL 7)
+ */
+ void InternReadFile_3DGS_MDL7( );
+
+ // -------------------------------------------------------------------
+ /** Import a CS:S/HL2 MDL file (not fully implemented)
+ */
+ void InternReadFile_HL2( );
+
+ // -------------------------------------------------------------------
+ /** Check whether a given position is inside the valid range
+ * Throw a DeadlyImportError if it is not
+ * \param szPos Cursor position
+ * \param szFile Name of the source file from which the function was called
+ * \param iLine Source code line from which the function was called
+ */
+ void SizeCheck(const void* szPos);
+ void SizeCheck(const void* szPos, const char* szFile, unsigned int iLine);
+
+
+ // -------------------------------------------------------------------
+ /** Validate the header data structure of a game studio MDL7 file
+ * \param pcHeader Input header to be validated
+ */
+ void ValidateHeader_3DGS_MDL7(const MDL::Header_MDL7* pcHeader);
+
+ // -------------------------------------------------------------------
+ /** Validate the header data structure of a Quake 1 model
+ * \param pcHeader Input header to be validated
+ */
+ void ValidateHeader_Quake1(const MDL::Header* pcHeader);
+
+
+ // -------------------------------------------------------------------
+ /** Try to load a palette from the current directory (colormap.lmp)
+ * If it is not found the default palette of Quake1 is returned
+ */
+ void SearchPalette(const unsigned char** pszColorMap);
+
+ // -------------------------------------------------------------------
+ /** Free a palette created with a previous call to SearchPalette()
+ */
+ void FreePalette(const unsigned char* pszColorMap);
+
+
+ // -------------------------------------------------------------------
+ /** Load a paletized texture from the file and convert it to 32bpp
+ */
+ void CreateTextureARGB8_3DGS_MDL3(const unsigned char* szData);
+
+ // -------------------------------------------------------------------
+ /** Used to load textures from MDL3/4
+ * \param szData Input data
+ * \param iType Color data type
+ * \param piSkip Receive: Size to skip, in bytes
+ */
+ void CreateTexture_3DGS_MDL4(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip);
+
+
+ // -------------------------------------------------------------------
+ /** Used to load textures from MDL5
+ * \param szData Input data
+ * \param iType Color data type
+ * \param piSkip Receive: Size to skip, in bytes
+ */
+ void CreateTexture_3DGS_MDL5(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip);
+
+
+ // -------------------------------------------------------------------
+ /** Checks whether a texture can be replaced with a single color
+ * This is useful for all file formats before MDL7 (all those
+ * that are not containing material colors separate from textures).
+ * MED seems to write dummy 8x8 monochrome images instead.
+ * \param pcTexture Input texture
+ * \return aiColor.r is set to qnan if the function fails and no
+ * color can be found.
+ */
+ aiColor4D ReplaceTextureWithColor(const aiTexture* pcTexture);
+
+
+ // -------------------------------------------------------------------
+ /** Converts the absolute texture coordinates in MDL5 files to
+ * relative in a range between 0 and 1
+ */
+ void CalculateUVCoordinates_MDL5();
+
+
+ // -------------------------------------------------------------------
+ /** Read an UV coordinate from the file. If the file format is not
+ * MDL5, the function calculates relative texture coordinates
+ * \param vOut Receives the output UV coord
+ * \param pcSrc UV coordinate buffer
+ * \param UV coordinate index
+ */
+ void ImportUVCoordinate_3DGS_MDL345( aiVector3D& vOut,
+ const MDL::TexCoord_MDL3* pcSrc,
+ unsigned int iIndex);
+
+ // -------------------------------------------------------------------
+ /** Setup the material properties for Quake and MDL<7 models.
+ * These formats don't support more than one material per mesh,
+ * therefore the method processes only ONE skin and removes
+ * all others.
+ */
+ void SetupMaterialProperties_3DGS_MDL5_Quake1( );
+
+
+ // -------------------------------------------------------------------
+ /** Parse a skin lump in a MDL7/HMP7 file with all of its features
+ * variant 1: Current cursor position is the beginning of the skin header
+ * \param szCurrent Current data pointer
+ * \param szCurrentOut Output data pointer
+ * \param pcMats Material list for this group. To be filled ...
+ */
+ void ParseSkinLump_3DGS_MDL7(
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ std::vector<MaterialHelper*>& pcMats);
+
+ // -------------------------------------------------------------------
+ /** Parse a skin lump in a MDL7/HMP7 file with all of its features
+ * variant 2: Current cursor position is the beginning of the skin data
+ * \param szCurrent Current data pointer
+ * \param szCurrentOut Output data pointer
+ * \param pcMatOut Output material
+ * \param iType header.typ
+ * \param iWidth header.width
+ * \param iHeight header.height
+ */
+ void ParseSkinLump_3DGS_MDL7(
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ MaterialHelper* pcMatOut,
+ unsigned int iType,
+ unsigned int iWidth,
+ unsigned int iHeight);
+
+ // -------------------------------------------------------------------
+ /** Skip a skin lump in a MDL7/HMP7 file
+ * \param szCurrent Current data pointer
+ * \param szCurrentOut Output data pointer. Points to the byte just
+ * behind the last byte of the skin.
+ * \param iType header.typ
+ * \param iWidth header.width
+ * \param iHeight header.height
+ */
+ void SkipSkinLump_3DGS_MDL7(const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ unsigned int iType,
+ unsigned int iWidth,
+ unsigned int iHeight);
+
+ // -------------------------------------------------------------------
+ /** Parse texture color data for MDL5, MDL6 and MDL7 formats
+ * \param szData Current data pointer
+ * \param iType type of the texture data. No DDS or external
+ * \param piSkip Receive the number of bytes to skip
+ * \param pcNew Must point to fully initialized data. Width and
+ * height must be set. If pcNew->pcData is set to 0xffffffff,
+ * piSkip will receive the size of the texture, in bytes, but no
+ * color data will be read.
+ */
+ void ParseTextureColorData(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip,
+ aiTexture* pcNew);
+
+ // -------------------------------------------------------------------
+ /** Join two materials / skins. Setup UV source ... etc
+ * \param pcMat1 First input material
+ * \param pcMat2 Second input material
+ * \param pcMatOut Output material instance to be filled. Must be empty
+ */
+ void JoinSkins_3DGS_MDL7(MaterialHelper* pcMat1,
+ MaterialHelper* pcMat2,
+ MaterialHelper* pcMatOut);
+
+ // -------------------------------------------------------------------
+ /** Add a bone transformation key to an animation
+ * \param iTrafo Index of the transformation (always==frame index?)
+ * No need to validate this index, it is always valid.
+ * \param pcBoneTransforms Bone transformation for this index
+ * \param apcOutBones Output bones array
+ */
+ void AddAnimationBoneTrafoKey_3DGS_MDL7(unsigned int iTrafo,
+ const MDL::BoneTransform_MDL7* pcBoneTransforms,
+ MDL::IntBone_MDL7** apcBonesOut);
+
+ // -------------------------------------------------------------------
+ /** Load the bone list of a MDL7 file
+ * \return If the bones could be loaded successfully, a valid
+ * array containing pointers to a temporary bone
+ * representation. NULL if the bones could not be loaded.
+ */
+ MDL::IntBone_MDL7** LoadBones_3DGS_MDL7();
+
+ // -------------------------------------------------------------------
+ /** Load bone transformation keyframes from a file chunk
+ * \param groupInfo -> doc of data structure
+ * \param frame -> doc of data structure
+ * \param shared -> doc of data structure
+ */
+ void ParseBoneTrafoKeys_3DGS_MDL7(
+ const MDL::IntGroupInfo_MDL7& groupInfo,
+ IntFrameInfo_MDL7& frame,
+ MDL::IntSharedData_MDL7& shared);
+
+ // -------------------------------------------------------------------
+ /** Calculate absolute bone animation matrices for each bone
+ * \param apcOutBones Output bones array
+ */
+ void CalcAbsBoneMatrices_3DGS_MDL7(MDL::IntBone_MDL7** apcOutBones);
+
+ // -------------------------------------------------------------------
+ /** Add all bones to the nodegraph (as children of the root node)
+ * \param apcBonesOut List of bones
+ * \param pcParent Parent node. New nodes will be added to this node
+ * \param iParentIndex Index of the parent bone
+ */
+ void AddBonesToNodeGraph_3DGS_MDL7(const MDL::IntBone_MDL7** apcBonesOut,
+ aiNode* pcParent,uint16_t iParentIndex);
+
+ // -------------------------------------------------------------------
+ /** Build output animations
+ * \param apcBonesOut List of bones
+ */
+ void BuildOutputAnims_3DGS_MDL7(const MDL::IntBone_MDL7** apcBonesOut);
+
+ // -------------------------------------------------------------------
+ /** Handles materials that are just referencing another material
+ * There is no test file for this feature, but Conitec's doc
+ * say it is used.
+ */
+ void HandleMaterialReferences_3DGS_MDL7();
+
+ // -------------------------------------------------------------------
+ /** Copies only the material that are referenced by at least one
+ * mesh to the final output material list. All other materials
+ * will be discarded.
+ * \param shared -> doc of data structure
+ */
+ void CopyMaterials_3DGS_MDL7(MDL::IntSharedData_MDL7 &shared);
+
+ // -------------------------------------------------------------------
+ /** Process the frame section at the end of a group
+ * \param groupInfo -> doc of data structure
+ * \param shared -> doc of data structure
+ * \param szCurrent Pointer to the start of the frame section
+ * \param szCurrentOut Receives a pointer to the first byte of the
+ * next data section.
+ * \return false to read no further groups (a small workaround for
+ * some tiny and unsolved problems ... )
+ */
+ bool ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSharedData_MDL7& shared,
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Sort all faces by their materials. If the mesh is using
+ * multiple materials per face (that are blended together) the function
+ * might create new materials.
+ * \param groupInfo -> doc of data structure
+ * \param groupData -> doc of data structure
+ * \param splittedGroupData -> doc of data structure
+ */
+ void SortByMaterials_3DGS_MDL7(
+ const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSplittedGroupData_MDL7& splittedGroupData);
+
+ // -------------------------------------------------------------------
+ /** Read all faces and vertices from a MDL7 group. The function fills
+ * preallocated memory buffers.
+ * \param groupInfo -> doc of data structure
+ * \param groupData -> doc of data structure
+ */
+ void ReadFaces_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
+ MDL::IntGroupData_MDL7& groupData);
+
+ // -------------------------------------------------------------------
+ /** Generate the final output meshes for a7 models
+ * \param groupData -> doc of data structure
+ * \param splittedGroupData -> doc of data structure
+ */
+ void GenerateOutputMeshes_3DGS_MDL7(
+ MDL::IntGroupData_MDL7& groupData,
+ MDL::IntSplittedGroupData_MDL7& splittedGroupData);
+
+protected:
+
+ /** Configuration option: frame to be loaded */
+ unsigned int configFrameID;
+
+ /** Configuration option: palette to be used to decode palletized images*/
+ std::string configPalette;
+
+ /** Buffer to hold the loaded file */
+ unsigned char* mBuffer;
+
+ /** For GameStudio MDL files: The number in the magic word, either 3,4 or 5
+ * (MDL7 doesn't need this, the format has a separate loader) */
+ unsigned int iGSFileVersion;
+
+ /** Output I/O handler. used to load external lmp files */
+ IOSystem* pIOHandler;
+
+ /** Output scene to be filled */
+ aiScene* pScene;
+
+ /** Size of the input file in bytes */
+ unsigned int iFileSize;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/MDLMaterialLoader.cpp b/3rdparty/assimp/code/MDLMaterialLoader.cpp
new file mode 100644
index 000000000..5ed30c30b
--- /dev/null
+++ b/3rdparty/assimp/code/MDLMaterialLoader.cpp
@@ -0,0 +1,823 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the material part of the MDL importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
+
+// internal headers
+#include "MDLLoader.h"
+#include "MDLDefaultColorMap.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Find a suitable pallette file or take teh default one
+void MDLImporter::SearchPalette(const unsigned char** pszColorMap)
+{
+ // now try to find the color map in the current directory
+ IOStream* pcStream = pIOHandler->Open(configPalette,"rb");
+
+ const unsigned char* szColorMap = (const unsigned char*)::g_aclrDefaultColorMap;
+ if (pcStream)
+ {
+ if (pcStream->FileSize() >= 768)
+ {
+ szColorMap = new unsigned char[256*3];
+ pcStream->Read(const_cast<unsigned char*>(szColorMap),256*3,1);
+
+ DefaultLogger::get()->info("Found valid colormap.lmp in directory. "
+ "It will be used to decode embedded textures in palletized formats.");
+ }
+ delete pcStream;
+ pcStream = NULL;
+ }
+ *pszColorMap = szColorMap;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Free the palette again
+void MDLImporter::FreePalette(const unsigned char* szColorMap)
+{
+ if (szColorMap != (const unsigned char*)::g_aclrDefaultColorMap)
+ delete[] szColorMap;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether we can replace a texture with a single color
+aiColor4D MDLImporter::ReplaceTextureWithColor(const aiTexture* pcTexture)
+{
+ ai_assert(NULL != pcTexture);
+
+ aiColor4D clrOut;
+ clrOut.r = get_qnan();
+ if (!pcTexture->mHeight || !pcTexture->mWidth)
+ return clrOut;
+
+ const unsigned int iNumPixels = pcTexture->mHeight*pcTexture->mWidth;
+ const aiTexel* pcTexel = pcTexture->pcData+1;
+ const aiTexel* const pcTexelEnd = &pcTexture->pcData[iNumPixels];
+
+ while (pcTexel != pcTexelEnd)
+ {
+ if (*pcTexel != *(pcTexel-1))
+ {
+ pcTexel = NULL;
+ break;
+ }
+ ++pcTexel;
+ }
+ if (pcTexel)
+ {
+ clrOut.r = pcTexture->pcData->r / 255.0f;
+ clrOut.g = pcTexture->pcData->g / 255.0f;
+ clrOut.b = pcTexture->pcData->b / 255.0f;
+ clrOut.a = pcTexture->pcData->a / 255.0f;
+ }
+ return clrOut;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a texture from a MDL3 file
+void MDLImporter::CreateTextureARGB8_3DGS_MDL3(const unsigned char* szData)
+{
+ const MDL::Header *pcHeader = (const MDL::Header*)mBuffer; //the endianess is allready corrected in the InternReadFile_3DGS_MDL345 function
+
+ VALIDATE_FILE_SIZE(szData + pcHeader->skinwidth *
+ pcHeader->skinheight);
+
+ // allocate a new texture object
+ aiTexture* pcNew = new aiTexture();
+ pcNew->mWidth = pcHeader->skinwidth;
+ pcNew->mHeight = pcHeader->skinheight;
+
+ pcNew->pcData = new aiTexel[pcNew->mWidth * pcNew->mHeight];
+
+ const unsigned char* szColorMap;
+ this->SearchPalette(&szColorMap);
+
+ // copy texture data
+ for (unsigned int i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ const unsigned char val = szData[i];
+ const unsigned char* sz = &szColorMap[val*3];
+
+ pcNew->pcData[i].a = 0xFF;
+ pcNew->pcData[i].r = *sz++;
+ pcNew->pcData[i].g = *sz++;
+ pcNew->pcData[i].b = *sz;
+ }
+
+ FreePalette(szColorMap);
+
+ // store the texture
+ aiTexture** pc = this->pScene->mTextures;
+ this->pScene->mTextures = new aiTexture*[pScene->mNumTextures+1];
+ for (unsigned int i = 0; i <pScene->mNumTextures;++i)
+ pScene->mTextures[i] = pc[i];
+
+ pScene->mTextures[this->pScene->mNumTextures] = pcNew;
+ pScene->mNumTextures++;
+ delete[] pc;
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a texture from a MDL4 file
+void MDLImporter::CreateTexture_3DGS_MDL4(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip)
+{
+ ai_assert(NULL != piSkip);
+
+ const MDL::Header *pcHeader = (const MDL::Header*)mBuffer; //the endianess is allready corrected in the InternReadFile_3DGS_MDL345 function
+
+ if (iType == 1 || iType > 3)
+ {
+ DefaultLogger::get()->error("Unsupported texture file format");
+ return;
+ }
+
+ bool bNoRead = *piSkip == 0xffffffff;
+
+ // allocate a new texture object
+ aiTexture* pcNew = new aiTexture();
+ pcNew->mWidth = pcHeader->skinwidth;
+ pcNew->mHeight = pcHeader->skinheight;
+
+ if (bNoRead)pcNew->pcData = (aiTexel*)0xffffffff;
+ ParseTextureColorData(szData,iType,piSkip,pcNew);
+
+ // store the texture
+ if (!bNoRead)
+ {
+ if (!this->pScene->mNumTextures)
+ {
+ pScene->mNumTextures = 1;
+ pScene->mTextures = new aiTexture*[1];
+ pScene->mTextures[0] = pcNew;
+ }
+ else
+ {
+ aiTexture** pc = pScene->mTextures;
+ pScene->mTextures = new aiTexture*[pScene->mNumTextures+1];
+ for (unsigned int i = 0; i < this->pScene->mNumTextures;++i)
+ pScene->mTextures[i] = pc[i];
+ pScene->mTextures[pScene->mNumTextures] = pcNew;
+ pScene->mNumTextures++;
+ delete[] pc;
+ }
+ }
+ else {
+ pcNew->pcData = NULL;
+ delete pcNew;
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Load color data of a texture and convert it to our output format
+void MDLImporter::ParseTextureColorData(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip,
+ aiTexture* pcNew)
+{
+ // allocate storage for the texture image
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ pcNew->pcData = new aiTexel[pcNew->mWidth * pcNew->mHeight];
+
+ // R5G6B5 format (with or without MIPs)
+ // ****************************************************************
+ if (2 == iType || 10 == iType)
+ {
+ VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*2);
+
+ // copy texture data
+ unsigned int i;
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ {
+ for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ MDL::RGB565 val = ((MDL::RGB565*)szData)[i];
+ AI_SWAP2(val);
+
+ pcNew->pcData[i].a = 0xFF;
+ pcNew->pcData[i].r = (unsigned char)val.b << 3;
+ pcNew->pcData[i].g = (unsigned char)val.g << 2;
+ pcNew->pcData[i].b = (unsigned char)val.r << 3;
+ }
+ }
+ else i = pcNew->mWidth*pcNew->mHeight;
+ *piSkip = i * 2;
+
+ // apply MIP maps
+ if (10 == iType)
+ {
+ *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 1;
+ VALIDATE_FILE_SIZE(szData + *piSkip);
+ }
+ }
+ // ARGB4 format (with or without MIPs)
+ // ****************************************************************
+ else if (3 == iType || 11 == iType)
+ {
+ VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*4);
+
+ // copy texture data
+ unsigned int i;
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ {
+ for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ MDL::ARGB4 val = ((MDL::ARGB4*)szData)[i];
+ AI_SWAP2(val);
+
+ pcNew->pcData[i].a = (unsigned char)val.a << 4;
+ pcNew->pcData[i].r = (unsigned char)val.r << 4;
+ pcNew->pcData[i].g = (unsigned char)val.g << 4;
+ pcNew->pcData[i].b = (unsigned char)val.b << 4;
+ }
+ }
+ else i = pcNew->mWidth*pcNew->mHeight;
+ *piSkip = i * 2;
+
+ // apply MIP maps
+ if (11 == iType)
+ {
+ *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 1;
+ VALIDATE_FILE_SIZE(szData + *piSkip);
+ }
+ }
+ // RGB8 format (with or without MIPs)
+ // ****************************************************************
+ else if (4 == iType || 12 == iType)
+ {
+ VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*3);
+
+ // copy texture data
+ unsigned int i;
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ {
+ for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ const unsigned char* _szData = &szData[i*3];
+
+ pcNew->pcData[i].a = 0xFF;
+ pcNew->pcData[i].b = *_szData++;
+ pcNew->pcData[i].g = *_szData++;
+ pcNew->pcData[i].r = *_szData;
+ }
+ }
+ else i = pcNew->mWidth*pcNew->mHeight;
+
+
+ // apply MIP maps
+ *piSkip = i * 3;
+ if (12 == iType)
+ {
+ *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) *3;
+ VALIDATE_FILE_SIZE(szData + *piSkip);
+ }
+ }
+ // ARGB8 format (with ir without MIPs)
+ // ****************************************************************
+ else if (5 == iType || 13 == iType)
+ {
+ VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight*4);
+
+ // copy texture data
+ unsigned int i;
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ {
+ for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ const unsigned char* _szData = &szData[i*4];
+
+ pcNew->pcData[i].b = *_szData++;
+ pcNew->pcData[i].g = *_szData++;
+ pcNew->pcData[i].r = *_szData++;
+ pcNew->pcData[i].a = *_szData;
+ }
+ }
+ else i = pcNew->mWidth*pcNew->mHeight;
+
+ // apply MIP maps
+ *piSkip = i << 2;
+ if (13 == iType)
+ {
+ *piSkip += ((i >> 2) + (i >> 4) + (i >> 6)) << 2;
+ }
+ }
+ // palletized 8 bit texture. As for Quake 1
+ // ****************************************************************
+ else if (0 == iType)
+ {
+ VALIDATE_FILE_SIZE(szData + pcNew->mWidth*pcNew->mHeight);
+
+ // copy texture data
+ unsigned int i;
+ if ((aiTexel*)0xffffffff != pcNew->pcData)
+ {
+
+ const unsigned char* szColorMap;
+ this->SearchPalette(&szColorMap);
+
+ for (i = 0; i < pcNew->mWidth*pcNew->mHeight;++i)
+ {
+ const unsigned char val = szData[i];
+ const unsigned char* sz = &szColorMap[val*3];
+
+ pcNew->pcData[i].a = 0xFF;
+ pcNew->pcData[i].r = *sz++;
+ pcNew->pcData[i].g = *sz++;
+ pcNew->pcData[i].b = *sz;
+ }
+ this->FreePalette(szColorMap);
+
+ }
+ else i = pcNew->mWidth*pcNew->mHeight;
+ *piSkip = i;
+
+ // FIXME: Also support for MIP maps?
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a texture from a MDL5 file
+void MDLImporter::CreateTexture_3DGS_MDL5(const unsigned char* szData,
+ unsigned int iType,
+ unsigned int* piSkip)
+{
+ ai_assert(NULL != piSkip);
+ bool bNoRead = *piSkip == 0xffffffff;
+
+ // allocate a new texture object
+ aiTexture* pcNew = new aiTexture();
+
+ VALIDATE_FILE_SIZE(szData+8);
+
+ // first read the size of the texture
+ pcNew->mWidth = *((uint32_t*)szData);
+ AI_SWAP4(pcNew->mWidth);
+ szData += sizeof(uint32_t);
+
+ pcNew->mHeight = *((uint32_t*)szData);
+ AI_SWAP4(pcNew->mHeight);
+ szData += sizeof(uint32_t);
+
+ if (bNoRead)pcNew->pcData = (aiTexel*)0xffffffff;
+
+ // this should not occur - at least the docs say it shouldn't
+ // however, you can easily try out what MED does if you have
+ // a model with a DDS texture and export it to MDL5 ...
+ // yes, you're right. It embedds the DDS texture ... :cry:
+ if (6 == iType)
+ {
+ // this is a compressed texture in DDS format
+ *piSkip = pcNew->mWidth;
+ VALIDATE_FILE_SIZE(szData + *piSkip);
+
+ if (!bNoRead)
+ {
+ // place a hint and let the application know that it's
+ // a DDS file
+ pcNew->mHeight = 0;
+ pcNew->achFormatHint[0] = 'd';
+ pcNew->achFormatHint[1] = 'd';
+ pcNew->achFormatHint[2] = 's';
+ pcNew->achFormatHint[3] = '\0';
+
+ pcNew->pcData = (aiTexel*) new unsigned char[pcNew->mWidth];
+ ::memcpy(pcNew->pcData,szData,pcNew->mWidth);
+ }
+ }
+ else
+ {
+ // parse the color data of the texture
+ ParseTextureColorData(szData,iType,
+ piSkip,pcNew);
+ }
+ *piSkip += sizeof(uint32_t) * 2;
+
+ if (!bNoRead)
+ {
+ // store the texture
+ if (!this->pScene->mNumTextures)
+ {
+ pScene->mNumTextures = 1;
+ pScene->mTextures = new aiTexture*[1];
+ pScene->mTextures[0] = pcNew;
+ }
+ else
+ {
+ aiTexture** pc = pScene->mTextures;
+ pScene->mTextures = new aiTexture*[pScene->mNumTextures+1];
+ for (unsigned int i = 0; i < pScene->mNumTextures;++i)
+ this->pScene->mTextures[i] = pc[i];
+
+ pScene->mTextures[pScene->mNumTextures] = pcNew;
+ pScene->mNumTextures++;
+ delete[] pc;
+ }
+ }
+ else {
+ pcNew->pcData = NULL;
+ delete pcNew;
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a skin from a MDL7 file - more complex than all other subformats
+void MDLImporter::ParseSkinLump_3DGS_MDL7(
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ MaterialHelper* pcMatOut,
+ unsigned int iType,
+ unsigned int iWidth,
+ unsigned int iHeight)
+{
+ aiTexture* pcNew = NULL;
+
+ // get the type of the skin
+ unsigned int iMasked = (unsigned int)(iType & 0xF);
+
+ if (0x1 == iMasked)
+ {
+ // ***** REFERENCE TO ANOTHER SKIN INDEX *****
+ int referrer = (int)iWidth;
+ pcMatOut->AddProperty<int>(&referrer,1,AI_MDL7_REFERRER_MATERIAL);
+ }
+ else if (0x6 == iMasked)
+ {
+ // ***** EMBEDDED DDS FILE *****
+ if (1 != iHeight)
+ {
+ DefaultLogger::get()->warn("Found a reference to an embedded DDS texture, "
+ "but texture height is not equal to 1, which is not supported by MED");
+ }
+
+ pcNew = new aiTexture();
+ pcNew->mHeight = 0;
+ pcNew->mWidth = iWidth;
+
+ // place a proper format hint
+ pcNew->achFormatHint[0] = 'd';
+ pcNew->achFormatHint[1] = 'd';
+ pcNew->achFormatHint[2] = 's';
+ pcNew->achFormatHint[3] = '\0';
+
+ pcNew->pcData = (aiTexel*) new unsigned char[pcNew->mWidth];
+ memcpy(pcNew->pcData,szCurrent,pcNew->mWidth);
+ szCurrent += iWidth;
+ }
+ if (0x7 == iMasked)
+ {
+ // ***** REFERENCE TO EXTERNAL FILE *****
+ if (1 != iHeight)
+ {
+ DefaultLogger::get()->warn("Found a reference to an external texture, "
+ "but texture height is not equal to 1, which is not supported by MED");
+ }
+
+ aiString szFile;
+ const size_t iLen = strlen((const char*)szCurrent);
+ size_t iLen2 = iLen+1;
+ iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2;
+ memcpy(szFile.data,(const char*)szCurrent,iLen2);
+ szFile.length = iLen;
+
+ szCurrent += iLen2;
+
+ // place this as diffuse texture
+ pcMatOut->AddProperty(&szFile,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ else if (iMasked || !iType || (iType && iWidth && iHeight))
+ {
+ // ***** STANDARD COLOR TEXTURE *****
+ pcNew = new aiTexture();
+ if (!iHeight || !iWidth)
+ {
+ DefaultLogger::get()->warn("Found embedded texture, but its width "
+ "an height are both 0. Is this a joke?");
+
+ // generate an empty chess pattern
+ pcNew->mWidth = pcNew->mHeight = 8;
+ pcNew->pcData = new aiTexel[64];
+ for (unsigned int x = 0; x < 8;++x)
+ {
+ for (unsigned int y = 0; y < 8;++y)
+ {
+ const bool bSet = ((0 == x % 2 && 0 != y % 2) ||
+ (0 != x % 2 && 0 == y % 2));
+
+ aiTexel* pc = &pcNew->pcData[y * 8 + x];
+ pc->r = pc->b = pc->g = (bSet?0xFF:0);
+ pc->a = 0xFF;
+ }
+ }
+ }
+ else
+ {
+ // it is a standard color texture. Fill in width and height
+ // and call the same function we used for loading MDL5 files
+
+ pcNew->mWidth = iWidth;
+ pcNew->mHeight = iHeight;
+
+ unsigned int iSkip = 0;
+ this->ParseTextureColorData(szCurrent,iMasked,&iSkip,pcNew);
+
+ // skip length of texture data
+ szCurrent += iSkip;
+ }
+ }
+
+ // sometimes there are MDL7 files which have a monochrome
+ // texture instead of material colors ... posssible they have
+ // been converted to MDL7 from other formats, such as MDL5
+ aiColor4D clrTexture;
+ if (pcNew)clrTexture = this->ReplaceTextureWithColor(pcNew);
+ else clrTexture.r = get_qnan();
+
+ // check whether a material definition is contained in the skin
+ if (iType & AI_MDL7_SKINTYPE_MATERIAL)
+ {
+ BE_NCONST MDL::Material_MDL7* pcMatIn = (BE_NCONST MDL::Material_MDL7*)szCurrent;
+ szCurrent = (unsigned char*)(pcMatIn+1);
+ VALIDATE_FILE_SIZE(szCurrent);
+
+ aiColor3D clrTemp;
+
+#define COLOR_MULTIPLY_RGB() \
+ if (is_not_qnan(clrTexture.r)) \
+ { \
+ clrTemp.r *= clrTexture.r; \
+ clrTemp.g *= clrTexture.g; \
+ clrTemp.b *= clrTexture.b; \
+ }
+
+ // read diffuse color
+ clrTemp.r = pcMatIn->Diffuse.r;
+ AI_SWAP4(clrTemp.r);
+ clrTemp.g = pcMatIn->Diffuse.g;
+ AI_SWAP4(clrTemp.g);
+ clrTemp.b = pcMatIn->Diffuse.b;
+ AI_SWAP4(clrTemp.b);
+ COLOR_MULTIPLY_RGB();
+ pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ // read specular color
+ clrTemp.r = pcMatIn->Specular.r;
+ AI_SWAP4(clrTemp.r);
+ clrTemp.g = pcMatIn->Specular.g;
+ AI_SWAP4(clrTemp.g);
+ clrTemp.b = pcMatIn->Specular.b;
+ AI_SWAP4(clrTemp.b);
+ COLOR_MULTIPLY_RGB();
+ pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_SPECULAR);
+
+ // read ambient color
+ clrTemp.r = pcMatIn->Ambient.r;
+ AI_SWAP4(clrTemp.r);
+ clrTemp.g = pcMatIn->Ambient.g;
+ AI_SWAP4(clrTemp.g);
+ clrTemp.b = pcMatIn->Ambient.b;
+ AI_SWAP4(clrTemp.b);
+ COLOR_MULTIPLY_RGB();
+ pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_AMBIENT);
+
+ // read emissive color
+ clrTemp.r = pcMatIn->Emissive.r;
+ AI_SWAP4(clrTemp.r);
+ clrTemp.g = pcMatIn->Emissive.g;
+ AI_SWAP4(clrTemp.g);
+ clrTemp.b = pcMatIn->Emissive.b;
+ AI_SWAP4(clrTemp.b);
+ pcMatOut->AddProperty<aiColor3D>(&clrTemp,1,AI_MATKEY_COLOR_EMISSIVE);
+
+#undef COLOR_MULITPLY_RGB
+
+ // FIX: Take the opacity from the ambient color
+ // the doc says something else, but it is fact that MED exports the
+ // opacity like this .... ARRRGGHH!
+ clrTemp.r = pcMatIn->Ambient.a;
+ AI_SWAP4(clrTemp.r);
+ if (is_not_qnan(clrTexture.r)) {
+ clrTemp.r *= clrTexture.a;
+ }
+ pcMatOut->AddProperty<float>(&clrTemp.r,1,AI_MATKEY_OPACITY);
+
+ // read phong power
+ int iShadingMode = (int)aiShadingMode_Gouraud;
+ AI_SWAP4(pcMatIn->Power);
+ if (0.0f != pcMatIn->Power)
+ {
+ iShadingMode = (int)aiShadingMode_Phong;
+ pcMatOut->AddProperty<float>(&pcMatIn->Power,1,AI_MATKEY_SHININESS);
+ }
+ pcMatOut->AddProperty<int>(&iShadingMode,1,AI_MATKEY_SHADING_MODEL);
+ }
+ else if (is_not_qnan(clrTexture.r))
+ {
+ pcMatOut->AddProperty<aiColor4D>(&clrTexture,1,AI_MATKEY_COLOR_DIFFUSE);
+ pcMatOut->AddProperty<aiColor4D>(&clrTexture,1,AI_MATKEY_COLOR_SPECULAR);
+ }
+ // if the texture could be replaced by a single material color
+ // we don't need the texture anymore
+ if (is_not_qnan(clrTexture.r))
+ {
+ delete pcNew;
+ pcNew = NULL;
+ }
+
+ // if an ASCII effect description (HLSL?) is contained in the file,
+ // we can simply ignore it ...
+ if (iType & AI_MDL7_SKINTYPE_MATERIAL_ASCDEF)
+ {
+ VALIDATE_FILE_SIZE(szCurrent);
+ int32_t iMe = *((int32_t*)szCurrent);
+ AI_SWAP4(iMe);
+ szCurrent += sizeof(char) * iMe + sizeof(int32_t);
+ VALIDATE_FILE_SIZE(szCurrent);
+ }
+
+ // If an embedded texture has been loaded setup the corresponding
+ // data structures in the aiScene instance
+ if (pcNew && this->pScene->mNumTextures <= 999)
+ {
+
+ // place this as diffuse texture
+ char szCurrent[5];
+ ::sprintf(szCurrent,"*%i",this->pScene->mNumTextures);
+
+ aiString szFile;
+ const size_t iLen = strlen((const char*)szCurrent);
+ ::memcpy(szFile.data,(const char*)szCurrent,iLen+1);
+ szFile.length = iLen;
+
+ pcMatOut->AddProperty(&szFile,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ // store the texture
+ if (!this->pScene->mNumTextures)
+ {
+ this->pScene->mNumTextures = 1;
+ this->pScene->mTextures = new aiTexture*[1];
+ this->pScene->mTextures[0] = pcNew;
+ }
+ else
+ {
+ aiTexture** pc = this->pScene->mTextures;
+ this->pScene->mTextures = new aiTexture*[this->pScene->mNumTextures+1];
+ for (unsigned int i = 0; i < this->pScene->mNumTextures;++i)
+ this->pScene->mTextures[i] = pc[i];
+
+ this->pScene->mTextures[this->pScene->mNumTextures] = pcNew;
+ this->pScene->mNumTextures++;
+ delete[] pc;
+ }
+ }
+ VALIDATE_FILE_SIZE(szCurrent);
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Skip a skin lump
+void MDLImporter::SkipSkinLump_3DGS_MDL7(
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ unsigned int iType,
+ unsigned int iWidth,
+ unsigned int iHeight)
+{
+ // get the type of the skin
+ unsigned int iMasked = (unsigned int)(iType & 0xF);
+
+ if (0x6 == iMasked)
+ {
+ szCurrent += iWidth;
+ }
+ if (0x7 == iMasked)
+ {
+ const size_t iLen = ::strlen((const char*)szCurrent);
+ szCurrent += iLen+1;
+ }
+ else if (iMasked || !iType)
+ {
+ if (iMasked || !iType || (iType && iWidth && iHeight))
+ {
+ // ParseTextureColorData(..., aiTexture::pcData == 0xffffffff) will simply
+ // return the size of the color data in bytes in iSkip
+ unsigned int iSkip = 0;
+
+ aiTexture tex;
+ tex.pcData = reinterpret_cast<aiTexel*>(0xffffffff);
+ tex.mHeight = iHeight;
+ tex.mWidth = iWidth;
+ this->ParseTextureColorData(szCurrent,iMasked,&iSkip,&tex);
+
+ // FIX: Important, otherwise the destructor will crash
+ tex.pcData = NULL;
+
+ // skip length of texture data
+ szCurrent += iSkip;
+ }
+ }
+
+ // check whether a material definition is contained in the skin
+ if (iType & AI_MDL7_SKINTYPE_MATERIAL)
+ {
+ BE_NCONST MDL::Material_MDL7* pcMatIn = (BE_NCONST MDL::Material_MDL7*)szCurrent;
+ szCurrent = (unsigned char*)(pcMatIn+1);
+ }
+
+ // if an ASCII effect description (HLSL?) is contained in the file,
+ // we can simply ignore it ...
+ if (iType & AI_MDL7_SKINTYPE_MATERIAL_ASCDEF)
+ {
+ int32_t iMe = *((int32_t*)szCurrent);
+ AI_SWAP4(iMe);
+ szCurrent += sizeof(char) * iMe + sizeof(int32_t);
+ }
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+// What the fuck does this function do? Can't remember
+void MDLImporter::ParseSkinLump_3DGS_MDL7(
+ const unsigned char* szCurrent,
+ const unsigned char** szCurrentOut,
+ std::vector<MaterialHelper*>& pcMats)
+{
+ ai_assert(NULL != szCurrent);
+ ai_assert(NULL != szCurrentOut);
+
+ *szCurrentOut = szCurrent;
+ BE_NCONST MDL::Skin_MDL7* pcSkin = (BE_NCONST MDL::Skin_MDL7*)szCurrent;
+ AI_SWAP4(pcSkin->width);
+ AI_SWAP4(pcSkin->height);
+ szCurrent += 12;
+
+ // allocate an output material
+ MaterialHelper* pcMatOut = new MaterialHelper();
+ pcMats.push_back(pcMatOut);
+
+ // skip length of file name
+ szCurrent += AI_MDL7_MAX_TEXNAMESIZE;
+
+ ParseSkinLump_3DGS_MDL7(szCurrent,szCurrentOut,pcMatOut,
+ pcSkin->typ,pcSkin->width,pcSkin->height);
+
+ // place the name of the skin in the material
+ if (pcSkin->texture_name[0])
+ {
+ // the 0 termination could be there or not - we can't know
+ aiString szFile;
+ ::memcpy(szFile.data,pcSkin->texture_name,sizeof(pcSkin->texture_name));
+ szFile.data[sizeof(pcSkin->texture_name)] = '\0';
+ szFile.length = ::strlen(szFile.data);
+
+ pcMatOut->AddProperty(&szFile,AI_MATKEY_NAME);
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_MDL_IMPORTER
diff --git a/3rdparty/assimp/code/MS3DLoader.cpp b/3rdparty/assimp/code/MS3DLoader.cpp
new file mode 100644
index 000000000..26ec89226
--- /dev/null
+++ b/3rdparty/assimp/code/MS3DLoader.cpp
@@ -0,0 +1,646 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MS3DLoader.cpp
+ * @brief Implementation of the Ms3D importer class.
+ * Written against http://chumbalum.swissquake.ch/ms3d/ms3dspec.txt
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
+
+// internal headers
+#include "MS3DLoader.h"
+#include "StreamReader.h"
+using namespace Assimp;
+
+// ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
+// (enable old code path, which generates extra nodes per mesh while
+// the newer code uses aiMesh::mName to express the name of the
+// meshes (a.k.a. groups in MS3D))
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+MS3DImporter::MS3DImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+MS3DImporter::~MS3DImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool MS3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ // first call - simple extension check
+ const std::string extension = GetExtension(pFile);
+ if (extension == "ms3d") {
+ return true;
+ }
+
+ // second call - check for magic identifiers
+ else if (!extension.length() || checkSig) {
+ if (!pIOHandler) {
+ return true;
+ }
+ const char* tokens[] = {"MS3D000000"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MS3DImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ms3d");
+}
+
+// ------------------------------------------------------------------------------------------------
+void ReadColor(StreamReaderLE& stream, aiColor4D& ambient)
+{
+ // aiColor4D is packed on gcc, implicit binding to float& fails therefore.
+ stream >> (float&)ambient.r >> (float&)ambient.g >> (float&)ambient.b >> (float&)ambient.a;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ReadVector(StreamReaderLE& stream, aiVector3D& pos)
+{
+ // See note in ReadColor()
+ stream >> (float&)pos.x >> (float&)pos.y >> (float&)pos.z;
+}
+
+// ------------------------------------------------------------------------------------------------
+template<typename T>
+void MS3DImporter :: ReadComments(StreamReaderLE& stream, std::vector<T>& outp)
+{
+ uint16_t cnt;
+ stream >> cnt;
+
+ for (unsigned int i = 0; i < cnt; ++i) {
+ uint32_t index, clength;
+ stream >> index >> clength;
+
+ if (index >= outp.size()) {
+ DefaultLogger::get()->warn("MS3D: Invalid index in comment section");
+ }
+ else if (clength > stream.GetRemainingSize()) {
+ throw DeadlyImportError("MS3D: Failure reading comment, length field is out of range");
+ }
+ else {
+ outp[index].comment = std::string(reinterpret_cast<char*>(stream.GetPtr()),clength);
+ }
+ stream.IncPtr(clength);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T, typename T2, typename T3> bool inrange(const T& in, const T2& lower, const T3& higher)
+{
+ return in > lower && in <= higher;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MS3DImporter :: CollectChildJoints(const std::vector<TempJoint>& joints,
+ std::vector<bool>& hadit,
+ aiNode* nd,
+ const aiMatrix4x4& absTrafo)
+{
+ unsigned int cnt = 0;
+ for (size_t i = 0; i < joints.size(); ++i) {
+ if (!hadit[i] && !strcmp(joints[i].parentName,nd->mName.data)) {
+ ++cnt;
+ }
+ }
+
+ nd->mChildren = new aiNode*[nd->mNumChildren = cnt];
+ cnt = 0;
+ for (size_t i = 0; i < joints.size(); ++i) {
+ if (!hadit[i] && !strcmp(joints[i].parentName,nd->mName.data)) {
+ aiNode* ch = nd->mChildren[cnt++] = new aiNode(joints[i].name);
+ ch->mParent = nd;
+
+ ch->mTransformation = aiMatrix4x4::Translation(joints[i].position,aiMatrix4x4()=aiMatrix4x4())*
+ // XXX actually, I don't *know* why we need the inverse here. Probably column vs. row order?
+ aiMatrix4x4().FromEulerAnglesXYZ(joints[i].rotation).Transpose();
+
+ const aiMatrix4x4 abs = absTrafo*ch->mTransformation;
+ for (unsigned int a = 0; a < mScene->mNumMeshes; ++a) {
+ aiMesh* const msh = mScene->mMeshes[a];
+ for (unsigned int n = 0; n < msh->mNumBones; ++n) {
+ aiBone* const bone = msh->mBones[n];
+
+ if (bone->mName == ch->mName) {
+ bone->mOffsetMatrix = aiMatrix4x4(abs).Inverse();
+ }
+ }
+ }
+
+ hadit[i] = true;
+ CollectChildJoints(joints,hadit,ch,abs);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void MS3DImporter :: CollectChildJoints(const std::vector<TempJoint>& joints, aiNode* nd)
+{
+ std::vector<bool> hadit(joints.size(),false);
+ aiMatrix4x4 trafo;
+
+ CollectChildJoints(joints,hadit,nd,trafo);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void MS3DImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ StreamReaderLE stream(pIOHandler->Open(pFile,"rb"));
+
+ // CanRead() should have done this already
+ char head[10];
+ int32_t version;
+
+ mScene = pScene;
+
+
+ // 1 ------------ read into temporary data structures mirroring the original file
+
+ stream.CopyAndAdvance(head,10);
+ stream >> version;
+ if (strncmp(head,"MS3D000000",10)) {
+ throw DeadlyImportError("Not a MS3D file, magic string MS3D000000 not found: "+pFile);
+ }
+
+ if (version != 4) {
+ throw DeadlyImportError("MS3D: Unsupported file format version, 4 was expected");
+ }
+
+ uint16_t verts;
+ stream >> verts;
+
+ std::vector<TempVertex> vertices(verts);
+ for (unsigned int i = 0; i < verts; ++i) {
+ TempVertex& v = vertices[i];
+
+ stream.IncPtr(1);
+ ReadVector(stream,v.pos);
+ v.bone_id[0] = stream.GetI1();
+ v.ref_cnt = stream.GetI1();
+
+ v.bone_id[1] = v.bone_id[2] = v.bone_id[3] = 0xffffffff;
+ v.weights[1] = v.weights[2] = v.weights[3] = 0.f;
+ v.weights[0] = 1.f;
+ }
+
+ uint16_t tris;
+ stream >> tris;
+
+ std::vector<TempTriangle> triangles(tris);
+ for (unsigned int i = 0;i < tris; ++i) {
+ TempTriangle& t = triangles[i];
+
+ stream.IncPtr(2);
+ for (unsigned int i = 0; i < 3; ++i) {
+ t.indices[i] = stream.GetI2();
+ }
+
+ for (unsigned int i = 0; i < 3; ++i) {
+ ReadVector(stream,t.normals[i]);
+ }
+
+ for (unsigned int i = 0; i < 3; ++i) {
+ stream >> (float&)(t.uv[i].x); // see note in ReadColor()
+ }
+ for (unsigned int i = 0; i < 3; ++i) {
+ stream >> (float&)(t.uv[i].y);
+ }
+
+ t.sg = stream.GetI1();
+ t.group = stream.GetI1();
+ }
+
+ uint16_t grp;
+ stream >> grp;
+
+ bool need_default = false;
+ std::vector<TempGroup> groups(grp);
+ for (unsigned int i = 0;i < grp; ++i) {
+ TempGroup& t = groups[i];
+
+ stream.IncPtr(1);
+ stream.CopyAndAdvance(t.name,32);
+
+ t.name[32] = '\0';
+ uint16_t num;
+ stream >> num;
+
+ t.triangles.resize(num);
+ for (unsigned int i = 0; i < num; ++i) {
+ t.triangles[i] = stream.GetI2();
+ }
+ t.mat = stream.GetI1();
+ if (t.mat == 0xffffffff) {
+ need_default = true;
+ }
+ }
+
+ uint16_t mat;
+ stream >> mat;
+
+ std::vector<TempMaterial> materials(mat);
+ for (unsigned int i = 0;i < mat; ++i) {
+ TempMaterial& t = materials[i];
+
+ stream.CopyAndAdvance(t.name,32);
+ t.name[32] = '\0';
+
+ ReadColor(stream,t.ambient);
+ ReadColor(stream,t.diffuse);
+ ReadColor(stream,t.specular);
+ ReadColor(stream,t.emissive);
+ stream >> t.shininess >> t.transparency;
+
+ stream.IncPtr(1);
+
+ stream.CopyAndAdvance(t.texture,128);
+ t.texture[128] = '\0';
+
+ stream.CopyAndAdvance(t.alphamap,128);
+ t.alphamap[128] = '\0';
+ }
+
+ float animfps, currenttime;
+ uint32_t totalframes;
+ stream >> animfps >> currenttime >> totalframes;
+
+ uint16_t joint;
+ stream >> joint;
+
+ std::vector<TempJoint> joints(joint);
+ for (unsigned int i = 0; i < joint; ++i) {
+ TempJoint& j = joints[i];
+
+ stream.IncPtr(1);
+ stream.CopyAndAdvance(j.name,32);
+ j.name[32] = '\0';
+
+ stream.CopyAndAdvance(j.parentName,32);
+ j.parentName[32] = '\0';
+
+ // DefaultLogger::get()->debug(j.name);
+ // DefaultLogger::get()->debug(j.parentName);
+
+ ReadVector(stream,j.rotation);
+ ReadVector(stream,j.position);
+
+ j.rotFrames.resize(stream.GetI2());
+ j.posFrames.resize(stream.GetI2());
+
+ for (unsigned int a = 0; a < j.rotFrames.size(); ++a) {
+ TempKeyFrame& kf = j.rotFrames[a];
+ stream >> kf.time;
+ ReadVector(stream,kf.value);
+ }
+ for (unsigned int a = 0; a < j.posFrames.size(); ++a) {
+ TempKeyFrame& kf = j.posFrames[a];
+ stream >> kf.time;
+ ReadVector(stream,kf.value);
+ }
+ }
+
+ if (stream.GetRemainingSize() > 4) {
+ uint32_t subversion;
+ stream >> subversion;
+ if (subversion == 1) {
+ ReadComments<TempGroup>(stream,groups);
+ ReadComments<TempMaterial>(stream,materials);
+ ReadComments<TempJoint>(stream,joints);
+
+ // model comment - print it for we have such a nice log.
+ if (stream.GetI4()) {
+ const size_t len = static_cast<size_t>(stream.GetI4());
+ if (len > stream.GetRemainingSize()) {
+ throw DeadlyImportError("MS3D: Model comment is too long");
+ }
+
+ const std::string& s = std::string(reinterpret_cast<char*>(stream.GetPtr()),len);
+ DefaultLogger::get()->info(s);
+ }
+
+ if (stream.GetRemainingSize() > 4 && inrange((stream >> subversion,subversion),1u,3u)) {
+ for (unsigned int i = 0; i < verts; ++i) {
+ TempVertex& v = vertices[i];
+ v.weights[3]=1.f;
+ for (unsigned int n = 0; n < 3; v.weights[3]-=v.weights[n++]) {
+ v.bone_id[n+1] = stream.GetI1();
+ v.weights[n] = static_cast<float>(static_cast<unsigned int>(stream.GetI1()))/255.f;
+ }
+ stream.IncPtr((subversion-1)<<2u);
+ }
+
+ // even further extra data is not of interest for us, at least now now.
+ }
+ }
+ }
+
+ // 2 ------------ convert to proper aiXX data structures -----------------------------------
+
+ if (need_default && materials.size()) {
+ DefaultLogger::get()->warn("MS3D: Found group with no material assigned, spawning default material");
+ // if one of the groups has no material assigned, but there are other
+ // groups with materials, a default material needs to be added (
+ // scenepreprocessor adds a default material only if nummat==0).
+ materials.push_back(TempMaterial());
+ TempMaterial& m = materials.back();
+
+ strcpy(m.name,"<MS3D_DefaultMat>");
+ m.diffuse = aiColor4D(0.6f,0.6f,0.6f,1.0);
+ m.transparency = 1.f;
+ m.shininess = 0.f;
+
+ // this is because these TempXXX struct's have no c'tors.
+ m.texture[0] = m.alphamap[0] = '\0';
+
+ for (unsigned int i = 0; i < groups.size(); ++i) {
+ TempGroup& g = groups[i];
+ if (g.mat == 0xffffffff) {
+ g.mat = materials.size()-1;
+ }
+ }
+ }
+
+ // convert materials to our generic key-value dict-alike
+ if (materials.size()) {
+ pScene->mMaterials = new aiMaterial*[materials.size()];
+ for (size_t i = 0; i < materials.size(); ++i) {
+
+ MaterialHelper* mo = new MaterialHelper();
+ pScene->mMaterials[pScene->mNumMaterials++] = mo;
+
+ const TempMaterial& mi = materials[i];
+
+ aiString tmp;
+ if (0[mi.alphamap]) {
+ tmp = aiString(mi.alphamap);
+ mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_OPACITY(0));
+ }
+ if (0[mi.texture]) {
+ tmp = aiString(mi.texture);
+ mo->AddProperty(&tmp,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ if (0[mi.name]) {
+ tmp = aiString(mi.name);
+ mo->AddProperty(&tmp,AI_MATKEY_NAME);
+ }
+
+ mo->AddProperty(&mi.ambient,1,AI_MATKEY_COLOR_AMBIENT);
+ mo->AddProperty(&mi.diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
+ mo->AddProperty(&mi.specular,1,AI_MATKEY_COLOR_SPECULAR);
+ mo->AddProperty(&mi.emissive,1,AI_MATKEY_COLOR_EMISSIVE);
+
+ mo->AddProperty(&mi.shininess,1,AI_MATKEY_SHININESS);
+ mo->AddProperty(&mi.transparency,1,AI_MATKEY_OPACITY);
+
+ const int sm = mi.shininess>0.f?aiShadingMode_Phong:aiShadingMode_Gouraud;
+ mo->AddProperty(&sm,1,AI_MATKEY_SHADING_MODEL);
+ }
+ }
+
+ // convert groups to meshes
+ if (groups.empty()) {
+ throw DeadlyImportError("MS3D: Didn't get any group records, file is malformed");
+ }
+
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes=static_cast<unsigned int>(groups.size())]();
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+
+ aiMesh* m = pScene->mMeshes[i] = new aiMesh();
+ const TempGroup& g = groups[i];
+
+ if (pScene->mNumMaterials && g.mat > pScene->mNumMaterials) {
+ throw DeadlyImportError("MS3D: Encountered invalid material index, file is malformed");
+ } // no error if no materials at all - scenepreprocessor adds one then
+
+ m->mMaterialIndex = g.mat;
+ m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ m->mFaces = new aiFace[m->mNumFaces = g.triangles.size()];
+ m->mNumVertices = m->mNumFaces*3;
+
+ // storage for vertices - verbose format, as requested by the postprocessing pipeline
+ m->mVertices = new aiVector3D[m->mNumVertices];
+ m->mNormals = new aiVector3D[m->mNumVertices];
+ m->mTextureCoords[0] = new aiVector3D[m->mNumVertices];
+ m->mNumUVComponents[0] = 2;
+
+ typedef std::map<unsigned int,unsigned int> BoneSet;
+ BoneSet mybones;
+
+ for (unsigned int i = 0,n = 0; i < m->mNumFaces; ++i) {
+ aiFace& f = m->mFaces[i];
+ if (g.triangles[i]>triangles.size()) {
+ throw DeadlyImportError("MS3D: Encountered invalid triangle index, file is malformed");
+ }
+
+ TempTriangle& t = triangles[g.triangles[i]];
+ f.mIndices = new unsigned int[f.mNumIndices=3];
+
+ for (unsigned int i = 0; i < 3; ++i,++n) {
+ if (t.indices[i]>vertices.size()) {
+ throw DeadlyImportError("MS3D: Encountered invalid vertex index, file is malformed");
+ }
+
+ const TempVertex& v = vertices[t.indices[i]];
+ for (unsigned int a = 0; a < 4; ++a) {
+ if (v.bone_id[a] != 0xffffffff) {
+ if (v.bone_id[a] >= joints.size()) {
+ throw DeadlyImportError("MS3D: Encountered invalid bone index, file is malformed");
+ }
+ if (mybones.find(v.bone_id[a]) == mybones.end()) {
+ mybones[v.bone_id[a]] = 1;
+ }
+ else ++mybones[v.bone_id[a]];
+ }
+ }
+
+ // collect vertex components
+ m->mVertices[n] = v.pos;
+
+ m->mNormals[n] = t.normals[i];
+ m->mTextureCoords[0][n] = aiVector3D(t.uv[i].x,1.f-t.uv[i].y,0.0);
+ f.mIndices[i] = n;
+ }
+ }
+
+ // allocate storage for bones
+ if (mybones.size()) {
+ std::vector<unsigned int> bmap(joints.size());
+ m->mBones = new aiBone*[mybones.size()]();
+ for (BoneSet::const_iterator it = mybones.begin(); it != mybones.end(); ++it) {
+ aiBone* const bn = m->mBones[m->mNumBones] = new aiBone();
+ const TempJoint& jnt = joints[(*it).first];
+
+ bn->mName.Set(jnt.name);
+ bn->mWeights = new aiVertexWeight[(*it).second];
+
+ bmap[(*it).first] = m->mNumBones++;
+ }
+
+ // .. and collect bone weights
+ for (unsigned int i = 0,n = 0; i < m->mNumFaces; ++i) {
+ TempTriangle& t = triangles[g.triangles[i]];
+
+ for (unsigned int i = 0; i < 3; ++i,++n) {
+ const TempVertex& v = vertices[t.indices[i]];
+ for (unsigned int a = 0; a < 4; ++a) {
+ const unsigned int bone = v.bone_id[a];
+ if (bone==0xffffffff){
+ continue;
+ }
+
+ aiBone* const outbone = m->mBones[bmap[bone]];
+ aiVertexWeight& outwght = outbone->mWeights[outbone->mNumWeights++];
+
+ outwght.mVertexId = n;
+ outwght.mWeight = v.weights[a];
+ }
+ }
+ }
+ }
+ }
+
+ // ... add dummy nodes under a single root, each holding a reference to one
+ // mesh. If we didn't do this, we'd loose the group name.
+ aiNode* rt = pScene->mRootNode = new aiNode("<MS3DRoot>");
+
+#ifdef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
+ rt->mChildren = new aiNode*[rt->mNumChildren=pScene->mNumMeshes+(joints.size()?1:0)]();
+
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ aiNode* nd = rt->mChildren[i] = new aiNode();
+
+ const TempGroup& g = groups[i];
+
+ // we need to generate an unique name for all mesh nodes.
+ // since we want to keep the group name, a prefix is
+ // prepended.
+ nd->mName = aiString("<MS3DMesh>_");
+ nd->mName.Append(g.name);
+ nd->mParent = rt;
+
+ nd->mMeshes = new unsigned int[nd->mNumMeshes = 1];
+ nd->mMeshes[0] = i;
+ }
+#else
+ rt->mMeshes = new unsigned int[pScene->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ rt->mMeshes[rt->mNumMeshes++] = i;
+ }
+#endif
+
+ // convert animations as well
+ if (joints.size()) {
+#ifndef ASSIMP_BUILD_MS3D_ONE_NODE_PER_MESH
+ rt->mChildren = new aiNode*[1]();
+#endif
+ aiNode* jt = rt->mChildren[pScene->mNumMeshes] = new aiNode();
+ jt->mParent = rt;
+ CollectChildJoints(joints,jt);
+ jt->mName.Set("<MS3DJointRoot>");
+
+ pScene->mAnimations = new aiAnimation*[ pScene->mNumAnimations = 1 ];
+ aiAnimation* const anim = pScene->mAnimations[0] = new aiAnimation();
+
+ anim->mName.Set("<MS3DMasterAnim>");
+
+ // carry the fps info to the user by scaling all times with it
+ anim->mTicksPerSecond = animfps;
+
+ // leave duration at its default, so ScenePreprocessor will fill an appropriate
+ // value (the values taken from some MS3D files seem to be too unreliable
+ // to pass the validation)
+ // anim->mDuration = totalframes/animfps;
+
+ anim->mChannels = new aiNodeAnim*[joints.size()]();
+ for (std::vector<TempJoint>::const_iterator it = joints.begin(); it != joints.end(); ++it) {
+ if ((*it).rotFrames.empty() && (*it).posFrames.empty()) {
+ continue;
+ }
+
+ aiNodeAnim* nd = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim();
+ nd->mNodeName.Set((*it).name);
+
+ if ((*it).rotFrames.size()) {
+ nd->mRotationKeys = new aiQuatKey[(*it).rotFrames.size()];
+ for (std::vector<TempKeyFrame>::const_iterator rot = (*it).rotFrames.begin(); rot != (*it).rotFrames.end(); ++rot) {
+ aiQuatKey& q = nd->mRotationKeys[nd->mNumRotationKeys++];
+
+ q.mTime = (*rot).time*animfps;
+
+ // XXX it seems our matrix&quaternion code has faults in its conversion routines --
+ // aiQuaternion(x,y,z) seems to besomething different as quat(matrix.fromeuler(x,y,z)).
+ q.mValue = aiQuaternion(aiMatrix3x3(aiMatrix4x4().FromEulerAnglesXYZ((*rot).value)*
+ aiMatrix4x4().FromEulerAnglesXYZ((*it).rotation)).Transpose());
+ }
+ }
+
+ if ((*it).posFrames.size()) {
+ nd->mPositionKeys = new aiVectorKey[(*it).posFrames.size()];
+
+ aiQuatKey* qu = nd->mRotationKeys;
+ for (std::vector<TempKeyFrame>::const_iterator pos = (*it).posFrames.begin(); pos != (*it).posFrames.end(); ++pos,++qu) {
+ aiVectorKey& v = nd->mPositionKeys[nd->mNumPositionKeys++];
+
+ v.mTime = (*pos).time*animfps;
+ v.mValue = (*it).position + (*pos).value;
+ }
+ }
+ }
+ // fixup to pass the validation if not a single animation channel is non-trivial
+ if (!anim->mNumChannels) {
+ anim->mChannels = NULL;
+ }
+ }
+}
+
+#endif
diff --git a/3rdparty/assimp/code/MS3DLoader.h b/3rdparty/assimp/code/MS3DLoader.h
new file mode 100644
index 000000000..391aec5a2
--- /dev/null
+++ b/3rdparty/assimp/code/MS3DLoader.h
@@ -0,0 +1,158 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MS3DLoader.h
+ * @brief Declaration of the MS3D importer class.
+ */
+#ifndef AI_MS3DLOADER_H_INCLUDED
+#define AI_MS3DLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------------------
+/** Milkshape 3D importer implementation */
+// ----------------------------------------------------------------------------------------------
+class MS3DImporter
+ : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+
+ MS3DImporter();
+ ~MS3DImporter();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+
+private:
+
+ struct TempJoint;
+ void CollectChildJoints(const std::vector<TempJoint>& joints, std::vector<bool>& hadit, aiNode* nd,const aiMatrix4x4& absTrafo);
+ void CollectChildJoints(const std::vector<TempJoint>& joints, aiNode* nd);
+
+ template<typename T> void ReadComments(StreamReaderLE& stream, std::vector<T>& outp);
+private:
+
+ aiScene* mScene;
+
+private:
+
+ struct TempVertex
+ {
+ aiVector3D pos;
+ unsigned int bone_id[4], ref_cnt;
+ float weights[4];
+ };
+
+ struct TempTriangle
+ {
+ unsigned int indices[3];
+ aiVector3D normals[3];
+ aiVector2D uv[3];
+
+ unsigned int sg, group;
+ };
+
+ struct TempGroup
+ {
+ char name[33]; // +0
+ std::vector<unsigned int> triangles;
+ unsigned int mat; // 0xff is no material
+ std::string comment;
+ };
+
+ struct TempMaterial
+ {
+ // again, add an extra 0 character to all strings -
+ char name[33];
+ char texture[129];
+ char alphamap[129];
+
+ aiColor4D diffuse,specular,ambient,emissive;
+ float shininess,transparency;
+ std::string comment;
+ };
+
+ struct TempKeyFrame
+ {
+ float time;
+ aiVector3D value;
+ };
+
+ struct TempJoint
+ {
+ char name[33];
+ char parentName[33];
+ aiVector3D rotation, position;
+
+ std::vector<TempKeyFrame> rotFrames;
+ std::vector<TempKeyFrame> posFrames;
+ std::string comment;
+ };
+
+ //struct TempModel {
+ // std::string comment;
+ //};
+};
+
+}
+#endif
diff --git a/3rdparty/assimp/code/MakeVerboseFormat.cpp b/3rdparty/assimp/code/MakeVerboseFormat.cpp
new file mode 100644
index 000000000..d4b3c7120
--- /dev/null
+++ b/3rdparty/assimp/code/MakeVerboseFormat.cpp
@@ -0,0 +1,218 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the post processing step "MakeVerboseFormat"
+*/
+
+#include "AssimpPCH.h"
+#include "MakeVerboseFormat.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+MakeVerboseFormatProcess::MakeVerboseFormatProcess()
+{
+ // nothing to do here
+}
+// ------------------------------------------------------------------------------------------------
+MakeVerboseFormatProcess::~MakeVerboseFormatProcess()
+{
+ // nothing to do here
+}
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void MakeVerboseFormatProcess::Execute( aiScene* pScene)
+{
+ ai_assert(NULL != pScene);
+ DefaultLogger::get()->debug("MakeVerboseFormatProcess begin");
+
+ bool bHas = false;
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ {
+ if ( MakeVerboseFormat( pScene->mMeshes[a]))
+ bHas = true;
+ }
+ if (bHas) DefaultLogger::get()->info("MakeVerboseFormatProcess finished. There was much work to do ...");
+ else DefaultLogger::get()->debug("MakeVerboseFormatProcess. There was nothing to do.");
+
+ pScene->mFlags &= ~AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
+
+}
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
+{
+ ai_assert(NULL != pcMesh);
+
+ unsigned int iOldNumVertices = pcMesh->mNumVertices;
+ const unsigned int iNumVerts = pcMesh->mNumFaces*3;
+
+ aiVector3D* pvPositions = new aiVector3D[ iNumVerts ];
+
+ aiVector3D* pvNormals = NULL;
+ if (pcMesh->HasNormals())
+ {
+ pvNormals = new aiVector3D[iNumVerts];
+ }
+ aiVector3D* pvTangents = NULL, *pvBitangents = NULL;
+ if (pcMesh->HasTangentsAndBitangents())
+ {
+ pvTangents = new aiVector3D[iNumVerts];
+ pvBitangents = new aiVector3D[iNumVerts];
+ }
+
+ ai_assert(AI_MAX_NUMBER_OF_TEXTURECOORDS == 4);
+ ai_assert(AI_MAX_NUMBER_OF_COLOR_SETS == 4);
+
+ aiVector3D* apvTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS] = {NULL,NULL,NULL,NULL};
+ aiColor4D* apvColorSets[AI_MAX_NUMBER_OF_COLOR_SETS] = {NULL,NULL,NULL,NULL};
+
+ unsigned int p = 0;
+ while (pcMesh->HasTextureCoords(p))
+ apvTextureCoords[p++] = new aiVector3D[iNumVerts];
+
+ p = 0;
+ while (pcMesh->HasVertexColors(p))
+ apvColorSets[p++] = new aiColor4D[iNumVerts];
+
+ // allocate enough memory to hold output bones and vertex weights ...
+ std::vector<aiVertexWeight>* newWeights = new std::vector<aiVertexWeight>[pcMesh->mNumBones];
+ for (unsigned int i = 0;i < pcMesh->mNumBones;++i) {
+ newWeights[i].reserve(pcMesh->mBones[i]->mNumWeights*3);
+ }
+
+ // iterate through all faces and build a clean list
+ unsigned int iIndex = 0;
+ for (unsigned int a = 0; a< pcMesh->mNumFaces;++a)
+ {
+ aiFace* pcFace = &pcMesh->mFaces[a];
+ for (unsigned int q = 0; q < pcFace->mNumIndices;++q,++iIndex)
+ {
+ // need to build a clean list of bones, too
+ for (unsigned int i = 0;i < pcMesh->mNumBones;++i)
+ {
+ for (unsigned int a = 0; a < pcMesh->mBones[i]->mNumWeights;a++)
+ {
+ const aiVertexWeight& w = pcMesh->mBones[i]->mWeights[a];
+ if (pcFace->mIndices[q] == w.mVertexId)
+ {
+ aiVertexWeight wNew;
+ wNew.mVertexId = iIndex;
+ wNew.mWeight = w.mWeight;
+ newWeights[i].push_back(wNew);
+ }
+ }
+ }
+
+ pvPositions[iIndex] = pcMesh->mVertices[pcFace->mIndices[q]];
+
+ if (pcMesh->HasNormals())
+ {
+ pvNormals[iIndex] = pcMesh->mNormals[pcFace->mIndices[q]];
+ }
+ if (pcMesh->HasTangentsAndBitangents())
+ {
+ pvTangents[iIndex] = pcMesh->mTangents[pcFace->mIndices[q]];
+ pvBitangents[iIndex] = pcMesh->mBitangents[pcFace->mIndices[q]];
+ }
+
+ unsigned int p = 0;
+ while (pcMesh->HasTextureCoords(p))
+ {
+ apvTextureCoords[p][iIndex] = pcMesh->mTextureCoords[p][pcFace->mIndices[q]];
+ ++p;
+ }
+ p = 0;
+ while (pcMesh->HasVertexColors(p))
+ {
+ apvColorSets[p][iIndex] = pcMesh->mColors[p][pcFace->mIndices[q]];
+ ++p;
+ }
+ pcFace->mIndices[q] = iIndex;
+ }
+ }
+
+ // build output vertex weights
+ for (unsigned int i = 0;i < pcMesh->mNumBones;++i)
+ {
+ delete pcMesh->mBones[i]->mWeights;
+ if (!newWeights[i].empty())
+ {
+ pcMesh->mBones[i]->mWeights = new aiVertexWeight[newWeights[i].size()];
+ memcpy(pcMesh->mBones[i]->mWeights,&newWeights[i][0],
+ sizeof(aiVertexWeight) * newWeights[i].size());
+ }
+ else pcMesh->mBones[i]->mWeights = NULL;
+ }
+
+ // delete the old members
+ delete[] pcMesh->mVertices;
+ pcMesh->mVertices = pvPositions;
+
+ p = 0;
+ while (pcMesh->HasTextureCoords(p))
+ {
+ delete pcMesh->mTextureCoords[p];
+ pcMesh->mTextureCoords[p] = apvTextureCoords[p];
+ ++p;
+ }
+ p = 0;
+ while (pcMesh->HasVertexColors(p))
+ {
+ delete pcMesh->mColors[p];
+ pcMesh->mColors[p] = apvColorSets[p];
+ ++p;
+ }
+ pcMesh->mNumVertices = iNumVerts;
+
+ if (pcMesh->HasNormals())
+ {
+ delete[] pcMesh->mNormals;
+ pcMesh->mNormals = pvNormals;
+ }
+ if (pcMesh->HasTangentsAndBitangents())
+ {
+ delete[] pcMesh->mTangents;
+ pcMesh->mTangents = pvTangents;
+ delete[] pcMesh->mBitangents;
+ pcMesh->mBitangents = pvBitangents;
+ }
+ return (pcMesh->mNumVertices != iOldNumVertices);
+}
diff --git a/3rdparty/assimp/code/MakeVerboseFormat.h b/3rdparty/assimp/code/MakeVerboseFormat.h
new file mode 100644
index 000000000..6d480e5a7
--- /dev/null
+++ b/3rdparty/assimp/code/MakeVerboseFormat.h
@@ -0,0 +1,106 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to bring a given scene
+ into the verbose format that is expected by most postprocess steps.
+ This is the inverse of the "JoinIdenticalVertices" step. */
+#ifndef AI_MAKEVERBOSEFORMAT_H_INC
+#define AI_MAKEVERBOSEFORMAT_H_INC
+
+#include "BaseProcess.h"
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** MakeVerboseFormatProcess: Class to convert an asset to the verbose
+ * format which is expected by most postprocess steps.
+ *
+ * This is the inverse of what the "JoinIdenticalVertices" step is doing.
+ * This step has no official flag (since it wouldn't make sense to run it
+ * during import). It is intended for applications intending to modify the
+ * returned aiScene. After this step has been executed, they can execute
+ * other postprocess steps on the data. The code might also be useful to
+ * quickly adapt code that doesn't result in a verbose representation of
+ * the scene data.
+ * The step has been added because it was required by the viewer, however
+ * it has been moved to the main library since others might find it
+ * useful, too. */
+class ASSIMP_API MakeVerboseFormatProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+
+ /** Constructor to be privately used by Importer, or by applications
+ which know what they are doing if they modify the aiScene object */
+ MakeVerboseFormatProcess();
+
+ /** Destructor, private as well */
+ ~MakeVerboseFormatProcess();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not */
+ bool IsActive( unsigned int /*pFlags*/ ) const
+ {
+ // NOTE: There is no direct flag that corresponds to
+ // this postprocess step.
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ /** 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:
+
+ //! Apply the postprocess step to a given submesh
+ bool MakeVerboseFormat (aiMesh* pcMesh);
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_KILLNORMALPROCESS_H_INC
diff --git a/3rdparty/assimp/code/MaterialSystem.cpp b/3rdparty/assimp/code/MaterialSystem.cpp
new file mode 100644
index 000000000..4733d184f
--- /dev/null
+++ b/3rdparty/assimp/code/MaterialSystem.cpp
@@ -0,0 +1,612 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MaterialSystem.cpp
+ * @brief Implementation of the material system of the library
+ */
+
+#include "AssimpPCH.h"
+
+#include "Hash.h"
+#include "fast_atof.h"
+#include "ParsingUtils.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Get a specific property from a material
+aiReturn aiGetMaterialProperty(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ const aiMaterialProperty** pPropOut)
+{
+ ai_assert (pMat != NULL);
+ ai_assert (pKey != NULL);
+ ai_assert (pPropOut != NULL);
+
+ /* Just search for a property with exactly this name ..
+ * could be improved by hashing, but it's possibly
+ * no worth the effort (we're bound to C structures,
+ * thus std::map or derivates are not applicable. */
+ for (unsigned int i = 0; i < pMat->mNumProperties;++i) {
+ aiMaterialProperty* prop = pMat->mProperties[i];
+
+ if (prop /* just for safety ... */
+ && 0 == strcmp( prop->mKey.data, pKey )
+ && (0xffffffff == type || prop->mSemantic == type) /* 0xffffffff is a wildcard, but this is undocumented :-) */
+ && (0xffffffff == index || prop->mIndex == index))
+ {
+ *pPropOut = pMat->mProperties[i];
+ return AI_SUCCESS;
+ }
+ }
+ *pPropOut = NULL;
+ return AI_FAILURE;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get an array of floating-point values from the material.
+aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ float* pOut,
+ unsigned int* pMax)
+{
+ ai_assert (pOut != NULL);
+ ai_assert (pMat != NULL);
+
+ const aiMaterialProperty* prop;
+ aiGetMaterialProperty(pMat,pKey,type,index, (const aiMaterialProperty**) &prop);
+ if (!prop) {
+ return AI_FAILURE;
+ }
+
+ // data is given in floats, simply copy it
+ unsigned int iWrite;
+ if ( aiPTI_Float == prop->mType || aiPTI_Buffer == prop->mType) {
+ iWrite = prop->mDataLength / sizeof(float);
+ if (pMax) {
+ iWrite = std::min(*pMax,iWrite); ;
+ }
+ for (unsigned int a = 0; a < iWrite;++a) {
+ pOut[a] = static_cast<float> ( reinterpret_cast<float*>(prop->mData)[a] );
+ }
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ // data is given in ints, convert to float
+ else if ( aiPTI_Integer == prop->mType) {
+ iWrite = prop->mDataLength / sizeof(int32_t);
+ if (pMax) {
+ iWrite = std::min(*pMax,iWrite); ;
+ }
+ for (unsigned int a = 0; a < iWrite;++a) {
+ pOut[a] = static_cast<float> ( reinterpret_cast<int32_t*>(prop->mData)[a] );
+ }
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ // a string ... read floats separated by spaces
+ else {
+ if (pMax) {
+ iWrite = *pMax;
+ }
+ // strings are zero-terminated with a 32 bit length prefix, so this is safe
+ const char* cur = prop->mData+4;
+ ai_assert(prop->mDataLength>=5 && !prop->mData[prop->mDataLength-1]);
+ for (unsigned int a = 0; ;++a) {
+ cur = fast_atof_move(cur,pOut[a]);
+ if (a==iWrite-1) {
+ break;
+ }
+ if (!IsSpace(*cur)) {
+ DefaultLogger::get()->error("Material property" + std::string(pKey) +
+ " is a string; failed to parse a float array out of it.");
+ return AI_FAILURE;
+ }
+ }
+
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ return AI_SUCCESS;
+
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get an array if integers from the material
+aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ int* pOut,
+ unsigned int* pMax)
+{
+ ai_assert (pOut != NULL);
+ ai_assert (pMat != NULL);
+
+ const aiMaterialProperty* prop;
+ aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**) &prop);
+ if (!prop) {
+ return AI_FAILURE;
+ }
+
+ // data is given in ints, simply copy it
+ unsigned int iWrite;
+ if ( aiPTI_Integer == prop->mType || aiPTI_Buffer == prop->mType) {
+ iWrite = prop->mDataLength / sizeof(int32_t);
+ if (pMax) {
+ iWrite = std::min(*pMax,iWrite); ;
+ }
+ for (unsigned int a = 0; a < iWrite;++a) {
+ pOut[a] = static_cast<int>(reinterpret_cast<int32_t*>(prop->mData)[a]);
+ }
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ // data is given in floats convert to int
+ else if ( aiPTI_Float == prop->mType) {
+ iWrite = prop->mDataLength / sizeof(float);
+ if (pMax) {
+ iWrite = std::min(*pMax,iWrite); ;
+ }
+ for (unsigned int a = 0; a < iWrite;++a) {
+ pOut[a] = static_cast<int>(reinterpret_cast<float*>(prop->mData)[a]);
+ }
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ // it is a string ... no way to read something out of this
+ else {
+ if (pMax) {
+ iWrite = *pMax;
+ }
+ // strings are zero-terminated with a 32 bit length prefix, so this is safe
+ const char* cur = prop->mData+4;
+ ai_assert(prop->mDataLength>=5 && !prop->mData[prop->mDataLength-1]);
+ for (unsigned int a = 0; ;++a) {
+ pOut[a] = strtol10s(cur,&cur);
+ if (a==iWrite-1) {
+ break;
+ }
+ if (!IsSpace(*cur)) {
+ DefaultLogger::get()->error("Material property" + std::string(pKey) +
+ " is a string; failed to parse an integer array out of it.");
+ return AI_FAILURE;
+ }
+ }
+
+ if (pMax) {
+ *pMax = iWrite;
+ }
+ }
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a color (3 or 4 floats) from the material
+aiReturn aiGetMaterialColor(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ aiColor4D* pOut)
+{
+ unsigned int iMax = 4;
+ const aiReturn eRet = aiGetMaterialFloatArray(pMat,pKey,type,index,(float*)pOut,&iMax);
+
+ // if no alpha channel is defined: set it to 1.0
+ if (3 == iMax) {
+ pOut->a = 1.0f;
+ }
+
+ return eRet;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a string from the material
+aiReturn aiGetMaterialString(const aiMaterial* pMat,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ aiString* pOut)
+{
+ ai_assert (pOut != NULL);
+
+ const aiMaterialProperty* prop;
+ aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**)&prop);
+ if (!prop) {
+ return AI_FAILURE;
+ }
+
+ if ( aiPTI_String == prop->mType) {
+ ai_assert(prop->mDataLength>=5);
+
+ // The string is stored as 32 but length prefix followed by zero-terminated UTF8 data
+ pOut->length = static_cast<unsigned int>(*reinterpret_cast<uint32_t*>(prop->mData));
+
+ ai_assert(pOut->length+1+4==prop->mDataLength && !prop->mData[prop->mDataLength-1]);
+ memcpy(pOut->data,prop->mData+4,pOut->length+1);
+ }
+ else {
+ // TODO - implement lexical cast as well
+ DefaultLogger::get()->error("Material property" + std::string(pKey) +
+ " was found, but is no string" );
+ return AI_FAILURE;
+ }
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the number of textures on a particular texture stack
+ASSIMP_API unsigned int aiGetMaterialTextureCount(const C_STRUCT aiMaterial* pMat,
+ C_ENUM aiTextureType type)
+{
+ ai_assert (pMat != NULL);
+
+ /* Textures are always stored with ascending indices (ValidateDS provides a check, so we don't need to do it again) */
+ unsigned int max = 0;
+ for (unsigned int i = 0; i < pMat->mNumProperties;++i) {
+ aiMaterialProperty* prop = pMat->mProperties[i];
+
+ if (prop /* just a sanity check ... */
+ && 0 == strcmp( prop->mKey.data, _AI_MATKEY_TEXTURE_BASE )
+ && prop->mSemantic == type) {
+
+ max = std::max(max,prop->mIndex+1);
+ }
+ }
+ return max;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat,
+ aiTextureType type,
+ unsigned int index,
+ C_STRUCT aiString* path,
+ aiTextureMapping* _mapping /*= NULL*/,
+ unsigned int* uvindex /*= NULL*/,
+ float* blend /*= NULL*/,
+ aiTextureOp* op /*= NULL*/,
+ aiTextureMapMode* mapmode /*= NULL*/,
+ unsigned int* flags /*= NULL*/
+ )
+{
+ ai_assert(NULL != mat && NULL != path);
+
+ // Get the path to the texture
+ if (AI_SUCCESS != aiGetMaterialString(mat,AI_MATKEY_TEXTURE(type,index),path)) {
+ return AI_FAILURE;
+ }
+ // Determine mapping type
+ aiTextureMapping mapping = aiTextureMapping_UV;
+ aiGetMaterialInteger(mat,AI_MATKEY_MAPPING(type,index),(int*)&mapping);
+ if (_mapping)
+ *_mapping = mapping;
+
+ // Get UV index
+ if (aiTextureMapping_UV == mapping && uvindex) {
+ aiGetMaterialInteger(mat,AI_MATKEY_UVWSRC(type,index),(int*)uvindex);
+ }
+ // Get blend factor
+ if (blend) {
+ aiGetMaterialFloat(mat,AI_MATKEY_TEXBLEND(type,index),blend);
+ }
+ // Get texture operation
+ if (op){
+ aiGetMaterialInteger(mat,AI_MATKEY_TEXOP(type,index),(int*)op);
+ }
+ // Get texture mapping modes
+ if (mapmode) {
+ aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_U(type,index),(int*)&mapmode[0]);
+ aiGetMaterialInteger(mat,AI_MATKEY_MAPPINGMODE_V(type,index),(int*)&mapmode[1]);
+ }
+ // Get texture flags
+ if (flags){
+ aiGetMaterialInteger(mat,AI_MATKEY_TEXFLAGS(type,index),(int*)flags);
+ }
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Construction. Actually the one and only way to get an aiMaterial instance
+MaterialHelper::MaterialHelper()
+{
+ // Allocate 5 entries by default
+ mNumProperties = 0;
+ mNumAllocated = 5;
+ mProperties = new aiMaterialProperty*[5];
+}
+
+// ------------------------------------------------------------------------------------------------
+MaterialHelper::~MaterialHelper()
+{
+ _InternDestruct();
+}
+
+// ------------------------------------------------------------------------------------------------
+aiMaterial::~aiMaterial()
+{
+ // HACK (Aramis): This is safe: aiMaterial has a private constructor,
+ // so instances must be created indirectly via MaterialHelper. We can't
+ // use a virtual d'tor because we need to preserve binary compatibility
+ // with good old C ...
+ ((MaterialHelper*)this)->_InternDestruct();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Manual destructor
+void MaterialHelper::_InternDestruct()
+{
+ // First clean up all properties
+ Clear();
+
+ // Then delete the array that stored them
+ delete[] mProperties;
+ AI_DEBUG_INVALIDATE_PTR(mProperties);
+
+ // Update members
+ mNumAllocated = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void MaterialHelper::Clear()
+{
+ for (unsigned int i = 0; i < mNumProperties;++i) {
+ // delete this entry
+ delete mProperties[i];
+ AI_DEBUG_INVALIDATE_PTR(mProperties[i]);
+ }
+ mNumProperties = 0;
+
+ // The array remains allocated, we just invalidated its contents
+}
+
+// ------------------------------------------------------------------------------------------------
+uint32_t MaterialHelper::ComputeHash(bool includeMatName /*= false*/)
+{
+ uint32_t hash = 1503; // magic start value, choosen to be my birthday :-)
+ for (unsigned int i = 0; i < mNumProperties;++i) {
+ aiMaterialProperty* prop;
+
+ // Exclude all properties whose first character is '?' from the hash
+ // See doc for aiMaterialProperty.
+ if ((prop = mProperties[i]) && (includeMatName || prop->mKey.data[0] != '?')) {
+
+ hash = SuperFastHash(prop->mKey.data,(unsigned int)prop->mKey.length,hash);
+ hash = SuperFastHash(prop->mData,prop->mDataLength,hash);
+
+ // Combine the semantic and the index with the hash
+ hash = SuperFastHash((const char*)&prop->mSemantic,sizeof(unsigned int),hash);
+ hash = SuperFastHash((const char*)&prop->mIndex,sizeof(unsigned int),hash);
+ }
+ }
+ return hash;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiReturn MaterialHelper::RemoveProperty (const char* pKey,unsigned int type,
+ unsigned int index
+ )
+{
+ ai_assert(NULL != pKey);
+
+ for (unsigned int i = 0; i < mNumProperties;++i) {
+ aiMaterialProperty* prop = mProperties[i];
+
+ if (prop && !strcmp( prop->mKey.data, pKey ) &&
+ prop->mSemantic == type && prop->mIndex == index)
+ {
+ // Delete this entry
+ delete mProperties[i];
+
+ // collapse the array behind --.
+ --mNumProperties;
+ for (unsigned int a = i; a < mNumProperties;++a) {
+ mProperties[a] = mProperties[a+1];
+ }
+ return AI_SUCCESS;
+ }
+ }
+
+ return AI_FAILURE;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiReturn MaterialHelper::AddBinaryProperty (const void* pInput,
+ unsigned int pSizeInBytes,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index,
+ aiPropertyTypeInfo pType
+ )
+{
+ ai_assert (pInput != NULL);
+ ai_assert (pKey != NULL);
+ ai_assert (0 != pSizeInBytes);
+
+ // first search the list whether there is already an entry with this key
+ unsigned int iOutIndex = 0xffffffff;
+ for (unsigned int i = 0; i < mNumProperties;++i) {
+ aiMaterialProperty* prop = mProperties[i];
+
+ if (prop /* just for safety */ && !strcmp( prop->mKey.data, pKey ) &&
+ prop->mSemantic == type && prop->mIndex == index){
+
+ delete mProperties[i];
+ iOutIndex = i;
+ }
+ }
+
+ // Allocate a new material property
+ aiMaterialProperty* pcNew = new aiMaterialProperty();
+
+ // .. and fill it
+ pcNew->mType = pType;
+ pcNew->mSemantic = type;
+ pcNew->mIndex = index;
+
+ pcNew->mDataLength = pSizeInBytes;
+ pcNew->mData = new char[pSizeInBytes];
+ memcpy (pcNew->mData,pInput,pSizeInBytes);
+
+ pcNew->mKey.length = ::strlen(pKey);
+ ai_assert ( MAXLEN > pcNew->mKey.length);
+ strcpy( pcNew->mKey.data, pKey );
+
+ if (0xffffffff != iOutIndex) {
+ mProperties[iOutIndex] = pcNew;
+ return AI_SUCCESS;
+ }
+
+ // resize the array ... double the storage allocated
+ if (mNumProperties == mNumAllocated) {
+ const unsigned int iOld = mNumAllocated;
+ mNumAllocated *= 2;
+
+ aiMaterialProperty** ppTemp;
+ try {
+ ppTemp = new aiMaterialProperty*[mNumAllocated];
+ } catch (std::bad_alloc&) {
+ return AI_OUTOFMEMORY;
+ }
+
+ // just copy all items over; then replace the old array
+ memcpy (ppTemp,mProperties,iOld * sizeof(void*));
+
+ delete[] mProperties;
+ mProperties = ppTemp;
+ }
+ // push back ...
+ mProperties[mNumProperties++] = pcNew;
+ return AI_SUCCESS;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiReturn MaterialHelper::AddProperty (const aiString* pInput,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ // We don't want to add the whole buffer .. write a 32 bit length
+ // prefix followed by the zero-terminated UTF8 string.
+ // (HACK) I don't want to break the ABI now, but we definitely
+ // ought to change aiString::mLength to uint32_t one day.
+ if (sizeof(size_t) == 8) {
+ aiString copy = *pInput;
+ uint32_t* s = reinterpret_cast<uint32_t*>(&copy.length);
+ s[1] = static_cast<uint32_t>(pInput->length);
+
+ return AddBinaryProperty(s+1,
+ pInput->length+1+4,
+ pKey,
+ type,
+ index,
+ aiPTI_String);
+ }
+ ai_assert(sizeof(size_t)==4);
+ return AddBinaryProperty(pInput,
+ pInput->length+1+4,
+ pKey,
+ type,
+ index,
+ aiPTI_String);
+}
+
+// ------------------------------------------------------------------------------------------------
+void MaterialHelper::CopyPropertyList(MaterialHelper* pcDest,
+ const MaterialHelper* pcSrc
+ )
+{
+ ai_assert(NULL != pcDest);
+ ai_assert(NULL != pcSrc);
+
+ unsigned int iOldNum = pcDest->mNumProperties;
+ pcDest->mNumAllocated += pcSrc->mNumAllocated;
+ pcDest->mNumProperties += pcSrc->mNumProperties;
+
+ aiMaterialProperty** pcOld = pcDest->mProperties;
+ pcDest->mProperties = new aiMaterialProperty*[pcDest->mNumAllocated];
+
+ if (iOldNum && pcOld) {
+ for (unsigned int i = 0; i < iOldNum;++i) {
+ pcDest->mProperties[i] = pcOld[i];
+ }
+
+ delete[] pcOld;
+ }
+ for (unsigned int i = iOldNum; i< pcDest->mNumProperties;++i) {
+ aiMaterialProperty* propSrc = pcSrc->mProperties[i];
+
+ // search whether we have already a property with this name -> if yes, overwrite it
+ aiMaterialProperty* prop;
+ for (unsigned int q = 0; q < iOldNum;++q) {
+ prop = pcDest->mProperties[q];
+ if (prop /* just for safety */ && prop->mKey == propSrc->mKey && prop->mSemantic == propSrc->mSemantic
+ && prop->mIndex == propSrc->mIndex) {
+ delete prop;
+
+ // collapse the whole array ...
+ memmove(&pcDest->mProperties[q],&pcDest->mProperties[q+1],i-q);
+ i--;
+ pcDest->mNumProperties--;
+ }
+ }
+
+ // Allocate the output property and copy the source property
+ prop = pcDest->mProperties[i] = new aiMaterialProperty();
+ prop->mKey = propSrc->mKey;
+ prop->mDataLength = propSrc->mDataLength;
+ prop->mType = propSrc->mType;
+ prop->mSemantic = propSrc->mSemantic;
+ prop->mIndex = propSrc->mIndex;
+
+ prop->mData = new char[propSrc->mDataLength];
+ memcpy(prop->mData,propSrc->mData,prop->mDataLength);
+ }
+ return;
+}
+
diff --git a/3rdparty/assimp/code/MaterialSystem.h b/3rdparty/assimp/code/MaterialSystem.h
new file mode 100644
index 000000000..ea8daf896
--- /dev/null
+++ b/3rdparty/assimp/code/MaterialSystem.h
@@ -0,0 +1,246 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MaterialSystem.h
+ * Definition of the #MaterialHelper utility class.
+ */
+#ifndef AI_MATERIALSYSTEM_H_INC
+#define AI_MATERIALSYSTEM_H_INC
+
+#include "../include/aiMaterial.h"
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------------
+/** Internal material helper class deriving from aiMaterial.
+ *
+ * Intended to be used to fill an aiMaterial structure more easily.
+ */
+class ASSIMP_API MaterialHelper : public ::aiMaterial
+{
+public:
+
+ // Construction and destruction
+ MaterialHelper();
+ ~MaterialHelper();
+
+ // ------------------------------------------------------------------------------
+ /** @brief Add a property with a given key and type info to the material
+ * structure
+ *
+ * @param pInput Pointer to input data
+ * @param pSizeInBytes Size of input data
+ * @param pKey Key/Usage of the property (AI_MATKEY_XXX)
+ * @param type Set by the AI_MATKEY_XXX macro
+ * @param index Set by the AI_MATKEY_XXX macro
+ * @param pType Type information hint
+ */
+ aiReturn AddBinaryProperty (const void* pInput,
+ unsigned int pSizeInBytes,
+ const char* pKey,
+ unsigned int type ,
+ unsigned int index ,
+ aiPropertyTypeInfo pType);
+
+ // ------------------------------------------------------------------------------
+ /** @brief Add a string property with a given key and type info to the
+ * material structure
+ *
+ * @param pInput Input string
+ * @param pKey Key/Usage of the property (AI_MATKEY_XXX)
+ * @param type Set by the AI_MATKEY_XXX macro
+ * @param index Set by the AI_MATKEY_XXX macro
+ */
+ aiReturn AddProperty (const aiString* pInput,
+ const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ // ------------------------------------------------------------------------------
+ /** @brief Add a property with a given key to the material structure
+ * @param pInput Pointer to the input data
+ * @param pNumValues Number of values in the array
+ * @param pKey Key/Usage of the property (AI_MATKEY_XXX)
+ * @param type Set by the AI_MATKEY_XXX macro
+ * @param index Set by the AI_MATKEY_XXX macro
+ */
+ template<class TYPE>
+ aiReturn AddProperty (const TYPE* pInput,
+ unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ // ------------------------------------------------------------------------------
+ /** @brief Remove a given key from the list.
+ *
+ * The function fails if the key isn't found
+ * @param pKey Key to be deleted
+ */
+ aiReturn RemoveProperty (const char* pKey,
+ unsigned int type = 0,
+ unsigned int index = 0);
+
+ // ------------------------------------------------------------------------------
+ /** @brief Removes all properties from the material.
+ *
+ * The data array remains allocated so adding new properties is quite fast.
+ */
+ void Clear();
+
+ // ------------------------------------------------------------------------------
+ /** Computes a hash (hopefully unique) from all material properties
+ * The hash value reflects the current property state, so if you add any
+ * proprty and call this method again, the resulting hash value will be
+ * different.
+ *
+ * @param includeMatName Set to 'true' to take all properties with
+ * '?' as initial character in their name into account.
+ * Currently #AI_MATKEY_NAME is the only example.
+ * @return Unique hash
+ */
+ uint32_t ComputeHash(bool includeMatName = false);
+
+ // ------------------------------------------------------------------------------
+ /** Copy the property list of a material
+ * @param pcDest Destination material
+ * @param pcSrc Source material
+ */
+ static void CopyPropertyList(MaterialHelper* pcDest,
+ const MaterialHelper* pcSrc);
+
+public:
+ // For internal use. That's why it's public.
+ void _InternDestruct();
+};
+
+
+// ----------------------------------------------------------------------------------------
+template<class TYPE>
+aiReturn MaterialHelper::AddProperty (const TYPE* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(TYPE),
+ pKey,type,index,aiPTI_Buffer);
+}
+
+// ----------------------------------------------------------------------------------------
+template<>
+inline aiReturn MaterialHelper::AddProperty<float> (const float* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(float),
+ pKey,type,index,aiPTI_Float);
+}
+
+// ----------------------------------------------------------------------------------------
+template<>
+inline aiReturn MaterialHelper::AddProperty<aiUVTransform> (const aiUVTransform* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiUVTransform),
+ pKey,type,index,aiPTI_Float);
+}
+
+// ----------------------------------------------------------------------------------------
+template<>
+inline aiReturn MaterialHelper::AddProperty<aiColor4D> (const aiColor4D* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiColor4D),
+ pKey,type,index,aiPTI_Float);
+}
+
+// ----------------------------------------------------------------------------------------
+template<>
+inline aiReturn MaterialHelper::AddProperty<aiColor3D> (const aiColor3D* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiColor3D),
+ pKey,type,index,aiPTI_Float);
+}
+
+// ----------------------------------------------------------------------------------------
+template<>
+inline aiReturn MaterialHelper::AddProperty<aiVector3D> (const aiVector3D* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(aiVector3D),
+ pKey,type,index,aiPTI_Float);
+}
+
+// ----------------------------------------------------------------------------------------
+template<>
+inline aiReturn MaterialHelper::AddProperty<int> (const int* pInput,
+ const unsigned int pNumValues,
+ const char* pKey,
+ unsigned int type,
+ unsigned int index)
+{
+ return AddBinaryProperty((const void*)pInput,
+ pNumValues * sizeof(int),
+ pKey,type,index,aiPTI_Integer);
+}
+} // ! namespace Assimp
+
+#endif //!! AI_MATERIALSYSTEM_H_INC
diff --git a/3rdparty/assimp/code/MemoryIOWrapper.h b/3rdparty/assimp/code/MemoryIOWrapper.h
new file mode 100644
index 000000000..d34d987aa
--- /dev/null
+++ b/3rdparty/assimp/code/MemoryIOWrapper.h
@@ -0,0 +1,181 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 MemoryIOWrapper.h
+ * Handy IOStream/IOSystem implemetation to read directly from a memory buffer */
+#ifndef AI_MEMORYIOSTREAM_H_INC
+#define AI_MEMORYIOSTREAM_H_INC
+namespace Assimp {
+#define AI_MEMORYIO_MAGIC_FILENAME "$$$___magic___$$$"
+#define AI_MEMORYIO_MAGIC_FILENAME_LENGTH 17
+
+// ----------------------------------------------------------------------------------
+/** Implementation of IOStream to read directly from a memory buffer */
+// ----------------------------------------------------------------------------------
+class MemoryIOStream : public IOStream
+{
+ //friend class MemoryIOSystem;
+public:
+ MemoryIOStream (const uint8_t* buff, size_t len)
+ : buffer (buff), length(len), pos((size_t)0) {
+ }
+
+public:
+ ~MemoryIOStream () {
+ }
+
+ // -------------------------------------------------------------------
+ // Read from stream
+ size_t Read(void* pvBuffer, size_t pSize, size_t pCount) {
+ const size_t cnt = std::min(pCount,(length-pos)/pSize),ofs = pSize*cnt;
+
+ memcpy(pvBuffer,buffer+pos,ofs);
+ pos += ofs;
+
+ return cnt;
+ }
+
+ // -------------------------------------------------------------------
+ // Write to stream
+ size_t Write(const void* /* pvBuffer */, size_t /* pSize */,size_t /* pCount */) {
+ ai_assert(false); // won't be needed
+ return 0;
+ }
+
+ // -------------------------------------------------------------------
+ // Seek specific position
+ aiReturn Seek(size_t pOffset, aiOrigin pOrigin) {
+ if (aiOrigin_SET == pOrigin) {
+ if (pOffset >= length) {
+ return AI_FAILURE;
+ }
+ pos = pOffset;
+ }
+ else if (aiOrigin_END == pOrigin) {
+ if (pOffset >= length) {
+ return AI_FAILURE;
+ }
+ pos = length-pOffset;
+ }
+ else {
+ if (pOffset+pos >= length) {
+ return AI_FAILURE;
+ }
+ pos += pOffset;
+ }
+ return AI_SUCCESS;
+ }
+
+ // -------------------------------------------------------------------
+ // Get current seek position
+ size_t Tell() const {
+ return pos;
+ }
+
+ // -------------------------------------------------------------------
+ // Get size of file
+ size_t FileSize() const {
+ return length;
+ }
+
+ // -------------------------------------------------------------------
+ // Flush file contents
+ void Flush() {
+ ai_assert(false); // won't be needed
+ }
+
+private:
+ const uint8_t* buffer;
+ size_t length,pos;
+};
+
+// ---------------------------------------------------------------------------
+/** Dummy IO system to read from a memory buffer */
+class MemoryIOSystem : public IOSystem
+{
+public:
+ /** Constructor. */
+ MemoryIOSystem (const uint8_t* buff, size_t len)
+ : buffer (buff), length(len) {
+ }
+
+ /** Destructor. */
+ ~MemoryIOSystem() {
+ }
+
+ // -------------------------------------------------------------------
+ /** Tests for the existence of a file at the given path. */
+ bool Exists( const char* pFile) const {
+ return !strncmp(pFile,AI_MEMORYIO_MAGIC_FILENAME,AI_MEMORYIO_MAGIC_FILENAME_LENGTH);
+ }
+
+ // -------------------------------------------------------------------
+ /** Returns the directory separator. */
+ char getOsSeparator() const {
+ return '/'; // why not? it doesn't care
+ }
+
+ // -------------------------------------------------------------------
+ /** Open a new file with a given path. */
+ IOStream* Open( const char* pFile, const char* /* pMode = "rb" */) {
+ if (strncmp(pFile,AI_MEMORYIO_MAGIC_FILENAME,AI_MEMORYIO_MAGIC_FILENAME_LENGTH)) {
+ return NULL;
+ }
+ return new MemoryIOStream(buffer,length);
+ }
+
+ // -------------------------------------------------------------------
+ /** Closes the given file and releases all resources associated with it. */
+ void Close( IOStream* /* pFile */) {
+ }
+
+ // -------------------------------------------------------------------
+ /** Compare two paths */
+ bool ComparePaths (const char* /* one */, const char* /* second */) const {
+ return false;
+ }
+
+private:
+ const uint8_t* buffer;
+ size_t length;
+};
+} // end namespace Assimp
+
+#endif
diff --git a/3rdparty/assimp/code/NDOLoader.cpp b/3rdparty/assimp/code/NDOLoader.cpp
new file mode 100644
index 000000000..c8dae587c
--- /dev/null
+++ b/3rdparty/assimp/code/NDOLoader.cpp
@@ -0,0 +1,289 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2009, ASSIMP Development 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 Development 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 NDOLoader.cpp
+ * Implementation of the NDO importer class.
+ */
+
+#include "AssimpPCH.h"
+#ifndef AI_BUILD_NO_NDO_IMPORTER
+#include "NDOLoader.h"
+
+using namespace Assimp;
+#define for_each BOOST_FOREACH
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+NDOImporter::NDOImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+NDOImporter::~NDOImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool NDOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ // check file extension
+ const std::string extension = GetExtension(pFile);
+
+ if ( extension == "ndo")
+ return true;
+
+ if ((checkSig || !extension.length()) && pIOHandler) {
+ const char* tokens[] = {"nendo"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1,5);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a string of all file extensions supported
+void NDOImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ndo");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void NDOImporter::SetupProperties(const Importer* /*pImp*/)
+{
+ // nothing to be done for the moment
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void NDOImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ StreamReaderBE reader(pIOHandler->Open( pFile, "rb"));
+
+ // first 9 bytes are nendo file format ("nendo 1.n")
+ const char* head = (const char*)reader.GetPtr();
+ reader.IncPtr(9);
+
+ if (strncmp("nendo ",head,6)) {
+ throw DeadlyImportError("Not a Nendo file; magic signature missing");
+ }
+ // check if this is a supported version. if not, continue, too -- users,
+ // please don't complain if it doesn't work then ...
+ unsigned int file_format = 12;
+ if (!strncmp("1.0",head+6,3)) {
+ file_format = 10;
+ DefaultLogger::get()->info("NDO file format is 1.0");
+ }
+ else if (!strncmp("1.1",head+6,3)) {
+ file_format = 11;
+ DefaultLogger::get()->info("NDO file format is 1.1");
+ }
+ else if (!strncmp("1.2",head+6,3)) {
+ file_format = 12;
+ DefaultLogger::get()->info("NDO file format is 1.2");
+ }
+ else {
+ DefaultLogger::get()->warn(std::string("Unrecognized nendo file format version, continuing happily ... :") + (head+6));
+ }
+
+ reader.IncPtr(2); /* skip flags */
+ if (file_format >= 12) {
+ reader.IncPtr(2);
+ }
+ unsigned int temp = reader.GetU1();
+
+ std::vector<Object> objects(temp); /* buffer to store all the loaded objects in */
+
+ // read all objects
+ for (unsigned int o = 0; o < objects.size(); ++o) {
+
+// if (file_format < 12) {
+ if (!reader.GetI1()) {
+ continue; /* skip over empty object */
+ }
+ // reader.GetI2();
+// }
+ Object& obj = objects[o];
+
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ head = (const char*)reader.GetPtr();
+ reader.IncPtr(temp + 76); /* skip unknown stuff */
+
+ obj.name = std::string(head, temp);
+
+ // read edge table
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ obj.edges.reserve(temp);
+ for (unsigned int e = 0; e < temp; ++e) {
+
+ obj.edges.push_back(Edge());
+ Edge& edge = obj.edges.back();
+
+ for (unsigned int i = 0; i< 8; ++i) {
+ edge.edge[i] = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ }
+ edge.hard = file_format >= 11 ? reader.GetU1() : 0;
+ for (unsigned int i = 0; i< 8; ++i) {
+ edge.color[i] = reader.GetU1();
+ }
+ }
+
+ // read face table
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ obj.faces.reserve(temp);
+ for (unsigned int e = 0; e < temp; ++e) {
+
+ obj.faces.push_back(Face());
+ Face& face = obj.faces.back();
+
+ face.elem = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ }
+
+ // read vertex table
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ obj.vertices.reserve(temp);
+ for (unsigned int e = 0; e < temp; ++e) {
+
+ obj.vertices.push_back(Vertex());
+ Vertex& v = obj.vertices.back();
+
+ v.num = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ v.val.x = reader.GetF4();
+ v.val.y = reader.GetF4();
+ v.val.z = reader.GetF4();
+ }
+
+ // read UVs
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ for (unsigned int e = 0; e < temp; ++e) {
+ file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ }
+
+ temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ for (unsigned int e = 0; e < temp; ++e) {
+ file_format >= 12 ? reader.GetU4() : reader.GetU2();
+ }
+
+ if (reader.GetU1()) {
+ const unsigned int x = reader.GetU2(), y = reader.GetU2();
+ temp = 0;
+ while (temp < x*y) {
+ unsigned int repeat = reader.GetU1();
+ reader.GetU1();
+ reader.GetU1();
+ reader.GetU1();
+ temp += repeat;
+ }
+ }
+ }
+
+ // construct a dummy node graph and add all named objects as child nodes
+ aiNode* root = pScene->mRootNode = new aiNode("$NDODummyRoot");
+ aiNode** cc = root->mChildren = new aiNode* [ root->mNumChildren = static_cast<unsigned int>( objects.size()) ] ();
+ pScene->mMeshes = new aiMesh* [ root->mNumChildren] ();
+
+ std::vector<aiVector3D> vertices;
+ std::vector<unsigned int> indices;
+
+ for_each(const Object& obj,objects) {
+ aiNode* nd = *cc++ = new aiNode(obj.name);
+ nd->mParent = root;
+
+ // translated from a python dict() - a vector might be sufficient as well
+ typedef std::map<unsigned int, unsigned int> FaceTable;
+ FaceTable face_table;
+
+ unsigned int n = 0;
+ for_each(const Edge& edge, obj.edges) {
+
+ face_table[edge.edge[2]] = n;
+ face_table[edge.edge[3]] = n;
+
+ ++n;
+ }
+
+ aiMesh* mesh = new aiMesh();
+ aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces=face_table.size()];
+
+ vertices.clear();
+ vertices.reserve(4 * face_table.size()); // arbitrarily choosen
+ for_each(FaceTable::value_type& v, face_table) {
+ indices.clear();
+
+ aiFace& f = *faces++;
+
+ const unsigned int key = v.first;
+ unsigned int cur_edge = v.second;
+ while (1) {
+ unsigned int next_edge, next_vert;
+ if (key == obj.edges[cur_edge].edge[3]) {
+ next_edge = obj.edges[cur_edge].edge[5];
+ next_vert = obj.edges[cur_edge].edge[1];
+ }
+ else {
+ next_edge = obj.edges[cur_edge].edge[4];
+ next_vert = obj.edges[cur_edge].edge[0];
+ }
+ indices.push_back( vertices.size() );
+ vertices.push_back(obj.vertices[ next_vert ].val);
+
+ cur_edge = next_edge;
+ if (cur_edge == v.second) {
+ break;
+ }
+ }
+
+ f.mIndices = new unsigned int[f.mNumIndices = indices.size()];
+ std::copy(indices.begin(),indices.end(),f.mIndices);
+ }
+
+ mesh->mVertices = new aiVector3D[mesh->mNumVertices = vertices.size()];
+ std::copy(vertices.begin(),vertices.end(),mesh->mVertices);
+
+ if (mesh->mNumVertices) {
+ pScene->mMeshes[pScene->mNumMeshes] = mesh;
+
+ (nd->mMeshes = new unsigned int[nd->mNumMeshes=1])[0]=pScene->mNumMeshes++;
+ }
+ }
+}
+
+#endif
diff --git a/3rdparty/assimp/code/NDOLoader.h b/3rdparty/assimp/code/NDOLoader.h
new file mode 100644
index 000000000..815b43c0c
--- /dev/null
+++ b/3rdparty/assimp/code/NDOLoader.h
@@ -0,0 +1,116 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development 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 Development 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 NDOLoader.h
+ * Declaration of the Nendo importer class.
+ */
+#ifndef INCLUDED_AI_NDO_LOADER_H
+#define INCLUDED_AI_NDO_LOADER_H
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** @brief Importer class to load meshes from Nendo.
+ *
+ * Basing on
+ * <blender>/blender/release/scripts/nendo_import.py by Anthony D'Agostino.
+*/
+class NDOImporter : public BaseImporter
+{
+ friend class Importer;
+protected:
+ /** Constructor to be privately used by Importer */
+ NDOImporter();
+
+ /** Destructor, private as well */
+ ~NDOImporter();
+
+public:
+
+ //! Represents a single edge
+ struct Edge
+ {
+ unsigned int edge[8];
+ unsigned int hard;
+ uint8_t color[8];
+ };
+
+ //! Represents a single face
+ struct Face
+ {
+ unsigned int elem;
+ };
+
+ struct Vertex
+ {
+ unsigned int num;
+ aiVector3D val;
+ };
+
+ //! Represents a single object
+ struct Object
+ {
+ std::string name;
+
+ std::vector<Edge> edges;
+ std::vector<Face> faces;
+ std::vector<Vertex> vertices;
+ };
+
+ // -------------------------------------------------------------------
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+}; // end of class NDOImporter
+} // end of namespace Assimp
+#endif // INCLUDED_AI_NDO_LOADER_H
diff --git a/3rdparty/assimp/code/NFFLoader.cpp b/3rdparty/assimp/code/NFFLoader.cpp
new file mode 100644
index 000000000..24a7b458c
--- /dev/null
+++ b/3rdparty/assimp/code/NFFLoader.cpp
@@ -0,0 +1,1256 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the STL importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
+
+// internal headers
+#include "NFFLoader.h"
+#include "ParsingUtils.h"
+#include "StandardShapes.h"
+#include "fast_atof.h"
+#include "RemoveComments.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+NFFImporter::NFFImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+NFFImporter::~NFFImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool NFFImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const
+{
+ return SimpleExtensionCheck(pFile,"nff","enff");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the list of all supported file extensions
+void NFFImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("enff");
+ extensions.insert("nff");
+}
+
+// ------------------------------------------------------------------------------------------------
+#define AI_NFF_PARSE_FLOAT(f) \
+ SkipSpaces(&sz); \
+ if (!::IsLineEnd(*sz))sz = fast_atof_move(sz, (float&)f);
+
+// ------------------------------------------------------------------------------------------------
+#define AI_NFF_PARSE_TRIPLE(v) \
+ AI_NFF_PARSE_FLOAT(v[0]) \
+ AI_NFF_PARSE_FLOAT(v[1]) \
+ AI_NFF_PARSE_FLOAT(v[2])
+
+// ------------------------------------------------------------------------------------------------
+#define AI_NFF_PARSE_SHAPE_INFORMATION() \
+ aiVector3D center, radius(1.0f,get_qnan(),get_qnan()); \
+ AI_NFF_PARSE_TRIPLE(center); \
+ AI_NFF_PARSE_TRIPLE(radius); \
+ if (is_qnan(radius.z))radius.z = radius.x; \
+ if (is_qnan(radius.y))radius.y = radius.x; \
+ currentMesh.radius = radius; \
+ currentMesh.center = center;
+
+// ------------------------------------------------------------------------------------------------
+#define AI_NFF2_GET_NEXT_TOKEN() \
+ do \
+ { \
+ if (!GetNextLine(buffer,line)) \
+ {DefaultLogger::get()->warn("NFF2: Unexpected EOF, can't read next token");break;} \
+ SkipSpaces(line,&sz); \
+ } \
+ while (IsLineEnd(*sz))
+
+
+// ------------------------------------------------------------------------------------------------
+// Loads the materail table for the NFF2 file format from an external file
+void NFFImporter::LoadNFF2MaterialTable(std::vector<ShadingInfo>& output,
+ const std::string& path, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( path, "rb"));
+
+ // Check whether we can read from the file
+ if ( !file.get()) {
+ DefaultLogger::get()->error("NFF2: Unable to open material library " + path + ".");
+ return;
+ }
+
+ // get the size of the file
+ const unsigned int m = (unsigned int)file->FileSize();
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ // (terminate it with zero)
+ std::vector<char> mBuffer2(m+1);
+ TextFileToBuffer(file.get(),mBuffer2);
+ const char* buffer = &mBuffer2[0];
+
+ // First of all: remove all comments from the file
+ CommentRemover::RemoveLineComments("//",&mBuffer2[0]);
+
+ // The file should start with the magic sequence "mat"
+ if (!TokenMatch(buffer,"mat",3)) {
+ DefaultLogger::get()->error("NFF2: Not a valid material library " + path + ".");
+ return;
+ }
+
+ ShadingInfo* curShader = NULL;
+
+ // No read the file line per line
+ char line[4096];
+ const char* sz;
+ while (GetNextLine(buffer,line))
+ {
+ SkipSpaces(line,&sz);
+
+ // 'version' defines the version of the file format
+ if (TokenMatch(sz,"version",7))
+ {
+ DefaultLogger::get()->info("NFF (Sense8) material library file format: " + std::string(sz));
+ }
+ // 'matdef' starts a new material in the file
+ else if (TokenMatch(sz,"matdef",6))
+ {
+ // add a new material to the list
+ output.push_back( ShadingInfo() );
+ curShader = & output.back();
+
+ // parse the name of the material
+ }
+ else if (!TokenMatch(sz,"valid",5))
+ {
+ // check whether we have an active material at the moment
+ if (!IsLineEnd(*sz))
+ {
+ if (!curShader)
+ {
+ DefaultLogger::get()->error(std::string("NFF2 material library: Found element ") +
+ sz + "but there is no active material");
+ continue;
+ }
+ }
+ else continue;
+
+ // now read the material property and determine its type
+ aiColor3D c;
+ if (TokenMatch(sz,"ambient",7))
+ {
+ AI_NFF_PARSE_TRIPLE(c);
+ curShader->ambient = c;
+ }
+ else if (TokenMatch(sz,"diffuse",7) || TokenMatch(sz,"ambientdiffuse",14) /* correct? */)
+ {
+ AI_NFF_PARSE_TRIPLE(c);
+ curShader->diffuse = curShader->ambient = c;
+ }
+ else if (TokenMatch(sz,"specular",8))
+ {
+ AI_NFF_PARSE_TRIPLE(c);
+ curShader->specular = c;
+ }
+ else if (TokenMatch(sz,"emission",8))
+ {
+ AI_NFF_PARSE_TRIPLE(c);
+ curShader->emissive = c;
+ }
+ else if (TokenMatch(sz,"shininess",9))
+ {
+ AI_NFF_PARSE_FLOAT(curShader->shininess);
+ }
+ else if (TokenMatch(sz,"opacity",7))
+ {
+ AI_NFF_PARSE_FLOAT(curShader->opacity);
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void NFFImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( !file.get())
+ throw DeadlyImportError( "Failed to open NFF file " + pFile + ".");
+
+ unsigned int m = (unsigned int)file->FileSize();
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ // (terminate it with zero)
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+ const char* buffer = &mBuffer2[0];
+
+ // mesh arrays - separate here to make the handling of the pointers below easier.
+ std::vector<MeshInfo> meshes;
+ std::vector<MeshInfo> meshesWithNormals;
+ std::vector<MeshInfo> meshesWithUVCoords;
+ std::vector<MeshInfo> meshesLocked;
+
+ char line[4096];
+ const char* sz;
+
+ // camera parameters
+ aiVector3D camPos, camUp(0.f,1.f,0.f), camLookAt(0.f,0.f,1.f);
+ float angle = 45.f;
+ aiVector2D resolution;
+
+ bool hasCam = false;
+
+ MeshInfo* currentMeshWithNormals = NULL;
+ MeshInfo* currentMesh = NULL;
+ MeshInfo* currentMeshWithUVCoords = NULL;
+
+ ShadingInfo s; // current material info
+
+ // degree of tesselation
+ unsigned int iTesselation = 4;
+
+ // some temporary variables we need to parse the file
+ unsigned int sphere = 0,
+ cylinder = 0,
+ cone = 0,
+ numNamed = 0,
+ dodecahedron = 0,
+ octahedron = 0,
+ tetrahedron = 0,
+ hexahedron = 0;
+
+ // lights imported from the file
+ std::vector<Light> lights;
+
+ // check whether this is the NFF2 file format
+ if (TokenMatch(buffer,"nff",3))
+ {
+ const float qnan = get_qnan();
+ const aiColor4D cQNAN = aiColor4D (qnan,0.f,0.f,1.f);
+ const aiVector3D vQNAN = aiVector3D(qnan,0.f,0.f);
+
+ // another NFF file format ... just a raw parser has been implemented
+ // no support for further details, I don't think it is worth the effort
+ // http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/nff/nff2.html
+ // http://www.netghost.narod.ru/gff/graphics/summary/sense8.htm
+
+ // First of all: remove all comments from the file
+ CommentRemover::RemoveLineComments("//",&mBuffer2[0]);
+
+ while (GetNextLine(buffer,line))
+ {
+ SkipSpaces(line,&sz);
+ if (TokenMatch(sz,"version",7))
+ {
+ DefaultLogger::get()->info("NFF (Sense8) file format: " + std::string(sz));
+ }
+ else if (TokenMatch(sz,"viewpos",7))
+ {
+ AI_NFF_PARSE_TRIPLE(camPos);
+ hasCam = true;
+ }
+ else if (TokenMatch(sz,"viewdir",7))
+ {
+ AI_NFF_PARSE_TRIPLE(camLookAt);
+ hasCam = true;
+ }
+ // This starts a new object section
+ else if (!IsSpaceOrNewLine(*sz))
+ {
+ unsigned int subMeshIdx = 0;
+
+ // read the name of the object, skip all spaces
+ // at the end of it.
+ const char* sz3 = sz;
+ while (!IsSpaceOrNewLine(*sz))++sz;
+ std::string objectName = std::string(sz3,(unsigned int)(sz-sz3));
+
+ const unsigned int objStart = (unsigned int)meshes.size();
+
+ // There could be a material table in a separate file
+ std::vector<ShadingInfo> materialTable;
+ while (true)
+ {
+ AI_NFF2_GET_NEXT_TOKEN();
+
+ // material table - an external file
+ if (TokenMatch(sz,"mtable",6))
+ {
+ SkipSpaces(&sz);
+ sz3 = sz;
+ while (!IsSpaceOrNewLine(*sz))++sz;
+ const unsigned int diff = (unsigned int)(sz-sz3);
+ if (!diff)DefaultLogger::get()->warn("NFF2: Found empty mtable token");
+ else
+ {
+ // The material table has the file extension .mat.
+ // If it is not there, we need to append it
+ std::string path = std::string(sz3,diff);
+ if (std::string::npos == path.find_last_of(".mat"))
+ {
+ path.append(".mat");
+ }
+
+ // Now extract the working directory from the path to
+ // this file and append the material library filename
+ // to it.
+ std::string::size_type s;
+ if ((std::string::npos == (s = path.find_last_of('\\')) || !s) &&
+ (std::string::npos == (s = path.find_last_of('/')) || !s) )
+ {
+ s = pFile.find_last_of('\\');
+ if (std::string::npos == s)s = pFile.find_last_of('/');
+ if (std::string::npos != s)
+ {
+ path = pFile.substr(0,s+1) + path;
+ }
+ }
+ LoadNFF2MaterialTable(materialTable,path,pIOHandler);
+ }
+ }
+ else break;
+ }
+
+ // read the numbr of vertices
+ unsigned int num = ::strtol10(sz,&sz);
+
+ // temporary storage
+ std::vector<aiColor4D> tempColors;
+ std::vector<aiVector3D> tempPositions,tempTextureCoords,tempNormals;
+
+ bool hasNormals = false,hasUVs = false,hasColor = false;
+
+ tempPositions.reserve (num);
+ tempColors.reserve (num);
+ tempNormals.reserve (num);
+ tempTextureCoords.reserve (num);
+ for (unsigned int i = 0; i < num; ++i)
+ {
+ AI_NFF2_GET_NEXT_TOKEN();
+ aiVector3D v;
+ AI_NFF_PARSE_TRIPLE(v);
+ tempPositions.push_back(v);
+
+ // parse all other attributes in the line
+ while (true)
+ {
+ SkipSpaces(&sz);
+ if (IsLineEnd(*sz))break;
+
+ // color definition
+ if (TokenMatch(sz,"0x",2))
+ {
+ hasColor = true;
+ register unsigned int numIdx = ::strtol16(sz,&sz);
+ aiColor4D clr;
+ clr.a = 1.f;
+
+ // 0xRRGGBB
+ clr.r = ((numIdx >> 16u) & 0xff) / 255.f;
+ clr.g = ((numIdx >> 8u) & 0xff) / 255.f;
+ clr.b = ((numIdx) & 0xff) / 255.f;
+ tempColors.push_back(clr);
+ }
+ // normal vector
+ else if (TokenMatch(sz,"norm",4))
+ {
+ hasNormals = true;
+ AI_NFF_PARSE_TRIPLE(v);
+ tempNormals.push_back(v);
+ }
+ // UV coordinate
+ else if (TokenMatch(sz,"uv",2))
+ {
+ hasUVs = true;
+ AI_NFF_PARSE_FLOAT(v.x);
+ AI_NFF_PARSE_FLOAT(v.y);
+ v.z = 0.f;
+ tempTextureCoords.push_back(v);
+ }
+ }
+
+ // fill in dummies for all attributes that have not been set
+ if (tempNormals.size() != tempPositions.size())
+ tempNormals.push_back(vQNAN);
+
+ if (tempTextureCoords.size() != tempPositions.size())
+ tempTextureCoords.push_back(vQNAN);
+
+ if (tempColors.size() != tempPositions.size())
+ tempColors.push_back(cQNAN);
+ }
+
+ AI_NFF2_GET_NEXT_TOKEN();
+ if (!num)throw DeadlyImportError("NFF2: There are zero vertices");
+ num = ::strtol10(sz,&sz);
+
+ std::vector<unsigned int> tempIdx;
+ tempIdx.reserve(10);
+ for (unsigned int i = 0; i < num; ++i)
+ {
+ AI_NFF2_GET_NEXT_TOKEN();
+ SkipSpaces(line,&sz);
+ unsigned int numIdx = ::strtol10(sz,&sz);
+
+ // read all faces indices
+ if (numIdx)
+ {
+ // mesh.faces.push_back(numIdx);
+ // tempIdx.erase(tempIdx.begin(),tempIdx.end());
+ tempIdx.resize(numIdx);
+
+ for (unsigned int a = 0; a < numIdx;++a)
+ {
+ SkipSpaces(sz,&sz);
+ m = ::strtol10(sz,&sz);
+ if (m >= (unsigned int)tempPositions.size())
+ {
+ DefaultLogger::get()->error("NFF2: Vertex index overflow");
+ m= 0;
+ }
+ // mesh.vertices.push_back (tempPositions[idx]);
+ tempIdx[a] = m;
+ }
+ }
+
+ // build a temporary shader object for the face.
+ ShadingInfo shader;
+ unsigned int matIdx = 0;
+
+ // white material color - we have vertex colors
+ shader.color = aiColor3D(1.f,1.f,1.f);
+ aiColor4D c = aiColor4D(1.f,1.f,1.f,1.f);
+ while (true)
+ {
+ SkipSpaces(sz,&sz);
+ if (IsLineEnd(*sz))break;
+
+ // per-polygon colors
+ if (TokenMatch(sz,"0x",2))
+ {
+ hasColor = true;
+ const char* sz2 = sz;
+ numIdx = ::strtol16(sz,&sz);
+ const unsigned int diff = (unsigned int)(sz-sz2);
+
+ // 0xRRGGBB
+ if (diff > 3)
+ {
+ c.r = ((numIdx >> 16u) & 0xff) / 255.f;
+ c.g = ((numIdx >> 8u) & 0xff) / 255.f;
+ c.b = ((numIdx) & 0xff) / 255.f;
+ }
+ // 0xRGB
+ else
+ {
+ c.r = ((numIdx >> 8u) & 0xf) / 16.f;
+ c.g = ((numIdx >> 4u) & 0xf) / 16.f;
+ c.b = ((numIdx) & 0xf) / 16.f;
+ }
+ }
+ // TODO - implement texture mapping here
+#if 0
+ // mirror vertex texture coordinate?
+ else if (TokenMatch(sz,"mirror",6))
+ {
+ }
+ // texture coordinate scaling
+ else if (TokenMatch(sz,"scale",5))
+ {
+ }
+ // texture coordinate translation
+ else if (TokenMatch(sz,"trans",5))
+ {
+ }
+ // texture coordinate rotation angle
+ else if (TokenMatch(sz,"rot",3))
+ {
+ }
+#endif
+
+ // texture file name for this polygon + mapping information
+ else if ('_' == sz[0])
+ {
+ // get mapping information
+ switch (sz[1])
+ {
+ case 'v':
+ case 'V':
+
+ shader.shaded = false;
+ break;
+
+ case 't':
+ case 'T':
+ case 'u':
+ case 'U':
+
+ DefaultLogger::get()->warn("Unsupported NFF2 texture attribute: trans");
+ };
+ if (!sz[1] || '_' != sz[2])
+ {
+ DefaultLogger::get()->warn("NFF2: Expected underscore after texture attributes");
+ continue;
+ }
+ const char* sz2 = sz+3;
+ while (!IsSpaceOrNewLine( *sz ))++sz;
+ const unsigned int diff = (unsigned int)(sz-sz2);
+ if (diff)shader.texFile = std::string(sz2,diff);
+ }
+
+ // Two-sided material?
+ else if (TokenMatch(sz,"both",4))
+ {
+ shader.twoSided = true;
+ }
+
+ // Material ID?
+ else if (!materialTable.empty() && TokenMatch(sz,"matid",5))
+ {
+ SkipSpaces(&sz);
+ matIdx = ::strtol10(sz,&sz);
+ if (matIdx >= materialTable.size())
+ {
+ DefaultLogger::get()->error("NFF2: Material index overflow.");
+ matIdx = 0;
+ }
+
+ // now combine our current shader with the shader we
+ // read from the material table.
+ ShadingInfo& mat = materialTable[matIdx];
+ shader.ambient = mat.ambient;
+ shader.diffuse = mat.diffuse;
+ shader.emissive = mat.emissive;
+ shader.opacity = mat.opacity;
+ shader.specular = mat.specular;
+ shader.shininess = mat.shininess;
+ }
+ else SkipToken(sz);
+ }
+
+ // search the list of all shaders we have for this object whether
+ // there is an identical one. In this case, we append our mesh
+ // data to it.
+ MeshInfo* mesh = NULL;
+ for (std::vector<MeshInfo>::iterator it = meshes.begin() + objStart, end = meshes.end();
+ it != end; ++it)
+ {
+ if ((*it).shader == shader && (*it).matIndex == matIdx)
+ {
+ // we have one, we can append our data to it
+ mesh = &(*it);
+ }
+ }
+ if (!mesh)
+ {
+ meshes.push_back(MeshInfo(PatchType_Simple,false));
+ mesh = &meshes.back();
+ mesh->matIndex = matIdx;
+
+ // We need to add a new mesh to the list. We assign
+ // an unique name to it to make sure the scene will
+ // pass the validation step for the moment.
+ // TODO: fix naming of objects in the scenegraph later
+ if (objectName.length())
+ {
+ ::strcpy(mesh->name,objectName.c_str());
+ ASSIMP_itoa10(&mesh->name[objectName.length()],30,subMeshIdx++);
+ }
+
+ // copy the shader to the mesh.
+ mesh->shader = shader;
+ }
+
+ // fill the mesh with data
+ if (!tempIdx.empty())
+ {
+ mesh->faces.push_back((unsigned int)tempIdx.size());
+ for (std::vector<unsigned int>::const_iterator it = tempIdx.begin(), end = tempIdx.end();
+ it != end;++it)
+ {
+ m = *it;
+
+ // copy colors -vertex color specifications override polygon color specifications
+ if (hasColor)
+ {
+ const aiColor4D& clr = tempColors[m];
+ mesh->colors.push_back((is_qnan( clr.r ) ? c : clr));
+ }
+
+ // positions should always be there
+ mesh->vertices.push_back (tempPositions[m]);
+
+ // copy normal vectors
+ if (hasNormals)
+ mesh->normals.push_back (tempNormals[m]);
+
+ // copy texture coordinates
+ if (hasUVs)
+ mesh->uvs.push_back (tempTextureCoords[m]);
+ }
+ }
+ }
+ if (!num)throw DeadlyImportError("NFF2: There are zero faces");
+ }
+ }
+ camLookAt = camLookAt + camPos;
+ }
+ else // "Normal" Neutral file format that is quite more common
+ {
+ while (GetNextLine(buffer,line))
+ {
+ sz = line;
+ if ('p' == line[0] || TokenMatch(sz,"tpp",3))
+ {
+ MeshInfo* out = NULL;
+
+ // 'tpp' - texture polygon patch primitive
+ if ('t' == line[0])
+ {
+ currentMeshWithUVCoords = NULL;
+ for (std::vector<MeshInfo>::iterator it = meshesWithUVCoords.begin(), end = meshesWithUVCoords.end();
+ it != end;++it)
+ {
+ if ((*it).shader == s)
+ {
+ currentMeshWithUVCoords = &(*it);
+ break;
+ }
+ }
+
+ if (!currentMeshWithUVCoords)
+ {
+ meshesWithUVCoords.push_back(MeshInfo(PatchType_UVAndNormals));
+ currentMeshWithUVCoords = &meshesWithUVCoords.back();
+ currentMeshWithUVCoords->shader = s;
+ }
+ out = currentMeshWithUVCoords;
+ }
+ // 'pp' - polygon patch primitive
+ else if ('p' == line[1])
+ {
+ currentMeshWithNormals = NULL;
+ for (std::vector<MeshInfo>::iterator it = meshesWithNormals.begin(), end = meshesWithNormals.end();
+ it != end;++it)
+ {
+ if ((*it).shader == s)
+ {
+ currentMeshWithNormals = &(*it);
+ break;
+ }
+ }
+
+ if (!currentMeshWithNormals)
+ {
+ meshesWithNormals.push_back(MeshInfo(PatchType_Normals));
+ currentMeshWithNormals = &meshesWithNormals.back();
+ currentMeshWithNormals->shader = s;
+ }
+ sz = &line[2];out = currentMeshWithNormals;
+ }
+ // 'p' - polygon primitive
+ else
+ {
+ currentMesh = NULL;
+ for (std::vector<MeshInfo>::iterator it = meshes.begin(), end = meshes.end();
+ it != end;++it)
+ {
+ if ((*it).shader == s)
+ {
+ currentMesh = &(*it);
+ break;
+ }
+ }
+
+ if (!currentMesh)
+ {
+ meshes.push_back(MeshInfo(PatchType_Simple));
+ currentMesh = &meshes.back();
+ currentMesh->shader = s;
+ }
+ sz = &line[1];out = currentMesh;
+ }
+ SkipSpaces(sz,&sz);
+ m = strtol10(sz);
+
+ // ---- flip the face order
+ out->vertices.resize(out->vertices.size()+m);
+ if (out != currentMesh)
+ {
+ out->normals.resize(out->vertices.size());
+ }
+ if (out == currentMeshWithUVCoords)
+ {
+ out->uvs.resize(out->vertices.size());
+ }
+ for (unsigned int n = 0; n < m;++n)
+ {
+ if (!GetNextLine(buffer,line))
+ {
+ DefaultLogger::get()->error("NFF: Unexpected EOF was encountered. Patch definition incomplete");
+ continue;
+ }
+
+ aiVector3D v; sz = &line[0];
+ AI_NFF_PARSE_TRIPLE(v);
+ out->vertices[out->vertices.size()-n-1] = v;
+
+ if (out != currentMesh)
+ {
+ AI_NFF_PARSE_TRIPLE(v);
+ out->normals[out->vertices.size()-n-1] = v;
+ }
+ if (out == currentMeshWithUVCoords)
+ {
+ // FIX: in one test file this wraps over multiple lines
+ SkipSpaces(&sz);
+ if (IsLineEnd(*sz))
+ {
+ GetNextLine(buffer,line);
+ sz = line;
+ }
+ AI_NFF_PARSE_FLOAT(v.x);
+ SkipSpaces(&sz);
+ if (IsLineEnd(*sz))
+ {
+ GetNextLine(buffer,line);
+ sz = line;
+ }
+ AI_NFF_PARSE_FLOAT(v.y);
+ v.y = 1.f - v.y;
+ out->uvs[out->vertices.size()-n-1] = v;
+ }
+ }
+ out->faces.push_back(m);
+ }
+ // 'f' - shading information block
+ else if (TokenMatch(sz,"f",1))
+ {
+ float d;
+
+ // read the RGB colors
+ AI_NFF_PARSE_TRIPLE(s.color);
+
+ // read the other properties
+ AI_NFF_PARSE_FLOAT(s.diffuse.r);
+ AI_NFF_PARSE_FLOAT(s.specular.r);
+ AI_NFF_PARSE_FLOAT(d); // skip shininess and transmittance
+ AI_NFF_PARSE_FLOAT(d);
+ AI_NFF_PARSE_FLOAT(s.refracti);
+
+ // NFF2 uses full colors here so we need to use them too
+ // although NFF uses simple scaling factors
+ s.diffuse.g = s.diffuse.b = s.diffuse.r;
+ s.specular.g = s.specular.b = s.specular.r;
+
+ // if the next one is NOT a number we assume it is a texture file name
+ // this feature is used by some NFF files on the internet and it has
+ // been implemented as it can be really useful
+ SkipSpaces(&sz);
+ if (!IsNumeric(*sz))
+ {
+ // TODO: Support full file names with spaces and quotation marks ...
+ const char* p = sz;
+ while (!IsSpaceOrNewLine( *sz ))++sz;
+
+ unsigned int diff = (unsigned int)(sz-p);
+ if (diff)
+ {
+ s.texFile = std::string(p,diff);
+ }
+ }
+ else
+ {
+ AI_NFF_PARSE_FLOAT(s.ambient); // optional
+ }
+ }
+ // 'shader' - other way to specify a texture
+ else if (TokenMatch(sz,"shader",6))
+ {
+ SkipSpaces(&sz);
+ const char* old = sz;
+ while (!IsSpaceOrNewLine(*sz))++sz;
+ s.texFile = std::string(old, (uintptr_t)sz - (uintptr_t)old);
+ }
+ // 'l' - light source
+ else if (TokenMatch(sz,"l",1))
+ {
+ lights.push_back(Light());
+ Light& light = lights.back();
+
+ AI_NFF_PARSE_TRIPLE(light.position);
+ AI_NFF_PARSE_FLOAT (light.intensity);
+ AI_NFF_PARSE_TRIPLE(light.color);
+ }
+ // 's' - sphere
+ else if (TokenMatch(sz,"s",1))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_SPHERE;
+
+ AI_NFF_PARSE_SHAPE_INFORMATION();
+
+ // we don't need scaling or translation here - we do it in the node's transform
+ StandardShapes::MakeSphere(iTesselation, currentMesh.vertices);
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh
+ ::sprintf(currentMesh.name,"sphere_%i",sphere++);
+ }
+ // 'dod' - dodecahedron
+ else if (TokenMatch(sz,"dod",3))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_SPHERE;
+
+ AI_NFF_PARSE_SHAPE_INFORMATION();
+
+ // we don't need scaling or translation here - we do it in the node's transform
+ StandardShapes::MakeDodecahedron(currentMesh.vertices);
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh
+ ::sprintf(currentMesh.name,"dodecahedron_%i",dodecahedron++);
+ }
+
+ // 'oct' - octahedron
+ else if (TokenMatch(sz,"oct",3))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_SPHERE;
+
+ AI_NFF_PARSE_SHAPE_INFORMATION();
+
+ // we don't need scaling or translation here - we do it in the node's transform
+ StandardShapes::MakeOctahedron(currentMesh.vertices);
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh
+ ::sprintf(currentMesh.name,"octahedron_%i",octahedron++);
+ }
+
+ // 'tet' - tetrahedron
+ else if (TokenMatch(sz,"tet",3))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_SPHERE;
+
+ AI_NFF_PARSE_SHAPE_INFORMATION();
+
+ // we don't need scaling or translation here - we do it in the node's transform
+ StandardShapes::MakeTetrahedron(currentMesh.vertices);
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh
+ ::sprintf(currentMesh.name,"tetrahedron_%i",tetrahedron++);
+ }
+
+ // 'hex' - hexahedron
+ else if (TokenMatch(sz,"hex",3))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_BOX;
+
+ AI_NFF_PARSE_SHAPE_INFORMATION();
+
+ // we don't need scaling or translation here - we do it in the node's transform
+ StandardShapes::MakeHexahedron(currentMesh.vertices);
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh
+ ::sprintf(currentMesh.name,"hexahedron_%i",hexahedron++);
+ }
+ // 'c' - cone
+ else if (TokenMatch(sz,"c",1))
+ {
+ meshesLocked.push_back(MeshInfo(PatchType_Simple,true));
+ MeshInfo& currentMesh = meshesLocked.back();
+ currentMesh.shader = s;
+ currentMesh.shader.mapping = aiTextureMapping_CYLINDER;
+
+ if (!GetNextLine(buffer,line))
+ {
+ DefaultLogger::get()->error("NFF: Unexpected end of file (cone definition not complete)");
+ break;
+ }
+ sz = line;
+
+ // read the two center points and the respective radii
+ aiVector3D center1, center2; float radius1, radius2;
+ AI_NFF_PARSE_TRIPLE(center1);
+ AI_NFF_PARSE_FLOAT(radius1);
+
+ if (!GetNextLine(buffer,line))
+ {
+ DefaultLogger::get()->error("NFF: Unexpected end of file (cone definition not complete)");
+ break;
+ }
+ sz = line;
+
+ AI_NFF_PARSE_TRIPLE(center2);
+ AI_NFF_PARSE_FLOAT(radius2);
+
+ // compute the center point of the cone/cylinder -
+ // it is its local transformation origin
+ currentMesh.dir = center2-center1;
+ currentMesh.center = center1+currentMesh.dir/2.f;
+
+ float f;
+ if (( f = currentMesh.dir.Length()) < 10e-3f )
+ {
+ DefaultLogger::get()->error("NFF: Cone height is close to zero");
+ continue;
+ }
+ currentMesh.dir /= f; // normalize
+
+ // generate the cone - it consists of simple triangles
+ StandardShapes::MakeCone(f, radius1, radius2,
+ integer_pow(4, iTesselation), currentMesh.vertices);
+
+ // MakeCone() returns tris
+ currentMesh.faces.resize(currentMesh.vertices.size()/3,3);
+
+ // generate a name for the mesh. 'cone' if it a cone,
+ // 'cylinder' if it is a cylinder. Funny, isn't it?
+ if (radius1 != radius2)
+ ::sprintf(currentMesh.name,"cone_%i",cone++);
+ else ::sprintf(currentMesh.name,"cylinder_%i",cylinder++);
+ }
+ // 'tess' - tesselation
+ else if (TokenMatch(sz,"tess",4))
+ {
+ SkipSpaces(&sz);
+ iTesselation = strtol10(sz);
+ }
+ // 'from' - camera position
+ else if (TokenMatch(sz,"from",4))
+ {
+ AI_NFF_PARSE_TRIPLE(camPos);
+ hasCam = true;
+ }
+ // 'at' - camera look-at vector
+ else if (TokenMatch(sz,"at",2))
+ {
+ AI_NFF_PARSE_TRIPLE(camLookAt);
+ hasCam = true;
+ }
+ // 'up' - camera up vector
+ else if (TokenMatch(sz,"up",2))
+ {
+ AI_NFF_PARSE_TRIPLE(camUp);
+ hasCam = true;
+ }
+ // 'angle' - (half?) camera field of view
+ else if (TokenMatch(sz,"angle",5))
+ {
+ AI_NFF_PARSE_FLOAT(angle);
+ hasCam = true;
+ }
+ // 'resolution' - used to compute the screen aspect
+ else if (TokenMatch(sz,"resolution",10))
+ {
+ AI_NFF_PARSE_FLOAT(resolution.x);
+ AI_NFF_PARSE_FLOAT(resolution.y);
+ hasCam = true;
+ }
+ // 'pb' - bezier patch. Not supported yet
+ else if (TokenMatch(sz,"pb",2))
+ {
+ DefaultLogger::get()->error("NFF: Encountered unsupported ID: bezier patch");
+ }
+ // 'pn' - NURBS. Not supported yet
+ else if (TokenMatch(sz,"pn",2) || TokenMatch(sz,"pnn",3))
+ {
+ DefaultLogger::get()->error("NFF: Encountered unsupported ID: NURBS");
+ }
+ // '' - comment
+ else if ('#' == line[0])
+ {
+ const char* sz;SkipSpaces(&line[1],&sz);
+ if (!IsLineEnd(*sz))DefaultLogger::get()->info(sz);
+ }
+ }
+ }
+
+ // copy all arrays into one large
+ meshes.reserve (meshes.size()+meshesLocked.size()+meshesWithNormals.size()+meshesWithUVCoords.size());
+ meshes.insert (meshes.end(),meshesLocked.begin(),meshesLocked.end());
+ meshes.insert (meshes.end(),meshesWithNormals.begin(),meshesWithNormals.end());
+ meshes.insert (meshes.end(),meshesWithUVCoords.begin(),meshesWithUVCoords.end());
+
+ // now generate output meshes. first find out how many meshes we'll need
+ std::vector<MeshInfo>::const_iterator it = meshes.begin(), end = meshes.end();
+ for (;it != end;++it)
+ {
+ if (!(*it).faces.empty())
+ {
+ ++pScene->mNumMeshes;
+ if ((*it).name[0])++numNamed;
+ }
+ }
+
+ // generate a dummy root node - assign all unnamed elements such
+ // as polygons and polygon patches to the root node and generate
+ // sub nodes for named objects such as spheres and cones.
+ aiNode* const root = new aiNode();
+ root->mName.Set("<NFF_Root>");
+ root->mNumChildren = numNamed + (hasCam ? 1 : 0) + (unsigned int) lights.size();
+ root->mNumMeshes = pScene->mNumMeshes-numNamed;
+
+ aiNode** ppcChildren = NULL;
+ unsigned int* pMeshes = NULL;
+ if (root->mNumMeshes)
+ pMeshes = root->mMeshes = new unsigned int[root->mNumMeshes];
+ if (root->mNumChildren)
+ ppcChildren = root->mChildren = new aiNode*[root->mNumChildren];
+
+ // generate the camera
+ if (hasCam)
+ {
+ aiNode* nd = *ppcChildren = new aiNode();
+ nd->mName.Set("<NFF_Camera>");
+ nd->mParent = root;
+
+ // allocate the camera in the scene
+ pScene->mNumCameras = 1;
+ pScene->mCameras = new aiCamera*[1];
+ aiCamera* c = pScene->mCameras[0] = new aiCamera;
+
+ c->mName = nd->mName; // make sure the names are identical
+ c->mHorizontalFOV = AI_DEG_TO_RAD( angle );
+ c->mLookAt = camLookAt - camPos;
+ c->mPosition = camPos;
+ c->mUp = camUp;
+
+ // If the resolution is not specified in the file, we
+ // need to set 1.0 as aspect.
+ c->mAspect = (!resolution.y ? 0.f : resolution.x / resolution.y);
+ ++ppcChildren;
+ }
+
+ // generate light sources
+ if (!lights.empty())
+ {
+ pScene->mNumLights = (unsigned int)lights.size();
+ pScene->mLights = new aiLight*[pScene->mNumLights];
+ for (unsigned int i = 0; i < pScene->mNumLights;++i,++ppcChildren)
+ {
+ const Light& l = lights[i];
+
+ aiNode* nd = *ppcChildren = new aiNode();
+ nd->mParent = root;
+
+ nd->mName.length = ::sprintf(nd->mName.data,"<NFF_Light%i>",i);
+
+ // allocate the light in the scene data structure
+ aiLight* out = pScene->mLights[i] = new aiLight();
+ out->mName = nd->mName; // make sure the names are identical
+ out->mType = aiLightSource_POINT;
+ out->mColorDiffuse = out->mColorSpecular = l.color * l.intensity;
+ out->mPosition = l.position;
+ }
+ }
+
+ if (!pScene->mNumMeshes)throw DeadlyImportError("NFF: No meshes loaded");
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = pScene->mNumMeshes];
+ for (it = meshes.begin(), m = 0; it != end;++it)
+ {
+ if ((*it).faces.empty())continue;
+
+ const MeshInfo& src = *it;
+ aiMesh* const mesh = pScene->mMeshes[m] = new aiMesh();
+ mesh->mNumVertices = (unsigned int)src.vertices.size();
+ mesh->mNumFaces = (unsigned int)src.faces.size();
+
+ // Generate sub nodes for named meshes
+ if (src.name[0])
+ {
+ aiNode* const node = *ppcChildren = new aiNode();
+ node->mParent = root;
+ node->mNumMeshes = 1;
+ node->mMeshes = new unsigned int[1];
+ node->mMeshes[0] = m;
+ node->mName.Set(src.name);
+
+ // setup the transformation matrix of the node
+ aiMatrix4x4::FromToMatrix(aiVector3D(0.f,1.f,0.f),
+ src.dir,node->mTransformation);
+
+ aiMatrix4x4& mat = node->mTransformation;
+ mat.a1 *= src.radius.x; mat.b1 *= src.radius.x; mat.c1 *= src.radius.x;
+ mat.a2 *= src.radius.y; mat.b2 *= src.radius.y; mat.c2 *= src.radius.y;
+ mat.a3 *= src.radius.z; mat.b3 *= src.radius.z; mat.c3 *= src.radius.z;
+ mat.a4 = src.center.x;
+ mat.b4 = src.center.y;
+ mat.c4 = src.center.z;
+
+ ++ppcChildren;
+ }
+ else *pMeshes++ = m;
+
+ // copy vertex positions
+ mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ ::memcpy(mesh->mVertices,&src.vertices[0],
+ sizeof(aiVector3D)*mesh->mNumVertices);
+
+ // NFF2: there could be vertex colors
+ if (!src.colors.empty())
+ {
+ ai_assert(src.colors.size() == src.vertices.size());
+
+ // copy vertex colors
+ mesh->mColors[0] = new aiColor4D[mesh->mNumVertices];
+ ::memcpy(mesh->mColors[0],&src.colors[0],
+ sizeof(aiColor4D)*mesh->mNumVertices);
+ }
+
+ if (!src.normals.empty())
+ {
+ ai_assert(src.normals.size() == src.vertices.size());
+
+ // copy normal vectors
+ mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+ ::memcpy(mesh->mNormals,&src.normals[0],
+ sizeof(aiVector3D)*mesh->mNumVertices);
+ }
+
+ if (!src.uvs.empty())
+ {
+ ai_assert(src.uvs.size() == src.vertices.size());
+
+ // copy texture coordinates
+ mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+ ::memcpy(mesh->mTextureCoords[0],&src.uvs[0],
+ sizeof(aiVector3D)*mesh->mNumVertices);
+ }
+
+ // generate faces
+ unsigned int p = 0;
+ aiFace* pFace = mesh->mFaces = new aiFace[mesh->mNumFaces];
+ for (std::vector<unsigned int>::const_iterator it2 = src.faces.begin(),
+ end2 = src.faces.end();
+ it2 != end2;++it2,++pFace)
+ {
+ pFace->mIndices = new unsigned int [ pFace->mNumIndices = *it2 ];
+ for (unsigned int o = 0; o < pFace->mNumIndices;++o)
+ pFace->mIndices[o] = p++;
+ }
+
+ // generate a material for the mesh
+ MaterialHelper* pcMat = (MaterialHelper*)(pScene->mMaterials[m] = new MaterialHelper());
+
+ mesh->mMaterialIndex = m++;
+
+ aiString s;
+ s.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcMat->AddProperty(&s, AI_MATKEY_NAME);
+
+ // FIX: Ignore diffuse == 0
+ aiColor3D c = src.shader.color * (src.shader.diffuse.r ? src.shader.diffuse : aiColor3D(1.f,1.f,1.f));
+ pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE);
+ c = src.shader.color * src.shader.specular;
+ pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR);
+
+ // NFF2 - default values for NFF
+ pcMat->AddProperty(&src.shader.ambient, 1,AI_MATKEY_COLOR_AMBIENT);
+ pcMat->AddProperty(&src.shader.emissive,1,AI_MATKEY_COLOR_EMISSIVE);
+ pcMat->AddProperty(&src.shader.opacity, 1,AI_MATKEY_OPACITY);
+
+ // setup the first texture layer, if existing
+ if (src.shader.texFile.length())
+ {
+ s.Set(src.shader.texFile);
+ pcMat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ if (aiTextureMapping_UV != src.shader.mapping) {
+
+ aiVector3D v(0.f,-1.f,0.f);
+ pcMat->AddProperty(&v, 1,AI_MATKEY_TEXMAP_AXIS_DIFFUSE(0));
+ pcMat->AddProperty((int*)&src.shader.mapping, 1,AI_MATKEY_MAPPING_DIFFUSE(0));
+ }
+ }
+
+ // setup the name of the material
+ if (src.shader.name.length())
+ {
+ s.Set(src.shader.texFile);
+ pcMat->AddProperty(&s,AI_MATKEY_NAME);
+ }
+
+ // setup some more material properties that are specific to NFF2
+ int i;
+ if (src.shader.twoSided)
+ {
+ i = 1;
+ pcMat->AddProperty(&i,1,AI_MATKEY_TWOSIDED);
+ }
+ i = (src.shader.shaded ? aiShadingMode_Gouraud : aiShadingMode_NoShading);
+ if (src.shader.shininess)
+ {
+ i = aiShadingMode_Phong;
+ pcMat->AddProperty(&src.shader.shininess,1,AI_MATKEY_SHININESS);
+ }
+ pcMat->AddProperty(&i,1,AI_MATKEY_SHADING_MODEL);
+ }
+ pScene->mRootNode = root;
+}
+
+#endif // !! ASSIMP_BUILD_NO_NFF_IMPORTER
diff --git a/3rdparty/assimp/code/NFFLoader.h b/3rdparty/assimp/code/NFFLoader.h
new file mode 100644
index 000000000..464792cac
--- /dev/null
+++ b/3rdparty/assimp/code/NFFLoader.h
@@ -0,0 +1,216 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 NFFLoader.h
+ * @brief Declaration of the NFF importer class.
+ */
+#ifndef AI_NFFLOADER_H_INCLUDED
+#define AI_NFFLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include <vector>
+
+#include "../include/aiTypes.h"
+
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+/** NFF (Neutral File Format) Importer class.
+ *
+ * The class implements both Eric Haynes NFF format and Sense8's NFF (NFF2) format.
+ * Both are quite different and the loading code is somewhat dirty at
+ * the moment. Sense8 should be moved to a separate loader.
+*/
+class NFFImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ NFFImporter();
+
+ /** Destructor, private as well */
+ ~NFFImporter();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+
+ // describes face material properties
+ struct ShadingInfo
+ {
+ ShadingInfo()
+ : color (0.6f,0.6f,0.6f)
+ , diffuse (1.f,1.f,1.f)
+ , specular (1.f,1.f,1.f)
+ , ambient (0.f,0.f,0.f)
+ , emissive (0.f,0.f,0.f)
+ , refracti (1.f)
+ , twoSided (false) // for NFF2
+ , shaded (true) // for NFF2
+ , opacity (1.f)
+ , shininess (0.f)
+ , mapping (aiTextureMapping_UV)
+ {}
+
+ aiColor3D color,diffuse,specular,ambient,emissive;
+ float refracti;
+
+ std::string texFile;
+
+ // For NFF2
+ bool twoSided;
+ bool shaded;
+ float opacity, shininess;
+
+ std::string name;
+
+ // texture mapping to be generated for the mesh - uv is the default
+ // it means: use UV if there, nothing otherwise. This property is
+ // used for locked meshes.
+ aiTextureMapping mapping;
+
+ // shininess is ignored for the moment
+ bool operator == (const ShadingInfo& other) const
+ {
+ return color == other.color &&
+ diffuse == other.diffuse &&
+ specular == other.specular &&
+ ambient == other.ambient &&
+ refracti == other.refracti &&
+ texFile == other.texFile &&
+ twoSided == other.twoSided &&
+ shaded == other.shaded;
+
+ // Some properties from NFF2 aren't compared by this operator.
+ // Comparing MeshInfo::matIndex should do that.
+ }
+ };
+
+ // describes a NFF light source
+ struct Light
+ {
+ Light()
+ : intensity (1.f)
+ , color (1.f,1.f,1.f)
+ {}
+
+ aiVector3D position;
+ float intensity;
+ aiColor3D color;
+ };
+
+ enum PatchType
+ {
+ PatchType_Simple = 0x0,
+ PatchType_Normals = 0x1,
+ PatchType_UVAndNormals = 0x2
+ };
+
+ // describes a NFF mesh
+ struct MeshInfo
+ {
+ MeshInfo(PatchType _pType, bool bL = false)
+ : pType (_pType)
+ , bLocked (bL)
+ , radius (1.f,1.f,1.f)
+ , dir (0.f,1.f,0.f)
+ , matIndex (0)
+ {
+ name[0] = '\0'; // by default meshes are unnamed
+ }
+
+ ShadingInfo shader;
+ PatchType pType;
+ bool bLocked;
+
+ // for spheres, cones and cylinders: center point of the object
+ aiVector3D center, radius, dir;
+
+ char name[128];
+
+ std::vector<aiVector3D> vertices, normals, uvs;
+ std::vector<unsigned int> faces;
+
+ // for NFF2
+ std::vector<aiColor4D> colors;
+ unsigned int matIndex;
+ };
+
+
+ // -------------------------------------------------------------------
+ /** Loads the material table for the NFF2 file format from an
+ * external file.
+ *
+ * @param output Receives the list of output meshes
+ * @param path Path to the file (abs. or rel.)
+ * @param pIOHandler IOSystem to be used to open the file
+ */
+ void LoadNFF2MaterialTable(std::vector<ShadingInfo>& output,
+ const std::string& path, IOSystem* pIOHandler);
+
+};
+
+} // end of namespace Assimp
+
+#endif // AI_NFFIMPORTER_H_IN
diff --git a/3rdparty/assimp/code/OFFLoader.cpp b/3rdparty/assimp/code/OFFLoader.cpp
new file mode 100644
index 000000000..38e21b49d
--- /dev/null
+++ b/3rdparty/assimp/code/OFFLoader.cpp
@@ -0,0 +1,211 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 OFFLoader.cpp
+ * @brief Implementation of the OFF importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
+
+// internal headers
+#include "OFFLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+OFFImporter::OFFImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+OFFImporter::~OFFImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "off")
+ return true;
+ else if (!extension.length() || checkSig)
+ {
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"off"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void OFFImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("off");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void OFFImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open OFF file " + pFile + ".");
+ }
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+ const char* buffer = &mBuffer2[0];
+
+ char line[4096];
+ GetNextLine(buffer,line);
+ if ('O' == line[0]) {
+ GetNextLine(buffer,line); // skip the 'OFF' line
+ }
+
+ const char* sz = line; SkipSpaces(&sz);
+ const unsigned int numVertices = strtol10(sz,&sz);SkipSpaces(&sz);
+ const unsigned int numFaces = strtol10(sz,&sz);
+
+ pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = 1 ];
+ aiMesh* mesh = pScene->mMeshes[0] = new aiMesh();
+ aiFace* faces = mesh->mFaces = new aiFace [mesh->mNumFaces = numFaces];
+
+ std::vector<aiVector3D> tempPositions(numVertices);
+
+ // now read all vertex lines
+ for (unsigned int i = 0; i< numVertices;++i)
+ {
+ if (!GetNextLine(buffer,line))
+ {
+ DefaultLogger::get()->error("OFF: The number of verts in the header is incorrect");
+ break;
+ }
+ aiVector3D& v = tempPositions[i];
+
+ sz = line; SkipSpaces(&sz);
+ sz = fast_atof_move(sz,(float&)v.x); SkipSpaces(&sz);
+ sz = fast_atof_move(sz,(float&)v.y); SkipSpaces(&sz);
+ fast_atof_move(sz,(float&)v.z);
+ }
+
+
+ // First find out how many vertices we'll need
+ const char* old = buffer;
+ for (unsigned int i = 0; i< mesh->mNumFaces;++i)
+ {
+ if (!GetNextLine(buffer,line))
+ {
+ DefaultLogger::get()->error("OFF: The number of faces in the header is incorrect");
+ break;
+ }
+ sz = line;SkipSpaces(&sz);
+ if (!(faces->mNumIndices = strtol10(sz,&sz)) || faces->mNumIndices > 9)
+ {
+ DefaultLogger::get()->error("OFF: Faces with zero indices aren't allowed");
+ --mesh->mNumFaces;
+ continue;
+ }
+ mesh->mNumVertices += faces->mNumIndices;
+ ++faces;
+ }
+
+ if (!mesh->mNumVertices)
+ throw DeadlyImportError("OFF: There are no valid faces");
+
+ // allocate storage for the output vertices
+ aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+
+ // second: now parse all face indices
+ buffer = old;faces = mesh->mFaces;
+ for (unsigned int i = 0, p = 0; i< mesh->mNumFaces;)
+ {
+ if (!GetNextLine(buffer,line))break;
+
+ unsigned int idx;
+ sz = line;SkipSpaces(&sz);
+ if (!(idx = strtol10(sz,&sz)) || idx > 9)
+ continue;
+
+ faces->mIndices = new unsigned int [faces->mNumIndices];
+ for (unsigned int m = 0; m < faces->mNumIndices;++m)
+ {
+ SkipSpaces(&sz);
+ if ((idx = strtol10(sz,&sz)) >= numVertices)
+ {
+ DefaultLogger::get()->error("OFF: Vertex index is out of range");
+ idx = numVertices-1;
+ }
+ faces->mIndices[m] = p++;
+ *verts++ = tempPositions[idx];
+ }
+ ++i;
+ ++faces;
+ }
+
+ // generate the output node graph
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<OFFRoot>");
+ pScene->mRootNode->mMeshes = new unsigned int [pScene->mRootNode->mNumMeshes = 1];
+ pScene->mRootNode->mMeshes[0] = 0;
+
+ // generate a default material
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = 1];
+ MaterialHelper* pcMat = new MaterialHelper();
+
+ aiColor4D clr(0.6f,0.6f,0.6f,1.0f);
+ pcMat->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+ pScene->mMaterials[0] = pcMat;
+
+ const int twosided =1;
+ pcMat->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED);
+}
+
+#endif // !! ASSIMP_BUILD_NO_OFF_IMPORTER
diff --git a/3rdparty/assimp/code/OFFLoader.h b/3rdparty/assimp/code/OFFLoader.h
new file mode 100644
index 000000000..995e0e3a8
--- /dev/null
+++ b/3rdparty/assimp/code/OFFLoader.h
@@ -0,0 +1,96 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 OFFLoader.h
+ * @brief Declaration of the OFF importer class.
+ */
+#ifndef AI_OFFLOADER_H_INCLUDED
+#define AI_OFFLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+#include <vector>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Importer class for the Object File Format (.off)
+*/
+class OFFImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ OFFImporter();
+
+ /** Destructor, private as well */
+ ~OFFImporter();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_IN
diff --git a/3rdparty/assimp/code/ObjFileData.h b/3rdparty/assimp/code/ObjFileData.h
new file mode 100644
index 000000000..37f91f101
--- /dev/null
+++ b/3rdparty/assimp/code/ObjFileData.h
@@ -0,0 +1,324 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 OBJ_FILEDATA_H_INC
+#define OBJ_FILEDATA_H_INC
+
+#include <vector>
+#include <map>
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+
+namespace Assimp
+{
+
+namespace ObjFile
+{
+// ------------------------------------------------------------------------------------------------
+struct Object;
+struct Face;
+struct Material;
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Face
+//! \brief Datastructure for a simple obj-face, descripes discredisation and materials
+struct Face
+{
+ typedef std::vector<unsigned int> IndexArray;
+
+ //! Primitive type
+ int m_PrimitiveType;
+ //! Vertex indices
+ IndexArray *m_pVertices;
+ //! Normal indices
+ IndexArray *m_pNormals;
+ //! Texture coordinates indices
+ IndexArray *m_pTexturCoords;
+ //! Pointer to assigned material
+ Material *m_pMaterial;
+
+ //! \brief Default constructor
+ //! \param pVertices Pointer to assigned vertex indexbuffer
+ //! \param pNormals Pointer to assigned normals indexbuffer
+ //! \param pTexCoords Pointer to assigned texture indexbuffer
+ Face( std::vector<unsigned int> *pVertices,
+ std::vector<unsigned int> *pNormals,
+ std::vector<unsigned int> *pTexCoords) :
+ m_PrimitiveType( 2 ),
+ m_pVertices( pVertices ),
+ m_pNormals( pNormals ),
+ m_pTexturCoords( pTexCoords ),
+ m_pMaterial( 0L )
+ {
+ // empty
+ }
+
+ //! \brief Destructor
+ ~Face()
+ {
+ delete m_pVertices;
+ m_pVertices = NULL;
+ delete m_pNormals;
+ m_pNormals = NULL;
+ delete m_pTexturCoords;
+ m_pTexturCoords = NULL;
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Object
+//! \brief Stores all objects of an objfile object definition
+struct Object
+{
+ enum ObjectType
+ {
+ ObjType,
+ GroupType
+ };
+
+ //! Object name
+ std::string m_strObjName;
+ //! Transformation matrix, stored in OpenGL format
+ aiMatrix4x4 m_Transformation;
+ //! All sub-objects referenced by this object
+ std::vector<Object*> m_SubObjects;
+ /// Assigned meshes
+ std::vector<unsigned int> m_Meshes;
+
+ //! \brief Default constructor
+ Object() :
+ m_strObjName("")
+ {
+ // empty
+ }
+
+ //! \brief Destructor
+ ~Object()
+ {
+ for (std::vector<Object*>::iterator it = m_SubObjects.begin();
+ it != m_SubObjects.end(); ++it)
+ {
+ delete *it;
+ }
+ m_SubObjects.clear();
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Material
+//! \brief Data structure to store all material specific data
+struct Material
+{
+ //! Name of material description
+ aiString MaterialName;
+
+ //! Texture names
+ aiString texture;
+ aiString textureSpecular;
+ aiString textureAmbient;
+ aiString textureBump;
+ aiString textureSpecularity;
+ aiString textureOpacity;
+
+ //! Ambient color
+ aiColor3D ambient;
+ //! Diffuse color
+ aiColor3D diffuse;
+ //! Speculao color
+ aiColor3D specular;
+ //! Alpha value
+ float alpha;
+ //! Shineness factor
+ float shineness;
+ //! Illumination model
+ int illumination_model;
+ //! Index of refraction
+ float ior;
+
+ //! Constructor
+ Material()
+ : diffuse (0.6f,0.6f,0.6f)
+ , alpha (1.f)
+ , shineness (0.0f)
+ , illumination_model (1)
+ , ior (1.f)
+ {
+ // empty
+ }
+
+ // Destructor
+ ~Material()
+ {
+ // empty
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Mesh
+//! \brief Data structure to store a mesh
+struct Mesh
+{
+ static const unsigned int NoMaterial = 999999999;
+
+ /// Array with pointer to all stored faces
+ std::vector<Face*> m_Faces;
+ /// Assigned material
+ Material *m_pMaterial;
+ /// Number of stored indices.
+ unsigned int m_uiNumIndices;
+ /// Number of UV
+ unsigned int m_uiUVCoordinates[ AI_MAX_NUMBER_OF_TEXTURECOORDS ];
+ /// Material index.
+ unsigned int m_uiMaterialIndex;
+ /// True, if normals are stored.
+ bool m_hasNormals;
+ /// Constructor
+ Mesh() :
+ m_pMaterial(NULL),
+ m_uiNumIndices(0),
+ m_uiMaterialIndex( NoMaterial ),
+ m_hasNormals(false)
+ {
+ memset(m_uiUVCoordinates, 0, sizeof( unsigned int ) * AI_MAX_NUMBER_OF_TEXTURECOORDS);
+ }
+
+ /// Destructor
+ ~Mesh()
+ {
+ for (std::vector<Face*>::iterator it = m_Faces.begin();
+ it != m_Faces.end(); ++it)
+ {
+ delete *it;
+ }
+
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+//! \struct Model
+//! \brief Data structure to store all obj-specific model datas
+struct Model
+{
+ typedef std::map<std::string*, std::vector<unsigned int>* > GroupMap;
+ typedef std::map<std::string*, std::vector<unsigned int>* >::iterator GroupMapIt;
+ typedef std::map<std::string*, std::vector<unsigned int>* >::const_iterator ConstGroupMapIt;
+
+ //! Model name
+ std::string m_ModelName;
+ //! List ob assigned objects
+ std::vector<Object*> m_Objects;
+ //! Pointer to current object
+ ObjFile::Object *m_pCurrent;
+ //! Pointer to current material
+ ObjFile::Material *m_pCurrentMaterial;
+ //! Pointer to default material
+ ObjFile::Material *m_pDefaultMaterial;
+ //! Vector with all generated materials
+ std::vector<std::string> m_MaterialLib;
+ //! Vector with all generated group
+ std::vector<std::string> m_GroupLib;
+ //! Vector with all generated vertices
+ std::vector<aiVector3D> m_Vertices;
+ //! vector with all generated normals
+ std::vector<aiVector3D> m_Normals;
+ //! Groupmap
+ GroupMap m_Groups;
+ //! Group to face id assignment
+ std::vector<unsigned int> *m_pGroupFaceIDs;
+ //! Active group
+ std::string m_strActiveGroup;
+ //! Vector with generated texture coordinates
+ std::vector<aiVector2D> m_TextureCoord;
+ //! Current mesh instance
+ Mesh *m_pCurrentMesh;
+ //! Vector with stored meshes
+ std::vector<Mesh*> m_Meshes;
+ //! Material map
+ std::map<std::string, Material*> m_MaterialMap;
+
+
+ //! \brief Default constructor
+ Model() :
+ m_ModelName(""),
+ m_pCurrent(NULL),
+ m_pCurrentMaterial(NULL),
+ m_pDefaultMaterial(NULL),
+ m_strActiveGroup(""),
+ m_pCurrentMesh(NULL)
+ {
+ // empty
+ }
+
+ //! \brief Destructor
+ ~Model()
+ {
+ // Clear all stored object instances
+ for (std::vector<Object*>::iterator it = m_Objects.begin();
+ it != m_Objects.end(); ++it)
+ {
+ delete *it;
+ }
+ m_Objects.clear();
+
+ // Clear all stored mesh instances
+ for (std::vector<Mesh*>::iterator it = m_Meshes.begin();
+ it != m_Meshes.end(); ++it)
+ {
+ delete *it;
+ }
+
+ m_Meshes.clear();
+
+ for (GroupMapIt it = m_Groups.begin();
+ it != m_Groups.end(); ++it)
+ {
+ delete it->second;
+ }
+
+ m_Groups.clear();
+ }
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace ObjFile
+} // Namespace Assimp
+
+#endif
diff --git a/3rdparty/assimp/code/ObjFileImporter.cpp b/3rdparty/assimp/code/ObjFileImporter.cpp
new file mode 100644
index 000000000..87030ea2f
--- /dev/null
+++ b/3rdparty/assimp/code/ObjFileImporter.cpp
@@ -0,0 +1,521 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
+
+#include "DefaultIOSystem.h"
+#include "ObjFileImporter.h"
+#include "ObjFileParser.h"
+#include "ObjFileData.h"
+
+namespace Assimp {
+
+using namespace std;
+
+// ------------------------------------------------------------------------------------------------
+// Default constructor
+ObjFileImporter::ObjFileImporter() :
+ m_Buffer(),
+ m_pRootObject( NULL ),
+ m_strAbsPath( "" )
+{
+ DefaultIOSystem io;
+ m_strAbsPath = io.getOsSeparator();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor.
+ObjFileImporter::~ObjFileImporter()
+{
+ // Release root object instance
+ if (NULL != m_pRootObject)
+ {
+ delete m_pRootObject;
+ m_pRootObject = NULL;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns true, if file is an obj file.
+bool ObjFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler , bool checkSig ) const
+{
+ if (!checkSig) //Check File Extension
+ {
+ return SimpleExtensionCheck(pFile,"obj");
+ }
+ else //Check file Header
+ {
+ const char* tokens[] = {"mtllib","usemtl","vt ","vn ","o "};
+ return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 5);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Obj-file import implementation
+void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ DefaultIOSystem io;
+
+ // Read file into memory
+ const std::string mode = "rb";
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, mode));
+ if (NULL == file.get())
+ throw DeadlyImportError( "Failed to open file " + pFile + ".");
+
+ // Get the filesize and vaslidate it, throwing an exception when failes
+ size_t fileSize = file->FileSize();
+ if ( fileSize < 16)
+ throw DeadlyImportError( "OBJ-file is too small.");
+
+ // Allocate buffer and read file into it
+ TextFileToBuffer(file.get(),m_Buffer);
+
+ // Get the model name
+ std::string strModelName;
+ std::string::size_type pos = pFile.find_last_of( "\\/" );
+ if ( pos != std::string::npos )
+ {
+ strModelName = pFile.substr(pos+1, pFile.size() - pos - 1);
+ }
+ else
+ {
+ strModelName = pFile;
+ }
+
+ // parse the file into a temporary representation
+ ObjFileParser parser(m_Buffer, strModelName, pIOHandler);
+
+ // And create the proper return structures out of it
+ CreateDataFromImport(parser.GetModel(), pScene);
+
+ // Clean up allocated storage for the next import
+ m_Buffer.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Create the data from parsed obj-file
+void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene)
+{
+ if (0L == pModel)
+ return;
+
+ // Create the root node of the scene
+ pScene->mRootNode = new aiNode;
+ if ( !pModel->m_ModelName.empty() )
+ {
+ // Set the name of the scene
+ pScene->mRootNode->mName.Set(pModel->m_ModelName);
+ }
+ else
+ {
+ // This is an error, so break down the application
+ ai_assert(false);
+ }
+
+ // Create nodes for the whole scene
+ std::vector<aiMesh*> MeshArray;
+ for (size_t index = 0; index < pModel->m_Objects.size(); index++)
+ {
+ createNodes(pModel, pModel->m_Objects[ index ], index, pScene->mRootNode, pScene, MeshArray);
+ }
+
+ // Create mesh pointer buffer for this scene
+ if (pScene->mNumMeshes > 0)
+ {
+ pScene->mMeshes = new aiMesh*[ MeshArray.size() ];
+ for (size_t index =0; index < MeshArray.size(); index++)
+ {
+ pScene->mMeshes [ index ] = MeshArray[ index ];
+ }
+ }
+
+ // Create all materials
+ createMaterials( pModel, pScene );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates all nodes of the model
+aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pObject,
+ unsigned int /*uiMeshIndex*/,
+ aiNode *pParent, aiScene* pScene,
+ std::vector<aiMesh*> &MeshArray )
+{
+ ai_assert( NULL != pModel );
+ if ( NULL == pObject )
+ return NULL;
+
+ // Store older mesh size to be able to computate mesh offsets for new mesh instances
+ const size_t oldMeshSize = MeshArray.size();
+ aiNode *pNode = new aiNode;
+ pNode->mName = aiString(pObject->m_strObjName);
+
+ if (pParent != NULL)
+ appendChildToParentNode(pParent, pNode);
+
+
+ for ( unsigned int i=0; i< pObject->m_Meshes.size(); i++ )
+ {
+ unsigned int meshId = pObject->m_Meshes[ i ];
+ aiMesh *pMesh = new aiMesh;
+ createTopology( pModel, pObject, meshId, pMesh );
+ if ( pMesh->mNumVertices > 0 )
+ {
+ MeshArray.push_back( pMesh );
+ }
+ else
+ {
+ delete pMesh;
+ }
+ }
+
+ // Create all nodes from the sub-objects stored in the current object
+ if ( !pObject->m_SubObjects.empty() )
+ {
+ size_t numChilds = pObject->m_SubObjects.size();
+ pNode->mNumChildren = static_cast<unsigned int>( numChilds );
+ pNode->mChildren = new aiNode*[ numChilds ];
+ pNode->mNumMeshes = 1;
+ pNode->mMeshes = new unsigned int[ 1 ];
+ }
+
+ // Set mesh instances into scene- and node-instances
+ const size_t meshSizeDiff = MeshArray.size()- oldMeshSize;
+ if ( meshSizeDiff > 0 )
+ {
+ pNode->mMeshes = new unsigned int[ meshSizeDiff ];
+ pNode->mNumMeshes = static_cast<unsigned int>( meshSizeDiff );
+ size_t index = 0;
+ for (size_t i = oldMeshSize; i < MeshArray.size(); i++)
+ {
+ pNode->mMeshes[ index ] = pScene->mNumMeshes;
+ pScene->mNumMeshes++;
+ index++;
+ }
+ }
+
+ return pNode;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Create topology data
+void ObjFileImporter::createTopology(const ObjFile::Model* pModel,
+ const ObjFile::Object* pData,
+ unsigned int uiMeshIndex,
+ aiMesh* pMesh )
+{
+ // Checking preconditions
+ ai_assert( NULL != pModel );
+ if (NULL == pData)
+ return;
+
+ // Create faces
+ ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ];
+ ai_assert( NULL != pObjMesh );
+ pMesh->mNumFaces = static_cast<unsigned int>( pObjMesh->m_Faces.size() );
+ if ( pMesh->mNumFaces > 0 )
+ {
+ pMesh->mFaces = new aiFace[ pMesh->mNumFaces ];
+ if ( pObjMesh->m_uiMaterialIndex != ObjFile::Mesh::NoMaterial )
+ {
+ pMesh->mMaterialIndex = pObjMesh->m_uiMaterialIndex;
+ }
+
+ // Copy all data from all stored meshes
+ for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++)
+ {
+ aiFace *pFace = &pMesh->mFaces[ index ];
+ const unsigned int uiNumIndices = (unsigned int) pObjMesh->m_Faces[ index ]->m_pVertices->size();
+ pFace->mNumIndices = (unsigned int) uiNumIndices;
+ if (pFace->mNumIndices > 0)
+ {
+ pFace->mIndices = new unsigned int[ uiNumIndices ];
+ ObjFile::Face::IndexArray *pIndexArray = pObjMesh->m_Faces[ index ]->m_pVertices;
+ ai_assert ( NULL != pIndexArray );
+ for ( size_t a=0; a<pFace->mNumIndices; a++ )
+ {
+ pFace->mIndices[ a ] = pIndexArray->at( a );
+ }
+ }
+ else
+ {
+ pFace->mIndices = NULL;
+ }
+ }
+ }
+
+ // Create mesh vertices
+ createVertexArray(pModel, pData, uiMeshIndex, pMesh);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates a vertex array
+void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
+ const ObjFile::Object* pCurrentObject,
+ unsigned int uiMeshIndex,
+ aiMesh* pMesh)
+{
+ // Checking preconditions
+ ai_assert( NULL != pCurrentObject );
+
+ // Break, if no faces are stored in object
+ if ( pCurrentObject->m_Meshes.empty() )
+ return;
+
+ // Get current mesh
+ ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ];
+ if ( NULL == pObjMesh || pObjMesh->m_uiNumIndices < 1)
+ return;
+
+ // Copy vertices of this mesh instance
+ pMesh->mNumVertices = (unsigned int) pObjMesh->m_uiNumIndices;
+ pMesh->mVertices = new aiVector3D[ pMesh->mNumVertices ];
+
+ // Allocate buffer for normal vectors
+ if ( !pModel->m_Normals.empty() && pObjMesh->m_hasNormals )
+ pMesh->mNormals = new aiVector3D[ pMesh->mNumVertices ];
+
+ // Allocate buffer for texture coordinates
+ if ( !pModel->m_TextureCoord.empty() && pObjMesh->m_uiUVCoordinates[0] )
+ {
+ pMesh->mNumUVComponents[ 0 ] = 2;
+ pMesh->mTextureCoords[ 0 ] = new aiVector3D[ pMesh->mNumVertices ];
+ }
+
+ // Copy vertices, normals and textures into aiMesh instance
+ unsigned int newIndex = 0;
+ for ( size_t index=0; index < pObjMesh->m_Faces.size(); index++ )
+ {
+ // Get destination face
+ aiFace *pDestFace = &pMesh->mFaces[ index ];
+
+ // Get source face
+ ObjFile::Face *pSourceFace = pObjMesh->m_Faces[ index ];
+
+ // Copy all index arrays
+ for ( size_t vertexIndex = 0; vertexIndex < pSourceFace->m_pVertices->size(); vertexIndex++ )
+ {
+ const unsigned int vertex = pSourceFace->m_pVertices->at( vertexIndex );
+ ai_assert( vertex < pModel->m_Vertices.size() );
+ pMesh->mVertices[ newIndex ] = pModel->m_Vertices[ vertex ];
+
+ // Copy all normals
+ if ( !pSourceFace->m_pNormals->empty() )
+ {
+ const unsigned int normal = pSourceFace->m_pNormals->at( vertexIndex );
+ ai_assert( normal < pModel->m_Normals.size() );
+ pMesh->mNormals[ newIndex ] = pModel->m_Normals[ normal ];
+ }
+
+ // Copy all texture coordinates
+ if ( !pModel->m_TextureCoord.empty() )
+ {
+ if ( !pSourceFace->m_pTexturCoords->empty() )
+ {
+ const unsigned int tex = pSourceFace->m_pTexturCoords->at( vertexIndex );
+ ai_assert( tex < pModel->m_TextureCoord.size() );
+ for ( size_t i=0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; i++ )
+ {
+ if ( pMesh->mNumUVComponents[ i ] > 0 )
+ {
+ aiVector2D coord2d = pModel->m_TextureCoord[ tex ];
+ pMesh->mTextureCoords[ i ][ newIndex ] = aiVector3D( coord2d.x, coord2d.y, 0.0 );
+ }
+ }
+ }
+ }
+
+ ai_assert( pMesh->mNumVertices > newIndex );
+ pDestFace->mIndices[ vertexIndex ] = newIndex;
+ ++newIndex;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts all stored meshes
+void ObjFileImporter::countObjects(const std::vector<ObjFile::Object*> &rObjects, int &iNumMeshes)
+{
+ iNumMeshes = 0;
+ if ( rObjects.empty() )
+ return;
+
+ iNumMeshes += static_cast<unsigned int>( rObjects.size() );
+ for (std::vector<ObjFile::Object*>::const_iterator it = rObjects.begin();
+ it != rObjects.end();
+ ++it)
+ {
+ if (!(*it)->m_SubObjects.empty())
+ {
+ countObjects((*it)->m_SubObjects, iNumMeshes);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the material
+void ObjFileImporter::createMaterials(const ObjFile::Model* pModel, aiScene* pScene )
+{
+ ai_assert( NULL != pScene );
+ if ( NULL == pScene )
+ return;
+
+ const unsigned int numMaterials = (unsigned int) pModel->m_MaterialLib.size();
+ pScene->mNumMaterials = 0;
+ if ( pModel->m_MaterialLib.empty() )
+ return;
+
+ pScene->mMaterials = new aiMaterial*[ numMaterials ];
+ for ( unsigned int matIndex = 0; matIndex < numMaterials; matIndex++ )
+ {
+ Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+
+ // Store material name
+ std::map<std::string, ObjFile::Material*>::const_iterator it;
+ it = pModel->m_MaterialMap.find( pModel->m_MaterialLib[ matIndex ] );
+
+ // No material found, use the default material
+ if ( pModel->m_MaterialMap.end() == it )
+ continue;
+
+ ObjFile::Material *pCurrentMaterial = (*it).second;
+ mat->AddProperty( &pCurrentMaterial->MaterialName, AI_MATKEY_NAME );
+
+ // convert illumination model
+ int sm = 0;
+ switch (pCurrentMaterial->illumination_model)
+ {
+ case 0:
+ sm = aiShadingMode_NoShading;
+ break;
+ case 1:
+ sm = aiShadingMode_Gouraud;
+ break;
+ case 2:
+ sm = aiShadingMode_Phong;
+ break;
+ default:
+ sm = aiShadingMode_Gouraud;
+ DefaultLogger::get()->error("OBJ/MTL: Unexpected illumination model (0-2 recognized)");
+ }
+ mat->AddProperty<int>( &sm, 1, AI_MATKEY_SHADING_MODEL);
+
+ // multiplying the specular exponent with 2 seems to yield better results
+ pCurrentMaterial->shineness *= 4.f;
+
+ // Adding material colors
+ mat->AddProperty( &pCurrentMaterial->ambient, 1, AI_MATKEY_COLOR_AMBIENT );
+ mat->AddProperty( &pCurrentMaterial->diffuse, 1, AI_MATKEY_COLOR_DIFFUSE );
+ mat->AddProperty( &pCurrentMaterial->specular, 1, AI_MATKEY_COLOR_SPECULAR );
+ mat->AddProperty( &pCurrentMaterial->shineness, 1, AI_MATKEY_SHININESS );
+ mat->AddProperty( &pCurrentMaterial->alpha, 1, AI_MATKEY_OPACITY );
+
+ // Adding refraction index
+ mat->AddProperty( &pCurrentMaterial->ior, 1, AI_MATKEY_REFRACTI );
+
+ // Adding textures
+ if ( 0 != pCurrentMaterial->texture.length )
+ mat->AddProperty( &pCurrentMaterial->texture, AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ if ( 0 != pCurrentMaterial->textureAmbient.length )
+ mat->AddProperty( &pCurrentMaterial->textureAmbient, AI_MATKEY_TEXTURE_AMBIENT(0));
+
+ if ( 0 != pCurrentMaterial->textureSpecular.length )
+ mat->AddProperty( &pCurrentMaterial->textureSpecular, AI_MATKEY_TEXTURE_SPECULAR(0));
+
+ if ( 0 != pCurrentMaterial->textureBump.length )
+ mat->AddProperty( &pCurrentMaterial->textureBump, AI_MATKEY_TEXTURE_HEIGHT(0));
+
+ if ( 0 != pCurrentMaterial->textureOpacity.length )
+ mat->AddProperty( &pCurrentMaterial->textureOpacity, AI_MATKEY_TEXTURE_OPACITY(0));
+
+ if ( 0 != pCurrentMaterial->textureSpecularity.length )
+ mat->AddProperty( &pCurrentMaterial->textureSpecularity, AI_MATKEY_TEXTURE_SHININESS(0));
+
+ // Store material property info in material array in scene
+ pScene->mMaterials[ pScene->mNumMaterials ] = mat;
+ pScene->mNumMaterials++;
+ }
+
+ // Test number of created materials.
+ ai_assert( pScene->mNumMaterials == numMaterials );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Appends this node to the parent node
+void ObjFileImporter::appendChildToParentNode(aiNode *pParent, aiNode *pChild)
+{
+ // Checking preconditions
+ ai_assert( NULL != pParent );
+ ai_assert( NULL != pChild );
+
+ // Assign parent to child
+ pChild->mParent = pParent;
+ size_t sNumChildren = 0;
+
+ // If already children was assigned to the parent node, store them in a
+ std::vector<aiNode*> temp;
+ if (pParent->mChildren != NULL)
+ {
+ sNumChildren = pParent->mNumChildren;
+ ai_assert( 0 != sNumChildren );
+ for (size_t index = 0; index < pParent->mNumChildren; index++)
+ {
+ temp.push_back(pParent->mChildren [ index ] );
+ }
+ delete [] pParent->mChildren;
+ }
+
+ // Copy node instances into parent node
+ pParent->mNumChildren++;
+ pParent->mChildren = new aiNode*[ pParent->mNumChildren ];
+ for (size_t index = 0; index < pParent->mNumChildren-1; index++)
+ {
+ pParent->mChildren[ index ] = temp [ index ];
+ }
+ pParent->mChildren[ pParent->mNumChildren-1 ] = pChild;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
diff --git a/3rdparty/assimp/code/ObjFileImporter.h b/3rdparty/assimp/code/ObjFileImporter.h
new file mode 100644
index 000000000..10f3cf0bb
--- /dev/null
+++ b/3rdparty/assimp/code/ObjFileImporter.h
@@ -0,0 +1,136 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 OBJ_FILE_IMPORTER_H_INC
+#define OBJ_FILE_IMPORTER_H_INC
+
+#include "BaseImporter.h"
+#include <vector>
+
+struct aiMesh;
+struct aiNode;
+
+namespace Assimp
+{
+
+namespace ObjFile
+{
+struct Object;
+struct Model;
+}
+
+// ------------------------------------------------------------------------------------------------
+/// \class ObjFileImporter
+/// \brief Imports a waveform obj file
+// ------------------------------------------------------------------------------------------------
+class ObjFileImporter :
+ BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /// \brief Default constructor
+ ObjFileImporter();
+
+ /// \brief Destructor
+ ~ObjFileImporter();
+
+public:
+ /// \brief Returns whether the class can handle the format of the given file.
+ /// \remark See BaseImporter::CanRead() for details.
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+
+private:
+
+ //! \brief Appends the supported extention.
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ //! \brief File import implementation.
+ void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+ //! \brief Create the data from imported content.
+ void CreateDataFromImport(const ObjFile::Model* pModel, aiScene* pScene);
+
+ //! \brief Creates all nodes stored in imported content.
+ aiNode *createNodes(const ObjFile::Model* pModel, const ObjFile::Object* pData, unsigned int uiMeshIndex,
+ aiNode *pParent, aiScene* pScene, std::vector<aiMesh*> &MeshArray);
+
+ //! \brief Creates topology data like faces and meshes for the geometry.
+ void createTopology(const ObjFile::Model* pModel, const ObjFile::Object* pData,
+ unsigned int uiMeshIndex, aiMesh* pMesh);
+
+ //! \brief Creates vertices from model.
+ void createVertexArray(const ObjFile::Model* pModel, const ObjFile::Object* pCurrentObject,
+ unsigned int uiMeshIndex, aiMesh* pMesh);
+
+ //! \brief Object counter helper method.
+ void countObjects(const std::vector<ObjFile::Object*> &rObjects, int &iNumMeshes);
+
+ //! \brief Material creation.
+ void createMaterials(const ObjFile::Model* pModel, aiScene* pScene);
+
+ //! \brief Appends a child node to a parentnode and updates the datastructures.
+ void appendChildToParentNode(aiNode *pParent, aiNode *pChild);
+
+ //! \brief TODO!
+ void createAnimations();
+
+private:
+ //! Data buffer
+ std::vector<char> m_Buffer;
+ //! Pointer to root object instance
+ ObjFile::Object *m_pRootObject;
+ //! Absolute pathname of model in filesystem
+ std::string m_strAbsPath;
+};
+
+// ------------------------------------------------------------------------------------------------
+//
+inline void ObjFileImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("obj");
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif
diff --git a/3rdparty/assimp/code/ObjFileMtlImporter.cpp b/3rdparty/assimp/code/ObjFileMtlImporter.cpp
new file mode 100644
index 000000000..67863dc01
--- /dev/null
+++ b/3rdparty/assimp/code/ObjFileMtlImporter.cpp
@@ -0,0 +1,294 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
+
+#include "ObjFileMtlImporter.h"
+#include "ObjTools.h"
+#include "ObjFileData.h"
+#include "fast_atof.h"
+
+namespace Assimp {
+
+// -------------------------------------------------------------------
+// Constructor
+ObjFileMtlImporter::ObjFileMtlImporter( std::vector<char> &buffer,
+ const std::string & /*strAbsPath*/,
+ ObjFile::Model *pModel ) :
+ m_DataIt( buffer.begin() ),
+ m_DataItEnd( buffer.end() ),
+ m_pModel( pModel ),
+ m_uiLine( 0 )
+{
+ ai_assert( NULL != m_pModel );
+ if ( NULL == m_pModel->m_pDefaultMaterial )
+ {
+ m_pModel->m_pDefaultMaterial = new ObjFile::Material;
+ m_pModel->m_pDefaultMaterial->MaterialName.Set( "default" );
+ }
+ load();
+}
+
+// -------------------------------------------------------------------
+// Destructor
+ObjFileMtlImporter::~ObjFileMtlImporter()
+{
+ // empty
+}
+
+// -------------------------------------------------------------------
+// Private copy constructor
+ObjFileMtlImporter::ObjFileMtlImporter(const ObjFileMtlImporter & /* rOther */ )
+{
+ // empty
+}
+
+// -------------------------------------------------------------------
+// Private copy constructor
+ObjFileMtlImporter &ObjFileMtlImporter::operator = ( const ObjFileMtlImporter & /*rOther */ )
+{
+ return *this;
+}
+
+// -------------------------------------------------------------------
+// Loads the material description
+void ObjFileMtlImporter::load()
+{
+ if ( m_DataIt == m_DataItEnd )
+ return;
+
+ while ( m_DataIt != m_DataItEnd )
+ {
+ switch (*m_DataIt)
+ {
+ case 'K':
+ {
+ ++m_DataIt;
+ if (*m_DataIt == 'a') // Ambient color
+ {
+ ++m_DataIt;
+ getColorRGBA( &m_pModel->m_pCurrentMaterial->ambient );
+ }
+ else if (*m_DataIt == 'd') // Diffuse color
+ {
+ ++m_DataIt;
+ getColorRGBA( &m_pModel->m_pCurrentMaterial->diffuse );
+ }
+ else if (*m_DataIt == 's')
+ {
+ ++m_DataIt;
+ getColorRGBA( &m_pModel->m_pCurrentMaterial->specular );
+ }
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+
+ case 'd': // Alpha value
+ {
+ ++m_DataIt;
+ getFloatValue( m_pModel->m_pCurrentMaterial->alpha );
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+
+ case 'N': // Shineness
+ {
+ ++m_DataIt;
+ switch(*m_DataIt)
+ {
+ case 's':
+ ++m_DataIt;
+ getFloatValue(m_pModel->m_pCurrentMaterial->shineness);
+ break;
+ case 'i': //Index Of refraction
+ ++m_DataIt;
+ getFloatValue(m_pModel->m_pCurrentMaterial->ior);
+ break;
+ }
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ break;
+ }
+ break;
+
+
+ case 'm': // Texture
+ case 'b': // quick'n'dirty - for 'bump' sections
+ {
+ getTexture();
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+
+ case 'n': // New material name
+ {
+ createMaterial();
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+
+ case 'i': // Illumination model
+ {
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ getIlluminationModel( m_pModel->m_pCurrentMaterial->illumination_model );
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+
+ default:
+ {
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+// Loads a color definition
+void ObjFileMtlImporter::getColorRGBA( aiColor3D *pColor )
+{
+ ai_assert( NULL != pColor );
+
+ float r, g, b;
+ m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, r );
+ pColor->r = r;
+
+ m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, g );
+ pColor->g = g;
+
+ m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, b );
+ pColor->b = b;
+}
+
+// -------------------------------------------------------------------
+// Loads the kind of illumination model.
+void ObjFileMtlImporter::getIlluminationModel( int &illum_model )
+{
+ m_DataIt = CopyNextWord<DataArrayIt>( m_DataIt, m_DataItEnd, m_buffer, BUFFERSIZE );
+ illum_model = atoi(m_buffer);
+}
+
+// -------------------------------------------------------------------
+// Loads a single float value.
+void ObjFileMtlImporter::getFloatValue( float &value )
+{
+ m_DataIt = CopyNextWord<DataArrayIt>( m_DataIt, m_DataItEnd, m_buffer, BUFFERSIZE );
+ value = (float) fast_atof(m_buffer);
+}
+
+// -------------------------------------------------------------------
+// Creates a material from loaded data.
+void ObjFileMtlImporter::createMaterial()
+{
+ std::string strName( "" );
+ m_DataIt = getName<DataArrayIt>( m_DataIt, m_DataItEnd, strName );
+ if ( m_DataItEnd == m_DataIt )
+ return;
+
+ std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strName );
+ if ( m_pModel->m_MaterialMap.end() == it)
+ {
+ // New Material created
+ m_pModel->m_pCurrentMaterial = new ObjFile::Material();
+ m_pModel->m_pCurrentMaterial->MaterialName.Set( strName );
+ m_pModel->m_MaterialLib.push_back( strName );
+ m_pModel->m_MaterialMap[ strName ] = m_pModel->m_pCurrentMaterial;
+ }
+ else
+ {
+ // Use older material
+ m_pModel->m_pCurrentMaterial = (*it).second;
+ }
+}
+
+// -------------------------------------------------------------------
+// Gets a texture name from data.
+void ObjFileMtlImporter::getTexture()
+{
+ aiString *out = NULL;
+
+ // FIXME: just a quick'n'dirty hack, consider cleanup later
+
+ // Diffuse texture
+ if (!ASSIMP_strincmp(&(*m_DataIt),"map_kd",6))
+ out = & m_pModel->m_pCurrentMaterial->texture;
+
+ // Ambient texture
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_ka",6))
+ out = & m_pModel->m_pCurrentMaterial->textureAmbient;
+
+ // Specular texture
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_ks",6))
+ out = & m_pModel->m_pCurrentMaterial->textureSpecular;
+
+ // Opacity texture
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_d",5))
+ out = & m_pModel->m_pCurrentMaterial->textureOpacity;
+
+ // Ambient texture
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_ka",6))
+ out = & m_pModel->m_pCurrentMaterial->textureAmbient;
+
+ // Bump texture
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_bump",8) || !ASSIMP_strincmp(&(*m_DataIt),"bump",4))
+ out = & m_pModel->m_pCurrentMaterial->textureBump;
+
+ // Specularity scaling (glossiness)
+ else if (!ASSIMP_strincmp(&(*m_DataIt),"map_ns",6))
+ out = & m_pModel->m_pCurrentMaterial->textureSpecularity;
+
+ else
+ {
+ DefaultLogger::get()->error("OBJ/MTL: Encountered unknown texture type");
+ return;
+ }
+
+ std::string strTexture;
+ m_DataIt = getName<DataArrayIt>( m_DataIt, m_DataItEnd, strTexture );
+ out->Set( strTexture );
+}
+
+// -------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
diff --git a/3rdparty/assimp/code/ObjFileMtlImporter.h b/3rdparty/assimp/code/ObjFileMtlImporter.h
new file mode 100644
index 000000000..8b83a0142
--- /dev/null
+++ b/3rdparty/assimp/code/ObjFileMtlImporter.h
@@ -0,0 +1,115 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 OBJFILEMTLIMPORTER_H_INC
+#define OBJFILEMTLIMPORTER_H_INC
+
+#include <vector>
+#include <string>
+
+struct aiColor3D;
+
+namespace Assimp
+{
+
+namespace ObjFile
+{
+struct Model;
+struct Material;
+
+}
+
+
+/**
+ * @class ObjFileMtlImporter
+ * @brief Loads the material description from a mtl file.
+ */
+class ObjFileMtlImporter
+{
+public:
+ static const size_t BUFFERSIZE = 2048;
+ typedef std::vector<char> DataArray;
+ typedef std::vector<char>::iterator DataArrayIt;
+ typedef std::vector<char>::const_iterator ConstDataArrayIt;
+
+public:
+ //! \brief Default constructor
+ ObjFileMtlImporter( std::vector<char> &buffer, const std::string &strAbsPath,
+ ObjFile::Model *pModel );
+
+ //! \brief DEstructor
+ ~ObjFileMtlImporter();
+
+private:
+ /// Copy constructor, empty.
+ ObjFileMtlImporter(const ObjFileMtlImporter &rOther);
+ /// \brief Assignment operator, returns only a reference of this instance.
+ ObjFileMtlImporter &operator = (const ObjFileMtlImporter &rOther);
+ /// Load the whole material description
+ void load();
+ /// Get color data.
+ void getColorRGBA( aiColor3D *pColor);
+ /// Get illumination model from loaded data
+ void getIlluminationModel( int &illum_model );
+ /// Gets a float value from data.
+ void getFloatValue( float &value );
+ /// Creates a new material from loaded data.
+ void createMaterial();
+ /// Get texture name from loaded data.
+ void getTexture();
+
+private:
+ //! Absolute pathname
+ std::string m_strAbsPath;
+ //! Data iterator showing to the current position in data buffer
+ DataArrayIt m_DataIt;
+ //! Data iterator to end of buffer
+ DataArrayIt m_DataItEnd;
+ //! USed model instance
+ ObjFile::Model *m_pModel;
+ //! Current line in file
+ unsigned int m_uiLine;
+ //! Helper buffer
+ char m_buffer[BUFFERSIZE];
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif
diff --git a/3rdparty/assimp/code/ObjFileParser.cpp b/3rdparty/assimp/code/ObjFileParser.cpp
new file mode 100644
index 000000000..98214ba48
--- /dev/null
+++ b/3rdparty/assimp/code/ObjFileParser.cpp
@@ -0,0 +1,664 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
+
+#include "ObjFileParser.h"
+#include "ObjFileMtlImporter.h"
+#include "ObjTools.h"
+#include "ObjFileData.h"
+#include "fast_atof.h"
+#include "../include/aiTypes.h"
+#include "DefaultIOSystem.h"
+
+namespace Assimp
+{
+
+// -------------------------------------------------------------------
+const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME;
+// fix: changed that to our standard default name
+
+// -------------------------------------------------------------------
+// Constructor with loaded data and directories.
+ObjFileParser::ObjFileParser(std::vector<char> &Data,const std::string &strModelName, IOSystem *io ) :
+ m_DataIt(Data.begin()),
+ m_DataItEnd(Data.end()),
+ m_pModel(NULL),
+ m_uiLine(0),
+ m_pIO( io )
+{
+ // Create the model instance to store all the data
+ m_pModel = new ObjFile::Model();
+ m_pModel->m_ModelName = strModelName;
+
+ m_pModel->m_pDefaultMaterial = new ObjFile::Material();
+ m_pModel->m_pDefaultMaterial->MaterialName.Set( DEFAULT_MATERIAL );
+ m_pModel->m_MaterialLib.push_back( DEFAULT_MATERIAL );
+ m_pModel->m_MaterialMap[ DEFAULT_MATERIAL ] = m_pModel->m_pDefaultMaterial;
+
+ // Start parsing the file
+ parseFile();
+}
+
+// -------------------------------------------------------------------
+// Destrcutor.
+ObjFileParser::~ObjFileParser()
+{
+ delete m_pModel->m_pDefaultMaterial;
+ m_pModel->m_pDefaultMaterial = NULL;
+
+ delete m_pModel;
+ m_pModel = NULL;
+}
+
+// -------------------------------------------------------------------
+// Returns a pointer to the model instance.
+ObjFile::Model *ObjFileParser::GetModel() const
+{
+ return m_pModel;
+}
+
+// -------------------------------------------------------------------
+// File parsing method.
+void ObjFileParser::parseFile()
+{
+ if (m_DataIt == m_DataItEnd)
+ return;
+
+ while (m_DataIt != m_DataItEnd)
+ {
+ switch (*m_DataIt)
+ {
+ case 'v': // Parse a vertex texture coordinate
+ {
+ ++m_DataIt;
+ if (*m_DataIt == ' ')
+ {
+ // Read in vertex definition
+ getVector3(m_pModel->m_Vertices);
+ }
+ else if (*m_DataIt == 't')
+ {
+ // Read in texture coordinate (2D)
+ ++m_DataIt;
+ getVector2(m_pModel->m_TextureCoord);
+ }
+ else if (*m_DataIt == 'n')
+ {
+ // Read in normal vector definition
+ ++m_DataIt;
+ getVector3( m_pModel->m_Normals );
+ }
+ }
+ break;
+
+ case 'f': // Parse a face
+ {
+ getFace();
+ }
+ break;
+
+ case '#': // Parse a comment
+ {
+ getComment();
+ }
+ break;
+
+ case 'u': // Parse a material desc. setter
+ {
+ getMaterialDesc();
+ }
+ break;
+
+ case 'm': // Parse a material library
+ {
+ getMaterialLib();
+ }
+ break;
+
+ case 'g': // Parse group name
+ {
+ getGroupName();
+ }
+ break;
+
+ case 's': // Parse group number
+ {
+ getGroupNumber();
+ }
+ break;
+
+ case 'o': // Parse object name
+ {
+ getObjectName();
+ }
+ break;
+
+ default:
+ {
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ }
+ break;
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+// Copy the next word in a temporary buffer
+void ObjFileParser::copyNextWord(char *pBuffer, size_t length)
+{
+ size_t index = 0;
+ m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+ while ( !isSeparator(*m_DataIt) && m_DataIt != m_DataItEnd )
+ {
+ pBuffer[index] = *m_DataIt;
+ index++;
+ if (index == length-1)
+ break;
+ ++m_DataIt;
+ }
+ pBuffer[index] = '\0';
+}
+
+// -------------------------------------------------------------------
+// Copy the next line into a temporary buffer
+void ObjFileParser::copyNextLine(char *pBuffer, size_t length)
+{
+ size_t index = 0;
+ while (m_DataIt != m_DataItEnd)
+ {
+ if (*m_DataIt == '\n' || *m_DataIt == '\r' || index == length-1)
+ break;
+
+ pBuffer[ index ] = *m_DataIt;
+ ++index;
+ ++m_DataIt;
+ }
+ pBuffer[ index ] = '\0';
+}
+
+// -------------------------------------------------------------------
+// Get values for a new 3D vector instance
+void ObjFileParser::getVector3(std::vector<aiVector3D> &point3d_array)
+{
+ float x, y, z;
+ copyNextWord(m_buffer, BUFFERSIZE);
+ x = (float) fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, BUFFERSIZE);
+ y = (float) fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, BUFFERSIZE);
+ z = (float) fast_atof(m_buffer);
+
+ point3d_array.push_back( aiVector3D( x, y, z ) );
+ //skipLine();
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Get values for a new 2D vector instance
+void ObjFileParser::getVector2( std::vector<aiVector2D> &point2d_array )
+{
+ float x, y;
+ copyNextWord(m_buffer, BUFFERSIZE);
+ x = (float) fast_atof(m_buffer);
+
+ copyNextWord(m_buffer, BUFFERSIZE);
+ y = (float) fast_atof(m_buffer);
+
+ point2d_array.push_back(aiVector2D(x, y));
+
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Get values for a new face instance
+void ObjFileParser::getFace()
+{
+ copyNextLine(m_buffer, BUFFERSIZE);
+ if (m_DataIt == m_DataItEnd)
+ return;
+
+ char *pPtr = m_buffer;
+ char *pEnd = &pPtr[BUFFERSIZE];
+ pPtr = getNextToken<char*>(pPtr, pEnd);
+ if (pPtr == '\0')
+ return;
+
+ std::vector<unsigned int> *pIndices = new std::vector<unsigned int>;
+ std::vector<unsigned int> *pTexID = new std::vector<unsigned int>;
+ std::vector<unsigned int> *pNormalID = new std::vector<unsigned int>;
+ bool hasNormal = false;
+
+ bool vt = (!m_pModel->m_TextureCoord.empty());
+ bool vn = (!m_pModel->m_Normals.empty());
+ int iStep = 0, iPos = 0;
+ while (pPtr != pEnd)
+ {
+ iStep = 1;
+ if (*pPtr == '\0')
+ break;
+
+ if (*pPtr=='\r')
+ break;
+
+ if (*pPtr=='/' )
+ {
+ if (iPos == 0)
+ {
+ //if there are no texturecoordinates in the obj file but normals
+ if (!vt && vn) {
+ iPos = 1;
+ iStep++;
+ }
+ }
+ iPos++;
+ }
+ else if ( isSeparator(*pPtr) )
+ {
+ iPos = 0;
+ }
+ else
+ {
+ //OBJ USES 1 Base ARRAYS!!!!
+ const int iVal = atoi( pPtr );
+ int tmp = iVal;
+ while ( ( tmp = tmp / 10 )!=0 )
+ ++iStep;
+
+ if ( iVal > 0 )
+ {
+ // Store parsed index
+ if ( 0 == iPos )
+ {
+ pIndices->push_back( iVal-1 );
+ }
+ else if ( 1 == iPos )
+ {
+ pTexID->push_back( iVal-1 );
+ }
+ else if ( 2 == iPos )
+ {
+ pNormalID->push_back( iVal-1 );
+ hasNormal = true;
+ }
+ else
+ {
+ reportErrorTokenInFace();
+ }
+ }
+ }
+ for ( int i=0; i<iStep; i++ )
+ ++pPtr;
+ }
+
+ ObjFile::Face *face = new ObjFile::Face( pIndices, pNormalID, pTexID );
+
+ // Set active material, if one set
+ if (NULL != m_pModel->m_pCurrentMaterial)
+ face->m_pMaterial = m_pModel->m_pCurrentMaterial;
+ else
+ face->m_pMaterial = m_pModel->m_pDefaultMaterial;
+
+ // Create a default object, if nothing there
+ if ( NULL == m_pModel->m_pCurrent )
+ createObject( "defaultobject" );
+
+ // Assign face to mesh
+ if ( NULL == m_pModel->m_pCurrentMesh )
+ {
+ createMesh();
+ }
+
+ // Store the face
+ m_pModel->m_pCurrentMesh->m_Faces.push_back( face );
+ m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int)face->m_pVertices->size();
+ m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += (unsigned int)face->m_pTexturCoords[0].size();
+ if ( !m_pModel->m_pCurrentMesh->m_hasNormals && hasNormal )
+ {
+ m_pModel->m_pCurrentMesh->m_hasNormals = true;
+ }
+ // Skip the rest of the line
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Get values for a new material description
+void ObjFileParser::getMaterialDesc()
+{
+ // Get next data for material data
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (m_DataIt == m_DataItEnd)
+ return;
+
+ char *pStart = &(*m_DataIt);
+ while ( !isSeparator(*m_DataIt) && m_DataIt != m_DataItEnd )
+ ++m_DataIt;
+
+ // Get name
+ std::string strName(pStart, &(*m_DataIt));
+ if ( strName.empty())
+ return;
+
+ // Search for material
+ std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strName );
+ if ( it == m_pModel->m_MaterialMap.end() )
+ {
+ // Not found, use default material
+ m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
+ }
+ else
+ {
+ // Found, using detected material
+ m_pModel->m_pCurrentMaterial = (*it).second;
+ if ( needsNewMesh( strName ))
+ {
+ createMesh();
+ }
+ m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strName );
+ }
+
+ // Skip rest of line
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Get a comment, values will be skipped
+void ObjFileParser::getComment()
+{
+ bool running = true;
+ while (running)
+ {
+ if ( '\n' == (*m_DataIt) || m_DataIt == m_DataItEnd )
+ {
+ ++m_DataIt;
+ break;
+ }
+ else
+ {
+ ++m_DataIt;
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+// Get material library from file.
+void ObjFileParser::getMaterialLib()
+{
+ // Translate tuple
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (m_DataIt == m_DataItEnd)
+ return;
+
+ char *pStart = &(*m_DataIt);
+ while (!isNewLine(*m_DataIt))
+ m_DataIt++;
+
+ // Check for existence
+ const std::string strMatName(pStart, &(*m_DataIt));
+ IOStream *pFile = m_pIO->Open(strMatName);
+
+ if (!pFile )
+ {
+ DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName);
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ return;
+ }
+
+ // Import material library data from file
+ std::vector<char> buffer;
+ BaseImporter::TextFileToBuffer(pFile,buffer);
+ m_pIO->Close( pFile );
+
+ // Importing the material library
+ ObjFileMtlImporter mtlImporter( buffer, strMatName, m_pModel );
+}
+
+// -------------------------------------------------------------------
+// Set a new material definition as the current material.
+void ObjFileParser::getNewMaterial()
+{
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if ( m_DataIt == m_DataItEnd )
+ return;
+
+ char *pStart = &(*m_DataIt);
+ std::string strMat( pStart, *m_DataIt );
+ while ( isSeparator( *m_DataIt ) )
+ m_DataIt++;
+ std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strMat );
+ if ( it == m_pModel->m_MaterialMap.end() )
+ {
+ // Show a warning, if material was not found
+ DefaultLogger::get()->warn("OBJ: Unsupported material requested: " + strMat);
+ m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial;
+ }
+ else
+ {
+ // Set new material
+ if ( needsNewMesh( strMat ) )
+ {
+ createMesh();
+ }
+ m_pModel->m_pCurrentMesh->m_uiMaterialIndex = getMaterialIndex( strMat );
+ }
+
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+int ObjFileParser::getMaterialIndex( const std::string &strMaterialName )
+{
+ int mat_index = -1;
+ if ( strMaterialName.empty() )
+ return mat_index;
+ for (size_t index = 0; index < m_pModel->m_MaterialLib.size(); ++index)
+ {
+ if ( strMaterialName == m_pModel->m_MaterialLib[ index ])
+ {
+ mat_index = (int)index;
+ break;
+ }
+ }
+ return mat_index;
+}
+
+// -------------------------------------------------------------------
+// Getter for a group name.
+void ObjFileParser::getGroupName()
+{
+ // Get next word from data buffer
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if ( isEndOfBuffer( m_DataIt, m_DataItEnd ) )
+ return;
+
+ // Store groupname in group library
+ char *pStart = &(*m_DataIt);
+ while ( !isSeparator(*m_DataIt) )
+ m_DataIt++;
+ std::string strGroupName(pStart, &(*m_DataIt));
+
+ // Change active group, if necessary
+ if ( m_pModel->m_strActiveGroup != strGroupName )
+ {
+ // Search for already existing entry
+ ObjFile::Model::ConstGroupMapIt it = m_pModel->m_Groups.find(&strGroupName);
+
+ // We are mapping groups into the object structure
+ createObject( strGroupName );
+
+ // New group name, creating a new entry
+ if (it == m_pModel->m_Groups.end())
+ {
+ std::vector<unsigned int> *pFaceIDArray = new std::vector<unsigned int>;
+ m_pModel->m_Groups[ &strGroupName ] = pFaceIDArray;
+ m_pModel->m_pGroupFaceIDs = (pFaceIDArray);
+ }
+ else
+ {
+ m_pModel->m_pGroupFaceIDs = (*it).second;
+ }
+ m_pModel->m_strActiveGroup = strGroupName;
+ }
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Not supported
+void ObjFileParser::getGroupNumber()
+{
+ // Not used
+
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+
+// -------------------------------------------------------------------
+// Stores values for a new object instance, name will be used to
+// identify it.
+void ObjFileParser::getObjectName()
+{
+ m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
+ if (m_DataIt == m_DataItEnd)
+ return;
+ char *pStart = &(*m_DataIt);
+ while ( !isSeparator( *m_DataIt ) )
+ ++m_DataIt;
+
+ std::string strObjectName(pStart, &(*m_DataIt));
+ if (!strObjectName.empty())
+ {
+ // Reset current object
+ m_pModel->m_pCurrent = NULL;
+
+ // Search for actual object
+ for (std::vector<ObjFile::Object*>::const_iterator it = m_pModel->m_Objects.begin();
+ it != m_pModel->m_Objects.end();
+ ++it)
+ {
+ if ((*it)->m_strObjName == strObjectName)
+ {
+ m_pModel->m_pCurrent = *it;
+ break;
+ }
+ }
+
+ // Allocate a new object, if current one was not found before
+ if ( NULL == m_pModel->m_pCurrent )
+ createObject(strObjectName);
+ }
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+}
+// -------------------------------------------------------------------
+// Creates a new object instance
+void ObjFileParser::createObject(const std::string &strObjectName)
+{
+ ai_assert( NULL != m_pModel );
+ //ai_assert( !strObjectName.empty() );
+
+ m_pModel->m_pCurrent = new ObjFile::Object;
+ m_pModel->m_pCurrent->m_strObjName = strObjectName;
+ m_pModel->m_Objects.push_back( m_pModel->m_pCurrent );
+
+ createMesh();
+
+ if ( m_pModel->m_pCurrentMaterial )
+ {
+ m_pModel->m_pCurrentMesh->m_uiMaterialIndex =
+ getMaterialIndex( m_pModel->m_pCurrentMaterial->MaterialName.data );
+ m_pModel->m_pCurrentMesh->m_pMaterial = m_pModel->m_pCurrentMaterial;
+ }
+}
+// -------------------------------------------------------------------
+// Creates a new mesh
+void ObjFileParser::createMesh()
+{
+ ai_assert( NULL != m_pModel );
+ m_pModel->m_pCurrentMesh = new ObjFile::Mesh;
+ m_pModel->m_Meshes.push_back( m_pModel->m_pCurrentMesh );
+ unsigned int meshId = m_pModel->m_Meshes.size()-1;
+ if ( NULL != m_pModel->m_pCurrent )
+ {
+ m_pModel->m_pCurrent->m_Meshes.push_back( meshId );
+ }
+ else
+ {
+ DefaultLogger::get()->error("OBJ: No object detected to attach a new mesh instance.");
+ }
+}
+
+// -------------------------------------------------------------------
+// Returns true, if a new mesh must be created.
+bool ObjFileParser::needsNewMesh( const std::string &rMaterialName )
+{
+ if (m_pModel->m_pCurrentMesh == 0)
+ {
+ // No mesh data yet
+ return true;
+ }
+ bool newMat = false;
+ int matIdx = getMaterialIndex( rMaterialName );
+ unsigned int curMatIdx = m_pModel->m_pCurrentMesh->m_uiMaterialIndex;
+ if ( curMatIdx != ObjFile::Mesh::NoMaterial || curMatIdx != (unsigned int)matIdx )
+ {
+ // New material -> only one material per mesh, so we need to create a new
+ // material
+ newMat = true;
+ }
+ return newMat;
+}
+
+// -------------------------------------------------------------------
+// Shows an error in parsing process.
+void ObjFileParser::reportErrorTokenInFace()
+{
+ m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+ DefaultLogger::get()->error("OBJ: Not supported token in face description detected");
+}
+
+// -------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OBJ_IMPORTER
diff --git a/3rdparty/assimp/code/ObjFileParser.h b/3rdparty/assimp/code/ObjFileParser.h
new file mode 100644
index 000000000..8ad34f68f
--- /dev/null
+++ b/3rdparty/assimp/code/ObjFileParser.h
@@ -0,0 +1,137 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 OBJ_FILEPARSER_H_INC
+#define OBJ_FILEPARSER_H_INC
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Assimp
+{
+
+namespace ObjFile
+{
+struct Model;
+struct Object;
+struct Material;
+struct Point3;
+struct Point2;
+}
+class ObjFileImporter;
+class IOSystem;
+
+/// \class ObjFileParser
+/// \brief Parser for a obj waveform file
+class ObjFileParser
+{
+public:
+ static const size_t BUFFERSIZE = 4096;
+ typedef std::vector<char> DataArray;
+ typedef std::vector<char>::iterator DataArrayIt;
+ typedef std::vector<char>::const_iterator ConstDataArrayIt;
+
+public:
+ /// \brief Constructor with data array.
+ ObjFileParser(std::vector<char> &Data,const std::string &strModelName, IOSystem* io);
+ /// \brief Destructor
+ ~ObjFileParser();
+ /// \brief Model getter.
+ ObjFile::Model *GetModel() const;
+
+private:
+ /// Parse the loadedfile
+ void parseFile();
+ /// Method to copy the new delimited word in the current line.
+ void copyNextWord(char *pBuffer, size_t length);
+ /// Method to copy the new line.
+ void copyNextLine(char *pBuffer, size_t length);
+ /// Stores the following 3d vector.
+ void getVector3( std::vector<aiVector3D> &point3d_array );
+ /// Stores the following 3d vector.
+ void getVector2(std::vector<aiVector2D> &point2d_array);
+ /// Stores the following face.
+ void getFace();
+ void getMaterialDesc();
+ /// Gets a comment.
+ void getComment();
+ /// Gets a a material library.
+ void getMaterialLib();
+ /// Creates a new material.
+ void getNewMaterial();
+ /// Gets the groupname from file.
+ void getGroupName();
+ /// Gets the group number from file.
+ void getGroupNumber();
+ /// Returns the index of the material. Is -1 if not material was found.
+ int getMaterialIndex( const std::string &strMaterialName );
+ /// Parse object name
+ void getObjectName();
+ /// Creates a new object.
+ void createObject(const std::string &strObjectName);
+ /// Creates a new mesh.
+ void createMesh();
+ /// Returns true, if a new mesh instance must be created.
+ bool needsNewMesh( const std::string &rMaterialName );
+ /// Error report in token
+ void reportErrorTokenInFace();
+
+private:
+ /// Default material name
+ static const std::string DEFAULT_MATERIAL;
+ //! Iterator to current position in buffer
+ DataArrayIt m_DataIt;
+ //! Iterator to end position of buffer
+ DataArrayIt m_DataItEnd;
+ //! Pointer to model instance
+ ObjFile::Model *m_pModel;
+ //! Current line (for debugging)
+ unsigned int m_uiLine;
+ //! Helper buffer
+ char m_buffer[BUFFERSIZE];
+ /// Pointer to IO system instance.
+ IOSystem *m_pIO;
+};
+
+} // Namespace Assimp
+
+#endif
diff --git a/3rdparty/assimp/code/ObjTools.h b/3rdparty/assimp/code/ObjTools.h
new file mode 100644
index 000000000..2fdfa0f13
--- /dev/null
+++ b/3rdparty/assimp/code/ObjTools.h
@@ -0,0 +1,220 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 ObjTools.h
+ * @brief Some helpful templates for text parsing
+ */
+#ifndef OBJ_TOOLS_H_INC
+#define OBJ_TOOLS_H_INC
+
+#include "fast_atof.h"
+
+namespace Assimp
+{
+
+/** @brief Returns true, if the last entry of the buffer is reached.
+ * @param it Iterator of current position.
+ * @param end Iterator with end of buffer.
+ * @return true, if the end of the buffer is reached.
+ */
+template<class char_t>
+inline bool isEndOfBuffer( char_t it, char_t end )
+{
+ if ( it == end )
+ {
+ return true;
+ }
+ else
+ {
+ end--;
+ }
+ return ( it == end );
+}
+
+/** @brief Returns true, if token is a space on any supported platform
+* @param token Token to search in
+* @return true, if token is a space
+*/
+inline bool isSeparator( char token )
+{
+ return ( token == ' ' ||
+ token == '\n' ||
+ token == '\f' ||
+ token == '\r' ||
+ token == '\t' );
+}
+
+/** @brief Returns true, fi token id a new line marking token.
+ * @param token Token to search in
+ * @return true, if token is a newline token.
+ */
+inline bool isNewLine( char token )
+{
+ return ( token == '\n' || token == '\f' || token == '\r' );
+}
+
+/** @brief Returns next word separated by a space
+ * @param pBuffer Pointer to data buffer
+ * @param pEnd Pointer to end of buffer
+ * @return Pointer to next space
+ */
+template<class Char_T>
+inline Char_T getNextWord( Char_T pBuffer, Char_T pEnd )
+{
+ while ( !isEndOfBuffer( pBuffer, pEnd ) )
+ {
+ if ( !isSeparator( *pBuffer ) || isNewLine( *pBuffer ) )
+ break;
+ pBuffer++;
+ }
+ return pBuffer;
+}
+
+/** @brief Returns ponter a next token
+ * @param pBuffer Pointer to data buffer
+ * @param pEnd Pointer to end of buffer
+ * @return Pointer to next token
+ */
+template<class Char_T>
+inline Char_T getNextToken( Char_T pBuffer, Char_T pEnd )
+{
+ while ( !isEndOfBuffer( pBuffer, pEnd ) )
+ {
+ if ( isSeparator( *pBuffer ) )
+ break;
+ pBuffer++;
+ }
+ return getNextWord( pBuffer, pEnd );
+}
+
+/** @brief Skips a line
+ * @param it Iterator set to current position
+ * @param end Iterator set to end of scratch buffer for readout
+ * @param uiLine Current linenumber in format
+ * @return Current-iterator with new position
+ */
+template<class char_t>
+inline char_t skipLine( char_t it, char_t end, unsigned int &uiLine )
+{
+ while ( !isEndOfBuffer( it, end ) && !isNewLine( *it ) )
+ ++it;
+ if ( it != end )
+ {
+ ++it;
+ ++uiLine;
+ }
+ // fix .. from time to time there are spaces at the beginning of a material line
+ while ( it != end && (*it == '\t' || *it == ' ') )
+ ++it;
+ return it;
+}
+
+/** @brief Get a name, must be separated with a blank.
+ * @param it set to current position
+ * @param end set to end of scratch buffer for readout
+ * @param name Separated name
+ * @return Current-iterator with new position
+ */
+template<class char_t>
+inline char_t getName( char_t it, char_t end, std::string &name )
+{
+ name = "";
+ it = getNextToken<char_t>( it, end );
+ if ( isEndOfBuffer( it, end ) )
+ return end;
+
+ char *pStart = &( *it );
+ while ( !isEndOfBuffer( it, end ) && !isSeparator( *it ) )
+ ++it;
+
+ // Get name
+ std::string strName( pStart, &(*it) );
+ if ( strName.empty() )
+ return it;
+ else
+ name = strName;
+
+ return it;
+}
+
+/** @brief Get next word from given line
+ * @param it set to current position
+ * @param end set to end of scratch buffer for readout
+ * @param pBuffer Buffer for next word
+ * @param length Buffer length
+ * @return Current-iterator with new position
+ */
+template<class char_t>
+inline char_t CopyNextWord( char_t it, char_t end, char *pBuffer, size_t length )
+{
+ size_t index = 0;
+ it = getNextWord<char_t>( it, end );
+ while ( !isSeparator( *it ) && !isEndOfBuffer( it, end ) )
+ {
+ pBuffer[index] = *it ;
+ index++;
+ if (index == length-1)
+ break;
+ ++it;
+ }
+ pBuffer[ index ] = '\0';
+ return it;
+}
+
+/** @brief Get next float from given line
+ * @param it set to current position
+ * @param end set to end of scratch buffer for readout
+ * @param value Separated float value.
+ * @return Current-iterator with new position
+ */
+template<class char_t>
+inline char_t getFloat( char_t it, char_t end, float &value )
+{
+ static const size_t BUFFERSIZE = 1024;
+ char buffer[ BUFFERSIZE ];
+ it = CopyNextWord<char_t>( it, end, buffer, BUFFERSIZE );
+ value = (float) fast_atof( buffer );
+
+ return it;
+}
+
+} // Namespace Assimp
+
+#endif
diff --git a/3rdparty/assimp/code/OgreImporter.cpp b/3rdparty/assimp/code/OgreImporter.cpp
new file mode 100644
index 000000000..07bb2c5ec
--- /dev/null
+++ b/3rdparty/assimp/code/OgreImporter.cpp
@@ -0,0 +1,873 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 OgreImporter.cpp
+ * @brief Implementation of the Ogre XML (.mesh.xml) loader.
+ */
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
+
+#include <vector>
+#include <sstream>
+using namespace std;
+
+//#include "boost/format.hpp"
+//#include "boost/foreach.hpp"
+//using namespace boost;
+
+#include "TinyFormatter.h"
+
+#include "OgreImporter.h"
+#include "irrXMLWrapper.h"
+
+
+namespace Assimp
+{
+namespace Ogre
+{
+
+
+bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const
+{
+ if (!checkSig)//Check File Extension
+ {
+ std::string extension("mesh.xml");
+ int l=extension.length();
+ return pFile.substr(pFile.length()-l, l)==extension;
+ }
+ else//Check file Header
+ {
+ const char* tokens[] = {"<mesh>"};
+ return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
+ }
+}
+
+
+
+void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Assimp::IOSystem *pIOHandler)
+{
+ m_CurrentFilename=pFile;
+ m_CurrentIOHandler=pIOHandler;
+ m_CurrentScene=pScene;
+
+ //Open the File:
+ boost::scoped_ptr<IOStream> file(pIOHandler->Open(pFile));
+ if ( file.get() == NULL)
+ throw DeadlyImportError("Failed to open file "+pFile+".");
+
+ //Read the Mesh File:
+ boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper( new CIrrXML_IOStreamReader( file.get()));
+ XmlReader* MeshFile = irr::io::createIrrXMLReader(mIOWrapper.get());
+ if (!MeshFile)//parse the xml file
+ throw DeadlyImportError("Failed to create XML Reader for "+pFile);
+
+
+ DefaultLogger::get()->debug("Mesh File opened");
+
+ //Read root Node:
+ if (!(XmlRead(MeshFile) && string(MeshFile->getNodeName())=="mesh"))
+ {
+ throw DeadlyImportError("Root Node is not <mesh>! "+pFile+" "+MeshFile->getNodeName());
+ }
+
+ //Go to the submeshs:
+ if (!(XmlRead(MeshFile) && string(MeshFile->getNodeName())=="submeshes"))
+ {
+ throw DeadlyImportError("No <submeshes> node in <mesh> node! "+pFile);
+ }
+
+
+ //-------------------Read the submeshs and materials:-----------------------
+ std::list<boost::shared_ptr<SubMesh> > SubMeshes;
+ vector<aiMaterial*> Materials;
+ XmlRead(MeshFile);
+ while (MeshFile->getNodeName()==string("submesh"))
+ {
+ SubMesh* theSubMesh=new SubMesh();
+ theSubMesh->MaterialName=GetAttribute<string>(MeshFile, "material");
+ DefaultLogger::get()->debug("Loading Submehs with Material: "+theSubMesh->MaterialName);
+ ReadSubMesh(*theSubMesh, MeshFile);
+
+ //just a index in a array, we add a mesh in each loop cycle, so we get indicies like 0, 1, 2 ... n;
+ //so it is important to do this before pushing the mesh in the vector!
+ theSubMesh->MaterialIndex=SubMeshes.size();
+
+ SubMeshes.push_back(boost::shared_ptr<SubMesh>(theSubMesh));
+
+ //Load the Material:
+ aiMaterial* MeshMat=LoadMaterial(theSubMesh->MaterialName);
+
+ //Set the Material:
+ Materials.push_back(MeshMat);
+ }
+
+ if (SubMeshes.empty())
+ throw DeadlyImportError("no submesh loaded!");
+ if (SubMeshes.size()!=Materials.size())
+ throw DeadlyImportError("materialcount doesn't match mesh count!");
+
+ //____________________________________________________________
+
+
+ //----------------Load the skeleton: -------------------------------
+ vector<Bone> Bones;
+ vector<Animation> Animations;
+ if (MeshFile->getNodeName()==string("skeletonlink"))
+ {
+ string SkeletonFile=GetAttribute<string>(MeshFile, "name");
+ LoadSkeleton(SkeletonFile, Bones, Animations);
+ }
+ else
+ {
+ DefaultLogger::get()->warn("No skeleton file will be loaded");
+ DefaultLogger::get()->warn(MeshFile->getNodeName());
+ }
+ //__________________________________________________________________
+
+
+ //----------------- Now fill the Assimp scene ---------------------------
+
+ //put the aiMaterials in the scene:
+ m_CurrentScene->mMaterials=new aiMaterial*[Materials.size()];
+ m_CurrentScene->mNumMaterials=Materials.size();
+ for (unsigned int i=0; i<Materials.size(); ++i)
+ m_CurrentScene->mMaterials[i]=Materials[i];
+
+ //create the aiMehs...
+ vector<aiMesh*> aiMeshes;
+ BOOST_FOREACH(boost::shared_ptr<SubMesh> theSubMesh, SubMeshes)
+ {
+ aiMeshes.push_back(CreateAssimpSubMesh(*theSubMesh, Bones));
+ }
+ //... and put them in the scene:
+ m_CurrentScene->mNumMeshes=aiMeshes.size();
+ m_CurrentScene->mMeshes=new aiMesh*[aiMeshes.size()];
+ memcpy(m_CurrentScene->mMeshes, &(aiMeshes[0]), sizeof(aiMeshes[0])*aiMeshes.size());
+
+ //Create the root node
+ m_CurrentScene->mRootNode=new aiNode("root");
+
+ //link the meshs with the root node:
+ m_CurrentScene->mRootNode->mMeshes=new unsigned int[SubMeshes.size()];
+ m_CurrentScene->mRootNode->mNumMeshes=SubMeshes.size();
+ for (unsigned int i=0; i<SubMeshes.size(); ++i)
+ m_CurrentScene->mRootNode->mMeshes[i]=i;
+
+
+
+ CreateAssimpSkeleton(Bones, Animations);
+ PutAnimationsInScene(Bones, Animations);
+ //___________________________________________________________
+}
+
+
+
+void OgreImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("mesh.xml");
+}
+
+
+void OgreImporter::SetupProperties(const Importer* pImp)
+{
+ m_MaterialLibFilename=pImp->GetPropertyString(AI_CONFIG_IMPORT_OGRE_MATERIAL_FILE, "Scene.material");
+}
+
+void OgreImporter::ReadSubMesh(SubMesh &theSubMesh, XmlReader *Reader)
+{
+ XmlRead(Reader);
+ //TODO: maybe we have alsways just 1 faces and 1 geometry and always in this order. this loop will only work correct, when the order
+ //of faces and geometry changed, and not if we have more than one of one
+ while (Reader->getNodeName()==string("faces") || string(Reader->getNodeName())=="geometry" || Reader->getNodeName()==string("boneassignments"))
+ {
+ if (string(Reader->getNodeName())=="faces")//Read the face list
+ {
+ //some info logging:
+ unsigned int NumFaces=GetAttribute<int>(Reader, "count");
+ stringstream ss; ss <<"Submesh has " << NumFaces << " Faces.";
+ DefaultLogger::get()->debug(ss.str());
+
+ while (XmlRead(Reader) && Reader->getNodeName()==string("face"))
+ {
+ Face NewFace;
+ NewFace.VertexIndices[0]=GetAttribute<int>(Reader, "v1");
+ NewFace.VertexIndices[1]=GetAttribute<int>(Reader, "v2");
+ NewFace.VertexIndices[2]=GetAttribute<int>(Reader, "v3");
+ if (Reader->getAttributeValue("v4"))//this should be supported in the future
+ {
+ throw DeadlyImportError("Submesh has quads, only traingles are supported!");
+ }
+ theSubMesh.FaceList.push_back(NewFace);
+ }
+
+ }//end of faces
+ else if (string(Reader->getNodeName())=="geometry")//Read the vertexdata
+ {
+ //some info logging:
+ unsigned int NumVertices=GetAttribute<int>(Reader, "vertexcount");
+ stringstream ss; ss<<"VertexCount: "<<NumVertices;
+ DefaultLogger::get()->debug(ss.str());
+
+ //General Informations about vertices
+ XmlRead(Reader);
+ if (!(Reader->getNodeName()==string("vertexbuffer")))
+ {
+ throw DeadlyImportError("vertexbuffer node is not first in geometry node!");
+ }
+ theSubMesh.HasPositions=GetAttribute<bool>(Reader, "positions");
+ theSubMesh.HasNormals=GetAttribute<bool>(Reader, "normals");
+ if (!Reader->getAttributeValue("texture_coords"))//we can have 1 or 0 uv channels, and if the mesh has no uvs, it also doesn't have the attribute
+ theSubMesh.NumUvs=0;
+ else
+ theSubMesh.NumUvs=GetAttribute<int>(Reader, "texture_coords");
+ if (theSubMesh.NumUvs>1)
+ throw DeadlyImportError("too many texcoords (just 1 supported!)");
+
+ //read all the vertices:
+ XmlRead(Reader);
+ while (Reader->getNodeName()==string("vertex"))
+ {
+ //read all vertex attributes:
+
+ //Position
+ if (theSubMesh.HasPositions)
+ {
+ XmlRead(Reader);
+ aiVector3D NewPos;
+ NewPos.x=GetAttribute<float>(Reader, "x");
+ NewPos.y=GetAttribute<float>(Reader, "y");
+ NewPos.z=GetAttribute<float>(Reader, "z");
+ theSubMesh.Positions.push_back(NewPos);
+ }
+
+ //Normal
+ if (theSubMesh.HasNormals)
+ {
+ XmlRead(Reader);
+ aiVector3D NewNormal;
+ NewNormal.x=GetAttribute<float>(Reader, "x");
+ NewNormal.y=GetAttribute<float>(Reader, "y");
+ NewNormal.z=GetAttribute<float>(Reader, "z");
+ theSubMesh.Normals.push_back(NewNormal);
+ }
+
+ //Uv:
+ if (1==theSubMesh.NumUvs)
+ {
+ XmlRead(Reader);
+ aiVector3D NewUv;
+ NewUv.x=GetAttribute<float>(Reader, "u");
+ NewUv.y=GetAttribute<float>(Reader, "v")*(-1)+1;//flip the uv vertikal, blender exports them so!
+ theSubMesh.Uvs.push_back(NewUv);
+ }
+ XmlRead(Reader);
+ }
+
+ }//end of "geometry
+
+
+ else if (string(Reader->getNodeName())=="boneassignments")
+ {
+ theSubMesh.Weights.resize(theSubMesh.Positions.size());
+ while (XmlRead(Reader) && Reader->getNodeName()==string("vertexboneassignment"))
+ {
+ Weight NewWeight;
+ unsigned int VertexId=GetAttribute<int>(Reader, "vertexindex");
+ NewWeight.BoneId=GetAttribute<int>(Reader, "boneindex");
+ NewWeight.Value=GetAttribute<float>(Reader, "weight");
+ theSubMesh.BonesUsed=max(theSubMesh.BonesUsed, NewWeight.BoneId+1);//calculate the number of bones used (this is the highest id +1 becuase bone ids start at 0)
+
+ theSubMesh.Weights[VertexId].push_back(NewWeight);
+
+ //XmlRead(Reader);//Once i had this line, and than i got only every second boneassignment, but my first test models had even boneassignment counts, so i thougt, everything would work. And yes, i HATE irrXML!!!
+ }
+
+ }//end of boneassignments
+ }
+ DefaultLogger::get()->debug((Formatter::format(),
+ "Positionen: ",theSubMesh.Positions.size(),
+ " Normale: ",theSubMesh.Normals.size(),
+ " TexCoords: ",theSubMesh.Uvs.size()
+ ));
+ DefaultLogger::get()->warn(Reader->getNodeName());
+
+
+
+ //---------------Make all Vertexes unique: (this is required by assimp)-----------------------
+ vector<Face> UniqueFaceList(theSubMesh.FaceList.size());
+ unsigned int UniqueVertexCount=theSubMesh.FaceList.size()*3;//*3 because each face consists of 3 vertexes, because we only support triangles^^
+ vector<aiVector3D> UniquePositions(UniqueVertexCount);
+ vector<aiVector3D> UniqueNormals(UniqueVertexCount);
+ vector<aiVector3D> UniqueUvs(UniqueVertexCount);
+ vector< vector<Weight> > UniqueWeights((theSubMesh.Weights.size() ? UniqueVertexCount : 0));
+
+ for (unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
+ {
+ //We precalculate the index vlaues her, because we need them in all vertex attributes
+ unsigned int Vertex1=theSubMesh.FaceList[i].VertexIndices[0];
+ unsigned int Vertex2=theSubMesh.FaceList[i].VertexIndices[1];
+ unsigned int Vertex3=theSubMesh.FaceList[i].VertexIndices[2];
+
+ UniquePositions[3*i+0]=theSubMesh.Positions[Vertex1];
+ UniquePositions[3*i+1]=theSubMesh.Positions[Vertex2];
+ UniquePositions[3*i+2]=theSubMesh.Positions[Vertex3];
+
+ UniqueNormals[3*i+0]=theSubMesh.Normals[Vertex1];
+ UniqueNormals[3*i+1]=theSubMesh.Normals[Vertex2];
+ UniqueNormals[3*i+2]=theSubMesh.Normals[Vertex3];
+
+ if (1==theSubMesh.NumUvs)
+ {
+ UniqueUvs[3*i+0]=theSubMesh.Uvs[Vertex1];
+ UniqueUvs[3*i+1]=theSubMesh.Uvs[Vertex2];
+ UniqueUvs[3*i+2]=theSubMesh.Uvs[Vertex3];
+ }
+
+ if (theSubMesh.Weights.size()) {
+ UniqueWeights[3*i+0]=theSubMesh.Weights[Vertex1];
+ UniqueWeights[3*i+1]=theSubMesh.Weights[Vertex2];
+ UniqueWeights[3*i+2]=theSubMesh.Weights[Vertex3];
+ }
+
+ //The indexvalues a just continuous numbers (0, 1, 2, 3, 4, 5, 6...)
+ UniqueFaceList[i].VertexIndices[0]=3*i+0;
+ UniqueFaceList[i].VertexIndices[1]=3*i+1;
+ UniqueFaceList[i].VertexIndices[2]=3*i+2;
+ }
+ //_________________________________________________________________________________________
+
+ //now we have the unique datas, but want them in the SubMesh, so we swap all the containers:
+ theSubMesh.FaceList.swap(UniqueFaceList);
+ theSubMesh.Positions.swap(UniquePositions);
+ theSubMesh.Normals.swap(UniqueNormals);
+ theSubMesh.Uvs.swap(UniqueUvs);
+ theSubMesh.Weights.swap(UniqueWeights);
+
+ //------------- normalize weights -----------------------------
+ //The Blender exporter doesn't care about whether the sum of all boneweights for a single vertex equals 1 or not,
+ //so we have to make this sure:
+ for (unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
+ {
+ float WeightSum=0.0f;
+ for (unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
+ {
+ WeightSum+=theSubMesh.Weights[VertexId][BoneId].Value;
+ }
+
+ //check if the sum is too far away from 1
+ if (WeightSum<1.0f-0.05f || WeightSum>1.0f+0.05f)
+ {
+ //normalize all weights:
+ for (unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
+ {
+ theSubMesh.Weights[VertexId][BoneId].Value/=WeightSum;
+ }
+ }
+ }
+ //_________________________________________________________
+}
+
+
+aiMesh* OgreImporter::CreateAssimpSubMesh(const SubMesh& theSubMesh, const vector<Bone>& Bones) const
+{
+ // const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
+
+ aiMesh* NewAiMesh=new aiMesh();
+
+ //Positions
+ NewAiMesh->mVertices=new aiVector3D[theSubMesh.Positions.size()];
+ memcpy(NewAiMesh->mVertices, &theSubMesh.Positions[0], theSubMesh.Positions.size()*sizeof(aiVector3D));
+ NewAiMesh->mNumVertices=theSubMesh.Positions.size();
+
+ //Normals
+ NewAiMesh->mNormals=new aiVector3D[theSubMesh.Normals.size()];
+ memcpy(NewAiMesh->mNormals, &theSubMesh.Normals[0], theSubMesh.Normals.size()*sizeof(aiVector3D));
+
+ //Uvs
+ if (0!=theSubMesh.NumUvs)
+ {
+ NewAiMesh->mNumUVComponents[0]=2;
+ NewAiMesh->mTextureCoords[0]= new aiVector3D[theSubMesh.Uvs.size()];
+ memcpy(NewAiMesh->mTextureCoords[0], &theSubMesh.Uvs[0], theSubMesh.Uvs.size()*sizeof(aiVector3D));
+ }
+
+
+ //---------------------------------------- Bones --------------------------------------------
+
+ //Copy the weights in in Bone-Vertices Struktur
+ //(we have them in a Vertex-Bones Structur, this is much easier for making them unique, which is required by assimp
+ vector< vector<aiVertexWeight> > aiWeights(theSubMesh.BonesUsed);//now the outer list are the bones, and the inner vector the vertices
+ for (unsigned int VertexId=0; VertexId<theSubMesh.Weights.size(); ++VertexId)//iterate over all vertices
+ {
+ for (unsigned int BoneId=0; BoneId<theSubMesh.Weights[VertexId].size(); ++BoneId)//iterate over all bones
+ {
+ aiVertexWeight NewWeight;
+ NewWeight.mVertexId=VertexId;//the current Vertex, we can't use the Id form the submehs weights, because they are bone id's
+ NewWeight.mWeight=theSubMesh.Weights[VertexId][BoneId].Value;
+ aiWeights[theSubMesh.Weights[VertexId][BoneId].BoneId].push_back(NewWeight);
+ }
+ }
+
+
+
+ vector<aiBone*> aiBones;
+ aiBones.reserve(theSubMesh.BonesUsed);//the vector might be smaller, because there might be empty bones (bones that are not attached to any vertex)
+
+ //create all the bones and fill them with informations
+ for (unsigned int i=0; i<theSubMesh.BonesUsed; ++i)
+ {
+ if (aiWeights[i].size()>0)
+ {
+ aiBone* NewBone=new aiBone();
+ NewBone->mNumWeights=aiWeights[i].size();
+ NewBone->mWeights=new aiVertexWeight[aiWeights[i].size()];
+ memcpy(NewBone->mWeights, &(aiWeights[i][0]), sizeof(aiVertexWeight)*aiWeights[i].size());
+ NewBone->mName=Bones[i].Name;//The bone list should be sorted after its id's, this was done in LoadSkeleton
+ NewBone->mOffsetMatrix=Bones[i].BoneToWorldSpace;
+
+ aiBones.push_back(NewBone);
+ }
+ }
+ NewAiMesh->mNumBones=aiBones.size();
+
+ // mBones must be NULL if mNumBones is non 0 or the validation fails.
+ if (aiBones.size()) {
+ NewAiMesh->mBones=new aiBone* [aiBones.size()];
+ memcpy(NewAiMesh->mBones, &(aiBones[0]), aiBones.size()*sizeof(aiBone*));
+ }
+
+ //______________________________________________________________________________________________________
+
+
+
+ //Faces
+ NewAiMesh->mFaces=new aiFace[theSubMesh.FaceList.size()];
+ for (unsigned int i=0; i<theSubMesh.FaceList.size(); ++i)
+ {
+ NewAiMesh->mFaces[i].mNumIndices=3;
+ NewAiMesh->mFaces[i].mIndices=new unsigned int[3];
+
+ NewAiMesh->mFaces[i].mIndices[0]=theSubMesh.FaceList[i].VertexIndices[0];
+ NewAiMesh->mFaces[i].mIndices[1]=theSubMesh.FaceList[i].VertexIndices[1];
+ NewAiMesh->mFaces[i].mIndices[2]=theSubMesh.FaceList[i].VertexIndices[2];
+ }
+ NewAiMesh->mNumFaces=theSubMesh.FaceList.size();
+
+ //Link the material:
+ NewAiMesh->mMaterialIndex=theSubMesh.MaterialIndex;//the index is set by the function who called ReadSubMesh
+
+ return NewAiMesh;
+}
+
+
+void OgreImporter::LoadSkeleton(std::string FileName, vector<Bone> &Bones, vector<Animation> &Animations) const
+{
+ //const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
+
+
+ //most likely the skeleton file will only end with .skeleton
+ //But this is a xml reader, so we need: .skeleton.xml
+ FileName+=".xml";
+
+ DefaultLogger::get()->debug(string("Loading Skeleton: ")+FileName);
+
+ //Open the File:
+ boost::scoped_ptr<IOStream> File(m_CurrentIOHandler->Open(FileName));
+ if (NULL==File.get())
+ throw DeadlyImportError("Failed to open skeleton file "+FileName+".");
+
+ //Read the Mesh File:
+ boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(File.get()));
+ XmlReader* SkeletonFile = irr::io::createIrrXMLReader(mIOWrapper.get());
+ if (!SkeletonFile)
+ throw DeadlyImportError(string("Failed to create XML Reader for ")+FileName);
+
+ //Quick note: Whoever read this should know this one thing: irrXml fucking sucks!!!
+
+ XmlRead(SkeletonFile);
+ if (string("skeleton")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("No <skeleton> node in SkeletonFile: "+FileName);
+
+
+
+ //------------------------------------load bones-----------------------------------------
+ XmlRead(SkeletonFile);
+ if (string("bones")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("No bones node in skeleton "+FileName);
+
+ XmlRead(SkeletonFile);
+
+ while (string("bone")==SkeletonFile->getNodeName())
+ {
+ //TODO: Maybe we can have bone ids for the errrors, but normaly, they should never appear, so what....
+
+ //read a new bone:
+ Bone NewBone;
+ NewBone.Id=GetAttribute<int>(SkeletonFile, "id");
+ NewBone.Name=GetAttribute<string>(SkeletonFile, "name");
+
+ //load the position:
+ XmlRead(SkeletonFile);
+ if (string("position")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("Position is not first node in Bone!");
+ NewBone.Position.x=GetAttribute<float>(SkeletonFile, "x");
+ NewBone.Position.y=GetAttribute<float>(SkeletonFile, "y");
+ NewBone.Position.z=GetAttribute<float>(SkeletonFile, "z");
+
+ //Rotation:
+ XmlRead(SkeletonFile);
+ if (string("rotation")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("Rotation is not the second node in Bone!");
+ NewBone.RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
+ XmlRead(SkeletonFile);
+ if (string("axis")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("No axis specified for bone rotation!");
+ NewBone.RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
+ NewBone.RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
+ NewBone.RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
+
+ //append the newly loaded bone to the bone list
+ Bones.push_back(NewBone);
+
+ //Proceed to the next bone:
+ XmlRead(SkeletonFile);
+ }
+ //The bones in the file a not neccesarly ordered by there id's so we do it now:
+ std::sort(Bones.begin(), Bones.end());
+
+ //now the id of each bone should be equal to its position in the vector:
+ //so we do a simple check:
+ {
+ bool IdsOk=true;
+ for (int i=0; i<static_cast<signed int>(Bones.size()); ++i)//i is signed, because all Id's are also signed!
+ {
+ if (Bones[i].Id!=i)
+ IdsOk=false;
+ }
+ if (!IdsOk)
+ throw DeadlyImportError("Bone Ids are not valid!"+FileName);
+ }
+ DefaultLogger::get()->debug((Formatter::format(),"Number of bones: ",Bones.size()));
+ //________________________________________________________________________________
+
+
+
+
+
+
+ //----------------------------load bonehierarchy--------------------------------
+ if (string("bonehierarchy")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("no bonehierarchy node in "+FileName);
+
+ DefaultLogger::get()->debug("loading bonehierarchy...");
+ XmlRead(SkeletonFile);
+ while (string("boneparent")==SkeletonFile->getNodeName())
+ {
+ string Child, Parent;
+ Child=GetAttribute<string>(SkeletonFile, "bone");
+ Parent=GetAttribute<string>(SkeletonFile, "parent");
+
+ unsigned int ChildId, ParentId;
+ ChildId=find(Bones.begin(), Bones.end(), Child)->Id;
+ ParentId=find(Bones.begin(), Bones.end(), Parent)->Id;
+
+ Bones[ChildId].ParentId=ParentId;
+ Bones[ParentId].Children.push_back(ChildId);
+
+ XmlRead(SkeletonFile);//i once forget this line, which led to an endless loop, did i mentioned, that irrxml sucks??
+ }
+ //_____________________________________________________________________________
+
+
+ //--------- Calculate the WorldToBoneSpace Matrix recursivly for all bones: ------------------
+ BOOST_FOREACH(Bone theBone, Bones)
+ {
+ if (-1==theBone.ParentId) //the bone is a root bone
+ {
+ theBone.CalculateBoneToWorldSpaceMatrix(Bones);
+ }
+ }
+ //_______________________________________________________________________
+
+
+ //---------------------------load animations-----------------------------
+ if (string("animations")==SkeletonFile->getNodeName())//animations are optional values
+ {
+ DefaultLogger::get()->debug("Loading Animations");
+ XmlRead(SkeletonFile);
+ while (string("animation")==SkeletonFile->getNodeName())
+ {
+ Animation NewAnimation;
+ NewAnimation.Name=GetAttribute<string>(SkeletonFile, "name");
+ NewAnimation.Length=GetAttribute<float>(SkeletonFile, "length");
+
+ //Load all Tracks
+ XmlRead(SkeletonFile);
+ if (string("tracks")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("no tracks node in animation");
+ XmlRead(SkeletonFile);
+ while (string("track")==SkeletonFile->getNodeName())
+ {
+ Track NewTrack;
+ NewTrack.BoneName=GetAttribute<string>(SkeletonFile, "bone");
+
+ //Load all keyframes;
+ XmlRead(SkeletonFile);
+ if (string("keyframes")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("no keyframes node!");
+ XmlRead(SkeletonFile);
+ while (string("keyframe")==SkeletonFile->getNodeName())
+ {
+ Keyframe NewKeyframe;
+ NewKeyframe.Time=GetAttribute<float>(SkeletonFile, "time");
+
+ //Position:
+ XmlRead(SkeletonFile);
+ if (string("translate")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("translate node not first in keyframe");
+ NewKeyframe.Position.x=GetAttribute<float>(SkeletonFile, "x");
+ NewKeyframe.Position.y=GetAttribute<float>(SkeletonFile, "y");
+ NewKeyframe.Position.z=GetAttribute<float>(SkeletonFile, "z");
+
+ //Rotation:
+ XmlRead(SkeletonFile);
+ if (string("rotate")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("rotate is not second node in keyframe");
+ float RotationAngle=GetAttribute<float>(SkeletonFile, "angle");
+ aiVector3D RotationAxis;
+ XmlRead(SkeletonFile);
+ if (string("axis")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("No axis for keyframe rotation!");
+ RotationAxis.x=GetAttribute<float>(SkeletonFile, "x");
+ RotationAxis.y=GetAttribute<float>(SkeletonFile, "y");
+ RotationAxis.z=GetAttribute<float>(SkeletonFile, "z");
+ NewKeyframe.Rotation=aiQuaternion(RotationAxis, RotationAngle);
+
+ //Scaling:
+ XmlRead(SkeletonFile);
+ if (string("scale")!=SkeletonFile->getNodeName())
+ throw DeadlyImportError("no scalling key in keyframe!");
+ NewKeyframe.Scaling.x=GetAttribute<float>(SkeletonFile, "x");
+ NewKeyframe.Scaling.y=GetAttribute<float>(SkeletonFile, "y");
+ NewKeyframe.Scaling.z=GetAttribute<float>(SkeletonFile, "z");
+
+
+ NewTrack.Keyframes.push_back(NewKeyframe);
+ XmlRead(SkeletonFile);
+ }
+
+
+ NewAnimation.Tracks.push_back(NewTrack);
+ }
+
+ Animations.push_back(NewAnimation);
+ }
+ }
+ //_____________________________________________________________________________
+
+}
+
+
+void OgreImporter::CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &/*Animations*/)
+{
+ if (!m_CurrentScene->mRootNode)
+ throw DeadlyImportError("No root node exists!!");
+ if (0!=m_CurrentScene->mRootNode->mNumChildren)
+ throw DeadlyImportError("Root Node already has childnodes!");
+
+
+ //Createt the assimp bone hierarchy
+ DefaultLogger::get()->debug("Root Bones");
+ vector<aiNode*> RootBoneNodes;
+ BOOST_FOREACH(Bone theBone, Bones)
+ {
+ if (-1==theBone.ParentId) //the bone is a root bone
+ {
+ DefaultLogger::get()->debug(theBone.Name);
+ RootBoneNodes.push_back(CreateAiNodeFromBone(theBone.Id, Bones, m_CurrentScene->mRootNode));//which will recursily add all other nodes
+ }
+ }
+ m_CurrentScene->mRootNode->mNumChildren=RootBoneNodes.size();
+ m_CurrentScene->mRootNode->mChildren=new aiNode*[RootBoneNodes.size()];
+ memcpy(m_CurrentScene->mRootNode->mChildren, &RootBoneNodes[0], sizeof(aiNode*)*RootBoneNodes.size());
+}
+
+
+void OgreImporter::PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations)
+{
+ //-----------------Create the Assimp Animations --------------------
+ if (Animations.size()>0)//Maybe the model had only a skeleton and no animations. (If it also has no skeleton, this function would'nt have been called
+ {
+ m_CurrentScene->mNumAnimations=Animations.size();
+ m_CurrentScene->mAnimations=new aiAnimation*[Animations.size()];
+ for (unsigned int i=0; i<Animations.size(); ++i)//create all animations
+ {
+ aiAnimation* NewAnimation=new aiAnimation();
+ NewAnimation->mName=Animations[i].Name;
+ NewAnimation->mDuration=Animations[i].Length;
+ NewAnimation->mTicksPerSecond=1.0f;
+
+ //Create all tracks in this animation
+ NewAnimation->mNumChannels=Animations[i].Tracks.size();
+ NewAnimation->mChannels=new aiNodeAnim*[Animations[i].Tracks.size()];
+ for (unsigned int j=0; j<Animations[i].Tracks.size(); ++j)
+ {
+ aiNodeAnim* NewNodeAnim=new aiNodeAnim();
+ NewNodeAnim->mNodeName=Animations[i].Tracks[j].BoneName;
+
+ //we need this, to acces the bones default pose, which we need to make keys absolute
+ vector<Bone>::const_iterator CurBone=find(Bones.begin(), Bones.end(), NewNodeAnim->mNodeName);
+ aiMatrix4x4 t0, t1;
+ aiMatrix4x4 DefBonePose=//The default bone pose doesnt have a scaling value
+ aiMatrix4x4::Rotation(CurBone->RotationAngle, CurBone->RotationAxis, t0)
+ * aiMatrix4x4::Translation(CurBone->Position, t1);
+
+ //Create the keyframe arrays...
+ unsigned int KeyframeCount=Animations[i].Tracks[j].Keyframes.size();
+ NewNodeAnim->mNumPositionKeys=KeyframeCount;
+ NewNodeAnim->mPositionKeys=new aiVectorKey[KeyframeCount];
+ NewNodeAnim->mNumRotationKeys=KeyframeCount;
+ NewNodeAnim->mRotationKeys=new aiQuatKey[KeyframeCount];
+ NewNodeAnim->mNumScalingKeys=KeyframeCount;
+ NewNodeAnim->mScalingKeys=new aiVectorKey[KeyframeCount];
+
+ //...and fill them
+ for (unsigned int k=0; k<KeyframeCount; ++k)
+ {
+ aiMatrix4x4 t2, t3;
+
+ //Create a matrix to transfrom a vector from the bones default pose to the bone bones in this animation key
+ aiMatrix4x4 PoseToKey=aiMatrix4x4::Scaling(Animations[i].Tracks[j].Keyframes[k].Scaling, t2) //scale
+ * aiMatrix4x4(Animations[i].Tracks[j].Keyframes[k].Rotation.GetMatrix()) //rot
+ * aiMatrix4x4::Translation(Animations[i].Tracks[j].Keyframes[k].Position, t3); //pos
+
+
+ //calculate the complete transformation from world space to bone space
+ aiMatrix4x4 CompleteTransform=DefBonePose * PoseToKey;
+
+ aiVector3D Pos;
+ aiQuaternion Rot;
+ aiVector3D Scale;
+
+ CompleteTransform.Decompose(Scale, Rot, Pos);
+
+
+ NewNodeAnim->mPositionKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
+ NewNodeAnim->mPositionKeys[k].mValue=Pos;
+
+ NewNodeAnim->mRotationKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
+ NewNodeAnim->mRotationKeys[k].mValue=Rot;
+
+ NewNodeAnim->mScalingKeys[k].mTime=Animations[i].Tracks[j].Keyframes[k].Time;
+ NewNodeAnim->mScalingKeys[k].mValue=Scale;
+ }
+
+ NewAnimation->mChannels[j]=NewNodeAnim;
+ }
+
+ m_CurrentScene->mAnimations[i]=NewAnimation;
+ }
+ }
+//TODO: Auf nicht vorhandene Animationskeys achten!
+//#pragma warning (s.o.)
+ //__________________________________________________________________
+}
+
+
+
+aiNode* OgreImporter::CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode) const
+{
+ //const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
+
+ //----Create the node for this bone and set its values-----
+ aiNode* NewNode=new aiNode(Bones[BoneId].Name);
+ NewNode->mParent=ParentNode;
+
+ aiMatrix4x4 t0,t1;
+ //create a matrix from the transformation values of the ogre bone
+ NewNode->mTransformation=aiMatrix4x4::Rotation(Bones[BoneId].RotationAngle, Bones[BoneId].RotationAxis, t1)
+ * aiMatrix4x4::Translation(Bones[BoneId].Position, t0)
+
+ ;
+ //__________________________________________________________
+
+
+ //---------- recursivly create all children Nodes: ----------
+ NewNode->mNumChildren=Bones[BoneId].Children.size();
+ NewNode->mChildren=new aiNode*[Bones[BoneId].Children.size()];
+ for (unsigned int i=0; i<Bones[BoneId].Children.size(); ++i)
+ {
+ NewNode->mChildren[i]=CreateAiNodeFromBone(Bones[BoneId].Children[i], Bones, NewNode);
+ }
+ //____________________________________________________
+
+
+ return NewNode;
+}
+
+
+void Bone::CalculateBoneToWorldSpaceMatrix(vector<Bone> &Bones)
+{
+ //Calculate the matrix for this bone:
+
+ aiMatrix4x4 t0,t1;
+ aiMatrix4x4 Transf=aiMatrix4x4::Translation(-Position, t0)
+ * aiMatrix4x4::Rotation(-RotationAngle, RotationAxis, t1)
+ ;
+ if (-1==ParentId)
+ {
+ BoneToWorldSpace=Transf;
+ }
+ else
+ {
+ BoneToWorldSpace=Transf*Bones[ParentId].BoneToWorldSpace;
+ }
+
+ //and recursivly for all children:
+ BOOST_FOREACH(int theChildren, Children)
+ {
+ Bones[theChildren].CalculateBoneToWorldSpaceMatrix(Bones);
+ }
+}
+
+}//namespace Ogre
+}//namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
diff --git a/3rdparty/assimp/code/OgreImporter.h b/3rdparty/assimp/code/OgreImporter.h
new file mode 100644
index 000000000..9d530afc3
--- /dev/null
+++ b/3rdparty/assimp/code/OgreImporter.h
@@ -0,0 +1,151 @@
+#include "BaseImporter.h"
+
+#include <vector>
+
+#include "OgreXmlHelper.h"
+#include "irrXMLWrapper.h"
+
+namespace Assimp
+{
+namespace Ogre
+{
+
+
+//Forward declarations:
+struct SubMesh;
+struct Face;
+struct Weight;
+struct Bone;
+struct Animation;
+struct Track;
+struct Keyframe;
+
+///The Main Ogre Importer Class
+class OgreImporter : public BaseImporter
+{
+public:
+ virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+ virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+ virtual void GetExtensionList(std::set<std::string>& extensions);
+ virtual void SetupProperties(const Importer* pImp);
+private:
+
+ /// Helper Functions to read parts of the XML File
+ void ReadSubMesh(SubMesh& theSubMesh, XmlReader* Reader);//the submesh reference is the result value
+
+ /// writes the results in Bones and Animations, Filename is not const, because its call-by-value and the function will change it!
+ void LoadSkeleton(std::string FileName, std::vector<Bone> &Bones, std::vector<Animation> &Animations) const;
+
+ /// converts the animations in aiAnimations and puts them into the scene
+ void PutAnimationsInScene(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
+
+ /// uses the bone data to convert a SubMesh into a aiMesh which will be created and returned
+ aiMesh* CreateAssimpSubMesh(const SubMesh &theSubMesh, const std::vector<Bone>& Bones) const;
+
+ //creates the aiskeleton in current scene
+ void CreateAssimpSkeleton(const std::vector<Bone> &Bones, const std::vector<Animation> &Animations);
+
+ aiMaterial* LoadMaterial(const std::string MaterialName) const;
+
+ ///Recursivly creates a filled aiNode from a given root bone
+ aiNode* CreateAiNodeFromBone(int BoneId, const std::vector<Bone> &Bones, aiNode* ParentNode) const;
+
+ //Now we don't have to give theses parameters to all functions
+ std::string m_CurrentFilename;
+ std::string m_MaterialLibFilename;
+ IOSystem* m_CurrentIOHandler;
+ aiScene *m_CurrentScene;
+};
+
+///A submesh from Ogre
+struct SubMesh
+{
+ std::string Name;
+ std::string MaterialName;
+ std::vector<Face> FaceList;
+ std::vector<aiVector3D> Positions; bool HasPositions;
+ std::vector<aiVector3D> Normals; bool HasNormals;
+ std::vector<aiVector3D> Uvs; unsigned int NumUvs;//nearly always 2d, but assimp has always 3d texcoords
+ std::vector< std::vector<Weight> > Weights;//a list of bones for each vertex
+ int MaterialIndex;///< The Index in the Assimp Materialarray from the material witch is attached to this submesh
+ unsigned int BonesUsed;//the highest index of a bone from a bone weight, this is needed to create the assimp bone structur (converting from Vertex-Bones to Bone-Vertices)
+
+ SubMesh(): HasPositions(false), HasNormals(false), NumUvs(0), MaterialIndex(-1), BonesUsed(0) {}//initialize everything
+};
+
+///For the moment just triangles, no other polygon types!
+struct Face
+{
+ unsigned int VertexIndices[3];
+};
+
+struct BoneAssignment
+{
+ unsigned int BoneId;//this is, what we get from ogre
+ std::string BoneName;//this is, what we need for assimp
+};
+
+///for a vertex->bone structur
+struct Weight
+{
+ unsigned int BoneId;
+ float Value;
+};
+
+
+/// Helper Class to describe an ogre-bone for the skeleton:
+/** All Id's are signed ints, because than we have -1 as a simple INVALID_ID Value (we start from 0 so 0 is a valid bone ID!*/
+struct Bone
+{
+ int Id;
+ int ParentId;
+ std::string Name;
+ aiVector3D Position;
+ float RotationAngle;
+ aiVector3D RotationAxis;
+ std::vector<int> Children;
+ aiMatrix4x4 BoneToWorldSpace;
+
+ ///ctor
+ Bone(): Id(-1), ParentId(-1), RotationAngle(0.0f) {}
+ ///this operator is needed to sort the bones after Id's
+ bool operator<(const Bone& rval) const
+ {return Id<rval.Id; }
+ ///this operator is needed to find a bone by its name in a vector<Bone>
+ bool operator==(const std::string& rval) const
+ {return Name==rval; }
+ bool operator==(const aiString& rval) const
+ {return Name==std::string(rval.data); }
+
+ void CalculateBoneToWorldSpaceMatrix(std::vector<Bone>& Bones);
+
+};
+
+
+
+///Describes an Ogre Animation
+struct Animation
+{
+ std::string Name;
+ float Length;
+ std::vector<Track> Tracks;
+};
+
+///a track (keyframes for one bone) from an animation
+struct Track
+{
+ std::string BoneName;
+ std::vector<Keyframe> Keyframes;
+};
+
+/// keyframe (bone transformation) from a track from a animation
+struct Keyframe
+{
+ float Time;
+ aiVector3D Position;
+ aiQuaternion Rotation;
+ aiVector3D Scaling;
+};
+
+}//namespace Ogre
+}//namespace Assimp
diff --git a/3rdparty/assimp/code/OgreImporterMaterial.cpp b/3rdparty/assimp/code/OgreImporterMaterial.cpp
new file mode 100644
index 000000000..c3986e002
--- /dev/null
+++ b/3rdparty/assimp/code/OgreImporterMaterial.cpp
@@ -0,0 +1,255 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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.
+
+----------------------------------------------------------------------
+*/
+
+/**
+This file contains material related code. This is
+spilitted up from the main file OgreImporter.cpp
+to make it shorter easier to maintain.
+*/
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
+
+#include <vector>
+#include <sstream>
+using namespace std;
+
+//#include "boost/format.hpp"
+//#include "boost/foreach.hpp"
+//using namespace boost;
+
+#include "OgreImporter.h"
+#include "irrXMLWrapper.h"
+#include "TinyFormatter.h"
+
+namespace Assimp
+{
+namespace Ogre
+{
+
+
+
+aiMaterial* OgreImporter::LoadMaterial(const std::string MaterialName) const
+{
+ // const aiScene* const m_CurrentScene=this->m_CurrentScene;//make sure, that we can access but not change the scene
+
+ MaterialHelper *NewMaterial=new MaterialHelper();
+
+ aiString ts(MaterialName.c_str());
+ NewMaterial->AddProperty(&ts, AI_MATKEY_NAME);
+ /*For bettetr understanding of the material parser, here is a material example file:
+
+ material Sarg
+ {
+ receive_shadows on
+ technique
+ {
+ pass
+ {
+ ambient 0.500000 0.500000 0.500000 1.000000
+ diffuse 0.640000 0.640000 0.640000 1.000000
+ specular 0.500000 0.500000 0.500000 1.000000 12.500000
+ emissive 0.000000 0.000000 0.000000 1.000000
+ texture_unit
+ {
+ texture SargTextur.tga
+ tex_address_mode wrap
+ filtering linear linear none
+ }
+ }
+ }
+ }
+
+ */
+
+
+ const string MaterialFileName=m_CurrentFilename.substr(0, m_CurrentFilename.find('.'))+".material";
+ DefaultLogger::get()->info("Trying to load " +MaterialFileName);
+
+ //Read the file into memory and put it in a stringstream
+ stringstream ss;
+ {// after this block, the temporarly loaded data will be released
+ IOStream* MatFilePtr=m_CurrentIOHandler->Open(MaterialFileName);
+ if (NULL==MatFilePtr)
+ {
+ MatFilePtr=m_CurrentIOHandler->Open(m_MaterialLibFilename);
+ if (NULL==MatFilePtr)
+ {
+ DefaultLogger::get()->error(m_MaterialLibFilename+" and "+MaterialFileName + " could not be opened, Material will not be loaded!");
+ return NewMaterial;
+ }
+ }
+ boost::scoped_ptr<IOStream> MaterialFile(MatFilePtr);
+ vector<char> FileData(MaterialFile->FileSize());
+ MaterialFile->Read(&FileData[0], MaterialFile->FileSize(), 1);
+ BaseImporter::ConvertToUTF8(FileData);
+
+ ss << &FileData[0];
+ }
+
+ string Line;
+ ss >> Line;
+// unsigned int Level=0;//Hierarchielevels in the material file, like { } blocks into another
+ while (!ss.eof())
+ {
+ if (Line=="material")
+ {
+ ss >> Line;
+ if (Line==MaterialName)//Load the next material
+ {
+ ss >> Line;
+ if (Line!="{")
+ throw DeadlyImportError("empty material!");
+
+ while (Line!="}")//read until the end of the material
+ {
+ //Proceed to the first technique
+ ss >> Line;
+ if (Line=="technique")
+ {
+ ss >> Line;
+ if (Line!="{")
+ throw DeadlyImportError("empty technique!");
+ while (Line!="}")//read until the end of the technique
+ {
+ ss >> Line;
+ if (Line=="pass")
+ {
+ ss >> Line;
+ if (Line!="{")
+ throw DeadlyImportError("empty pass!");
+ while (Line!="}")//read until the end of the pass
+ {
+ ss >> Line;
+ if (Line=="ambient")
+ {
+ float r,g,b;
+ ss >> r >> g >> b;
+ const aiColor3D Color(r,g,b);
+ NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_AMBIENT);
+ }
+ else if (Line=="diffuse")
+ {
+ float r,g,b;
+ ss >> r >> g >> b;
+ const aiColor3D Color(r,g,b);
+ NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_DIFFUSE);
+ }
+ else if (Line=="specular")
+ {
+ float r,g,b;
+ ss >> r >> g >> b;
+ const aiColor3D Color(r,g,b);
+ NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_SPECULAR);
+ }
+ else if (Line=="emmisive")
+ {
+ float r,g,b;
+ ss >> r >> g >> b;
+ const aiColor3D Color(r,g,b);
+ NewMaterial->AddProperty(&Color, 1, AI_MATKEY_COLOR_EMISSIVE);
+ }
+ else if (Line=="texture_unit")
+ {
+ ss >> Line;
+ if (Line!="{")
+ throw DeadlyImportError("empty texture unit!");
+ while (Line!="}")//read until the end of the texture_unit
+ {
+ ss >> Line;
+ if (Line=="texture")
+ {
+ ss >> Line;
+ aiString ts(Line.c_str());
+ NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));
+ }
+ }//end of texture unit
+ }
+ }
+ }
+ }//end of technique
+
+
+ }
+
+
+ DefaultLogger::get()->info(Line);
+ //read informations from a custom material:
+ if (Line=="set")
+ {
+ ss >> Line;
+ if (Line=="$specular")//todo load this values:
+ {
+ }
+ if (Line=="$diffuse")
+ {
+ }
+ if (Line=="$ambient")
+ {
+ }
+ if (Line=="$colormap")
+ {
+ ss >> Line;
+ aiString ts(Line.c_str());
+ NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));
+ }
+ if (Line=="$normalmap")
+ {
+ ss >> Line;
+ aiString ts(Line.c_str());
+ NewMaterial->AddProperty(&ts, AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0));
+ }
+ }
+ }//end of material
+ }
+ else {} //this is the wrong material, proceed the file until we reach the next material
+ }
+ ss >> Line;
+ }
+
+ return NewMaterial;
+}
+
+
+
+}//namespace Ogre
+}//namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_OGRE_IMPORTER
diff --git a/3rdparty/assimp/code/OgreXmlHelper.h b/3rdparty/assimp/code/OgreXmlHelper.h
new file mode 100644
index 000000000..be72deee9
--- /dev/null
+++ b/3rdparty/assimp/code/OgreXmlHelper.h
@@ -0,0 +1,79 @@
+
+#include "irrXMLWrapper.h"
+#include "fast_atof.h"
+
+namespace Assimp
+{
+namespace Ogre
+{
+
+typedef irr::io::IrrXMLReader XmlReader;
+
+
+//------------Helper Funktion to Get a Attribute Save---------------
+template<typename t> inline t GetAttribute(XmlReader* Reader, std::string Name);
+
+/*
+{
+ BOOST_STATIC_ASSERT(false);
+ return t();
+}
+*/
+
+template<> inline int GetAttribute<int>(XmlReader* Reader, std::string Name)
+{
+ const char* Value=Reader->getAttributeValue(Name.c_str());
+ if (Value)
+ return atoi(Value);
+ else
+ throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+}
+
+template<> inline float GetAttribute<float>(XmlReader* Reader, std::string Name)
+{
+ const char* Value=Reader->getAttributeValue(Name.c_str());
+ if (Value)
+ return fast_atof(Value);
+ else
+ throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+}
+
+template<> inline std::string GetAttribute<std::string>(XmlReader* Reader, std::string Name)
+{
+ const char* Value=Reader->getAttributeValue(Name.c_str());
+ if (Value)
+ return std::string(Value);
+ else
+ throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+}
+
+template<> inline bool GetAttribute<bool>(XmlReader* Reader, std::string Name)
+{
+ const char* Value=Reader->getAttributeValue(Name.c_str());
+ if (Value)
+ {
+ if (Value==std::string("true"))
+ return true;
+ else if (Value==std::string("false"))
+ return false;
+ else
+ throw DeadlyImportError(std::string("Bool value has invalid value: "+Name+" / "+Value+" / "+Reader->getNodeName()));
+ }
+ else
+ throw DeadlyImportError(std::string("Attribute "+Name+" does not exist in "+Reader->getNodeName()).c_str());
+}
+//__________________________________________________________________
+
+inline bool XmlRead(XmlReader* Reader)
+{
+ do
+ {
+ if (!Reader->read())
+ return false;
+ }
+ while (Reader->getNodeType()!=irr::io::EXN_ELEMENT);
+ return true;
+}
+
+}//namespace Ogre
+}//namespace Assimp
diff --git a/3rdparty/assimp/code/OptimizeGraph.cpp b/3rdparty/assimp/code/OptimizeGraph.cpp
new file mode 100644
index 000000000..a4829e126
--- /dev/null
+++ b/3rdparty/assimp/code/OptimizeGraph.cpp
@@ -0,0 +1,347 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 OptimizeGraph.cpp
+ * @brief Implementation of the aiProcess_OptimizGraph step
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
+
+using namespace Assimp;
+#include "OptimizeGraph.h"
+#include "ProcessHelper.h"
+#include "SceneCombiner.h"
+
+#define AI_RESERVED_NODE_NAME "$Reserved_And_Evil"
+
+/* AI_OG_USE_HASHING enables the use of hashing to speed-up std::set lookups.
+ * The unhashed variant should be faster, except for *very* large data sets
+ */
+#ifdef AI_OG_USE_HASHING
+ // Use our standard hashing function to compute the hash
+# define AI_OG_GETKEY(str) SuperFastHash(str.data,str.length)
+#else
+ // Otherwise hope that std::string will utilize a static buffer
+ // for shorter node names. This would avoid endless heap copying.
+# define AI_OG_GETKEY(str) std::string(str.data)
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+OptimizeGraphProcess::OptimizeGraphProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+OptimizeGraphProcess::~OptimizeGraphProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const
+{
+ return (0 != (pFlags & aiProcess_OptimizeGraph));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties for the postprocessing step
+void OptimizeGraphProcess::SetupProperties(const Importer* pImp)
+{
+ // Get value of AI_CONFIG_PP_OG_EXCLUDE_LIST
+ std::string tmp = pImp->GetPropertyString(AI_CONFIG_PP_OG_EXCLUDE_LIST,"");
+ AddLockedNodeList(tmp);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Collect new children
+void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& nodes)
+{
+ nodes_in += nd->mNumChildren;
+
+ // Process children
+ std::list<aiNode*> child_nodes;
+ for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
+
+ CollectNewChildren(nd->mChildren[i],child_nodes);
+ nd->mChildren[i] = NULL;
+ }
+
+ // Check whether we need this node; if not we can replace it by our own children (warn, danger of incest).
+ if (locked.find(AI_OG_GETKEY(nd->mName)) == locked.end() ) {
+ for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
+
+ if (locked.find(AI_OG_GETKEY((*it)->mName)) == locked.end()) {
+ (*it)->mTransformation = nd->mTransformation * (*it)->mTransformation;
+ nodes.push_back(*it);
+
+ it = child_nodes.erase(it);
+ continue;
+ }
+ ++it;
+ }
+
+ if (nd->mNumMeshes || child_nodes.size()) {
+ nodes.push_back(nd);
+ }
+ else {
+ delete nd; /* bye, node */
+ return;
+ }
+ }
+ else {
+
+ // Retain our current position in the hierarchy
+ nodes.push_back(nd);
+
+ // Now check for possible optimizations in our list of child nodes. join as many as possible
+ aiNode* join_master = NULL;
+ aiMatrix4x4 inv;
+
+ const LockedSetType::const_iterator end = locked.end();
+
+ std::list<aiNode*> join;
+ for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
+ aiNode* child = *it;
+ if (child->mNumChildren == 0 && locked.find(AI_OG_GETKEY(child->mName)) == end) {
+
+ // There may be no instanced meshes
+ unsigned int n = 0;
+ for (; n < child->mNumMeshes;++n) {
+ if (meshes[child->mMeshes[n]] > 1) {
+ break;
+ }
+ }
+ if (n == child->mNumMeshes) {
+
+ if (!join_master) {
+ join_master = child;
+ inv = join_master->mTransformation;
+ inv.Inverse();
+ }
+ else {
+
+ child->mTransformation = inv * child->mTransformation ;
+
+ join.push_back(child);
+ it = child_nodes.erase(it);
+ continue;
+ }
+ }
+ }
+ ++it;
+ }
+ if (join_master && join.size()) {
+ join_master->mName.length = sprintf(join_master->mName.data,"$MergedNode_%i",count_merged++);
+
+ unsigned int out_meshes = 0;
+ for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) {
+ out_meshes += (*it)->mNumMeshes;
+ }
+
+ // copy all mesh references in one array
+ if (out_meshes) {
+ unsigned int* meshes = new unsigned int[out_meshes+join_master->mNumMeshes], *tmp = meshes;
+ for (unsigned int n = 0; n < join_master->mNumMeshes;++n) {
+ *tmp++ = join_master->mMeshes[n];
+ }
+
+ for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) {
+ for (unsigned int n = 0; n < (*it)->mNumMeshes; ++n) {
+
+ *tmp = (*it)->mMeshes[n];
+ aiMesh* mesh = mScene->mMeshes[*tmp++];
+
+ // manually move the mesh into the right coordinate system
+ const aiMatrix3x3 IT = aiMatrix3x3( (*it)->mTransformation ).Inverse().Transpose();
+ for (unsigned int a = 0; a < mesh->mNumVertices; ++a) {
+
+ mesh->mVertices[a] *= (*it)->mTransformation;
+
+ if (mesh->HasNormals())
+ mesh->mNormals[a] *= IT;
+
+ if (mesh->HasTangentsAndBitangents()) {
+ mesh->mTangents[a] *= IT;
+ mesh->mBitangents[a] *= IT;
+ }
+ }
+ }
+ delete *it; // bye, node
+ }
+ delete[] join_master->mMeshes;
+ join_master->mMeshes = meshes;
+ join_master->mNumMeshes += out_meshes;
+ }
+ }
+ }
+ // reassign children if something changed
+ if (child_nodes.empty() || child_nodes.size() > nd->mNumChildren) {
+
+ delete[] nd->mChildren;
+
+ if (child_nodes.size())
+ nd->mChildren = new aiNode*[child_nodes.size()];
+ else nd->mChildren = NULL;
+ }
+
+ nd->mNumChildren = child_nodes.size();
+
+ aiNode** tmp = nd->mChildren;
+ for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) {
+ aiNode* node = *tmp++ = *it;
+ node->mParent = nd;
+ }
+
+ nodes_out += child_nodes.size();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Execute the postprocessing step on the given scene
+void OptimizeGraphProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("OptimizeGraphProcess begin");
+ nodes_in = nodes_out = count_merged = 0;
+ mScene = pScene;
+
+ meshes.resize(pScene->mNumMeshes,0);
+ FindInstancedMeshes(pScene->mRootNode);
+
+ // build a blacklist of identifiers. If the name of a node matches one of these, we won't touch it
+ locked.clear();
+ for (std::list<std::string>::const_iterator it = locked_nodes.begin(); it != locked_nodes.end(); ++it) {
+#ifdef AI_OG_USE_HASHING
+ locked.insert(SuperFastHash((*it).c_str()));
+#else
+ locked.insert(*it);
+#endif
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) {
+ for (unsigned int a = 0; a < pScene->mAnimations[i]->mNumChannels; ++a) {
+
+ aiNodeAnim* anim = pScene->mAnimations[i]->mChannels[a];
+ locked.insert(AI_OG_GETKEY(anim->mNodeName));
+ }
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ for (unsigned int a = 0; a < pScene->mMeshes[i]->mNumBones; ++a) {
+
+ aiBone* bone = pScene->mMeshes[i]->mBones[a];
+ locked.insert(AI_OG_GETKEY(bone->mName));
+
+ // HACK: Meshes referencing bones may not be transformed; we need to look them.
+ // The easiest way to do this is to increase their reference counters ...
+ meshes[i] += 2;
+ }
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {
+ aiCamera* cam = pScene->mCameras[i];
+ locked.insert(AI_OG_GETKEY(cam->mName));
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumLights; ++i) {
+ aiLight* lgh = pScene->mLights[i];
+ locked.insert(AI_OG_GETKEY(lgh->mName));
+ }
+
+ // Insert a dummy master node and make it read-only
+ aiNode* dummy_root = new aiNode(AI_RESERVED_NODE_NAME);
+ locked.insert(AI_OG_GETKEY(dummy_root->mName));
+
+ const aiString prev = pScene->mRootNode->mName;
+ pScene->mRootNode->mParent = dummy_root;
+
+ dummy_root->mChildren = new aiNode*[dummy_root->mNumChildren = 1];
+ dummy_root->mChildren[0] = pScene->mRootNode;
+
+ // Do our recursive processing of scenegraph nodes. For each node collect
+ // a fully new list of children and allow their children to place themselves
+ // on the same hierarchy layer as their parents.
+ std::list<aiNode*> nodes;
+ CollectNewChildren (dummy_root,nodes);
+
+ ai_assert(nodes.size() == 1);
+
+ if (dummy_root->mNumChildren > 1) {
+ pScene->mRootNode = dummy_root;
+
+ // Keep the dummy node but assign the name of the old root node to it
+ pScene->mRootNode->mName = prev;
+ }
+ else {
+
+ // Remove the dummy root node again.
+ pScene->mRootNode = dummy_root->mChildren[0];
+
+ dummy_root->mChildren[0] = NULL;
+ delete dummy_root;
+ }
+
+ pScene->mRootNode->mParent = NULL;
+ if (!DefaultLogger::isNullLogger()) {
+ if ( nodes_in != nodes_out) {
+
+ char buf[512];
+ sprintf(buf,"OptimizeGraphProcess finished; Input nodes: %i, Output nodes: %i",nodes_in,nodes_out);
+ DefaultLogger::get()->info(buf);
+ }
+ else DefaultLogger::get()->debug("OptimizeGraphProcess finished");
+ }
+ meshes.clear();
+ locked.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Buidl a LUT of all instanced meshes
+void OptimizeGraphProcess::FindInstancedMeshes (aiNode* pNode)
+{
+ for (unsigned int i = 0; i < pNode->mNumMeshes;++i) {
+ ++meshes[pNode->mMeshes[i]];
+ }
+
+ for (unsigned int i = 0; i < pNode->mNumChildren; ++i)
+ FindInstancedMeshes(pNode->mChildren[i]);
+}
+
+#endif // !! ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
diff --git a/3rdparty/assimp/code/OptimizeGraph.h b/3rdparty/assimp/code/OptimizeGraph.h
new file mode 100644
index 000000000..0572419ea
--- /dev/null
+++ b/3rdparty/assimp/code/OptimizeGraph.h
@@ -0,0 +1,147 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 OptimizeGraph.h
+ * @brief Declares a post processing step to optimize the scenegraph
+ */
+#ifndef AI_OPTIMIZEGRAPHPROCESS_H_INC
+#define AI_OPTIMIZEGRAPHPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "ProcessHelper.h"
+#include "../include/aiTypes.h"
+
+struct aiMesh;
+class OptimizeGraphProcessTest;
+namespace Assimp {
+
+// -----------------------------------------------------------------------------
+/** @brief Postprocessing step to optimize the scenegraph
+ *
+ * The implementation tries to merge nodes, even if they use different
+ * transformations. Animations are preserved.
+ *
+ * @see aiProcess_OptimizeGraph for a detailed description of the
+ * algorithm being applied.
+ */
+class ASSIMP_API OptimizeGraphProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::OptimizeGraphProcessTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ OptimizeGraphProcess();
+
+ /** Destructor, private as well */
+ ~OptimizeGraphProcess();
+
+public:
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Add a list of node names to be locked and not modified.
+ * @param in List of nodes. See #AI_CONFIG_PP_OG_EXCLUDE_LIST for
+ * format explanations.
+ */
+ inline void AddLockedNodeList(std::string& in)
+ {
+ ConvertListToStrings (in,locked_nodes);
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Add another node to be locked and not modified.
+ * @param name Name to be locked
+ */
+ inline void AddLockedNode(std::string& name)
+ {
+ locked_nodes.push_back(name);
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Rmeove a node from the list of locked nodes.
+ * @param name Name to be unlocked
+ */
+ inline void RemoveLockedNode(std::string& name)
+ {
+ locked_nodes.remove(name);
+ }
+
+protected:
+
+ void CollectNewChildren(aiNode* nd, std::list<aiNode*>& nodes);
+ void FindInstancedMeshes (aiNode* pNode);
+
+private:
+
+#ifdef AI_OG_USE_HASHING
+ typedef std::set<unsigned int> LockedSetType;
+#else
+ typedef std::set<std::string> LockedSetType;
+#endif
+
+
+ //! Scene we're working with
+ aiScene* mScene;
+
+ //! List of locked names. Stored is the hash of the name
+ LockedSetType locked;
+
+ //! List of nodes to be locked in addition to those with animations, lights or cameras assigned.
+ std::list<std::string> locked_nodes;
+
+ //! Node counters for logging purposes
+ unsigned int nodes_in,nodes_out, count_merged;
+
+ //! Reference counters for meshes
+ std::vector<unsigned int> meshes;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_OPTIMIZEGRAPHPROCESS_H_INC
diff --git a/3rdparty/assimp/code/OptimizeMeshes.cpp b/3rdparty/assimp/code/OptimizeMeshes.cpp
new file mode 100644
index 000000000..ca6127390
--- /dev/null
+++ b/3rdparty/assimp/code/OptimizeMeshes.cpp
@@ -0,0 +1,243 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 OptimizeMeshes.cpp
+ * @brief Implementation of the aiProcess_OptimizeMeshes step
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
+
+using namespace Assimp;
+#include "OptimizeMeshes.h"
+#include "ProcessHelper.h"
+#include "SceneCombiner.h"
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+OptimizeMeshesProcess::OptimizeMeshesProcess()
+: pts (false)
+, max_verts (0xffffffff)
+, max_faces (0xffffffff)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+OptimizeMeshesProcess::~OptimizeMeshesProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool OptimizeMeshesProcess::IsActive( unsigned int pFlags) const
+{
+ // Our behaviour needs to be different if the SortByPType or SplitLargeMeshes
+ // steps are active. Thus we need to query their flags here and store the
+ // information, although we're breaking const-correctness.
+ // That's a serious design flaw, consider redesign.
+ if ( 0 != (pFlags & aiProcess_OptimizeMeshes) ) {
+ pts = (0 != (pFlags & aiProcess_SortByPType));
+ max_verts = (0 != (pFlags & aiProcess_SplitLargeMeshes)) ? 0xdeadbeef : 0;
+ return true;
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties for the postprocessing step
+void OptimizeMeshesProcess::SetupProperties(const Importer* pImp)
+{
+ if (max_verts == 0xdeadbeef /* magic hack */) {
+ max_faces = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES);
+ max_verts = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Execute step
+void OptimizeMeshesProcess::Execute( aiScene* pScene)
+{
+ const unsigned int num_old = pScene->mNumMeshes;
+ if (num_old <= 1) {
+ DefaultLogger::get()->debug("Skipping OptimizeMeshesProcess");
+ return;
+ }
+
+ DefaultLogger::get()->debug("OptimizeMeshesProcess begin");
+ mScene = pScene;
+
+ // need to clear persistent members from previous runs
+ merge_list.clear();
+ output.clear();
+
+ merge_list.reserve(pScene->mNumMeshes);
+ output.reserve(pScene->mNumMeshes);
+
+ // Prepare lookup tables
+ meshes.resize(pScene->mNumMeshes);
+ FindInstancedMeshes(pScene->mRootNode);
+ if (max_verts == 0xdeadbeef) /* undo the magic hack */
+ max_verts = 0xffffffff;
+
+ // ... instanced meshes are immediately processed and added to the output list
+ for (unsigned int i = 0, n = 0; i < pScene->mNumMeshes;++i) {
+ meshes[i].vertex_format = GetMeshVFormatUnique(pScene->mMeshes[i]);
+
+ if (meshes[i].instance_cnt > 1 && meshes[i].output_id == 0xffffffff) {
+ meshes[i].output_id = n++;
+ output.push_back(mScene->mMeshes[i]);
+ }
+ }
+
+ // and process all nodes in the scenegraoh recursively
+ ProcessNode(pScene->mRootNode);
+ if (!output.size()) {
+ throw DeadlyImportError("OptimizeMeshes: No meshes remaining; there's definitely something wrong");
+ }
+
+ meshes.clear();
+ ai_assert(output.size() <= num_old);
+
+ mScene->mNumMeshes = output.size();
+ std::copy(output.begin(),output.end(),mScene->mMeshes);
+
+ if (output.size() != num_old) {
+ char tmp[512];
+ ::sprintf(tmp,"OptimizeMeshesProcess finished. Input meshes: %i, Output meshes: %i",num_old,pScene->mNumMeshes);
+ DefaultLogger::get()->info(tmp);
+ }
+ else DefaultLogger::get()->debug("OptimizeMeshesProcess finished");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Process meshes for a single node
+void OptimizeMeshesProcess::ProcessNode( aiNode* pNode)
+{
+ for (unsigned int i = 0; i < pNode->mNumMeshes;++i) {
+ unsigned int& im = pNode->mMeshes[i];
+
+ if (meshes[im].instance_cnt > 1) {
+ im = meshes[im].output_id;
+ }
+ else {
+ merge_list.clear();
+ unsigned int verts = 0, faces = 0;
+
+ // Find meshes to merge with us
+ for (unsigned int a = i+1; a < pNode->mNumMeshes;++a) {
+ register unsigned int am = pNode->mMeshes[a];
+ if (meshes[am].instance_cnt == 1 && CanJoin(im,am,verts,faces)) {
+
+ merge_list.push_back(mScene->mMeshes[am]);
+ verts += mScene->mMeshes[am]->mNumVertices;
+ faces += mScene->mMeshes[am]->mNumFaces;
+
+ --pNode->mNumMeshes;
+ for (unsigned int n = a; n < pNode->mNumMeshes; ++n)
+ pNode->mMeshes[n] = pNode->mMeshes[n+1];
+
+ --a;
+ }
+ }
+
+ // and merge all meshes which we found, replace the old ones
+ if (!merge_list.empty()) {
+ merge_list.push_back(mScene->mMeshes[im]);
+
+ aiMesh* out;
+ SceneCombiner::MergeMeshes(&out,0,merge_list.begin(),merge_list.end());
+ output.push_back(out);
+ }
+ else {
+ output.push_back(mScene->mMeshes[im]);
+ }
+ im = output.size()-1;
+ }
+ }
+
+
+ for (unsigned int i = 0; i < pNode->mNumChildren; ++i)
+ ProcessNode(pNode->mChildren[i]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Check whether two meshes can be joined
+bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned int verts, unsigned int faces )
+{
+ if (meshes[a].vertex_format != meshes[b].vertex_format)
+ return false;
+
+ aiMesh* ma = mScene->mMeshes[a], *mb = mScene->mMeshes[b];
+
+ if ((0xffffffff != max_verts && verts+mb->mNumVertices > max_verts) ||
+ (0xffffffff != max_faces && faces+mb->mNumFaces > max_faces)) {
+ return false;
+ }
+
+ // Never merge unskinned meshes with skinned meshes
+ if (ma->mMaterialIndex != mb->mMaterialIndex || ma->HasBones() != mb->HasBones())
+ return false;
+
+ // Never merge meshes with different kinds of primitives if SortByPType did already
+ // do its work. We would destroy everything again ...
+ if (pts && ma->mPrimitiveTypes != mb->mPrimitiveTypes)
+ return false;
+
+ // If both meshes are skinned, check whether we have many bones defined in both meshes.
+ // If yes, we can savely join them.
+ if (ma->HasBones()) {
+ // TODO
+ return false;
+ }
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Buidl a LUT of all instanced meshes
+void OptimizeMeshesProcess::FindInstancedMeshes (aiNode* pNode)
+{
+ for (unsigned int i = 0; i < pNode->mNumMeshes;++i)
+ ++meshes[pNode->mMeshes[i]].instance_cnt;
+
+ for (unsigned int i = 0; i < pNode->mNumChildren; ++i)
+ FindInstancedMeshes(pNode->mChildren[i]);
+}
+
+#endif // !! ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
diff --git a/3rdparty/assimp/code/OptimizeMeshes.h b/3rdparty/assimp/code/OptimizeMeshes.h
new file mode 100644
index 000000000..f7284cccc
--- /dev/null
+++ b/3rdparty/assimp/code/OptimizeMeshes.h
@@ -0,0 +1,187 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 OptimizeMeshes.h
+ * @brief Declares a post processing step to join meshes, if possible
+ */
+#ifndef AI_OPTIMIZEMESHESPROCESS_H_INC
+#define AI_OPTIMIZEMESHESPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiTypes.h"
+
+struct aiMesh;
+class OptimizeMeshesProcessTest;
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** @brief Postprocessing step to optimize mesh usage
+ *
+ * The implementation looks for meshes that could be joined and joins them.
+ * Usually this will reduce the number of drawcalls.
+ *
+ * @note Instanced meshes are currently not processed.
+ */
+class ASSIMP_API OptimizeMeshesProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::OptimizeMeshesProcessTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ OptimizeMeshesProcess();
+
+ /** Destructor, private as well */
+ ~OptimizeMeshesProcess();
+
+
+ /** @brief Internal utility to store additional mesh info
+ */
+ struct MeshInfo
+ {
+ MeshInfo()
+ : instance_cnt (0)
+ , vertex_format (0)
+ , output_id (0xffffffff)
+ {}
+
+ //! Number of times this mesh is referenced
+ unsigned int instance_cnt;
+
+ //! Vertex format id
+ unsigned int vertex_format;
+
+ //! Output ID
+ unsigned int output_id;
+ };
+
+public:
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Specify whether you want meshes with different
+ * primitive types to be merged as well.
+ *
+ * IsActive() sets this property automatically to true if the
+ * aiProcess_SortByPType flag is found.
+ */
+ void EnablePrimitiveTypeSorting(bool enable) {
+ pts = enable;
+ }
+
+ // Getter
+ bool IsPrimitiveTypeSortingEnabled () const {
+ return pts;
+ }
+
+
+ // -------------------------------------------------------------------
+ /** @brief Specify a maximum size of a single output mesh.
+ *
+ * If a single input mesh already exceeds this limit, it won't
+ * be splitted.
+ * @param verts Maximum number of vertices per mesh
+ * @param faces Maximum number of faces per mesh
+ */
+ void SetPreferredMeshSizeLimit (unsigned int verts, unsigned int faces)
+ {
+ max_verts = verts;
+ max_faces = faces;
+ }
+
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** @brief Do the actual optimization on all meshes of this node
+ * @param pNode Node we're working with
+ */
+ void ProcessNode( aiNode* pNode);
+
+ // -------------------------------------------------------------------
+ /** @brief Returns true if b can be joined with a
+ *
+ * @param verts Number of output verts up to now
+ * @param faces Number of output faces up to now
+ */
+ bool CanJoin ( unsigned int a, unsigned int b,
+ unsigned int verts, unsigned int faces );
+
+ // -------------------------------------------------------------------
+ /** @brief Find instanced meshes, for the moment we're excluding
+ * them from all optimizations
+ */
+ void FindInstancedMeshes (aiNode* pNode);
+
+private:
+
+ //! Scene we're working with
+ aiScene* mScene;
+
+ //! Per mesh info
+ std::vector<MeshInfo> meshes;
+
+ //! Next output mesh
+ aiMesh* mesh;
+
+ //! Output meshes
+ std::vector<aiMesh*> output;
+
+ //! @see EnablePrimitiveTypeSorting
+ mutable bool pts;
+
+ //! @see SetPreferredMeshSizeLimit
+ mutable unsigned int max_verts,max_faces;
+
+ //! Temporary storage
+ std::vector<aiMesh*> merge_list;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CALCTANGENTSPROCESS_H_INC
diff --git a/3rdparty/assimp/code/ParsingUtils.h b/3rdparty/assimp/code/ParsingUtils.h
new file mode 100644
index 000000000..4cae6519b
--- /dev/null
+++ b/3rdparty/assimp/code/ParsingUtils.h
@@ -0,0 +1,181 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 ParsingUtils.h
+ * @brief Defines helper functions for text parsing
+ */
+#ifndef AI_PARSING_UTILS_H_INC
+#define AI_PARSING_UTILS_H_INC
+
+#include "StringComparison.h"
+namespace Assimp {
+
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE bool IsSpace( const char_t in)
+{
+ return (in == (char_t)' ' || in == (char_t)'\t');
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE bool IsLineEnd( const char_t in)
+{
+ return (in == (char_t)'\r' || in == (char_t)'\n' || in == (char_t)'\0');
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE bool IsSpaceOrNewLine( const char_t in)
+{
+ return IsSpace<char_t>(in) || IsLineEnd<char_t>(in);
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE bool SkipSpaces( const char_t* in, const char_t** out)
+{
+ while (*in == (char_t)' ' || *in == (char_t)'\t')in++;
+ *out = in;
+ return !IsLineEnd<char_t>(*in);
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE bool SkipSpaces( const char_t** inout)
+{
+ return SkipSpaces<char_t>(*inout,inout);
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool SkipLine( const char_t* in, const char_t** out)
+{
+ while (*in != (char_t)'\r' && *in != (char_t)'\n' && *in != (char_t)'\0')in++;
+
+ // files are opened in binary mode. Ergo there are both NL and CR
+ while (*in == (char_t)'\r' || *in == (char_t)'\n')in++;
+ *out = in;
+ return *in != (char_t)'\0';
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool SkipLine( const char_t** inout)
+{
+ return SkipLine<char_t>(*inout,inout);
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out)
+{
+ while (*in == (char_t)' ' || *in == (char_t)'\t' ||
+ *in == (char_t)'\r' || *in == (char_t)'\n')in++;
+ *out = in;
+ return *in != '\0';
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool SkipSpacesAndLineEnd( const char_t** inout)
+{
+ return SkipSpacesAndLineEnd<char_t>(*inout,inout);
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+inline bool GetNextLine(const char_t*& buffer, char_t out[4096])
+{
+ if ((char_t)'\0' == *buffer)return false;
+
+ char* _out = out;
+ char* const end = _out+4096;
+ while (!IsLineEnd( *buffer ) && _out < end)
+ *_out++ = *buffer++;
+ *_out = (char_t)'\0';
+
+ while (IsLineEnd( *buffer ) && '\0' != *buffer)++buffer;
+ return true;
+}
+// ---------------------------------------------------------------------------------
+template <class char_t>
+AI_FORCE_INLINE bool IsNumeric( char_t in)
+{
+ return ( in >= '0' && in <= '9' ) || '-' == in || '+' == in;
+}
+// ---------------------------------------------------------------------------------
+AI_FORCE_INLINE bool TokenMatch(char*& in, const char* token, unsigned int len)
+{
+ if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len]))
+ {
+ in += len+1;
+ return true;
+ }
+ return false;
+}
+// ---------------------------------------------------------------------------------
+/** @brief Case-ignoring version of TokenMatch
+ * @param in Input
+ * @param token Token to check for
+ * @param len Number of characters to check
+ */
+AI_FORCE_INLINE bool TokenMatchI(const char*& in, const char* token, unsigned int len)
+{
+ if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len]))
+ {
+ in += len+1;
+ return true;
+ }
+ return false;
+}
+// ---------------------------------------------------------------------------------
+AI_FORCE_INLINE bool TokenMatch(const char*& in, const char* token, unsigned int len)
+{
+ return TokenMatch(const_cast<char*&>(in), token, len);
+}
+// ---------------------------------------------------------------------------------
+AI_FORCE_INLINE void SkipToken(const char*& in)
+{
+ SkipSpaces(&in);
+ while (!IsSpaceOrNewLine(*in))++in;
+}
+// ---------------------------------------------------------------------------------
+AI_FORCE_INLINE std::string GetNextToken(const char*& in)
+{
+ SkipSpacesAndLineEnd(&in);
+ const char* cur = in;
+ while (!IsSpaceOrNewLine(*in))++in;
+ return std::string(cur,(size_t)(in-cur));
+}
+} // ! namespace Assimp
+#endif // ! AI_PARSING_UTILS_H_INC
diff --git a/3rdparty/assimp/code/PlyLoader.cpp b/3rdparty/assimp/code/PlyLoader.cpp
new file mode 100644
index 000000000..f7f7d05ca
--- /dev/null
+++ b/3rdparty/assimp/code/PlyLoader.cpp
@@ -0,0 +1,1050 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 PlyLoader.cpp
+ * @brief Implementation of the PLY importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
+
+// internal headers
+#include "PlyLoader.h"
+#include "MaterialSystem.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+PLYImporter::PLYImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+PLYImporter::~PLYImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool PLYImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "ply")
+ return true;
+ else if (!extension.length() || checkSig)
+ {
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"ply"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void PLYImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ply");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void PLYImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open PLY file " + pFile + ".");
+ }
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+ mBuffer = (unsigned char*)&mBuffer2[0];
+
+ // the beginning of the file must be PLY - magic, magic
+ if ((mBuffer[0] != 'P' && mBuffer[0] != 'p') ||
+ (mBuffer[1] != 'L' && mBuffer[1] != 'l') ||
+ (mBuffer[2] != 'Y' && mBuffer[2] != 'y')) {
+ throw DeadlyImportError( "Invalid .ply file: Magic number \'ply\' is no there");
+ }
+
+ char* szMe = (char*)&this->mBuffer[3];
+ SkipSpacesAndLineEnd(szMe,(const char**)&szMe);
+
+ // determine the format of the file data
+ PLY::DOM sPlyDom;
+ if (TokenMatch(szMe,"format",6))
+ {
+ if (TokenMatch(szMe,"ascii",5))
+ {
+ SkipLine(szMe,(const char**)&szMe);
+ if (!PLY::DOM::ParseInstance(szMe,&sPlyDom))
+ throw DeadlyImportError( "Invalid .ply file: Unable to build DOM (#1)");
+ }
+ else if (!::strncmp(szMe,"binary_",7))
+ {
+ bool bIsBE = false;
+ szMe+=7;
+
+ // binary_little_endian
+ // binary_big_endian
+#if (defined AI_BUILD_BIG_ENDIAN)
+ if ('l' == *szMe || 'L' == *szMe)bIsBE = true;
+#else
+ if ('b' == *szMe || 'B' == *szMe)bIsBE = true;
+#endif // ! AI_BUILD_BIG_ENDIAN
+
+ // skip the line, parse the rest of the header and build the DOM
+ SkipLine(szMe,(const char**)&szMe);
+ if (!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE))
+ throw DeadlyImportError( "Invalid .ply file: Unable to build DOM (#2)");
+ }
+ else throw DeadlyImportError( "Invalid .ply file: Unknown file format");
+ }
+ else
+ {
+ delete[] this->mBuffer;
+ AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
+ throw DeadlyImportError( "Invalid .ply file: Missing format specification");
+ }
+ this->pcDOM = &sPlyDom;
+
+ // now load a list of vertices. This must be sucessfull in order to procede
+ std::vector<aiVector3D> avPositions;
+ this->LoadVertices(&avPositions,false);
+
+ if (avPositions.empty())
+ throw DeadlyImportError( "Invalid .ply file: No vertices found. "
+ "Unable to parse the data format of the PLY file.");
+
+ // now load a list of normals.
+ std::vector<aiVector3D> avNormals;
+ LoadVertices(&avNormals,true);
+
+ // load the face list
+ std::vector<PLY::Face> avFaces;
+ LoadFaces(&avFaces);
+
+ // if no face list is existing we assume that the vertex
+ // list is containing a list of triangles
+ if (avFaces.empty())
+ {
+ if (avPositions.size() < 3)
+ {
+ throw DeadlyImportError( "Invalid .ply file: Not enough "
+ "vertices to build a proper face list. ");
+ }
+
+ const unsigned int iNum = (unsigned int)avPositions.size() / 3;
+ for (unsigned int i = 0; i< iNum;++i)
+ {
+ PLY::Face sFace;
+ sFace.mIndices.push_back((iNum*3));
+ sFace.mIndices.push_back((iNum*3)+1);
+ sFace.mIndices.push_back((iNum*3)+2);
+ avFaces.push_back(sFace);
+ }
+ }
+
+ // now load a list of all materials
+ std::vector<MaterialHelper*> avMaterials;
+ LoadMaterial(&avMaterials);
+
+ // now load a list of all vertex color channels
+ std::vector<aiColor4D> avColors;
+ avColors.reserve(avPositions.size());
+ LoadVertexColor(&avColors);
+
+ // now try to load texture coordinates
+ std::vector<aiVector2D> avTexCoords;
+ avTexCoords.reserve(avPositions.size());
+ LoadTextureCoordinates(&avTexCoords);
+
+ // now replace the default material in all faces and validate all material indices
+ ReplaceDefaultMaterial(&avFaces,&avMaterials);
+
+ // now convert this to a list of aiMesh instances
+ std::vector<aiMesh*> avMeshes;
+ avMeshes.reserve(avMaterials.size()+1);
+ ConvertMeshes(&avFaces,&avPositions,&avNormals,
+ &avColors,&avTexCoords,&avMaterials,&avMeshes);
+
+ if (avMeshes.empty())
+ throw DeadlyImportError( "Invalid .ply file: Unable to extract mesh data ");
+
+ // now generate the output scene object. Fill the material list
+ pScene->mNumMaterials = (unsigned int)avMaterials.size();
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ pScene->mMaterials[i] = avMaterials[i];
+
+ // fill the mesh list
+ pScene->mNumMeshes = (unsigned int)avMeshes.size();
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mMeshes[i] = avMeshes[i];
+
+ // generate a simple node structure
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+ pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+
+ for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes;++i)
+ pScene->mRootNode->mMeshes[i] = i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Split meshes by material IDs
+void PLYImporter::ConvertMeshes(std::vector<PLY::Face>* avFaces,
+ const std::vector<aiVector3D>* avPositions,
+ const std::vector<aiVector3D>* avNormals,
+ const std::vector<aiColor4D>* avColors,
+ const std::vector<aiVector2D>* avTexCoords,
+ const std::vector<MaterialHelper*>* avMaterials,
+ std::vector<aiMesh*>* avOut)
+{
+ ai_assert(NULL != avFaces);
+ ai_assert(NULL != avPositions);
+ ai_assert(NULL != avMaterials);
+
+ // split by materials
+ std::vector<unsigned int>* aiSplit = new std::vector<unsigned int>[avMaterials->size()];
+
+ unsigned int iNum = 0;
+ for (std::vector<PLY::Face>::const_iterator i = avFaces->begin();i != avFaces->end();++i,++iNum)
+ aiSplit[(*i).iMaterialIndex].push_back(iNum);
+
+ // now generate submeshes
+ for (unsigned int p = 0; p < avMaterials->size();++p)
+ {
+ if (aiSplit[p].size() != 0)
+ {
+ // allocate the mesh object
+ aiMesh* p_pcOut = new aiMesh();
+ p_pcOut->mMaterialIndex = p;
+
+ p_pcOut->mNumFaces = (unsigned int)aiSplit[p].size();
+ p_pcOut->mFaces = new aiFace[aiSplit[p].size()];
+
+ // at first we need to determine the size of the output vector array
+ unsigned int iNum = 0;
+ for (unsigned int i = 0; i < aiSplit[p].size();++i)
+ {
+ iNum += (unsigned int)(*avFaces)[aiSplit[p][i]].mIndices.size();
+ }
+ p_pcOut->mNumVertices = iNum;
+ p_pcOut->mVertices = new aiVector3D[iNum];
+
+ if (!avColors->empty())
+ p_pcOut->mColors[0] = new aiColor4D[iNum];
+ if (!avTexCoords->empty())
+ {
+ p_pcOut->mNumUVComponents[0] = 2;
+ p_pcOut->mTextureCoords[0] = new aiVector3D[iNum];
+ }
+ if (!avNormals->empty())
+ p_pcOut->mNormals = new aiVector3D[iNum];
+
+ // add all faces
+ iNum = 0;
+ unsigned int iVertex = 0;
+ for (std::vector<unsigned int>::const_iterator i = aiSplit[p].begin();
+ i != aiSplit[p].end();++i,++iNum)
+ {
+ p_pcOut->mFaces[iNum].mNumIndices = (unsigned int)(*avFaces)[*i].mIndices.size();
+ p_pcOut->mFaces[iNum].mIndices = new unsigned int[p_pcOut->mFaces[iNum].mNumIndices];
+
+ // build an unique set of vertices/colors for this face
+ for (unsigned int q = 0; q < p_pcOut->mFaces[iNum].mNumIndices;++q)
+ {
+ p_pcOut->mFaces[iNum].mIndices[q] = iVertex;
+ p_pcOut->mVertices[iVertex] = (*avPositions)[(*avFaces)[*i].mIndices[q]];
+
+ if (!avColors->empty())
+ p_pcOut->mColors[0][iVertex] = (*avColors)[(*avFaces)[*i].mIndices[q]];
+
+ if (!avTexCoords->empty())
+ {
+ const aiVector2D& vec = (*avTexCoords)[(*avFaces)[*i].mIndices[q]];
+ p_pcOut->mTextureCoords[0][iVertex].x = vec.x;
+ p_pcOut->mTextureCoords[0][iVertex].y = vec.y;
+ }
+
+ if (!avNormals->empty())
+ p_pcOut->mNormals[iVertex] = (*avNormals)[(*avFaces)[*i].mIndices[q]];
+ iVertex++;
+ }
+
+ }
+ // add the mesh to the output list
+ avOut->push_back(p_pcOut);
+ }
+ }
+ delete[] aiSplit; // cleanup
+}
+
+// ------------------------------------------------------------------------------------------------
+// Generate a default material if none was specified and apply it to all vanilla faces
+void PLYImporter::ReplaceDefaultMaterial(std::vector<PLY::Face>* avFaces,
+ std::vector<MaterialHelper*>* avMaterials)
+{
+ bool bNeedDefaultMat = false;
+
+ for (std::vector<PLY::Face>::iterator i = avFaces->begin();i != avFaces->end();++i) {
+ if (0xFFFFFFFF == (*i).iMaterialIndex) {
+ bNeedDefaultMat = true;
+ (*i).iMaterialIndex = (unsigned int)avMaterials->size();
+ }
+ else if ((*i).iMaterialIndex >= avMaterials->size() ) {
+ // clamp the index
+ (*i).iMaterialIndex = (unsigned int)avMaterials->size()-1;
+ }
+ }
+
+ if (bNeedDefaultMat) {
+ // generate a default material
+ MaterialHelper* pcHelper = new MaterialHelper();
+
+ // fill in a default material
+ int iMode = (int)aiShadingMode_Gouraud;
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.6f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ // The face order is absolutely undefined for PLY, so we have to
+ // use two-sided rendering to be sure it's ok.
+ const int two_sided = 1;
+ pcHelper->AddProperty(&two_sided,1,AI_MATKEY_TWOSIDED);
+
+ avMaterials->push_back(pcHelper);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void PLYImporter::LoadTextureCoordinates(std::vector<aiVector2D>* pvOut)
+{
+ ai_assert(NULL != pvOut);
+
+ unsigned int aiPositions[2] = {0xFFFFFFFF,0xFFFFFFFF};
+ PLY::EDataType aiTypes[2] = {EDT_Char,EDT_Char};
+ PLY::ElementInstanceList* pcList = NULL;
+ unsigned int cnt = 0;
+
+ // serach in the DOM for a vertex entry
+ unsigned int _i = 0;
+ for (std::vector<PLY::Element>::const_iterator i = pcDOM->alElements.begin();
+ i != pcDOM->alElements.end();++i,++_i)
+ {
+ if (PLY::EEST_Vertex == (*i).eSemantic)
+ {
+ pcList = &this->pcDOM->alElementData[_i];
+
+ // now check whether which normal components are available
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if ((*a).bIsList)continue;
+ if (PLY::EST_UTextureCoord == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[0] = _a;
+ aiTypes[0] = (*a).eType;
+ }
+ else if (PLY::EST_VTextureCoord == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[1] = _a;
+ aiTypes[1] = (*a).eType;
+ }
+ }
+ }
+ }
+ // check whether we have a valid source for the texture coordinates data
+ if (NULL != pcList && 0 != cnt)
+ {
+ pvOut->reserve(pcList->alInstances.size());
+ for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();
+ i != pcList->alInstances.end();++i)
+ {
+ // convert the vertices to sp floats
+ aiVector2D vOut;
+
+ if (0xFFFFFFFF != aiPositions[0])
+ {
+ vOut.x = PLY::PropertyInstance::ConvertTo<float>(
+ (*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]);
+ }
+
+ if (0xFFFFFFFF != aiPositions[1])
+ {
+ vOut.y = PLY::PropertyInstance::ConvertTo<float>(
+ (*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]);
+ }
+ // and add them to our nice list
+ pvOut->push_back(vOut);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to extract vertices from the PLY DOM
+void PLYImporter::LoadVertices(std::vector<aiVector3D>* pvOut, bool p_bNormals)
+{
+ ai_assert(NULL != pvOut);
+
+ unsigned int aiPositions[3] = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF};
+ PLY::EDataType aiTypes[3] = {EDT_Char,EDT_Char,EDT_Char};
+ PLY::ElementInstanceList* pcList = NULL;
+ unsigned int cnt = 0;
+
+ // serach in the DOM for a vertex entry
+ unsigned int _i = 0;
+ for (std::vector<PLY::Element>::const_iterator i = pcDOM->alElements.begin();
+ i != pcDOM->alElements.end();++i,++_i)
+ {
+ if (PLY::EEST_Vertex == (*i).eSemantic)
+ {
+ pcList = &pcDOM->alElementData[_i];
+
+ // load normal vectors?
+ if (p_bNormals)
+ {
+ // now check whether which normal components are available
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if ((*a).bIsList)continue;
+ if (PLY::EST_XNormal == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[0] = _a;
+ aiTypes[0] = (*a).eType;
+ }
+ else if (PLY::EST_YNormal == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[1] = _a;
+ aiTypes[1] = (*a).eType;
+ }
+ else if (PLY::EST_ZNormal == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[2] = _a;
+ aiTypes[2] = (*a).eType;
+ }
+ }
+ }
+ // load vertex coordinates
+ else
+ {
+ // now check whether which coordinate sets are available
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if ((*a).bIsList)continue;
+ if (PLY::EST_XCoord == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[0] = _a;
+ aiTypes[0] = (*a).eType;
+ }
+ else if (PLY::EST_YCoord == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[1] = _a;
+ aiTypes[1] = (*a).eType;
+ }
+ else if (PLY::EST_ZCoord == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[2] = _a;
+ aiTypes[2] = (*a).eType;
+ }
+ if (3 == cnt)break;
+ }
+ }
+ break;
+ }
+ }
+ // check whether we have a valid source for the vertex data
+ if (NULL != pcList && 0 != cnt)
+ {
+ pvOut->reserve(pcList->alInstances.size());
+ for (std::vector<ElementInstance>::const_iterator
+ i = pcList->alInstances.begin();
+ i != pcList->alInstances.end();++i)
+ {
+ // convert the vertices to sp floats
+ aiVector3D vOut;
+
+ if (0xFFFFFFFF != aiPositions[0])
+ {
+ vOut.x = PLY::PropertyInstance::ConvertTo<float>(
+ (*i).alProperties[aiPositions[0]].avList.front(),aiTypes[0]);
+ }
+
+ if (0xFFFFFFFF != aiPositions[1])
+ {
+ vOut.y = PLY::PropertyInstance::ConvertTo<float>(
+ (*i).alProperties[aiPositions[1]].avList.front(),aiTypes[1]);
+ }
+
+ if (0xFFFFFFFF != aiPositions[2])
+ {
+ vOut.z = PLY::PropertyInstance::ConvertTo<float>(
+ (*i).alProperties[aiPositions[2]].avList.front(),aiTypes[2]);
+ }
+
+ // and add them to our nice list
+ pvOut->push_back(vOut);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert a color component to [0...1]
+float PLYImporter::NormalizeColorValue (PLY::PropertyInstance::ValueUnion val,
+ PLY::EDataType eType)
+{
+ switch (eType)
+ {
+ case EDT_Float:
+ return val.fFloat;
+ case EDT_Double:
+ return (float)val.fDouble;
+
+ case EDT_UChar:
+ return (float)val.iUInt / (float)0xFF;
+ case EDT_Char:
+ return (float)(val.iInt+(0xFF/2)) / (float)0xFF;
+ case EDT_UShort:
+ return (float)val.iUInt / (float)0xFFFF;
+ case EDT_Short:
+ return (float)(val.iInt+(0xFFFF/2)) / (float)0xFFFF;
+ case EDT_UInt:
+ return (float)val.iUInt / (float)0xFFFF;
+ case EDT_Int:
+ return ((float)val.iInt / (float)0xFF) + 0.5f;
+ default: ;
+ };
+ return 0.0f;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to extract proper vertex colors from the PLY DOM
+void PLYImporter::LoadVertexColor(std::vector<aiColor4D>* pvOut)
+{
+ ai_assert(NULL != pvOut);
+
+ unsigned int aiPositions[4] = {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF};
+ PLY::EDataType aiTypes[4] = {EDT_Char, EDT_Char, EDT_Char, EDT_Char}; // silencing gcc
+ unsigned int cnt = 0;
+ PLY::ElementInstanceList* pcList = NULL;
+
+ // serach in the DOM for a vertex entry
+ unsigned int _i = 0;
+ for (std::vector<PLY::Element>::const_iterator i = pcDOM->alElements.begin();
+ i != pcDOM->alElements.end();++i,++_i)
+ {
+ if (PLY::EEST_Vertex == (*i).eSemantic)
+ {
+ pcList = &this->pcDOM->alElementData[_i];
+
+ // now check whether which coordinate sets are available
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator
+ a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if ((*a).bIsList)continue;
+ if (PLY::EST_Red == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[0] = _a;
+ aiTypes[0] = (*a).eType;
+ }
+ else if (PLY::EST_Green == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[1] = _a;
+ aiTypes[1] = (*a).eType;
+ }
+ else if (PLY::EST_Blue == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[2] = _a;
+ aiTypes[2] = (*a).eType;
+ }
+ else if (PLY::EST_Alpha == (*a).Semantic)
+ {
+ cnt++;
+ aiPositions[3] = _a;
+ aiTypes[3] = (*a).eType;
+ }
+ if (4 == cnt)break;
+ }
+ break;
+ }
+ }
+ // check whether we have a valid source for the vertex data
+ if (NULL != pcList && 0 != cnt)
+ {
+ pvOut->reserve(pcList->alInstances.size());
+ for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();
+ i != pcList->alInstances.end();++i)
+ {
+ // convert the vertices to sp floats
+ aiColor4D vOut;
+
+ if (0xFFFFFFFF != aiPositions[0])
+ {
+ vOut.r = NormalizeColorValue((*i).alProperties[
+ aiPositions[0]].avList.front(),aiTypes[0]);
+ }
+
+ if (0xFFFFFFFF != aiPositions[1])
+ {
+ vOut.g = NormalizeColorValue((*i).alProperties[
+ aiPositions[1]].avList.front(),aiTypes[1]);
+ }
+
+ if (0xFFFFFFFF != aiPositions[2])
+ {
+ vOut.b = NormalizeColorValue((*i).alProperties[
+ aiPositions[2]].avList.front(),aiTypes[2]);
+ }
+
+ // assume 1.0 for the alpha channel ifit is not set
+ if (0xFFFFFFFF == aiPositions[3])vOut.a = 1.0f;
+ else
+ {
+ vOut.a = NormalizeColorValue((*i).alProperties[
+ aiPositions[3]].avList.front(),aiTypes[3]);
+ }
+
+ // and add them to our nice list
+ pvOut->push_back(vOut);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Try to extract proper faces from the PLY DOM
+void PLYImporter::LoadFaces(std::vector<PLY::Face>* pvOut)
+{
+ ai_assert(NULL != pvOut);
+
+ PLY::ElementInstanceList* pcList = NULL;
+ bool bOne = false;
+
+ // index of the vertex index list
+ unsigned int iProperty = 0xFFFFFFFF;
+ PLY::EDataType eType = EDT_Char;
+ bool bIsTristrip = false;
+
+ // index of the material index property
+ unsigned int iMaterialIndex = 0xFFFFFFFF;
+ PLY::EDataType eType2 = EDT_Char;
+
+ // serach in the DOM for a face entry
+ unsigned int _i = 0;
+ for (std::vector<PLY::Element>::const_iterator i = pcDOM->alElements.begin();
+ i != pcDOM->alElements.end();++i,++_i)
+ {
+ // face = unique number of vertex indices
+ if (PLY::EEST_Face == (*i).eSemantic)
+ {
+ pcList = &pcDOM->alElementData[_i];
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if (PLY::EST_VertexIndex == (*a).Semantic)
+ {
+ // must be a dynamic list!
+ if (!(*a).bIsList)continue;
+ iProperty = _a;
+ bOne = true;
+ eType = (*a).eType;
+ }
+ else if (PLY::EST_MaterialIndex == (*a).Semantic)
+ {
+ if ((*a).bIsList)continue;
+ iMaterialIndex = _a;
+ bOne = true;
+ eType2 = (*a).eType;
+ }
+ }
+ break;
+ }
+ // triangle strip
+ // TODO: triangle strip and material index support???
+ else if (PLY::EEST_TriStrip == (*i).eSemantic)
+ {
+ // find a list property in this ...
+ pcList = &this->pcDOM->alElementData[_i];
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ // must be a dynamic list!
+ if (!(*a).bIsList)continue;
+ iProperty = _a;
+ bOne = true;
+ bIsTristrip = true;
+ eType = (*a).eType;
+ break;
+ }
+ break;
+ }
+ }
+ // check whether we have at least one per-face information set
+ if (pcList && bOne)
+ {
+ if (!bIsTristrip)
+ {
+ pvOut->reserve(pcList->alInstances.size());
+ for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();
+ i != pcList->alInstances.end();++i)
+ {
+ PLY::Face sFace;
+
+ // parse the list of vertex indices
+ if (0xFFFFFFFF != iProperty)
+ {
+ const unsigned int iNum = (unsigned int)(*i).alProperties[iProperty].avList.size();
+ sFace.mIndices.resize(iNum);
+
+ std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator p =
+ (*i).alProperties[iProperty].avList.begin();
+
+ for (unsigned int a = 0; a < iNum;++a,++p)
+ {
+ sFace.mIndices[a] = PLY::PropertyInstance::ConvertTo<unsigned int>(*p,eType);
+ }
+ }
+
+ // parse the material index
+ if (0xFFFFFFFF != iMaterialIndex)
+ {
+ sFace.iMaterialIndex = PLY::PropertyInstance::ConvertTo<unsigned int>(
+ (*i).alProperties[iMaterialIndex].avList.front(),eType2);
+ }
+ pvOut->push_back(sFace);
+ }
+ }
+ else // triangle strips
+ {
+ // normally we have only one triangle strip instance where
+ // a value of -1 indicates a restart of the strip
+ bool flip = false;
+ for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();i != pcList->alInstances.end();++i) {
+ const std::vector<PLY::PropertyInstance::ValueUnion>& quak = (*i).alProperties[iProperty].avList;
+ pvOut->reserve(pvOut->size() + quak.size() + (quak.size()>>2u));
+
+ int aiTable[2] = {-1,-1};
+ for (std::vector<PLY::PropertyInstance::ValueUnion>::const_iterator a = quak.begin();a != quak.end();++a) {
+ const int p = PLY::PropertyInstance::ConvertTo<int>(*a,eType);
+
+ if (-1 == p) {
+ // restart the strip ...
+ aiTable[0] = aiTable[1] = -1;
+ flip = false;
+ continue;
+ }
+ if (-1 == aiTable[0]) {
+ aiTable[0] = p;
+ continue;
+ }
+ if (-1 == aiTable[1]) {
+ aiTable[1] = p;
+ continue;
+ }
+
+ pvOut->push_back(PLY::Face());
+ PLY::Face& sFace = pvOut->back();
+ sFace.mIndices[0] = aiTable[0];
+ sFace.mIndices[1] = aiTable[1];
+ sFace.mIndices[2] = p;
+ if ((flip = !flip)) {
+ std::swap(sFace.mIndices[0],sFace.mIndices[1]);
+ }
+
+ aiTable[0] = aiTable[1];
+ aiTable[1] = p;
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a RGBA color in [0...1] range
+void PLYImporter::GetMaterialColor(const std::vector<PLY::PropertyInstance>& avList,
+ unsigned int aiPositions[4],
+ PLY::EDataType aiTypes[4],
+ aiColor4D* clrOut)
+{
+ ai_assert(NULL != clrOut);
+
+ if (0xFFFFFFFF == aiPositions[0])clrOut->r = 0.0f;
+ else
+ {
+ clrOut->r = NormalizeColorValue(avList[
+ aiPositions[0]].avList.front(),aiTypes[0]);
+ }
+
+ if (0xFFFFFFFF == aiPositions[1])clrOut->g = 0.0f;
+ else
+ {
+ clrOut->g = NormalizeColorValue(avList[
+ aiPositions[1]].avList.front(),aiTypes[1]);
+ }
+
+ if (0xFFFFFFFF == aiPositions[2])clrOut->b = 0.0f;
+ else
+ {
+ clrOut->b = NormalizeColorValue(avList[
+ aiPositions[2]].avList.front(),aiTypes[2]);
+ }
+
+ // assume 1.0 for the alpha channel ifit is not set
+ if (0xFFFFFFFF == aiPositions[3])clrOut->a = 1.0f;
+ else
+ {
+ clrOut->a = NormalizeColorValue(avList[
+ aiPositions[3]].avList.front(),aiTypes[3]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Extract a material from the PLY DOM
+void PLYImporter::LoadMaterial(std::vector<MaterialHelper*>* pvOut)
+{
+ ai_assert(NULL != pvOut);
+
+ // diffuse[4], specular[4], ambient[4]
+ // rgba order
+ unsigned int aaiPositions[3][4] = {
+
+ {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
+ {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
+ {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF},
+ };
+
+ PLY::EDataType aaiTypes[3][4] = {
+ {EDT_Char,EDT_Char,EDT_Char,EDT_Char},
+ {EDT_Char,EDT_Char,EDT_Char,EDT_Char},
+ {EDT_Char,EDT_Char,EDT_Char,EDT_Char}
+ };
+ PLY::ElementInstanceList* pcList = NULL;
+
+ unsigned int iPhong = 0xFFFFFFFF;
+ PLY::EDataType ePhong = EDT_Char;
+
+ unsigned int iOpacity = 0xFFFFFFFF;
+ PLY::EDataType eOpacity = EDT_Char;
+
+ // serach in the DOM for a vertex entry
+ unsigned int _i = 0;
+ for (std::vector<PLY::Element>::const_iterator i = this->pcDOM->alElements.begin();
+ i != this->pcDOM->alElements.end();++i,++_i)
+ {
+ if (PLY::EEST_Material == (*i).eSemantic)
+ {
+ pcList = &this->pcDOM->alElementData[_i];
+
+ // now check whether which coordinate sets are available
+ unsigned int _a = 0;
+ for (std::vector<PLY::Property>::const_iterator
+ a = (*i).alProperties.begin();
+ a != (*i).alProperties.end();++a,++_a)
+ {
+ if ((*a).bIsList)continue;
+
+ // pohng specularity -----------------------------------
+ if (PLY::EST_PhongPower == (*a).Semantic)
+ {
+ iPhong = _a;
+ ePhong = (*a).eType;
+ }
+
+ // general opacity -----------------------------------
+ if (PLY::EST_Opacity == (*a).Semantic)
+ {
+ iOpacity = _a;
+ eOpacity = (*a).eType;
+ }
+
+ // diffuse color channels -----------------------------------
+ if (PLY::EST_DiffuseRed == (*a).Semantic)
+ {
+ aaiPositions[0][0] = _a;
+ aaiTypes[0][0] = (*a).eType;
+ }
+ else if (PLY::EST_DiffuseGreen == (*a).Semantic)
+ {
+ aaiPositions[0][1] = _a;
+ aaiTypes[0][1] = (*a).eType;
+ }
+ else if (PLY::EST_DiffuseBlue == (*a).Semantic)
+ {
+ aaiPositions[0][2] = _a;
+ aaiTypes[0][2] = (*a).eType;
+ }
+ else if (PLY::EST_DiffuseAlpha == (*a).Semantic)
+ {
+ aaiPositions[0][3] = _a;
+ aaiTypes[0][3] = (*a).eType;
+ }
+ // specular color channels -----------------------------------
+ else if (PLY::EST_SpecularRed == (*a).Semantic)
+ {
+ aaiPositions[1][0] = _a;
+ aaiTypes[1][0] = (*a).eType;
+ }
+ else if (PLY::EST_SpecularGreen == (*a).Semantic)
+ {
+ aaiPositions[1][1] = _a;
+ aaiTypes[1][1] = (*a).eType;
+ }
+ else if (PLY::EST_SpecularBlue == (*a).Semantic)
+ {
+ aaiPositions[1][2] = _a;
+ aaiTypes[1][2] = (*a).eType;
+ }
+ else if (PLY::EST_SpecularAlpha == (*a).Semantic)
+ {
+ aaiPositions[1][3] = _a;
+ aaiTypes[1][3] = (*a).eType;
+ }
+ // ambient color channels -----------------------------------
+ else if (PLY::EST_AmbientRed == (*a).Semantic)
+ {
+ aaiPositions[2][0] = _a;
+ aaiTypes[2][0] = (*a).eType;
+ }
+ else if (PLY::EST_AmbientGreen == (*a).Semantic)
+ {
+ aaiPositions[2][1] = _a;
+ aaiTypes[2][1] = (*a).eType;
+ }
+ else if (PLY::EST_AmbientBlue == (*a).Semantic)
+ {
+ aaiPositions[2][2] = _a;
+ aaiTypes[2][2] = (*a).eType;
+ }
+ else if (PLY::EST_AmbientAlpha == (*a).Semantic)
+ {
+ aaiPositions[2][3] = _a;
+ aaiTypes[2][3] = (*a).eType;
+ }
+ }
+ break;
+ }
+ }
+ // check whether we have a valid source for the material data
+ if (NULL != pcList) {
+ for (std::vector<ElementInstance>::const_iterator i = pcList->alInstances.begin();i != pcList->alInstances.end();++i) {
+ aiColor4D clrOut;
+ MaterialHelper* pcHelper = new MaterialHelper();
+
+ // build the diffuse material color
+ GetMaterialColor((*i).alProperties,aaiPositions[0],aaiTypes[0],&clrOut);
+ pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ // build the specular material color
+ GetMaterialColor((*i).alProperties,aaiPositions[1],aaiTypes[1],&clrOut);
+ pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_SPECULAR);
+
+ // build the ambient material color
+ GetMaterialColor((*i).alProperties,aaiPositions[2],aaiTypes[2],&clrOut);
+ pcHelper->AddProperty<aiColor4D>(&clrOut,1,AI_MATKEY_COLOR_AMBIENT);
+
+ // handle phong power and shading mode
+ int iMode;
+ if (0xFFFFFFFF != iPhong) {
+ float fSpec = PLY::PropertyInstance::ConvertTo<float>((*i).alProperties[iPhong].avList.front(),ePhong);
+
+ // if shininess is 0 (and the pow() calculation would therefore always
+ // become 1, not depending on the angle), use gouraud lighting
+ if (fSpec) {
+ // scale this with 15 ... hopefully this is correct
+ fSpec *= 15;
+ pcHelper->AddProperty<float>(&fSpec, 1, AI_MATKEY_SHININESS);
+
+ iMode = (int)aiShadingMode_Phong;
+ }
+ else iMode = (int)aiShadingMode_Gouraud;
+ }
+ else iMode = (int)aiShadingMode_Gouraud;
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ // handle opacity
+ if (0xFFFFFFFF != iOpacity) {
+ float fOpacity = PLY::PropertyInstance::ConvertTo<float>((*i).alProperties[iPhong].avList.front(),eOpacity);
+ pcHelper->AddProperty<float>(&fOpacity, 1, AI_MATKEY_OPACITY);
+ }
+
+ // The face order is absolutely undefined for PLY, so we have to
+ // use two-sided rendering to be sure it's ok.
+ const int two_sided = 1;
+ pcHelper->AddProperty(&two_sided,1,AI_MATKEY_TWOSIDED);
+
+ // add the newly created material instance to the list
+ pvOut->push_back(pcHelper);
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER
diff --git a/3rdparty/assimp/code/PlyLoader.h b/3rdparty/assimp/code/PlyLoader.h
new file mode 100644
index 000000000..814321d7c
--- /dev/null
+++ b/3rdparty/assimp/code/PlyLoader.h
@@ -0,0 +1,174 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 PLYLoader.h
+ * @brief Declaration of the .ply importer class.
+ */
+#ifndef AI_PLYLOADER_H_INCLUDED
+#define AI_PLYLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+struct aiNode;
+
+#include "PlyParser.h"
+
+namespace Assimp {
+class MaterialHelper;
+
+using namespace PLY;
+
+// ---------------------------------------------------------------------------
+/** Importer class to load the stanford PLY file format
+*/
+class PLYImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ PLYImporter();
+
+ /** Destructor, private as well */
+ ~PLYImporter();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Extract vertices from the DOM
+ */
+ void LoadVertices(std::vector<aiVector3D>* pvOut,
+ bool p_bNormals = false);
+
+ // -------------------------------------------------------------------
+ /** Extract vertex color channels from the DOM
+ */
+ void LoadVertexColor(std::vector<aiColor4D>* pvOut);
+
+ // -------------------------------------------------------------------
+ /** Extract texture coordinate channels from the DOM
+ */
+ void LoadTextureCoordinates(std::vector<aiVector2D>* pvOut);
+
+ // -------------------------------------------------------------------
+ /** Extract a face list from the DOM
+ */
+ void LoadFaces(std::vector<PLY::Face>* pvOut);
+
+ // -------------------------------------------------------------------
+ /** Extract a material list from the DOM
+ */
+ void LoadMaterial(std::vector<MaterialHelper*>* pvOut);
+
+
+ // -------------------------------------------------------------------
+ /** Validate material indices, replace default material identifiers
+ */
+ void ReplaceDefaultMaterial(std::vector<PLY::Face>* avFaces,
+ std::vector<MaterialHelper*>* avMaterials);
+
+
+ // -------------------------------------------------------------------
+ /** Convert all meshes into our ourer representation
+ */
+ void ConvertMeshes(std::vector<PLY::Face>* avFaces,
+ const std::vector<aiVector3D>* avPositions,
+ const std::vector<aiVector3D>* avNormals,
+ const std::vector<aiColor4D>* avColors,
+ const std::vector<aiVector2D>* avTexCoords,
+ const std::vector<MaterialHelper*>* avMaterials,
+ std::vector<aiMesh*>* avOut);
+
+
+ // -------------------------------------------------------------------
+ /** Static helper to parse a color from four single channels in
+ */
+ static void GetMaterialColor(
+ const std::vector<PLY::PropertyInstance>& avList,
+ unsigned int aiPositions[4],
+ PLY::EDataType aiTypes[4],
+ aiColor4D* clrOut);
+
+
+ // -------------------------------------------------------------------
+ /** Static helper to parse a color channel value. The input value
+ * is normalized to 0-1.
+ */
+ static float NormalizeColorValue (
+ PLY::PropertyInstance::ValueUnion val,
+ PLY::EDataType eType);
+
+
+ /** Buffer to hold the loaded file */
+ unsigned char* mBuffer;
+
+ /** Document object model representation extracted from the file */
+ PLY::DOM* pcDOM;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/PlyParser.cpp b/3rdparty/assimp/code/PlyParser.cpp
new file mode 100644
index 000000000..d0e3fa0f5
--- /dev/null
+++ b/3rdparty/assimp/code/PlyParser.cpp
@@ -0,0 +1,919 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the PLY parser class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
+
+#include "PlyLoader.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+PLY::EDataType PLY::Property::ParseDataType(const char* pCur,const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+ PLY::EDataType eOut = PLY::EDT_INVALID;
+
+ if (TokenMatch(pCur,"char",4) ||
+ TokenMatch(pCur,"int8",4))
+ {
+ eOut = PLY::EDT_Char;
+ }
+ else if (TokenMatch(pCur,"uchar",5) ||
+ TokenMatch(pCur,"uint8",5))
+ {
+ eOut = PLY::EDT_UChar;
+ }
+ else if (TokenMatch(pCur,"short",5) ||
+ TokenMatch(pCur,"int16",5))
+ {
+ eOut = PLY::EDT_Short;
+ }
+ else if (TokenMatch(pCur,"ushort",6) ||
+ TokenMatch(pCur,"uint16",6))
+ {
+ eOut = PLY::EDT_UShort;
+ }
+ else if (TokenMatch(pCur,"int32",5) || TokenMatch(pCur,"int",3))
+ {
+ eOut = PLY::EDT_Int;
+ }
+ else if (TokenMatch(pCur,"uint32",6) || TokenMatch(pCur,"uint",4))
+ {
+ eOut = PLY::EDT_UInt;
+ }
+ else if (TokenMatch(pCur,"float",5) || TokenMatch(pCur,"float32",7))
+ {
+ eOut = PLY::EDT_Float;
+ }
+ else if (TokenMatch(pCur,"double64",8) || TokenMatch(pCur,"double",6) ||
+ TokenMatch(pCur,"float64",7))
+ {
+ eOut = PLY::EDT_Double;
+ }
+ if (PLY::EDT_INVALID == eOut)
+ {
+ DefaultLogger::get()->info("Found unknown data type in PLY file. This is OK");
+ }
+ *pCurOut = pCur;
+ return eOut;
+}
+
+// ------------------------------------------------------------------------------------------------
+PLY::ESemantic PLY::Property::ParseSemantic(const char* pCur,const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+
+ PLY::ESemantic eOut = PLY::EST_INVALID;
+ if (TokenMatch(pCur,"red",3))
+ {
+ eOut = PLY::EST_Red;
+ }
+ else if (TokenMatch(pCur,"green",5))
+ {
+ eOut = PLY::EST_Green;
+ }
+ else if (TokenMatch(pCur,"blue",4))
+ {
+ eOut = PLY::EST_Blue;
+ }
+ else if (TokenMatch(pCur,"alpha",5))
+ {
+ eOut = PLY::EST_Alpha;
+ }
+ else if (TokenMatch(pCur,"vertex_index",12) || TokenMatch(pCur,"vertex_indices",14))
+ {
+ eOut = PLY::EST_VertexIndex;
+ }
+ else if (TokenMatch(pCur,"material_index",14))
+ {
+ eOut = PLY::EST_MaterialIndex;
+ }
+ else if (TokenMatch(pCur,"ambient_red",11))
+ {
+ eOut = PLY::EST_AmbientRed;
+ }
+ else if (TokenMatch(pCur,"ambient_green",13))
+ {
+ eOut = PLY::EST_AmbientGreen;
+ }
+ else if (TokenMatch(pCur,"ambient_blue",12))
+ {
+ eOut = PLY::EST_AmbientBlue;
+ }
+ else if (TokenMatch(pCur,"ambient_alpha",13))
+ {
+ eOut = PLY::EST_AmbientAlpha;
+ }
+ else if (TokenMatch(pCur,"diffuse_red",11))
+ {
+ eOut = PLY::EST_DiffuseRed;
+ }
+ else if (TokenMatch(pCur,"diffuse_green",13))
+ {
+ eOut = PLY::EST_DiffuseGreen;
+ }
+ else if (TokenMatch(pCur,"diffuse_blue",12))
+ {
+ eOut = PLY::EST_DiffuseBlue;
+ }
+ else if (TokenMatch(pCur,"diffuse_alpha",13))
+ {
+ eOut = PLY::EST_DiffuseAlpha;
+ }
+ else if (TokenMatch(pCur,"specular_red",12))
+ {
+ eOut = PLY::EST_SpecularRed;
+ }
+ else if (TokenMatch(pCur,"specular_green",14))
+ {
+ eOut = PLY::EST_SpecularGreen;
+ }
+ else if (TokenMatch(pCur,"specular_blue",13))
+ {
+ eOut = PLY::EST_SpecularBlue;
+ }
+ else if (TokenMatch(pCur,"specular_alpha",14))
+ {
+ eOut = PLY::EST_SpecularAlpha;
+ }
+ else if (TokenMatch(pCur,"opacity",7))
+ {
+ eOut = PLY::EST_Opacity;
+ }
+ else if (TokenMatch(pCur,"specular_power",6))
+ {
+ eOut = PLY::EST_PhongPower;
+ }
+ else if (TokenMatch(pCur,"r",1))
+ {
+ eOut = PLY::EST_Red;
+ }
+ else if (TokenMatch(pCur,"g",1))
+ {
+ eOut = PLY::EST_Green;
+ }
+ else if (TokenMatch(pCur,"b",1))
+ {
+ eOut = PLY::EST_Blue;
+ }
+ // NOTE: Blender3D exports texture coordinates as s,t tuples
+ else if (TokenMatch(pCur,"u",1) || TokenMatch(pCur,"s",1) || TokenMatch(pCur,"tx",2))
+ {
+ eOut = PLY::EST_UTextureCoord;
+ }
+ else if (TokenMatch(pCur,"v",1) || TokenMatch(pCur,"t",1) || TokenMatch(pCur,"ty",2))
+ {
+ eOut = PLY::EST_VTextureCoord;
+ }
+ else if (TokenMatch(pCur,"x",1))
+ {
+ eOut = PLY::EST_XCoord;
+ }
+ else if (TokenMatch(pCur,"y",1))
+ {
+ eOut = PLY::EST_YCoord;
+ }
+ else if (TokenMatch(pCur,"z",1))
+ {
+ eOut = PLY::EST_ZCoord;
+ }
+ else if (TokenMatch(pCur,"nx",2))
+ {
+ eOut = PLY::EST_XNormal;
+ }
+ else if (TokenMatch(pCur,"ny",2))
+ {
+ eOut = PLY::EST_YNormal;
+ }
+ else if (TokenMatch(pCur,"nz",2))
+ {
+ eOut = PLY::EST_ZNormal;
+ }
+ else
+ {
+ DefaultLogger::get()->info("Found unknown property semantic in file. This is ok");
+ SkipLine(&pCur);
+ }
+ *pCurOut = pCur;
+ return eOut;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::Property::ParseProperty (const char* pCur,
+ const char** pCurOut,
+ PLY::Property* pOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+
+ // Forms supported:
+ // "property float x"
+ // "property list uchar int vertex_index"
+ *pCurOut = pCur;
+
+ // skip leading spaces
+ if (!SkipSpaces(pCur,&pCur))return false;
+
+ // skip the "property" string at the beginning
+ if (!TokenMatch(pCur,"property",8))
+ {
+ // seems not to be a valid property entry
+ return false;
+ }
+ // get next word
+ if (!SkipSpaces(pCur,&pCur))return false;
+ if (TokenMatch(pCur,"list",4))
+ {
+ pOut->bIsList = true;
+
+ // seems to be a list.
+ if (EDT_INVALID == (pOut->eFirstType = PLY::Property::ParseDataType(pCur, &pCur)))
+ {
+ // unable to parse list size data type
+ SkipLine(pCur,&pCur);
+ *pCurOut = pCur;
+ return false;
+ }
+ if (!SkipSpaces(pCur,&pCur))return false;
+ if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(pCur, &pCur)))
+ {
+ // unable to parse list data type
+ SkipLine(pCur,&pCur);
+ *pCurOut = pCur;
+ return false;
+ }
+ }
+ else
+ {
+ if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(pCur, &pCur)))
+ {
+ // unable to parse data type. Skip the property
+ SkipLine(pCur,&pCur);
+ *pCurOut = pCur;
+ return false;
+ }
+ }
+
+ if (!SkipSpaces(pCur,&pCur))return false;
+ const char* szCur = pCur;
+ pOut->Semantic = PLY::Property::ParseSemantic(pCur, &pCur);
+
+ if (PLY::EST_INVALID == pOut->Semantic)
+ {
+ // store the name of the semantic
+ uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
+
+ DefaultLogger::get()->info("Found unknown semantic in PLY file. This is OK");
+ pOut->szName = std::string(szCur,iDiff);
+ }
+
+ SkipSpacesAndLineEnd(pCur,&pCur);
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+PLY::EElementSemantic PLY::Element::ParseSemantic(const char* pCur,
+ const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+ PLY::EElementSemantic eOut = PLY::EEST_INVALID;
+ if (TokenMatch(pCur,"vertex",6))
+ {
+ eOut = PLY::EEST_Vertex;
+ }
+ else if (TokenMatch(pCur,"face",4))
+ {
+ eOut = PLY::EEST_Face;
+ }
+#if 0
+ // TODO: maybe implement this?
+ else if (TokenMatch(pCur,"range_grid",10))
+ {
+ eOut = PLY::EEST_Face;
+ }
+#endif
+ else if (TokenMatch(pCur,"tristrips",9))
+ {
+ eOut = PLY::EEST_TriStrip;
+ }
+ else if (TokenMatch(pCur,"edge",4))
+ {
+ eOut = PLY::EEST_Edge;
+ }
+ else if (TokenMatch(pCur,"material",8))
+ {
+ eOut = PLY::EEST_Material;
+ }
+ *pCurOut = pCur;
+ return eOut;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::Element::ParseElement (const char* pCur,
+ const char** pCurOut,
+ PLY::Element* pOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != pOut);
+
+ // Example format: "element vertex 8"
+ *pCurOut = pCur;
+
+ // skip leading spaces
+ if (!SkipSpaces(&pCur))return false;
+
+ // skip the "element" string at the beginning
+ if (!TokenMatch(pCur,"element",7))
+ {
+ // seems not to be a valid property entry
+ return false;
+ }
+ // get next word
+ if (!SkipSpaces(&pCur))return false;
+
+ // parse the semantic of the element
+ const char* szCur = pCur;
+ pOut->eSemantic = PLY::Element::ParseSemantic(pCur,&pCur);
+ if (PLY::EEST_INVALID == pOut->eSemantic)
+ {
+ // if the exact semantic can't be determined, just store
+ // the original string identifier
+ uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
+ pOut->szName = std::string(szCur,iDiff);
+ }
+
+ if (!SkipSpaces(&pCur))return false;
+
+ //parse the number of occurences of this element
+ pOut->NumOccur = strtol10(pCur,&pCur);
+
+ // go to the next line
+ SkipSpacesAndLineEnd(pCur,&pCur);
+
+ // now parse all properties of the element
+ while (true)
+ {
+ // skip all comments
+ PLY::DOM::SkipComments(pCur,&pCur);
+
+ PLY::Property prop;
+ if (!PLY::Property::ParseProperty(pCur,&pCur,&prop))break;
+ pOut->alProperties.push_back(prop);
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::SkipComments (const char* pCur,
+ const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+ *pCurOut = pCur;
+
+ // skip spaces
+ if (!SkipSpaces(pCur,&pCur))return false;
+
+ if (TokenMatch(pCur,"comment",7))
+ {
+ SkipLine(pCur,&pCur);
+ SkipComments(pCur,&pCur);
+ *pCurOut = pCur;
+ return true;
+ }
+ *pCurOut = pCur;
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseHeader (const char* pCur,const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+ DefaultLogger::get()->debug("PLY::DOM::ParseHeader() begin");
+
+ // after ply and format line
+ *pCurOut = pCur;
+
+ // parse all elements
+ while (true)
+ {
+ // skip all comments
+ PLY::DOM::SkipComments(pCur,&pCur);
+
+ PLY::Element out;
+ if (PLY::Element::ParseElement(pCur,&pCur,&out))
+ {
+ // add the element to the list of elements
+ alElements.push_back(out);
+ }
+ else if (TokenMatch(pCur,"end_header",10))
+ {
+ // we have reached the end of the header
+ break;
+ }
+ else
+ {
+ // ignore unknown header elements
+ SkipLine(&pCur);
+ }
+ }
+ SkipSpacesAndLineEnd(pCur,&pCur);
+ *pCurOut = pCur;
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseHeader() succeeded");
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseElementInstanceLists (
+ const char* pCur,
+ const char** pCurOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceLists() begin");
+ *pCurOut = pCur;
+
+ alElementData.resize(alElements.size());
+
+ std::vector<PLY::Element>::const_iterator i = alElements.begin();
+ std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin();
+
+ // parse all element instances
+ for (;i != alElements.end();++i,++a)
+ {
+ (*a).alInstances.resize((*i).NumOccur);
+ PLY::ElementInstanceList::ParseInstanceList(pCur,&pCur,&(*i),&(*a));
+ }
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceLists() succeeded");
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseElementInstanceListsBinary (
+ const char* pCur,
+ const char** pCurOut,
+ bool p_bBE)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut);
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceListsBinary() begin");
+ *pCurOut = pCur;
+
+ alElementData.resize(alElements.size());
+
+ std::vector<PLY::Element>::const_iterator i = alElements.begin();
+ std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin();
+
+ // parse all element instances
+ for (;i != alElements.end();++i,++a)
+ {
+ (*a).alInstances.resize((*i).NumOccur);
+ PLY::ElementInstanceList::ParseInstanceListBinary(pCur,&pCur,&(*i),&(*a),p_bBE);
+ }
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseElementInstanceListsBinary() succeeded");
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseInstanceBinary (const char* pCur,DOM* p_pcOut,bool p_bBE)
+{
+ ai_assert(NULL != pCur && NULL != p_pcOut);
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() begin");
+
+ if (!p_pcOut->ParseHeader(pCur,&pCur))
+ {
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() failure");
+ return false;
+ }
+ if (!p_pcOut->ParseElementInstanceListsBinary(pCur,&pCur,p_bBE))
+ {
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() failure");
+ return false;
+ }
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstanceBinary() succeeded");
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::DOM::ParseInstance (const char* pCur,DOM* p_pcOut)
+{
+ ai_assert(NULL != pCur);
+ ai_assert(NULL != p_pcOut);
+
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstance() begin");
+
+
+ if (!p_pcOut->ParseHeader(pCur,&pCur))
+ {
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstance() failure");
+ return false;
+ }
+ if (!p_pcOut->ParseElementInstanceLists(pCur,&pCur))
+ {
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstance() failure");
+ return false;
+ }
+ DefaultLogger::get()->debug("PLY::DOM::ParseInstance() succeeded");
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstanceList::ParseInstanceList (
+ const char* pCur,
+ const char** pCurOut,
+ const PLY::Element* pcElement,
+ PLY::ElementInstanceList* p_pcOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != pcElement && NULL != p_pcOut);
+
+ if (EEST_INVALID == pcElement->eSemantic || pcElement->alProperties.empty())
+ {
+ // if the element has an unknown semantic we can skip all lines
+ // However, there could be comments
+ for (unsigned int i = 0; i < pcElement->NumOccur;++i)
+ {
+ PLY::DOM::SkipComments(pCur,&pCur);
+ SkipLine(pCur,&pCur);
+ }
+ }
+ else
+ {
+ // be sure to have enough storage
+ for (unsigned int i = 0; i < pcElement->NumOccur;++i)
+ {
+ PLY::DOM::SkipComments(pCur,&pCur);
+ PLY::ElementInstance::ParseInstance(pCur, &pCur,pcElement,
+ &p_pcOut->alInstances[i]);
+ }
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstanceList::ParseInstanceListBinary (
+ const char* pCur,
+ const char** pCurOut,
+ const PLY::Element* pcElement,
+ PLY::ElementInstanceList* p_pcOut,
+ bool p_bBE /* = false */)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != pcElement && NULL != p_pcOut);
+
+ // we can add special handling code for unknown element semantics since
+ // we can't skip it as a whole block (we don't know its exact size
+ // due to the fact that lists could be contained in the property list
+ // of the unknown element)
+ for (unsigned int i = 0; i < pcElement->NumOccur;++i)
+ {
+ PLY::ElementInstance::ParseInstanceBinary(pCur, &pCur,pcElement,
+ &p_pcOut->alInstances[i], p_bBE);
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstance::ParseInstance (
+ const char* pCur,
+ const char** pCurOut,
+ const PLY::Element* pcElement,
+ PLY::ElementInstance* p_pcOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != pcElement && NULL != p_pcOut);
+
+ if (!SkipSpaces(pCur, &pCur))return false;
+
+ // allocate enough storage
+ p_pcOut->alProperties.resize(pcElement->alProperties.size());
+
+ std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
+ std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
+ for (;i != p_pcOut->alProperties.end();++i,++a)
+ {
+ if (!(PLY::PropertyInstance::ParseInstance(pCur, &pCur,&(*a),&(*i))))
+ {
+ DefaultLogger::get()->warn("Unable to parse property instance. "
+ "Skipping this element instance");
+
+ // skip the rest of the instance
+ SkipLine(pCur, &pCur);
+
+ PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType);
+ (*i).avList.push_back(v);
+ }
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::ElementInstance::ParseInstanceBinary (
+ const char* pCur,
+ const char** pCurOut,
+ const PLY::Element* pcElement,
+ PLY::ElementInstance* p_pcOut,
+ bool p_bBE /* = false */)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != pcElement && NULL != p_pcOut);
+
+ // allocate enough storage
+ p_pcOut->alProperties.resize(pcElement->alProperties.size());
+
+ std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
+ std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
+ for (;i != p_pcOut->alProperties.end();++i,++a)
+ {
+ if (!(PLY::PropertyInstance::ParseInstanceBinary(pCur, &pCur,&(*a),&(*i),p_bBE)))
+ {
+ DefaultLogger::get()->warn("Unable to parse binary property instance. "
+ "Skipping this element instance");
+
+ (*i).avList.push_back(PLY::PropertyInstance::DefaultValue((*a).eType));
+ }
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseInstance (const char* pCur,const char** pCurOut,
+ const PLY::Property* prop, PLY::PropertyInstance* p_pcOut)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != prop && NULL != p_pcOut);
+
+ *pCurOut = pCur;
+
+ // skip spaces at the beginning
+ if (!SkipSpaces(pCur, &pCur))return false;
+
+ if (prop->bIsList)
+ {
+ // parse the number of elements in the list
+ PLY::PropertyInstance::ValueUnion v;
+ PLY::PropertyInstance::ParseValue(pCur, &pCur,prop->eFirstType,&v);
+
+ // convert to unsigned int
+ unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v,prop->eFirstType);
+
+ // parse all list elements
+ p_pcOut->avList.resize(iNum);
+ for (unsigned int i = 0; i < iNum;++i)
+ {
+ if (!SkipSpaces(pCur, &pCur))return false;
+ PLY::PropertyInstance::ParseValue(pCur, &pCur,prop->eType,&p_pcOut->avList[i]);
+ }
+ }
+ else
+ {
+ // parse the property
+ PLY::PropertyInstance::ValueUnion v;
+
+ PLY::PropertyInstance::ParseValue(pCur, &pCur,prop->eType,&v);
+ p_pcOut->avList.push_back(v);
+ }
+ SkipSpacesAndLineEnd(pCur, &pCur);
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseInstanceBinary (
+ const char* pCur,
+ const char** pCurOut,
+ const PLY::Property* prop,
+ PLY::PropertyInstance* p_pcOut,
+ bool p_bBE)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != prop && NULL != p_pcOut);
+
+ if (prop->bIsList)
+ {
+ // parse the number of elements in the list
+ PLY::PropertyInstance::ValueUnion v;
+ PLY::PropertyInstance::ParseValueBinary(pCur, &pCur,prop->eFirstType,&v,p_bBE);
+
+ // convert to unsigned int
+ unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v,prop->eFirstType);
+
+ // parse all list elements
+ p_pcOut->avList.resize(iNum);
+ for (unsigned int i = 0; i < iNum;++i){
+ PLY::PropertyInstance::ParseValueBinary(pCur, &pCur,prop->eType,&p_pcOut->avList[i],p_bBE);
+ }
+ }
+ else
+ {
+ // parse the property
+ PLY::PropertyInstance::ValueUnion v;
+ PLY::PropertyInstance::ParseValueBinary(pCur, &pCur,prop->eType,&v,p_bBE);
+ p_pcOut->avList.push_back(v);
+ }
+ *pCurOut = pCur;
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+PLY::PropertyInstance::ValueUnion PLY::PropertyInstance::DefaultValue(
+ PLY::EDataType eType)
+{
+ PLY::PropertyInstance::ValueUnion out;
+
+ switch (eType)
+ {
+ case EDT_Float:
+ out.fFloat = 0.f;
+ return out;
+
+ case EDT_Double:
+ out.fDouble = 0.;
+ return out;
+
+ default: ;
+ };
+ out.iUInt = 0;
+ return out;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseValue(
+ const char* pCur,
+ const char** pCurOut,
+ PLY::EDataType eType,
+ PLY::PropertyInstance::ValueUnion* out)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != out);
+
+ register bool ret = true;
+ *pCurOut = pCur;
+ switch (eType)
+ {
+ case EDT_UInt:
+ case EDT_UShort:
+ case EDT_UChar:
+
+ out->iUInt = (uint32_t)strtol10(pCur, &pCur);
+ break;
+
+ case EDT_Int:
+ case EDT_Short:
+ case EDT_Char:
+
+ out->iInt = (int32_t)strtol10s(pCur, &pCur);
+ break;
+
+ case EDT_Float:
+
+ pCur = fast_atof_move(pCur,out->fFloat);
+ break;
+
+ case EDT_Double:
+
+ float f;
+ pCur = fast_atof_move(pCur,f);
+ out->fDouble = (double)f;
+ break;
+
+ default:
+ ret = false;
+ }
+ *pCurOut = pCur;
+ return ret;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool PLY::PropertyInstance::ParseValueBinary(
+ const char* pCur,
+ const char** pCurOut,
+ PLY::EDataType eType,
+ PLY::PropertyInstance::ValueUnion* out,
+ bool p_bBE)
+{
+ ai_assert(NULL != pCur && NULL != pCurOut && NULL != out);
+
+ register bool ret = true;
+ switch (eType)
+ {
+ case EDT_UInt:
+ out->iUInt = (uint32_t)*((uint32_t*)pCur);
+ pCur += 4;
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap((int32_t*)&out->iUInt);
+ break;
+
+ case EDT_UShort:
+ {
+ int16_t i = *((uint16_t*)pCur);
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap(&i);
+ out->iUInt = (uint32_t)i;
+ pCur += 2;
+ break;
+ }
+
+ case EDT_UChar:
+ {
+ out->iUInt = (uint32_t)(*((uint8_t*)pCur));
+ pCur ++;
+ break;
+ }
+
+ case EDT_Int:
+ out->iInt = *((int32_t*)pCur);
+ pCur += 4;
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap(&out->iInt);
+ break;
+
+ case EDT_Short:
+ {
+ int16_t i = *((int16_t*)pCur);
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap(&i);
+ out->iInt = (int32_t)i;
+ pCur += 2;
+ break;
+ }
+
+ case EDT_Char:
+ out->iInt = (int32_t)*((int8_t*)pCur);
+ pCur ++;
+ break;
+
+ case EDT_Float:
+ {
+ out->fFloat = *((float*)pCur);
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap((int32_t*)&out->fFloat);
+ pCur += 4;
+ break;
+ }
+ case EDT_Double:
+ {
+ out->fDouble = *((double*)pCur);
+
+ // Swap endianess
+ if (p_bBE)ByteSwap::Swap((int64_t*)&out->fDouble);
+ pCur += 8;
+ break;
+ }
+ default:
+ ret = false;
+ }
+ *pCurOut = pCur;
+ return ret;
+}
+
+#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER
diff --git a/3rdparty/assimp/code/PlyParser.h b/3rdparty/assimp/code/PlyParser.h
new file mode 100644
index 000000000..379ea4a0a
--- /dev/null
+++ b/3rdparty/assimp/code/PlyParser.h
@@ -0,0 +1,501 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines the helper data structures for importing PLY files */
+#ifndef AI_PLYFILEHELPER_H_INC
+#define AI_PLYFILEHELPER_H_INC
+
+
+#include "ParsingUtils.h"
+
+
+namespace Assimp
+{
+
+// http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/
+// http://w3.impa.br/~lvelho/outgoing/sossai/old/ViHAP_D4.4.2_PLY_format_v1.1.pdf
+// http://www.okino.com/conv/exp_ply.htm
+namespace PLY
+{
+
+
+// ---------------------------------------------------------------------------------
+/*
+name type number of bytes
+---------------------------------------
+char character 1
+uchar unsigned character 1
+short short integer 2
+ushort unsigned short integer 2
+int integer 4
+uint unsigned integer 4
+float single-precision float 4
+double double-precision float 8
+
+int8
+int16
+uint8 ... forms are also used
+*/
+enum EDataType
+{
+ EDT_Char = 0x0u,
+ EDT_UChar,
+ EDT_Short,
+ EDT_UShort,
+ EDT_Int,
+ EDT_UInt,
+ EDT_Float,
+ EDT_Double,
+
+ // Marks invalid entries
+ EDT_INVALID
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Specifies semantics for PLY element properties
+ *
+ * Semantics define the usage of a property, e.g. x coordinate
+*/
+enum ESemantic
+{
+ //! vertex position x coordinate
+ EST_XCoord = 0x0u,
+ //! vertex position x coordinate
+ EST_YCoord,
+ //! vertex position x coordinate
+ EST_ZCoord,
+
+ //! vertex normal x coordinate
+ EST_XNormal,
+ //! vertex normal y coordinate
+ EST_YNormal,
+ //! vertex normal z coordinate
+ EST_ZNormal,
+
+ //! u texture coordinate
+ EST_UTextureCoord,
+ //! v texture coordinate
+ EST_VTextureCoord,
+
+ //! vertex colors, red channel
+ EST_Red,
+ //! vertex colors, green channel
+ EST_Green,
+ //! vertex colors, blue channel
+ EST_Blue,
+ //! vertex colors, alpha channel
+ EST_Alpha,
+
+ //! vertex index list
+ EST_VertexIndex,
+
+ //! texture index
+ EST_TextureIndex,
+
+ //! texture coordinates (stored as element of a face)
+ EST_TextureCoordinates,
+
+ //! material index
+ EST_MaterialIndex,
+
+ //! ambient color, red channel
+ EST_AmbientRed,
+ //! ambient color, green channel
+ EST_AmbientGreen,
+ //! ambient color, blue channel
+ EST_AmbientBlue,
+ //! ambient color, alpha channel
+ EST_AmbientAlpha,
+
+ //! diffuse color, red channel
+ EST_DiffuseRed,
+ //! diffuse color, green channel
+ EST_DiffuseGreen,
+ //! diffuse color, blue channel
+ EST_DiffuseBlue,
+ //! diffuse color, alpha channel
+ EST_DiffuseAlpha,
+
+ //! specular color, red channel
+ EST_SpecularRed,
+ //! specular color, green channel
+ EST_SpecularGreen,
+ //! specular color, blue channel
+ EST_SpecularBlue,
+ //! specular color, alpha channel
+ EST_SpecularAlpha,
+
+ //! specular power for phong shading
+ EST_PhongPower,
+
+ //! opacity between 0 and 1
+ EST_Opacity,
+
+ //! Marks invalid entries
+ EST_INVALID
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Specifies semantics for PLY elements
+ *
+ * Semantics define the usage of an element, e.g. vertex or material
+*/
+enum EElementSemantic
+{
+ //! The element is a vertex
+ EEST_Vertex = 0x0u,
+
+ //! The element is a face description (index table)
+ EEST_Face,
+
+ //! The element is a tristrip description (index table)
+ EEST_TriStrip,
+
+ //! The element is an edge description (ignored)
+ EEST_Edge,
+
+ //! The element is a material description
+ EEST_Material,
+
+ //! Marks invalid entries
+ EEST_INVALID
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Helper class for a property in a PLY file.
+ *
+ * This can e.g. be a part of the vertex declaration
+ */
+class Property
+{
+public:
+
+ //! Default constructor
+ Property()
+ : eType (EDT_Int), bIsList(false), eFirstType(EDT_UChar)
+ {}
+
+ //! Data type of the property
+ EDataType eType;
+
+ //! Semantical meaning of the property
+ ESemantic Semantic;
+
+ //! Of the semantic of the property could not be parsed:
+ //! Contains the semantic specified in the file
+ std::string szName;
+
+ //! Specifies whether the data type is a list where
+ //! the first element specifies the size of the list
+ bool bIsList;
+ EDataType eFirstType;
+
+ // -------------------------------------------------------------------
+ //! Parse a property from a string. The end of the
+ //! string is either '\n', '\r' or '\0'. Return valie is false
+ //! if the input string is NOT a valid property (E.g. does
+ //! not start with the "property" keyword)
+ static bool ParseProperty (const char* pCur, const char** pCurOut,
+ Property* pOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a data type from a string
+ static EDataType ParseDataType(const char* pCur,const char** pCurOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a semantic from a string
+ static ESemantic ParseSemantic(const char* pCur,const char** pCurOut);
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Helper class for an element in a PLY file.
+ *
+ * This can e.g. be the vertex declaration. Elements contain a
+ * well-defined number of properties.
+ */
+class Element
+{
+public:
+
+ //! Default constructor
+ Element()
+ : eSemantic (EEST_INVALID)
+ , NumOccur(0)
+ {}
+
+ //! List of properties assigned to the element
+ //! std::vector to support operator[]
+ std::vector<Property> alProperties;
+
+ //! Semantic of the element
+ EElementSemantic eSemantic;
+
+ //! Of the semantic of the element could not be parsed:
+ //! Contains the semantic specified in the file
+ std::string szName;
+
+ //! How many times will the element occur?
+ unsigned int NumOccur;
+
+
+ // -------------------------------------------------------------------
+ //! Parse an element from a string.
+ //! The function will parse all properties contained in the
+ //! element, too.
+ static bool ParseElement (const char* pCur, const char** pCurOut,
+ Element* pOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a semantic from a string
+ static EElementSemantic ParseSemantic(const char* pCur,
+ const char** pCurOut);
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Instance of a property in a PLY file
+ */
+class PropertyInstance
+{
+public:
+
+ //! Default constructor
+ PropertyInstance ()
+ {}
+
+ union ValueUnion
+ {
+
+ //! uInt32 representation of the property. All
+ // uint types are automatically converted to uint32
+ uint32_t iUInt;
+
+ //! Int32 representation of the property. All
+ // int types are automatically converted to int32
+ int32_t iInt;
+
+ //! Float32 representation of the property
+ float fFloat;
+
+ //! Float64 representation of the property
+ double fDouble;
+
+ };
+
+ // -------------------------------------------------------------------
+ //! List of all values parsed. Contains only one value
+ // for non-list properties
+ std::vector<ValueUnion> avList;
+
+ // -------------------------------------------------------------------
+ //! Parse a property instance
+ static bool ParseInstance (const char* pCur,const char** pCurOut,
+ const Property* prop, PropertyInstance* p_pcOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a property instance in binary format
+ static bool ParseInstanceBinary (const char* pCur,const char** pCurOut,
+ const Property* prop, PropertyInstance* p_pcOut,bool p_bBE);
+
+ // -------------------------------------------------------------------
+ //! Get the default value for a given data type
+ static ValueUnion DefaultValue(EDataType eType);
+
+ // -------------------------------------------------------------------
+ //! Parse a value
+ static bool ParseValue(const char* pCur,const char** pCurOut,
+ EDataType eType,ValueUnion* out);
+
+ // -------------------------------------------------------------------
+ //! Parse a binary value
+ static bool ParseValueBinary(const char* pCur,const char** pCurOut,
+ EDataType eType,ValueUnion* out,bool p_bBE);
+
+ // -------------------------------------------------------------------
+ //! Convert a property value to a given type TYPE
+ template <typename TYPE>
+ static TYPE ConvertTo(ValueUnion v, EDataType eType);
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Class for an element instance in a PLY file
+ */
+class ElementInstance
+{
+public:
+
+ //! Default constructor
+ ElementInstance ()
+ {}
+
+ //! List of all parsed properties
+ std::vector< PropertyInstance > alProperties;
+
+ // -------------------------------------------------------------------
+ //! Parse an element instance
+ static bool ParseInstance (const char* pCur,const char** pCurOut,
+ const Element* pcElement, ElementInstance* p_pcOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a binary element instance
+ static bool ParseInstanceBinary (const char* pCur,const char** pCurOut,
+ const Element* pcElement, ElementInstance* p_pcOut,bool p_bBE);
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Class for an element instance list in a PLY file
+ */
+class ElementInstanceList
+{
+public:
+
+ //! Default constructor
+ ElementInstanceList ()
+ {}
+
+ //! List of all element instances
+ std::vector< ElementInstance > alInstances;
+
+ // -------------------------------------------------------------------
+ //! Parse an element instance list
+ static bool ParseInstanceList (const char* pCur,const char** pCurOut,
+ const Element* pcElement, ElementInstanceList* p_pcOut);
+
+ // -------------------------------------------------------------------
+ //! Parse a binary element instance list
+ static bool ParseInstanceListBinary (const char* pCur,const char** pCurOut,
+ const Element* pcElement, ElementInstanceList* p_pcOut,bool p_bBE);
+};
+// ---------------------------------------------------------------------------------
+/** \brief Class to represent the document object model of an ASCII or binary
+ * (both little and big-endian) PLY file
+ */
+class DOM
+{
+public:
+
+ //! Default constructor
+ DOM()
+ {}
+
+
+ //! Contains all elements of the file format
+ std::vector<Element> alElements;
+ //! Contains the real data of each element's instance list
+ std::vector<ElementInstanceList> alElementData;
+
+ //! Parse the DOM for a PLY file. The input string is assumed
+ //! to be terminated with zero
+ static bool ParseInstance (const char* pCur,DOM* p_pcOut);
+ static bool ParseInstanceBinary (const char* pCur,
+ DOM* p_pcOut,bool p_bBE);
+
+ //! Skip all comment lines after this
+ static bool SkipComments (const char* pCur,const char** pCurOut);
+
+private:
+
+ // -------------------------------------------------------------------
+ //! Handle the file header and read all element descriptions
+ bool ParseHeader (const char* pCur,const char** pCurOut);
+
+ // -------------------------------------------------------------------
+ //! Read in all element instance lists
+ bool ParseElementInstanceLists (const char* pCur,const char** pCurOut);
+
+ // -------------------------------------------------------------------
+ //! Read in all element instance lists for a binary file format
+ bool ParseElementInstanceListsBinary (const char* pCur,
+ const char** pCurOut,bool p_bBE);
+};
+
+// ---------------------------------------------------------------------------------
+/** \brief Helper class to represent a loaded PLY face
+ */
+class Face
+{
+public:
+
+ Face()
+ : iMaterialIndex(0xFFFFFFFF)
+ {
+ // set all indices to zero by default
+ mIndices.resize(3,0);
+ }
+
+public:
+
+ //! List of vertex indices
+ std::vector<unsigned int> mIndices;
+
+ //! Material index
+ unsigned int iMaterialIndex;
+};
+
+// ---------------------------------------------------------------------------------
+template <typename TYPE>
+inline TYPE PLY::PropertyInstance::ConvertTo(
+ PLY::PropertyInstance::ValueUnion v, PLY::EDataType eType)
+{
+ switch (eType)
+ {
+ case EDT_Float:
+ return (TYPE)v.fFloat;
+ case EDT_Double:
+ return (TYPE)v.fDouble;
+
+ case EDT_UInt:
+ case EDT_UShort:
+ case EDT_UChar:
+ return (TYPE)v.iUInt;
+
+ case EDT_Int:
+ case EDT_Short:
+ case EDT_Char:
+ return (TYPE)v.iInt;
+ default: ;
+ };
+ return (TYPE)0;
+}
+
+} // Namespace PLY
+} // Namespace AssImp
+
+#endif // !! include guard
diff --git a/3rdparty/assimp/code/PretransformVertices.cpp b/3rdparty/assimp/code/PretransformVertices.cpp
new file mode 100644
index 000000000..6da3bdf73
--- /dev/null
+++ b/3rdparty/assimp/code/PretransformVertices.cpp
@@ -0,0 +1,711 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 PretransformVertices.cpp
+ * @brief Implementation of the "PretransformVertices" post processing step
+*/
+
+#include "AssimpPCH.h"
+#include "PretransformVertices.h"
+#include "ProcessHelper.h"
+#include "SceneCombiner.h"
+
+using namespace Assimp;
+
+// some array offsets
+#define AI_PTVS_VERTEX 0x0
+#define AI_PTVS_FACE 0x1
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+PretransformVertices::PretransformVertices()
+: configKeepHierarchy (false)
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+PretransformVertices::~PretransformVertices()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool PretransformVertices::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_PreTransformVertices) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import configuration
+void PretransformVertices::SetupProperties(const Importer* pImp)
+{
+ // Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY and AI_CONFIG_PP_PTV_NORMALIZE
+ configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY,0));
+ configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE,0));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Count the number of nodes
+unsigned int PretransformVertices::CountNodes( aiNode* pcNode )
+{
+ unsigned int iRet = 1;
+ for (unsigned int i = 0;i < pcNode->mNumChildren;++i)
+ {
+ iRet += CountNodes(pcNode->mChildren[i]);
+ }
+ return iRet;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a bitwise combination identifying the vertex format of a mesh
+unsigned int PretransformVertices::GetMeshVFormat(aiMesh* pcMesh)
+{
+ // the vertex format is stored in aiMesh::mBones for later retrieval.
+ // there isn't a good reason to compute it a few hundred times
+ // from scratch. The pointer is unused as animations are lost
+ // during PretransformVertices.
+ if (pcMesh->mBones)
+ return (unsigned int)(uint64_t)pcMesh->mBones;
+
+
+ const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
+
+ // store the value for later use
+ pcMesh->mBones = (aiBone**)(uint64_t)iRet;
+ return iRet;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Count the number of vertices in the whole scene and a given
+// material index
+void PretransformVertices::CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
+ unsigned int iVFormat, unsigned int* piFaces, unsigned int* piVertices)
+{
+ for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
+ {
+ aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
+ if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
+ {
+ *piVertices += pcMesh->mNumVertices;
+ *piFaces += pcMesh->mNumFaces;
+ }
+ }
+ for (unsigned int i = 0;i < pcNode->mNumChildren;++i)
+ {
+ CountVerticesAndFaces(pcScene,pcNode->mChildren[i],iMat,
+ iVFormat,piFaces,piVertices);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Collect vertex/face data
+void PretransformVertices::CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat,
+ unsigned int iVFormat, aiMesh* pcMeshOut,
+ unsigned int aiCurrent[2], unsigned int* num_refs)
+{
+ // No need to multiply if there's no transformation
+ const bool identity = pcNode->mTransformation.IsIdentity();
+ for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
+ {
+ aiMesh* pcMesh = pcScene->mMeshes[ pcNode->mMeshes[i] ];
+ if (iMat == pcMesh->mMaterialIndex && iVFormat == GetMeshVFormat(pcMesh))
+ {
+ // Decrement mesh reference counter
+ unsigned int& num_ref = num_refs[pcNode->mMeshes[i]];
+ ai_assert(0 != num_ref);
+ --num_ref;
+
+ if (identity) {
+ // copy positions without modifying them
+ ::memcpy(pcMeshOut->mVertices + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mVertices,
+ pcMesh->mNumVertices * sizeof(aiVector3D));
+
+ if (iVFormat & 0x2) {
+ // copy normals without modifying them
+ ::memcpy(pcMeshOut->mNormals + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mNormals,
+ pcMesh->mNumVertices * sizeof(aiVector3D));
+ }
+ if (iVFormat & 0x4)
+ {
+ // copy tangents without modifying them
+ ::memcpy(pcMeshOut->mTangents + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mTangents,
+ pcMesh->mNumVertices * sizeof(aiVector3D));
+ // copy bitangents without modifying them
+ ::memcpy(pcMeshOut->mBitangents + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mBitangents,
+ pcMesh->mNumVertices * sizeof(aiVector3D));
+ }
+ }
+ else
+ {
+ // copy positions, transform them to worldspace
+ for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
+ pcMeshOut->mVertices[aiCurrent[AI_PTVS_VERTEX]+n] = pcNode->mTransformation * pcMesh->mVertices[n];
+ }
+ aiMatrix4x4 mWorldIT = pcNode->mTransformation;
+ mWorldIT.Inverse().Transpose();
+
+ // TODO: implement Inverse() for aiMatrix3x3
+ aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
+
+ if (iVFormat & 0x2)
+ {
+ // copy normals, transform them to worldspace
+ for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
+ pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] =
+ m * pcMesh->mNormals[n];
+ }
+ }
+ if (iVFormat & 0x4)
+ {
+ // copy tangents and bitangents, transform them to worldspace
+ for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) {
+ pcMeshOut->mTangents [aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mTangents[n];
+ pcMeshOut->mBitangents[aiCurrent[AI_PTVS_VERTEX]+n] = m * pcMesh->mBitangents[n];
+ }
+ }
+ }
+ unsigned int p = 0;
+ while (iVFormat & (0x100 << p))
+ {
+ // copy texture coordinates
+ memcpy(pcMeshOut->mTextureCoords[p] + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mTextureCoords[p],
+ pcMesh->mNumVertices * sizeof(aiVector3D));
+ ++p;
+ }
+ p = 0;
+ while (iVFormat & (0x1000000 << p))
+ {
+ // copy vertex colors
+ memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX],
+ pcMesh->mColors[p],
+ pcMesh->mNumVertices * sizeof(aiColor4D));
+ ++p;
+ }
+ // now we need to copy all faces. since we will delete the source mesh afterwards,
+ // we don't need to reallocate the array of indices except if this mesh is
+ // referenced multiple times.
+ for (unsigned int planck = 0;planck < pcMesh->mNumFaces;++planck)
+ {
+ aiFace& f_src = pcMesh->mFaces[planck];
+ aiFace& f_dst = pcMeshOut->mFaces[aiCurrent[AI_PTVS_FACE]+planck];
+
+ const unsigned int num_idx = f_src.mNumIndices;
+
+ f_dst.mNumIndices = num_idx;
+
+ unsigned int* pi;
+ if (!num_ref) { /* if last time the mesh is referenced -> no reallocation */
+ pi = f_dst.mIndices = f_src.mIndices;
+
+ // offset all vertex indices
+ for (unsigned int hahn = 0; hahn < num_idx;++hahn){
+ pi[hahn] += aiCurrent[AI_PTVS_VERTEX];
+ }
+ }
+ else {
+ pi = f_dst.mIndices = new unsigned int[num_idx];
+
+ // copy and offset all vertex indices
+ for (unsigned int hahn = 0; hahn < num_idx;++hahn){
+ pi[hahn] = f_src.mIndices[hahn] + aiCurrent[AI_PTVS_VERTEX];
+ }
+ }
+
+ // Update the mPrimitiveTypes member of the mesh
+ switch (pcMesh->mFaces[planck].mNumIndices)
+ {
+ case 0x1:
+ pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ break;
+ case 0x2:
+ pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ break;
+ case 0x3:
+ pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ break;
+ default:
+ pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ break;
+ };
+ }
+ aiCurrent[AI_PTVS_VERTEX] += pcMesh->mNumVertices;
+ aiCurrent[AI_PTVS_FACE] += pcMesh->mNumFaces;
+ }
+ }
+
+ // append all children of us
+ for (unsigned int i = 0;i < pcNode->mNumChildren;++i) {
+ CollectData(pcScene,pcNode->mChildren[i],iMat,
+ iVFormat,pcMeshOut,aiCurrent,num_refs);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all vertex formats that occur for a given material index
+// The output list contains duplicate elements
+void PretransformVertices::GetVFormatList( aiScene* pcScene, unsigned int iMat,
+ std::list<unsigned int>& aiOut)
+{
+ for (unsigned int i = 0; i < pcScene->mNumMeshes;++i)
+ {
+ aiMesh* pcMesh = pcScene->mMeshes[ i ];
+ if (iMat == pcMesh->mMaterialIndex) {
+ aiOut.push_back(GetMeshVFormat(pcMesh));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Compute the absolute transformation matrices of each node
+void PretransformVertices::ComputeAbsoluteTransform( aiNode* pcNode )
+{
+ if (pcNode->mParent) {
+ pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation;
+ }
+
+ for (unsigned int i = 0;i < pcNode->mNumChildren;++i) {
+ ComputeAbsoluteTransform(pcNode->mChildren[i]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Apply the node transformation to a mesh
+void PretransformVertices::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)
+{
+ // Check whether we need to transform the coordinates at all
+ if (!mat.IsIdentity()) {
+
+ if (mesh->HasPositions()) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ mesh->mVertices[i] = mat * mesh->mVertices[i];
+ }
+ }
+ if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
+ aiMatrix4x4 mWorldIT = mat;
+ mWorldIT.Inverse().Transpose();
+
+ // TODO: implement Inverse() for aiMatrix3x3
+ aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
+
+ if (mesh->HasNormals()) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ mesh->mNormals[i] = m * mesh->mNormals[i];
+ }
+ }
+ if (mesh->HasTangentsAndBitangents()) {
+ for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+ mesh->mTangents[i] = m * mesh->mTangents[i];
+ mesh->mBitangents[i] = m * mesh->mBitangents[i];
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Simple routine to build meshes in worldspace, no further optimization
+void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in,
+ unsigned int numIn, aiNode* node)
+{
+ // NOTE:
+ // aiMesh::mNumBones store original source mesh, or 0xffffffff if not a copy
+ // aiMesh::mBones store reference to abs. transform we multiplied with
+
+ // process meshes
+ for (unsigned int i = 0; i < node->mNumMeshes;++i) {
+ aiMesh* mesh = in[node->mMeshes[i]];
+
+ // check whether we can operate on this mesh
+ if (!mesh->mBones || *reinterpret_cast<aiMatrix4x4*>(mesh->mBones) == node->mTransformation) {
+ // yes, we can.
+ mesh->mBones = reinterpret_cast<aiBone**> (&node->mTransformation);
+ mesh->mNumBones = 0xffffffff;
+ }
+ else {
+
+ // try to find us in the list of newly created meshes
+ for (unsigned int n = 0; n < out.size(); ++n) {
+ aiMesh* ctz = out[n];
+ if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4*>(ctz->mBones) == node->mTransformation) {
+
+ // ok, use this one. Update node mesh index
+ node->mMeshes[i] = numIn + n;
+ }
+ }
+ if (node->mMeshes[i] < numIn) {
+ // Worst case. Need to operate on a full copy of the mesh
+ DefaultLogger::get()->info("PretransformVertices: Copying mesh due to mismatching transforms");
+ aiMesh* ntz;
+
+ const unsigned int tmp = mesh->mNumBones; //
+ mesh->mNumBones = 0;
+ SceneCombiner::Copy(&ntz,mesh);
+ mesh->mNumBones = tmp;
+
+ ntz->mNumBones = node->mMeshes[i];
+ ntz->mBones = reinterpret_cast<aiBone**> (&node->mTransformation);
+
+ out.push_back(ntz);
+ }
+ }
+ }
+
+ // call children
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ BuildWCSMeshes(out,in,numIn,node->mChildren[i]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reset transformation matrices to identity
+void PretransformVertices::MakeIdentityTransform(aiNode* nd)
+{
+ nd->mTransformation = aiMatrix4x4();
+
+ // call children
+ for (unsigned int i = 0; i < nd->mNumChildren;++i)
+ MakeIdentityTransform(nd->mChildren[i]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build reference counters for all meshes
+void PretransformVertices::BuildMeshRefCountArray(aiNode* nd, unsigned int * refs)
+{
+ for (unsigned int i = 0; i< nd->mNumMeshes;++i)
+ refs[nd->mMeshes[i]]++;
+
+ // call children
+ for (unsigned int i = 0; i < nd->mNumChildren;++i)
+ BuildMeshRefCountArray(nd->mChildren[i],refs);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void PretransformVertices::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("PretransformVerticesProcess begin");
+
+ // Return immediately if we have no meshes
+ if (!pScene->mNumMeshes)
+ return;
+
+ const unsigned int iOldMeshes = pScene->mNumMeshes;
+ const unsigned int iOldAnimationChannels = pScene->mNumAnimations;
+ const unsigned int iOldNodes = CountNodes(pScene->mRootNode);
+
+ // first compute absolute transformation matrices for all nodes
+ ComputeAbsoluteTransform(pScene->mRootNode);
+
+ // Delete aiMesh::mBones for all meshes. The bones are
+ // removed during this step and we need the pointer as
+ // temporary storage
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i) {
+ aiMesh* mesh = pScene->mMeshes[i];
+
+ for (unsigned int a = 0; a < mesh->mNumBones;++a)
+ delete mesh->mBones[a];
+
+ delete[] mesh->mBones;
+ mesh->mBones = NULL;
+ }
+
+ // now build a list of output meshes
+ std::vector<aiMesh*> apcOutMeshes;
+
+ // Keep scene hierarchy? It's an easy job in this case ...
+ // we go on and transform all meshes, if one is referenced by nodes
+ // with different absolute transformations a depth copy of the mesh
+ // is required.
+ if ( configKeepHierarchy ) {
+
+ // Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
+ BuildWCSMeshes(apcOutMeshes,pScene->mMeshes,pScene->mNumMeshes, pScene->mRootNode);
+
+ // ... if new meshes have been generated, append them to the end of the scene
+ if (apcOutMeshes.size() > 0) {
+ aiMesh** npp = new aiMesh*[pScene->mNumMeshes + apcOutMeshes.size()];
+
+ memcpy(npp,pScene->mMeshes,sizeof(aiMesh*)*pScene->mNumMeshes);
+ memcpy(npp+pScene->mNumMeshes,&apcOutMeshes[0],sizeof(aiMesh*)*apcOutMeshes.size());
+
+ pScene->mNumMeshes += apcOutMeshes.size();
+ delete[] pScene->mMeshes; pScene->mMeshes = npp;
+ }
+
+ // now iterate through all meshes and transform them to worldspace
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ ApplyTransform(pScene->mMeshes[i],*reinterpret_cast<aiMatrix4x4*>( pScene->mMeshes[i]->mBones ));
+
+ // prevent improper destruction
+ pScene->mMeshes[i]->mBones = NULL;
+ pScene->mMeshes[i]->mNumBones = 0;
+ }
+ }
+ else {
+
+ apcOutMeshes.reserve(pScene->mNumMaterials<<1u);
+ std::list<unsigned int> aiVFormats;
+
+ std::vector<unsigned int> s(pScene->mNumMeshes,0);
+ BuildMeshRefCountArray(pScene->mRootNode,&s[0]);
+
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
+ // get the list of all vertex formats for this material
+ aiVFormats.clear();
+ GetVFormatList(pScene,i,aiVFormats);
+ aiVFormats.sort();
+ aiVFormats.unique();
+ for (std::list<unsigned int>::const_iterator j = aiVFormats.begin();j != aiVFormats.end();++j) {
+ unsigned int iVertices = 0;
+ unsigned int iFaces = 0;
+ CountVerticesAndFaces(pScene,pScene->mRootNode,i,*j,&iFaces,&iVertices);
+ if (0 != iFaces && 0 != iVertices)
+ {
+ apcOutMeshes.push_back(new aiMesh());
+ aiMesh* pcMesh = apcOutMeshes.back();
+ pcMesh->mNumFaces = iFaces;
+ pcMesh->mNumVertices = iVertices;
+ pcMesh->mFaces = new aiFace[iFaces];
+ pcMesh->mVertices = new aiVector3D[iVertices];
+ pcMesh->mMaterialIndex = i;
+ if ((*j) & 0x2)pcMesh->mNormals = new aiVector3D[iVertices];
+ if ((*j) & 0x4)
+ {
+ pcMesh->mTangents = new aiVector3D[iVertices];
+ pcMesh->mBitangents = new aiVector3D[iVertices];
+ }
+ iFaces = 0;
+ while ((*j) & (0x100 << iFaces))
+ {
+ pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices];
+ if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3;
+ else pcMesh->mNumUVComponents[iFaces] = 2;
+ iFaces++;
+ }
+ iFaces = 0;
+ while ((*j) & (0x1000000 << iFaces))
+ pcMesh->mColors[iFaces++] = new aiColor4D[iVertices];
+
+ // fill the mesh ...
+ unsigned int aiTemp[2] = {0,0};
+ CollectData(pScene,pScene->mRootNode,i,*j,pcMesh,aiTemp,&s[0]);
+ }
+ }
+ }
+
+ // now delete all meshes in the scene and build a new mesh list
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ {
+ aiMesh* mesh = pScene->mMeshes[i];
+ mesh->mNumBones = 0;
+ mesh->mBones = NULL;
+
+ // we're reusing the face index arrays. avoid destruction
+ for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
+ mesh->mFaces[a].mNumIndices = 0;
+ mesh->mFaces[a].mIndices = NULL;
+ }
+
+ delete mesh;
+
+ // Invalidate the contents of the old mesh array. We will most
+ // likely have less output meshes now, so the last entries of
+ // the mesh array are not overridden. We set them to NULL to
+ // make sure the developer gets notified when his application
+ // attempts to access these fields ...
+ mesh = NULL;
+ }
+
+ // If no meshes are referenced in the node graph it is possible that we get no output meshes.
+ if (apcOutMeshes.empty()) {
+ throw DeadlyImportError("No output meshes: all meshes are orphaned and are not referenced by nodes");
+ }
+ else
+ {
+ // It is impossible that we have more output meshes than
+ // input meshes, so we can easily reuse the old mesh array
+ pScene->mNumMeshes = (unsigned int)apcOutMeshes.size();
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mMeshes[i] = apcOutMeshes[i];
+ }
+ }
+
+ // remove all animations from the scene
+ for (unsigned int i = 0; i < pScene->mNumAnimations;++i)
+ delete pScene->mAnimations[i];
+ delete[] pScene->mAnimations;
+
+ pScene->mAnimations = NULL;
+ pScene->mNumAnimations = 0;
+
+ // --- we need to keep all cameras and lights
+ for (unsigned int i = 0; i < pScene->mNumCameras;++i)
+ {
+ aiCamera* cam = pScene->mCameras[i];
+ const aiNode* nd = pScene->mRootNode->FindNode(cam->mName);
+ ai_assert(NULL != nd);
+
+ // multiply all properties of the camera with the absolute
+ // transformation of the corresponding node
+ cam->mPosition = nd->mTransformation * cam->mPosition;
+ cam->mLookAt = aiMatrix3x3( nd->mTransformation ) * cam->mLookAt;
+ cam->mUp = aiMatrix3x3( nd->mTransformation ) * cam->mUp;
+ }
+
+ for (unsigned int i = 0; i < pScene->mNumLights;++i)
+ {
+ aiLight* l = pScene->mLights[i];
+ const aiNode* nd = pScene->mRootNode->FindNode(l->mName);
+ ai_assert(NULL != nd);
+
+ // multiply all properties of the camera with the absolute
+ // transformation of the corresponding node
+ l->mPosition = nd->mTransformation * l->mPosition;
+ l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection;
+ }
+
+ if ( !configKeepHierarchy ) {
+
+ // now delete all nodes in the scene and build a new
+ // flat node graph with a root node and some level 1 children
+ delete pScene->mRootNode;
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<dummy_root>");
+
+ if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras)
+ {
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+ }
+ else
+ {
+ pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras;
+ aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
+
+ // generate mesh nodes
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes)
+ {
+ aiNode* pcNode = *nodes = new aiNode();
+ pcNode->mParent = pScene->mRootNode;
+ pcNode->mName.length = ::sprintf(pcNode->mName.data,"mesh_%i",i);
+
+ // setup mesh indices
+ pcNode->mNumMeshes = 1;
+ pcNode->mMeshes = new unsigned int[1];
+ pcNode->mMeshes[0] = i;
+ }
+ // generate light nodes
+ for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes)
+ {
+ aiNode* pcNode = *nodes = new aiNode();
+ pcNode->mParent = pScene->mRootNode;
+ pcNode->mName.length = ::sprintf(pcNode->mName.data,"light_%i",i);
+ pScene->mLights[i]->mName = pcNode->mName;
+ }
+ // generate camera nodes
+ for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes)
+ {
+ aiNode* pcNode = *nodes = new aiNode();
+ pcNode->mParent = pScene->mRootNode;
+ pcNode->mName.length = ::sprintf(pcNode->mName.data,"cam_%i",i);
+ pScene->mCameras[i]->mName = pcNode->mName;
+ }
+ }
+ }
+ else {
+ // ... and finally set the transformation matrix of all nodes to identity
+ MakeIdentityTransform(pScene->mRootNode);
+ }
+
+ if (configNormalize) {
+ // compute the boundary of all meshes
+ aiVector3D min,max;
+ MinMaxChooser<aiVector3D> ()(min,max);
+
+ for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
+ aiMesh* m = pScene->mMeshes[a];
+ for (unsigned int i = 0; i < m->mNumVertices;++i) {
+ min = std::min(m->mVertices[i],min);
+ max = std::max(m->mVertices[i],max);
+ }
+ }
+
+ // find the dominant axis
+ aiVector3D d = max-min;
+ const float div = std::max(d.x,std::max(d.y,d.z))*0.5f;
+
+ d = min+d*0.5f;
+ for (unsigned int a = 0; a < pScene->mNumMeshes; ++a) {
+ aiMesh* m = pScene->mMeshes[a];
+ for (unsigned int i = 0; i < m->mNumVertices;++i) {
+ m->mVertices[i] = (m->mVertices[i]-d)/div;
+ }
+ }
+ }
+
+ // print statistics
+ if (!DefaultLogger::isNullLogger())
+ {
+ char buffer[4096];
+
+ DefaultLogger::get()->debug("PretransformVerticesProcess finished");
+
+ sprintf(buffer,"Removed %i nodes and %i animation channels (%i output nodes)",
+ iOldNodes,iOldAnimationChannels,CountNodes(pScene->mRootNode));
+ DefaultLogger::get()->info(buffer);
+
+ sprintf(buffer,"Kept %i lights and %i cameras",
+ pScene->mNumLights,pScene->mNumCameras);
+ DefaultLogger::get()->info(buffer);
+
+ sprintf(buffer,"Moved %i meshes to WCS (number of output meshes: %i)",
+ iOldMeshes,pScene->mNumMeshes);
+ DefaultLogger::get()->info(buffer);
+ }
+}
+
diff --git a/3rdparty/assimp/code/PretransformVertices.h b/3rdparty/assimp/code/PretransformVertices.h
new file mode 100644
index 000000000..41f2f38ed
--- /dev/null
+++ b/3rdparty/assimp/code/PretransformVertices.h
@@ -0,0 +1,166 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 PretransformVertices.h
+ * @brief Defines a post processing step to pretransform all
+ * vertices in the scenegraph
+ */
+#ifndef AI_PRETRANSFORMVERTICES_H_INC
+#define AI_PRETRANSFORMVERTICES_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+
+class PretransformVerticesTest;
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** The PretransformVertices pretransforms all vertices in the nodegraph
+ * and removes the whole graph. The output is a list of meshes, one for
+ * each material.
+*/
+class ASSIMP_API PretransformVertices : public BaseProcess
+{
+ friend class Importer;
+ friend class ::PretransformVerticesTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ PretransformVertices ();
+
+ /** Destructor, private as well */
+ ~PretransformVertices ();
+
+public:
+
+ // -------------------------------------------------------------------
+ // Check whether step is active
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Execute step on a given scene
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ // Setup import settings
+ void SetupProperties(const Importer* pImp);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Toggle the 'keep hierarchy' option
+ * @param d hm ... difficult to guess what this means, hu!?
+ */
+ void KeepHierarchy(bool d) {
+ configKeepHierarchy = d;
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Check whether 'keep hierarchy' is currently enabled.
+ * @return ...
+ */
+ bool IsHierarchyKept() const {
+ return configKeepHierarchy;
+ }
+
+private:
+
+ // -------------------------------------------------------------------
+ // Count the number of nodes
+ unsigned int CountNodes( aiNode* pcNode );
+
+ // -------------------------------------------------------------------
+ // Get a bitwise combination identifying the vertex format of a mesh
+ unsigned int GetMeshVFormat(aiMesh* pcMesh);
+
+ // -------------------------------------------------------------------
+ // Count the number of vertices in the whole scene and a given
+ // material index
+ void CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode,
+ unsigned int iMat,
+ unsigned int iVFormat,
+ unsigned int* piFaces,
+ unsigned int* piVertices);
+
+ // -------------------------------------------------------------------
+ // Collect vertex/face data
+ void CollectData( aiScene* pcScene, aiNode* pcNode,
+ unsigned int iMat,
+ unsigned int iVFormat,
+ aiMesh* pcMeshOut,
+ unsigned int aiCurrent[2],
+ unsigned int* num_refs);
+
+ // -------------------------------------------------------------------
+ // Get a list of all vertex formats that occur for a given material
+ // The output list contains duplicate elements
+ void GetVFormatList( aiScene* pcScene, unsigned int iMat,
+ std::list<unsigned int>& aiOut);
+
+ // -------------------------------------------------------------------
+ // Compute the absolute transformation matrices of each node
+ void ComputeAbsoluteTransform( aiNode* pcNode );
+
+ // -------------------------------------------------------------------
+ // Simple routine to build meshes in worldspace, no further optimization
+ void BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in,
+ unsigned int numIn, aiNode* node);
+
+ // -------------------------------------------------------------------
+ // Apply the node transformation to a mesh
+ void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat);
+
+ // -------------------------------------------------------------------
+ // Reset transformation matrices to identity
+ void MakeIdentityTransform(aiNode* nd);
+
+ // -------------------------------------------------------------------
+ // Build reference counters for all meshes
+ void BuildMeshRefCountArray(aiNode* nd, unsigned int * refs);
+
+
+
+ //! Configuration option: keep scene hierarchy as long as possible
+ bool configKeepHierarchy, configNormalize;
+
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_GENFACENORMALPROCESS_H_INC
diff --git a/3rdparty/assimp/code/ProcessHelper.h b/3rdparty/assimp/code/ProcessHelper.h
new file mode 100644
index 000000000..349da1735
--- /dev/null
+++ b/3rdparty/assimp/code/ProcessHelper.h
@@ -0,0 +1,564 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 AI_PROCESS_HELPER_H_INCLUDED
+#define AI_PROCESS_HELPER_H_INCLUDED
+
+#include "../include/aiPostProcess.h"
+
+#include "SpatialSort.h"
+#include "BaseProcess.h"
+#include "ParsingUtils.h"
+
+// -------------------------------------------------------------------------------
+// Some extensions to std namespace. Mainly std::min and std::max for all
+// flat data types in the aiScene. They're used to quickly determine the
+// min/max bounds of data arrays.
+#ifdef __cplusplus
+namespace std {
+
+ // std::min for aiVector3D
+ inline ::aiVector3D min (const ::aiVector3D& a, const ::aiVector3D& b) {
+ return ::aiVector3D (min(a.x,b.x),min(a.y,b.y),min(a.z,b.z));
+ }
+
+ // std::max for aiVector3D
+ inline ::aiVector3D max (const ::aiVector3D& a, const ::aiVector3D& b) {
+ return ::aiVector3D (max(a.x,b.x),max(a.y,b.y),max(a.z,b.z));
+ }
+
+ // std::min for aiColor4D
+ inline ::aiColor4D min (const ::aiColor4D& a, const ::aiColor4D& b) {
+ return ::aiColor4D (min(a.r,b.r),min(a.g,b.g),min(a.b,b.b),min(a.a,b.a));
+ }
+
+ // std::max for aiColor4D
+ inline ::aiColor4D max (const ::aiColor4D& a, const ::aiColor4D& b) {
+ return ::aiColor4D (max(a.r,b.r),max(a.g,b.g),max(a.b,b.b),max(a.a,b.a));
+ }
+
+ // std::min for aiQuaternion
+ inline ::aiQuaternion min (const ::aiQuaternion& a, const ::aiQuaternion& b) {
+ return ::aiQuaternion (min(a.w,b.w),min(a.x,b.x),min(a.y,b.y),min(a.z,b.z));
+ }
+
+ // std::max for aiQuaternion
+ inline ::aiQuaternion max (const ::aiQuaternion& a, const ::aiQuaternion& b) {
+ return ::aiQuaternion (max(a.w,b.w),max(a.x,b.x),max(a.y,b.y),max(a.z,b.z));
+ }
+
+ // std::min for aiVectorKey
+ inline ::aiVectorKey min (const ::aiVectorKey& a, const ::aiVectorKey& b) {
+ return ::aiVectorKey (min(a.mTime,b.mTime),min(a.mValue,b.mValue));
+ }
+
+ // std::max for aiVectorKey
+ inline ::aiVectorKey max (const ::aiVectorKey& a, const ::aiVectorKey& b) {
+ return ::aiVectorKey (max(a.mTime,b.mTime),max(a.mValue,b.mValue));
+ }
+
+ // std::min for aiQuatKey
+ inline ::aiQuatKey min (const ::aiQuatKey& a, const ::aiQuatKey& b) {
+ return ::aiQuatKey (min(a.mTime,b.mTime),min(a.mValue,b.mValue));
+ }
+
+ // std::max for aiQuatKey
+ inline ::aiQuatKey max (const ::aiQuatKey& a, const ::aiQuatKey& b) {
+ return ::aiQuatKey (max(a.mTime,b.mTime),max(a.mValue,b.mValue));
+ }
+
+ // std::min for aiVertexWeight
+ inline ::aiVertexWeight min (const ::aiVertexWeight& a, const ::aiVertexWeight& b) {
+ return ::aiVertexWeight (min(a.mVertexId,b.mVertexId),min(a.mWeight,b.mWeight));
+ }
+
+ // std::max for aiVertexWeight
+ inline ::aiVertexWeight max (const ::aiVertexWeight& a, const ::aiVertexWeight& b) {
+ return ::aiVertexWeight (max(a.mVertexId,b.mVertexId),max(a.mWeight,b.mWeight));
+ }
+
+} // end namespace std
+#endif // !! C++
+
+namespace Assimp {
+
+// -------------------------------------------------------------------------------
+// Start points for ArrayBounds<T> for all supported Ts
+template <typename T>
+struct MinMaxChooser;
+
+template <> struct MinMaxChooser<float> {
+ void operator ()(float& min,float& max) {
+ max = -10e10f;
+ min = 10e10f;
+}};
+template <> struct MinMaxChooser<double> {
+ void operator ()(double& min,double& max) {
+ max = -10e10;
+ min = 10e10;
+}};
+template <> struct MinMaxChooser<unsigned int> {
+ void operator ()(unsigned int& min,unsigned int& max) {
+ max = 0;
+ min = (1u<<(sizeof(unsigned int)*8-1));
+}};
+
+template <> struct MinMaxChooser<aiVector3D> {
+ void operator ()(aiVector3D& min,aiVector3D& max) {
+ max = aiVector3D(-10e10f,-10e10f,-10e10f);
+ min = aiVector3D( 10e10f, 10e10f, 10e10f);
+}};
+template <> struct MinMaxChooser<aiColor4D> {
+ void operator ()(aiColor4D& min,aiColor4D& max) {
+ max = aiColor4D(-10e10f,-10e10f,-10e10f,-10e10f);
+ min = aiColor4D( 10e10f, 10e10f, 10e10f, 10e10f);
+}};
+
+template <> struct MinMaxChooser<aiQuaternion> {
+ void operator ()(aiQuaternion& min,aiQuaternion& max) {
+ max = aiQuaternion(-10e10f,-10e10f,-10e10f,-10e10f);
+ min = aiQuaternion( 10e10f, 10e10f, 10e10f, 10e10f);
+}};
+
+template <> struct MinMaxChooser<aiVectorKey> {
+ void operator ()(aiVectorKey& min,aiVectorKey& max) {
+ MinMaxChooser<double>()(min.mTime,max.mTime);
+ MinMaxChooser<aiVector3D>()(min.mValue,max.mValue);
+}};
+template <> struct MinMaxChooser<aiQuatKey> {
+ void operator ()(aiQuatKey& min,aiQuatKey& max) {
+ MinMaxChooser<double>()(min.mTime,max.mTime);
+ MinMaxChooser<aiQuaternion>()(min.mValue,max.mValue);
+}};
+
+template <> struct MinMaxChooser<aiVertexWeight> {
+ void operator ()(aiVertexWeight& min,aiVertexWeight& max) {
+ MinMaxChooser<unsigned int>()(min.mVertexId,max.mVertexId);
+ MinMaxChooser<float>()(min.mWeight,max.mWeight);
+}};
+
+// -------------------------------------------------------------------------------
+/** @brief Find the min/max values of an array of Ts
+ * @param in Input array
+ * @param size Numebr of elements to process
+ * @param[out] min minimum value
+ * @param[out] max maximum value
+ */
+template <typename T>
+inline void ArrayBounds(const T* in, unsigned int size, T& min, T& max)
+{
+ MinMaxChooser<T> ()(min,max);
+ for (unsigned int i = 0; i < size;++i) {
+ min = std::min(in[i],min);
+ max = std::max(in[i],max);
+ }
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Extract single strings from a list of identifiers
+ * @param in Input string list.
+ * @param out Receives a list of clean output strings
+ * @sdee #AI_CONFIG_PP_OG_EXCLUDE_LIST
+ */
+inline void ConvertListToStrings(const std::string& in, std::list<std::string>& out)
+{
+ const char* s = in.c_str();
+ while (*s) {
+ SkipSpacesAndLineEnd(&s);
+ if (*s == '\'') {
+ const char* base = ++s;
+ while (*s != '\'') {
+ ++s;
+ if (*s == '\0') {
+ DefaultLogger::get()->error("ConvertListToString: String list is ill-formatted");
+ return;
+ }
+ }
+ out.push_back(std::string(base,(size_t)(s-base)));
+ ++s;
+ }
+ else {
+ out.push_back(GetNextToken(s));
+ }
+ }
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Compute the newell normal of a polygon regardless of its shape
+ *
+ * @param out Receives the output normal
+ * @param num Number of input vertices
+ * @param x X data source. x[ofs_x*n] is the n'th element.
+ * @param y Y data source. y[ofs_y*n] is the y'th element
+ * @param z Z data source. z[ofs_z*n] is the z'th element
+ *
+ * @note The data arrays must have storage for at least num+2 elements. Using
+ * this method is much faster than the 'other' NewellNormal()
+ */
+template <int ofs_x, int ofs_y, int ofs_z>
+inline void NewellNormal (aiVector3D& out, int num, float* x, float* y, float* z)
+{
+ // Duplicate the first two vertices at the end
+ x[(num+0)*ofs_x] = x[0];
+ x[(num+1)*ofs_x] = x[ofs_x];
+
+ y[(num+0)*ofs_y] = y[0];
+ y[(num+1)*ofs_y] = y[ofs_y];
+
+ z[(num+0)*ofs_z] = z[0];
+ z[(num+1)*ofs_z] = z[ofs_z];
+
+ float sum_xy = 0.0, sum_yz = 0.0, sum_zx = 0.0;
+
+ float *xptr = x +ofs_x, *xlow = x, *xhigh = x + ofs_x*2;
+ float *yptr = y +ofs_y, *ylow = y, *yhigh = y + ofs_y*2;
+ float *zptr = z +ofs_z, *zlow = z, *zhigh = z + ofs_z*2;
+
+ for (int tmp=0; tmp < num; tmp++) {
+ sum_xy += (*xptr) * ( (*yhigh) - (*ylow) );
+ sum_yz += (*yptr) * ( (*zhigh) - (*zlow) );
+ sum_zx += (*zptr) * ( (*xhigh) - (*xlow) );
+
+ xptr += ofs_x;
+ xlow += ofs_x;
+ xhigh += ofs_x;
+
+ yptr += ofs_y;
+ ylow += ofs_y;
+ yhigh += ofs_y;
+
+ zptr += ofs_z;
+ zlow += ofs_z;
+ zhigh += ofs_z;
+ }
+ out = aiVector3D(sum_yz,sum_zx,sum_xy);
+}
+
+#if 0
+// -------------------------------------------------------------------------------
+/** @brief Compute newell normal of a polgon regardless of its shape
+ *
+ * @param out Receives the output normal
+ * @param data Input vertices
+ * @param idx Index buffer
+ * @param num Number of indices
+ */
+inline void NewellNormal (aiVector3D& out, const aiVector3D* data, unsigned int* idx, unsigned int num )
+{
+ // TODO: intended to be used in GenNormals.
+}
+#endif
+
+// -------------------------------------------------------------------------------
+/** Little helper function to calculate the quadratic difference
+ * of two colours.
+ * @param pColor1 First color
+ * @param pColor2 second color
+ * @return Quadratic color difference
+ */
+inline float GetColorDifference( const aiColor4D& pColor1, const aiColor4D& pColor2)
+{
+ const aiColor4D c (pColor1.r - pColor2.r, pColor1.g - pColor2.g,
+ pColor1.b - pColor2.b, pColor1.a - pColor2.a);
+
+ return c.r*c.r + c.g*c.g + c.b*c.b + c.a*c.a;
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Compute the AABB of a mesh after applying a given transform
+ * @param mesh Input mesh
+ * @param[out] min Receives minimum transformed vertex
+ * @param[out] max Receives maximum transformed vertex
+ * @param m Transformation matrix to be applied
+ */
+inline void FindAABBTransformed (const aiMesh* mesh, aiVector3D& min, aiVector3D& max,
+ const aiMatrix4x4& m)
+{
+ min = aiVector3D (10e10f, 10e10f, 10e10f);
+ max = aiVector3D (-10e10f,-10e10f,-10e10f);
+ for (unsigned int i = 0;i < mesh->mNumVertices;++i)
+ {
+ const aiVector3D v = m * mesh->mVertices[i];
+ min = std::min(v,min);
+ max = std::max(v,max);
+ }
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Helper function to determine the 'real' center of a mesh
+ *
+ * That is the center of its axis-aligned bounding box.
+ * @param mesh Input mesh
+ * @param[out] min Minimum vertex of the mesh
+ * @param[out] max maximum vertex of the mesh
+ * @param[out] out Center point
+ */
+inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out, aiVector3D& min, aiVector3D& max)
+{
+ ArrayBounds(mesh->mVertices,mesh->mNumVertices, min,max);
+ out = min + (max-min)*0.5f;
+}
+
+// -------------------------------------------------------------------------------
+// Helper function to determine the 'real' center of a mesh after applying a given transform
+inline void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out, aiVector3D& min,
+ aiVector3D& max, const aiMatrix4x4& m)
+{
+ FindAABBTransformed(mesh,min,max,m);
+ out = min + (max-min)*0.5f;
+}
+
+// -------------------------------------------------------------------------------
+// Helper function to determine the 'real' center of a mesh
+inline void FindMeshCenter (aiMesh* mesh, aiVector3D& out)
+{
+ aiVector3D min,max;
+ FindMeshCenter(mesh,out,min,max);
+}
+
+// -------------------------------------------------------------------------------
+// Helper function to determine the 'real' center of a mesh after applying a given transform
+inline void FindMeshCenterTransformed (aiMesh* mesh, aiVector3D& out,
+ const aiMatrix4x4& m)
+{
+ aiVector3D min,max;
+ FindMeshCenterTransformed(mesh,out,min,max,m);
+}
+
+// -------------------------------------------------------------------------------
+// Compute a good epsilon value for position comparisons on a mesh
+inline float ComputePositionEpsilon(const aiMesh* pMesh)
+{
+ const float epsilon = 1e-4f;
+
+ // calculate the position bounds so we have a reliable epsilon to check position differences against
+ aiVector3D minVec, maxVec;
+ ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,minVec,maxVec);
+ return (maxVec - minVec).Length() * epsilon;
+}
+
+// -------------------------------------------------------------------------------
+// Compute a good epsilon value for position comparisons on a array of meshes
+inline float ComputePositionEpsilon(const aiMesh* const* pMeshes, size_t num)
+{
+ const float epsilon = 1e-4f;
+
+ // calculate the position bounds so we have a reliable epsilon to check position differences against
+ aiVector3D minVec, maxVec, mi, ma;
+ MinMaxChooser<aiVector3D>()(minVec,maxVec);
+
+ for (size_t a = 0; a < num; ++a) {
+ const aiMesh* pMesh = pMeshes[a];
+ ArrayBounds(pMesh->mVertices,pMesh->mNumVertices,mi,ma);
+
+ minVec = std::min(minVec,mi);
+ maxVec = std::max(maxVec,ma);
+ }
+ return (maxVec - minVec).Length() * epsilon;
+}
+
+// -------------------------------------------------------------------------------
+// Compute an unique value for the vertex format of a mesh
+inline unsigned int GetMeshVFormatUnique(aiMesh* pcMesh)
+{
+ ai_assert(NULL != pcMesh);
+
+ // FIX: the hash may never be 0. Otherwise a comparison against
+ // nullptr could be successful
+ unsigned int iRet = 1;
+
+ // normals
+ if (pcMesh->HasNormals())iRet |= 0x2;
+ // tangents and bitangents
+ if (pcMesh->HasTangentsAndBitangents())iRet |= 0x4;
+
+#ifdef BOOST_STATIC_ASSERT
+ BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
+ BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
+#endif
+
+ // texture coordinates
+ unsigned int p = 0;
+ while (pcMesh->HasTextureCoords(p))
+ {
+ iRet |= (0x100 << p);
+ if (3 == pcMesh->mNumUVComponents[p])
+ iRet |= (0x10000 << p);
+
+ ++p;
+ }
+ // vertex colors
+ p = 0;
+ while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++);
+ return iRet;
+}
+
+typedef std::pair <unsigned int,float> PerVertexWeight;
+typedef std::vector <PerVertexWeight> VertexWeightTable;
+
+// -------------------------------------------------------------------------------
+// Compute a per-vertex bone weight table
+// please .... delete result with operator delete[] ...
+inline VertexWeightTable* ComputeVertexBoneWeightTable(aiMesh* pMesh)
+{
+ if (!pMesh || !pMesh->mNumVertices || !pMesh->mNumBones)
+ return NULL;
+
+ VertexWeightTable* 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::pair<unsigned int,float>(i,weight.mWeight));
+ }
+ }
+ return avPerVertexWeights;
+}
+
+// -------------------------------------------------------------------------------
+// Get a string for a given aiTextureType
+inline const char* TextureTypeToString(aiTextureType in)
+{
+ switch (in)
+ {
+ case aiTextureType_NONE:
+ return "n/a";
+ case aiTextureType_DIFFUSE:
+ return "Diffuse";
+ case aiTextureType_SPECULAR:
+ return "Specular";
+ case aiTextureType_AMBIENT:
+ return "Ambient";
+ case aiTextureType_EMISSIVE:
+ return "Emissive";
+ case aiTextureType_OPACITY:
+ return "Opacity";
+ case aiTextureType_NORMALS:
+ return "Normals";
+ case aiTextureType_HEIGHT:
+ return "Height";
+ case aiTextureType_SHININESS:
+ return "Shininess";
+ case aiTextureType_DISPLACEMENT:
+ return "Displacement";
+ case aiTextureType_LIGHTMAP:
+ return "Lightmap";
+ case aiTextureType_REFLECTION:
+ return "Reflection";
+ case aiTextureType_UNKNOWN:
+ return "Unknown";
+ default:
+ return "HUGE ERROR. Expect BSOD (linux guys: kernel panic ...).";
+ }
+}
+
+// -------------------------------------------------------------------------------
+// Get a string for a given aiTextureMapping
+inline const char* MappingTypeToString(aiTextureMapping in)
+{
+ switch (in)
+ {
+ case aiTextureMapping_UV:
+ return "UV";
+ case aiTextureMapping_BOX:
+ return "Box";
+ case aiTextureMapping_SPHERE:
+ return "Sphere";
+ case aiTextureMapping_CYLINDER:
+ return "Cylinder";
+ case aiTextureMapping_PLANE:
+ return "Plane";
+ case aiTextureMapping_OTHER:
+ return "Other";
+ default:
+ return "HUGE ERROR. Expect BSOD (linux guys: kernel panic ...).";
+ }
+}
+
+// -------------------------------------------------------------------------------
+// Utility postprocess step to share the spatial sort tree between
+// all steps which use it to speedup its computations.
+class ComputeSpatialSortProcess : public BaseProcess
+{
+ bool IsActive( unsigned int pFlags) const
+ {
+ return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace |
+ aiProcess_GenNormals | aiProcess_JoinIdenticalVertices));
+ }
+
+ void Execute( aiScene* pScene)
+ {
+ typedef std::pair<SpatialSort, float> _Type;
+ DefaultLogger::get()->debug("Generate spatially-sorted vertex cache");
+
+ std::vector<_Type>* p = new std::vector<_Type>(pScene->mNumMeshes);
+ std::vector<_Type>::iterator it = p->begin();
+
+ for (unsigned int i = 0; i < pScene->mNumMeshes; ++i, ++it) {
+ aiMesh* mesh = pScene->mMeshes[i];
+ _Type& blubb = *it;
+ blubb.first.Fill(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D));
+ blubb.second = ComputePositionEpsilon(mesh);
+ }
+
+ shared->AddProperty(AI_SPP_SPATIAL_SORT,p);
+ }
+};
+
+// -------------------------------------------------------------------------------
+// ... and the same again to cleanup the whole stuff
+class DestroySpatialSortProcess : public BaseProcess
+{
+ bool IsActive( unsigned int pFlags) const
+ {
+ return NULL != shared && 0 != (pFlags & (aiProcess_CalcTangentSpace |
+ aiProcess_GenNormals | aiProcess_JoinIdenticalVertices));
+ }
+
+ void Execute( aiScene* /*pScene*/)
+ {
+ shared->RemoveProperty(AI_SPP_SPATIAL_SORT);
+ }
+};
+
+} // ! namespace Assimp
+#endif // !! AI_PROCESS_HELPER_H_INCLUDED
diff --git a/3rdparty/assimp/code/Profiler.h b/3rdparty/assimp/code/Profiler.h
new file mode 100644
index 000000000..74fed429a
--- /dev/null
+++ b/3rdparty/assimp/code/Profiler.h
@@ -0,0 +1,97 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Profiler.h
+ * @brief Utility to measure the respective runtime of each import step
+ */
+#ifndef INCLUDED_PROFILER_H
+#define INCLUDED_PROFILER_H
+
+#include "boost/timer.hpp"
+
+#include "../include/DefaultLogger.h"
+#include "TinyFormatter.h"
+
+namespace Assimp {
+ namespace Profiling {
+
+ using namespace Formatter;
+
+
+// ------------------------------------------------------------------------------------------------
+/** Simple wrapper around boost::timer to simplify reporting. Timings are automatically
+ * dumped to the log file.
+ */
+class Profiler
+{
+
+public:
+
+ Profiler() {}
+
+public:
+
+ /** Start a named timer */
+ void BeginRegion(const std::string& region) {
+ regions[region] = boost::timer();
+ DefaultLogger::get()->debug((format("START `"),region,"`"));
+ }
+
+
+ /** End a specific named timer and write its end time to the log */
+ void EndRegion(const std::string& region) {
+ RegionMap::const_iterator it = regions.find(region);
+ if (it == regions.end()) {
+ return;
+ }
+
+ DefaultLogger::get()->debug((format("END `"),region,"`, dt= ",(*it).second.elapsed()," s"));
+ }
+
+private:
+
+ typedef std::map<std::string,boost::timer> RegionMap;
+ RegionMap regions;
+};
+
+ }
+}
+
+#endif
diff --git a/3rdparty/assimp/code/Q3BSPFileData.h b/3rdparty/assimp/code/Q3BSPFileData.h
new file mode 100644
index 000000000..22e37a348
--- /dev/null
+++ b/3rdparty/assimp/code/Q3BSPFileData.h
@@ -0,0 +1,228 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development 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 Development 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_Q3BSPFILEDATA_H_INC
+#define ASSIMP_Q3BSPFILEDATA_H_INC
+
+#include <vector>
+
+namespace Assimp
+{
+namespace Q3BSP
+{
+
+static const unsigned int CE_BSP_LIGHTMAPWIDTH = 128;
+static const unsigned int CE_BSP_LIGHTMAPHEIGHT = 128;
+
+static const unsigned int CE_BSP_LIGHTMAPSIZE = 128*128*3; ///< = 128( width ) * 128 ( height ) * 3 ( channels / RGB ).
+static const int VERION_Q3LEVEL = 46; ///< Supported version.
+
+/// Geometric type enumeration
+enum Q3BSPGeoType
+{
+ Polygon = 1,
+ Patch,
+ TriangleMesh,
+ Billboard
+};
+
+/// Integer vector.
+struct ceVec3i
+{
+ int x, y, z;
+ ceVec3i(): x( 0 ), y( 0 ), z( 0 ) { /* empty */ }
+ ceVec3i( int iX, int iY=0, int iZ=0) : x( iX ), y( iY ), z( iZ ) { /* empty */ }
+};
+
+/// Fileheader
+struct sQ3BSPHeader
+{
+ char strID[ 4 ]; //!< Should be "IBSP"
+ int iVersion; //!< 46 for standard levels
+};
+
+/// Descripes an entry.
+struct sQ3BSPLump
+{
+ int iOffset; ///< Offset from startpointer of file
+ int iSize; ///< Size fo part
+};
+
+struct vec2f
+{
+ float x,y;
+};
+
+struct vec3f
+{
+ float x, y, z;
+};
+
+/// Vertex of a Q3 level
+struct sQ3BSPVertex
+{
+ vec3f vPosition; ///< Position of vertex
+ vec2f vTexCoord; ///< (u,v) Texturecoordinate of detailtexture
+ vec2f vLightmap; ///< (u,v) Texturecoordinate of lightmap
+ vec3f vNormal; ///< vertex normale
+ unsigned char bColor[ 4 ]; ///< Color in RGBA
+};
+
+/// A face in bsp format info
+struct sQ3BSPFace
+{
+ int iTextureID; ///< Index in texture array
+ int iEffect; ///< Index in effectarray (-1 = no effect)
+ int iType; ///< 1=Polygon, 2=Patch, 3=Mesh, 4=Billboard
+ int iVertexIndex; ///< Start index of polygon
+ int iNumOfVerts; ///< Number of vertices
+ int iFaceVertexIndex; ///< Index of first mesh vertex
+ int iNumOfFaceVerts; ///< Anzahl der Meshvertices
+ int iLightmapID; ///< Index to the lightmap array
+ int iLMapCorner[ 2 ]; ///< Die Ecke der Lightmap in der Textur
+ int iLMapSize[ 2 ]; ///< Size of the lightmap stored on the texture
+ vec3f vLMapPos; ///< 3D-Ursprung der Lightmap
+ vec3f vLMapVecs[ 2 ]; ///< 3D-s-t-Vektoren
+ vec3f vNormal; ///< Polygonnormale
+ int patchWidth, patchHeight; ///< bezier patch
+};
+
+/// A quake3 texture name.
+struct sQ3BSPTexture
+{
+ char strName[ 64 ]; ///< Name of the texture without extention
+ int iFlags; ///< Not used
+ int iContents; ///< Not used
+};
+
+/// A lightmap of the level, size 128 x 128, RGB components.
+struct sQ3BSPLightmap
+{
+ unsigned char bLMapData[ CE_BSP_LIGHTMAPSIZE ];
+ sQ3BSPLightmap()
+ {
+ memset(bLMapData, 0, CE_BSP_LIGHTMAPSIZE );
+ }
+};
+
+struct SubPatch
+{
+ std::vector<size_t> indices;
+ int lightmapID;
+};
+
+enum eLumps
+{
+ kEntities = 0,
+ kTextures,
+ kPlanes,
+ kNodes,
+ kLeafs,
+ kLeafFaces,
+ kLeafBrushes,
+ kModels,
+ kBrushes,
+ kBrushSides,
+ kVertices,
+ kMeshVerts,
+ kShaders,
+ kFaces,
+ kLightmaps,
+ kLightVolumes,
+ kVisData,
+ kMaxLumps
+};
+
+struct Q3BSPModel
+{
+ std::vector<unsigned char> m_Data;
+ std::vector<sQ3BSPLump*> m_Lumps;
+ std::vector<sQ3BSPVertex*> m_Vertices;
+ std::vector<sQ3BSPFace*> m_Faces;
+ std::vector<int> m_Indices;
+ std::vector<sQ3BSPTexture*> m_Textures;
+ std::vector<sQ3BSPLightmap*> m_Lightmaps;
+ std::vector<char> m_EntityData;
+ std::string m_ModelName;
+
+ Q3BSPModel() :
+ m_Data(),
+ m_Lumps(),
+ m_Vertices(),
+ m_Faces(),
+ m_Indices(),
+ m_Textures(),
+ m_Lightmaps(),
+ m_EntityData(),
+ m_ModelName( "" )
+ {
+ // empty
+ }
+
+ ~Q3BSPModel()
+ {
+ for ( unsigned int i=0; i<m_Lumps.size(); i++ )
+ if ( NULL != m_Lumps[i] )
+ delete m_Lumps[i];
+
+ for ( unsigned int i=0; i<m_Vertices.size(); i++ )
+ if ( NULL != m_Vertices[ i ] )
+ delete m_Vertices[ i ];
+ for ( unsigned int i=0; i<m_Faces.size(); i++ )
+ if ( NULL != m_Faces[ i ] )
+ delete m_Faces[ i ];
+ for ( unsigned int i=0; i<m_Textures.size(); i++ )
+ if ( NULL != m_Textures[ i ] )
+ delete m_Textures[ i ];
+ for ( unsigned int i=0; i<m_Lightmaps.size(); i++ )
+ if ( NULL != m_Lightmaps[ i ] )
+ delete m_Lightmaps[ i ];
+
+ m_Lumps.clear();
+ m_Vertices.clear();
+ m_Faces.clear();
+ m_Textures.clear();
+ m_Lightmaps.clear();
+ }
+};
+
+} // Namespace Q3BSP
+} // Namespace Assimp
+
+#endif // ASSIMP_Q3BSPFILEDATA_H_INC
diff --git a/3rdparty/assimp/code/Q3BSPFileImporter.cpp b/3rdparty/assimp/code/Q3BSPFileImporter.cpp
new file mode 100644
index 000000000..ce198c49a
--- /dev/null
+++ b/3rdparty/assimp/code/Q3BSPFileImporter.cpp
@@ -0,0 +1,731 @@
+/*
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---------------------------------------------------------------------------------------------------
+*/
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
+
+//#include <windows.h>
+#include "DefaultIOSystem.h"
+#include "Q3BSPFileImporter.h"
+#include "Q3BSPZipArchive.h"
+#include "Q3BSPFileParser.h"
+#include "Q3BSPFileData.h"
+
+#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+# include <zlib.h>
+#else
+# include "../contrib/zlib/zlib.h"
+#endif
+
+#include "../include/aiTypes.h"
+#include "../include/aiMesh.h"
+#include <vector>
+
+namespace Assimp
+{
+
+using namespace Q3BSP;
+
+static const std::string Q3BSPExtention = "pk3";
+
+// ------------------------------------------------------------------------------------------------
+// Local fnction to create a material keyname.
+static void createKey( int id1, int id2, std::string &rKey )
+{
+ std::stringstream str;
+ str << id1 << "." << id2;
+ rKey = str.str();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Local function to extract the texture ids from a material keyname.
+static void extractIds( const std::string &rKey, int &rId1, int &rId2 )
+{
+ rId1 = -1;
+ rId2 = -1;
+ if ( rKey.empty() )
+ return;
+
+ std::string::size_type pos = rKey.find( "." );
+ if ( std::string::npos == pos )
+ return;
+
+ std::string tmp1 = rKey.substr( 0, pos );
+ std::string tmp2 = rKey.substr( pos + 1, rKey.size() - pos - 1 );
+ rId1 = atoi( tmp1.c_str() );
+ rId2 = atoi( tmp2.c_str() );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Local helper fuction to normalize filenames.
+static void normalizePathName( const std::string &rPath, std::string &rNormalizedPath )
+{
+ rNormalizedPath = "";
+ if ( rPath.empty() )
+ return;
+
+#ifdef _WIN32
+ std::string sep = "\\";
+#else
+ std::string sep = "/";
+#endif
+
+ static const unsigned int numDelimiters = 2;
+ const char delimiters[ numDelimiters ] = { '/', '\\' };
+ rNormalizedPath = rPath;
+ for ( unsigned int i=0; i<numDelimiters; i++ )
+ {
+ for ( size_t j=0; j<rNormalizedPath.size(); j++ )
+ {
+ if ( rNormalizedPath[j] == delimiters[ i ] )
+ {
+ rNormalizedPath[ j ] = sep[ 0 ];
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructor.
+Q3BSPFileImporter::Q3BSPFileImporter() :
+ m_pCurrentMesh( NULL ),
+ m_pCurrentFace( NULL ),
+ m_MaterialLookupMap(),
+ mTextures()
+{
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor.
+Q3BSPFileImporter::~Q3BSPFileImporter()
+{
+ // For lint
+ m_pCurrentMesh = NULL;
+ m_pCurrentFace = NULL;
+
+ // Clear face-to-material map
+ for ( FaceMap::iterator it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end();
+ ++it )
+ {
+ const std::string matName = (*it).first;
+ if ( matName.empty() )
+ {
+ continue;
+ }
+
+ std::vector<Q3BSP::sQ3BSPFace*> *pCurFaceArray = (*it).second;
+ if ( NULL != pCurFaceArray )
+ {
+ delete pCurFaceArray;
+ }
+ }
+ m_MaterialLookupMap.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns true, if the loader can read this.
+bool Q3BSPFileImporter::CanRead( const std::string& rFile, IOSystem* /*pIOHandler*/, bool checkSig ) const
+{
+ bool isBSPData = false;
+ if ( checkSig )
+ isBSPData = SimpleExtensionCheck( rFile, Q3BSPExtention.c_str() );
+
+ return isBSPData;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Adds extensions.
+void Q3BSPFileImporter::GetExtensionList( std::set<std::string>& extensions )
+{
+ extensions.insert( Q3BSPExtention );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Import method.
+void Q3BSPFileImporter::InternReadFile(const std::string &rFile, aiScene* pScene, IOSystem* /*pIOHandler*/)
+{
+ Q3BSPZipArchive Archive( rFile );
+ if ( !Archive.isOpen() )
+ {
+ throw new DeadlyImportError( "Failed to open file " + rFile + "." );
+ }
+
+ std::string archiveName( "" ), mapName( "" );
+ separateMapName( rFile, archiveName, mapName );
+
+ if ( mapName.empty() )
+ {
+ if ( !findFirstMapInArchive( Archive, mapName ) )
+ {
+ return;
+ }
+ }
+
+ Q3BSPFileParser fileParser( mapName, &Archive );
+ Q3BSPModel *pBSPModel = fileParser.getModel();
+ if ( NULL != pBSPModel )
+ {
+ CreateDataFromImport( pBSPModel, pScene, &Archive );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Separates the map name from the import name.
+void Q3BSPFileImporter::separateMapName( const std::string &rImportName, std::string &rArchiveName,
+ std::string &rMapName )
+{
+ rArchiveName = "";
+ rMapName = "";
+ if ( rImportName.empty() )
+ return;
+
+ std::string::size_type pos = rImportName.rfind( "," );
+ if ( std::string::npos == pos )
+ {
+ rArchiveName = rImportName;
+ return;
+ }
+
+ rArchiveName = rImportName.substr( 0, pos );
+ rMapName = rImportName.substr( pos, rImportName.size() - pos - 1 );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the first map in the map archive.
+bool Q3BSPFileImporter::findFirstMapInArchive( Q3BSPZipArchive &rArchive, std::string &rMapName )
+{
+ rMapName = "";
+ std::vector<std::string> fileList;
+ rArchive.getFileList( fileList );
+ if ( fileList.empty() )
+ return false;
+
+ for ( std::vector<std::string>::iterator it = fileList.begin(); it != fileList.end();
+ ++it )
+ {
+ std::string::size_type pos = (*it).find( "maps/" );
+ if ( std::string::npos != pos )
+ {
+ std::string::size_type extPos = (*it).find( ".bsp" );
+ if ( std::string::npos != extPos )
+ {
+ rMapName = *it;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the assimp specific data.
+void Q3BSPFileImporter::CreateDataFromImport( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
+ Q3BSPZipArchive *pArchive )
+{
+ if ( NULL == pModel || NULL == pScene )
+ return;
+
+ pScene->mRootNode = new aiNode;
+ if ( !pModel->m_ModelName.empty() )
+ {
+ pScene->mRootNode->mName.Set( pModel->m_ModelName );
+ }
+
+ // Create the face to material relation map
+ createMaterialMap( pModel );
+
+ // Create all nodes
+ CreateNodes( pModel, pScene, pScene->mRootNode );
+
+ // Create the assigned materials
+ createMaterials( pModel, pScene, pArchive );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates all assimp nodes.
+void Q3BSPFileImporter::CreateNodes( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
+ aiNode *pParent )
+{
+ ai_assert( NULL != pModel );
+ if ( NULL == pModel )
+ {
+ return;
+ }
+
+ unsigned int matIdx = 0;
+ std::vector<aiMesh*> MeshArray;
+ std::vector<aiNode*> NodeArray;
+ for ( FaceMapIt it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end(); ++it )
+ {
+ std::vector<Q3BSP::sQ3BSPFace*> *pArray = (*it).second;
+ size_t numVerts = countData( *pArray );
+ if ( 0 != numVerts )
+ {
+ aiMesh* pMesh = new aiMesh;
+ aiNode *pNode = CreateTopology( pModel, matIdx, *pArray, pMesh );
+ if ( NULL != pNode )
+ {
+ NodeArray.push_back( pNode );
+ MeshArray.push_back( pMesh );
+ }
+ else
+ {
+ delete pMesh;
+ }
+ }
+ matIdx++;
+ }
+
+ pScene->mNumMeshes = MeshArray.size();
+ if ( pScene->mNumMeshes > 0 )
+ {
+ pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ];
+ for ( size_t i = 0; i < MeshArray.size(); i++ )
+ {
+ aiMesh *pMesh = MeshArray[ i ];
+ if ( NULL != pMesh )
+ {
+ pScene->mMeshes[ i ] = pMesh;
+ }
+ }
+ }
+
+ pParent->mNumChildren = MeshArray.size();
+ pParent->mChildren = new aiNode*[ pScene->mRootNode->mNumChildren ];
+ for ( size_t i=0; i<NodeArray.size(); i++ )
+ {
+ aiNode *pNode = NodeArray[ i ];
+ pNode->mParent = pParent;
+ pParent->mChildren[ i ] = pNode;
+ pParent->mChildren[ i ]->mMeshes[ 0 ] = i;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the topology.
+aiNode *Q3BSPFileImporter::CreateTopology( const Q3BSP::Q3BSPModel *pModel,
+ unsigned int materialIdx,
+ std::vector<sQ3BSPFace*> &rArray,
+ aiMesh* pMesh )
+{
+ size_t numVerts = countData( rArray );
+ if ( 0 == numVerts )
+ {
+ return NULL;
+ }
+
+ size_t numFaces = countFaces( rArray );
+ if ( 0 == numFaces )
+ {
+ return NULL;
+ }
+
+ size_t numTriangles = countTriangles( rArray );
+ pMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ pMesh->mFaces = new aiFace[ numTriangles ];
+ pMesh->mNumFaces = numTriangles;
+
+ pMesh->mNumVertices = numVerts;
+ pMesh->mVertices = new aiVector3D[ numVerts ];
+ pMesh->mNormals = new aiVector3D[ numVerts ];
+ pMesh->mTextureCoords[ 0 ] = new aiVector3D[ numVerts ];
+ pMesh->mTextureCoords[ 1 ] = new aiVector3D[ numVerts ];
+ pMesh->mMaterialIndex = materialIdx;
+
+ unsigned int faceIdx = 0;
+ unsigned int vertIdx = 0;
+ pMesh->mNumUVComponents[ 0 ] = 2;
+ pMesh->mNumUVComponents[ 1 ] = 2;
+ for ( std::vector<sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end(); ++it )
+ {
+ Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
+ ai_assert( NULL != pQ3BSPFace );
+ if ( NULL == pQ3BSPFace )
+ {
+ continue;
+ }
+
+ if ( pQ3BSPFace->iNumOfFaceVerts > 0 )
+ {
+ if ( pQ3BSPFace->iType == Polygon || pQ3BSPFace->iType == TriangleMesh )
+ {
+ createTriangleTopology( pModel, pQ3BSPFace, pMesh, faceIdx, vertIdx );
+ }
+ }
+ }
+
+ aiNode *pNode = new aiNode;
+ pNode->mNumMeshes = 1;
+ pNode->mMeshes = new unsigned int[ 1 ];
+
+ return pNode;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the triangle topology from a face array.
+void Q3BSPFileImporter::createTriangleTopology( const Q3BSP::Q3BSPModel *pModel,
+ Q3BSP::sQ3BSPFace *pQ3BSPFace,
+ aiMesh* pMesh,
+ unsigned int &rFaceIdx,
+ unsigned int &rVertIdx )
+{
+ ai_assert( rFaceIdx < pMesh->mNumFaces );
+
+ m_pCurrentFace = getNextFace( pMesh, rFaceIdx );
+ ai_assert( NULL != m_pCurrentFace );
+ if ( NULL == m_pCurrentFace )
+ {
+ return;
+ }
+
+ m_pCurrentFace->mNumIndices = 3;
+ m_pCurrentFace->mIndices = new unsigned int[ m_pCurrentFace->mNumIndices ];
+
+ size_t idx = 0;
+ for ( size_t i = 0; i < (size_t) pQ3BSPFace->iNumOfFaceVerts; i++ )
+ {
+ const size_t index = pQ3BSPFace->iVertexIndex + pModel->m_Indices[ pQ3BSPFace->iFaceVertexIndex + i ];
+ ai_assert( index < pModel->m_Vertices.size() );
+ if ( index >= pModel->m_Vertices.size() )
+ {
+ continue;
+ }
+
+ sQ3BSPVertex *pVertex = pModel->m_Vertices[ index ];
+ ai_assert( NULL != pVertex );
+ if ( NULL == pVertex )
+ {
+ continue;
+ }
+
+ pMesh->mVertices[ rVertIdx ].Set( pVertex->vPosition.x, pVertex->vPosition.y, pVertex->vPosition.z );
+ pMesh->mNormals[ rVertIdx ].Set( pVertex->vNormal.x, pVertex->vNormal.y, pVertex->vNormal.z );
+
+ pMesh->mTextureCoords[ 0 ][ rVertIdx ].Set( pVertex->vTexCoord.x, pVertex->vTexCoord.y, 0.0f );
+ pMesh->mTextureCoords[ 1 ][ rVertIdx ].Set( pVertex->vLightmap.x, pVertex->vLightmap.y, 0.0f );
+
+ m_pCurrentFace->mIndices[ idx ] = rVertIdx;
+ rVertIdx++;
+
+ idx++;
+ if ( idx > 2 )
+ {
+ idx = 0;
+ m_pCurrentFace = getNextFace( pMesh, rFaceIdx );
+ if ( NULL != m_pCurrentFace )
+ {
+ m_pCurrentFace->mNumIndices = 3;
+ m_pCurrentFace->mIndices = new unsigned int[ 3 ];
+ }
+ }
+ }
+ rFaceIdx--;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates all referenced materials.
+void Q3BSPFileImporter::createMaterials( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
+ Q3BSPZipArchive *pArchive )
+{
+ if ( m_MaterialLookupMap.empty() )
+ {
+ return;
+ }
+
+ pScene->mMaterials = new aiMaterial*[ m_MaterialLookupMap.size() ];
+ // size_t texIdx( 0 );
+ aiString aiMatName;
+ int textureId( -1 ), lightmapId( -1 );
+ for ( FaceMapIt it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end();
+ ++it )
+ {
+ const std::string matName = (*it).first;
+ if ( matName.empty() )
+ {
+ continue;
+ }
+
+ aiMatName.Set( matName );
+ Assimp::MaterialHelper *pMatHelper = new Assimp::MaterialHelper;
+ pMatHelper->AddProperty( &aiMatName, AI_MATKEY_NAME );
+
+ extractIds( matName, textureId, lightmapId );
+
+ // Adding the texture
+ if ( -1 != textureId )
+ {
+ sQ3BSPTexture *pTexture = pModel->m_Textures[ textureId ];
+ if ( NULL != pTexture )
+ {
+ std::string tmp( "*" ), texName( "" );
+ tmp += pTexture->strName;
+ tmp += ".jpg";
+ normalizePathName( tmp, texName );
+
+ if ( !importTextureFromArchive( pModel, pArchive, pScene, pMatHelper, textureId ) )
+ {
+ }
+ }
+
+ }
+ if ( -1 != lightmapId )
+ {
+ importLightmap( pModel, pScene, pMatHelper, lightmapId );
+ }
+ pScene->mMaterials[ pScene->mNumMaterials ] = pMatHelper;
+ pScene->mNumMaterials++;
+ }
+ pScene->mNumTextures = mTextures.size();
+ pScene->mTextures = new aiTexture*[ pScene->mNumTextures ];
+ std::copy( mTextures.begin(), mTextures.end(), pScene->mTextures );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts the number of referenced vertices.
+size_t Q3BSPFileImporter::countData( const std::vector<sQ3BSPFace*> &rArray ) const
+{
+ size_t numVerts = 0;
+ for ( std::vector<sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end();
+ ++it )
+ {
+ sQ3BSPFace *pQ3BSPFace = *it;
+ if ( pQ3BSPFace->iType == Polygon || pQ3BSPFace->iType == TriangleMesh )
+ {
+ Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
+ ai_assert( NULL != pQ3BSPFace );
+ numVerts += pQ3BSPFace->iNumOfFaceVerts;
+ }
+ }
+
+ return numVerts;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts the faces with vertices.
+size_t Q3BSPFileImporter::countFaces( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const
+{
+ size_t numFaces = 0;
+ for ( std::vector<sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end();
+ ++it )
+ {
+ Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
+ if ( pQ3BSPFace->iNumOfFaceVerts > 0 )
+ {
+ numFaces++;
+ }
+ }
+
+ return numFaces;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts the number of triangles in a Q3-facearray.
+size_t Q3BSPFileImporter::countTriangles( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const
+{
+ size_t numTriangles = 0;
+ for ( std::vector<Q3BSP::sQ3BSPFace*>::const_iterator it = rArray.begin(); it != rArray.end();
+ ++it )
+ {
+ const Q3BSP::sQ3BSPFace *pQ3BSPFace = *it;
+ if ( NULL != pQ3BSPFace )
+ {
+ numTriangles += pQ3BSPFace->iNumOfFaceVerts / 3;
+ }
+ }
+
+ return numTriangles;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the faces-to-material map.
+void Q3BSPFileImporter::createMaterialMap( const Q3BSP::Q3BSPModel *pModel )
+{
+ std::string key( "" );
+ std::vector<sQ3BSPFace*> *pCurFaceArray = NULL;
+ for ( size_t idx = 0; idx < pModel->m_Faces.size(); idx++ )
+ {
+ Q3BSP::sQ3BSPFace *pQ3BSPFace = pModel->m_Faces[ idx ];
+ const int texId = pQ3BSPFace->iTextureID;
+ const int lightMapId = pQ3BSPFace->iLightmapID;
+ createKey( texId, lightMapId, key );
+ FaceMapIt it = m_MaterialLookupMap.find( key );
+ if ( m_MaterialLookupMap.end() == it )
+ {
+ pCurFaceArray = new std::vector<Q3BSP::sQ3BSPFace*>;
+ m_MaterialLookupMap[ key ] = pCurFaceArray;
+ }
+ else
+ {
+ pCurFaceArray = (*it).second;
+ }
+ ai_assert( NULL != pCurFaceArray );
+ if ( NULL != pCurFaceArray )
+ {
+ pCurFaceArray->push_back( pQ3BSPFace );
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the next face.
+aiFace *Q3BSPFileImporter::getNextFace( aiMesh *pMesh, unsigned int &rFaceIdx )
+{
+ aiFace *pFace = NULL;
+ if ( rFaceIdx < pMesh->mNumFaces )
+ {
+ pFace = &pMesh->mFaces[ rFaceIdx ];
+ rFaceIdx++;
+ }
+ else
+ {
+ pFace = NULL;
+ }
+
+ return pFace;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports a texture file.
+bool Q3BSPFileImporter::importTextureFromArchive( const Q3BSP::Q3BSPModel *pModel,
+ Q3BSP::Q3BSPZipArchive *pArchive, aiScene* /*pScene*/,
+ Assimp::MaterialHelper *pMatHelper, int textureId )
+{
+ if ( NULL == pArchive || NULL == pArchive || NULL == pMatHelper )
+ {
+ return false;
+ }
+
+ if ( textureId < 0 || textureId >= static_cast<int>( pModel->m_Textures.size() ) )
+ {
+ return false;
+ }
+
+ bool res = true;
+ sQ3BSPTexture *pTexture = pModel->m_Textures[ textureId ];
+ if ( NULL == pTexture )
+ return false;
+
+ std::string textureName = pTexture->strName;
+ textureName += ".jpg";
+ if ( pArchive->Exists( textureName.c_str() ) )
+ {
+ IOStream *pTextureStream = pArchive->Open( textureName.c_str() );
+ if ( NULL != pTextureStream )
+ {
+ size_t texSize = pTextureStream->FileSize();
+ aiTexture *pTexture = new aiTexture;
+ pTexture->mHeight = 0;
+ pTexture->mWidth = texSize;
+ unsigned char *pData = new unsigned char[ pTexture->mWidth ];
+ size_t readSize = pTextureStream->Read( pData, sizeof( unsigned char ), pTexture->mWidth );
+ ai_assert( readSize == pTexture->mWidth );
+ pTexture->pcData = reinterpret_cast<aiTexel*>( pData );
+ pTexture->achFormatHint[ 0 ] = 'j';
+ pTexture->achFormatHint[ 1 ] = 'p';
+ pTexture->achFormatHint[ 2 ] = 'g';
+ pTexture->achFormatHint[ 2 ] = '\0';
+ res = true;
+
+ aiString name;
+ name.data[ 0 ] = '*';
+ name.length = 1 + ASSIMP_itoa10( name.data + 1, MAXLEN-1, mTextures.size() );
+
+ pArchive->Close( pTextureStream );
+
+ pMatHelper->AddProperty( &name, AI_MATKEY_TEXTURE_DIFFUSE( 0 ) );
+ mTextures.push_back( pTexture );
+ }
+ }
+
+ return res;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports a lightmap file.
+bool Q3BSPFileImporter::importLightmap( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene,
+ Assimp::MaterialHelper *pMatHelper, int lightmapId )
+{
+ if ( NULL == pModel || NULL == pScene || NULL == pMatHelper )
+ {
+ return false;
+ }
+
+ if ( lightmapId < 0 || lightmapId >= static_cast<int>( pModel->m_Lightmaps.size() ) )
+ {
+ return false;
+ }
+
+ sQ3BSPLightmap *pLightMap = pModel->m_Lightmaps[ lightmapId ];
+ if ( NULL == pLightMap )
+ {
+ return false;
+ }
+
+ aiTexture *pTexture = new aiTexture;
+ pTexture->mHeight = 0;
+ pTexture->mWidth = CE_BSP_LIGHTMAPWIDTH * CE_BSP_LIGHTMAPHEIGHT;
+
+ unsigned char *pData = new unsigned char[ pTexture->mWidth ];
+ pTexture->pcData = reinterpret_cast<aiTexel*>( pData );
+
+ pTexture->achFormatHint[ 0 ] = 'b';
+ pTexture->achFormatHint[ 1 ] = 'm';
+ pTexture->achFormatHint[ 2 ] = 'p';
+ pTexture->achFormatHint[ 3 ] = '\0';
+
+ memcpy( pTexture->pcData, pLightMap->bLMapData, pTexture->mWidth );
+
+ aiString name;
+ name.data[ 0 ] = '*';
+ name.length = 1 + ASSIMP_itoa10( name.data + 1, MAXLEN-1, mTextures.size() );
+
+ pMatHelper->AddProperty( &name,AI_MATKEY_TEXTURE_LIGHTMAP( 1 ) );
+ mTextures.push_back( pTexture );
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_Q3BSP_IMPORTER
diff --git a/3rdparty/assimp/code/Q3BSPFileImporter.h b/3rdparty/assimp/code/Q3BSPFileImporter.h
new file mode 100644
index 000000000..adf79e64c
--- /dev/null
+++ b/3rdparty/assimp/code/Q3BSPFileImporter.h
@@ -0,0 +1,110 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development 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 Development 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_Q3BSPFILEIMPORTER_H_INC
+#define ASSIMP_Q3BSPFILEIMPORTER_H_INC
+
+#include "BaseImporter.h"
+
+struct aiMesh;
+
+namespace Assimp
+{
+namespace Q3BSP
+{
+
+class Q3BSPZipArchive;
+struct Q3BSPModel;
+struct sQ3BSPFace;
+
+}
+
+/** Loader to import BSP-levels from a PK3 archive or from a unpacked BSP-level.
+ */
+class Q3BSPFileImporter : BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /// @brief Default constructor.
+ Q3BSPFileImporter();
+
+ /// @brief Destructor.
+ ~Q3BSPFileImporter();
+
+public:
+ /// @brief Returns whether the class can handle the format of the given file.
+ /// @remark See BaseImporter::CanRead() for details.
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig ) const;
+
+private:
+ typedef std::map<std::string, std::vector<Q3BSP::sQ3BSPFace*>*> FaceMap;
+ typedef std::map<std::string, std::vector<Q3BSP::sQ3BSPFace*>* >::iterator FaceMapIt;
+ typedef std::map<std::string, std::vector<Q3BSP::sQ3BSPFace*>*>::const_iterator FaceMapConstIt;
+
+ void GetExtensionList(std::set<std::string>& extensions);
+ void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+ void separateMapName( const std::string &rImportName, std::string &rArchiveName, std::string &rMapName );
+ bool findFirstMapInArchive( Q3BSP::Q3BSPZipArchive &rArchive, std::string &rMapName );
+ void CreateDataFromImport( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, Q3BSP::Q3BSPZipArchive *pArchive );
+ void CreateNodes( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, aiNode *pParent );
+ aiNode *CreateTopology( const Q3BSP::Q3BSPModel *pModel, unsigned int materialIdx,
+ std::vector<Q3BSP::sQ3BSPFace*> &rArray, aiMesh* pMesh );
+ void createTriangleTopology( const Q3BSP::Q3BSPModel *pModel, Q3BSP::sQ3BSPFace *pQ3BSPFace, aiMesh* pMesh, unsigned int &rFaceIdx,
+ unsigned int &rVertIdx );
+ void createMaterials( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, Q3BSP::Q3BSPZipArchive *pArchive );
+ size_t countData( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const;
+ size_t countFaces( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const;
+ size_t countTriangles( const std::vector<Q3BSP::sQ3BSPFace*> &rArray ) const;
+ void createMaterialMap( const Q3BSP::Q3BSPModel *pModel);
+ aiFace *getNextFace( aiMesh *pMesh, unsigned int &rFaceIdx );
+ bool importTextureFromArchive( const Q3BSP::Q3BSPModel *pModel, Q3BSP::Q3BSPZipArchive *pArchive, aiScene* pScene,
+ Assimp::MaterialHelper *pMatHelper, int textureId );
+ bool importLightmap( const Q3BSP::Q3BSPModel *pModel, aiScene* pScene, Assimp::MaterialHelper *pMatHelper, int lightmapId );
+
+private:
+ aiMesh *m_pCurrentMesh;
+ aiFace *m_pCurrentFace;
+ FaceMap m_MaterialLookupMap;
+ std::vector<aiTexture*> mTextures;
+};
+
+} // Namespace Assimp
+
+#endif // ASSIMP_Q3BSPFILEIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/Q3BSPFileParser.cpp b/3rdparty/assimp/code/Q3BSPFileParser.cpp
new file mode 100644
index 000000000..09411ca56
--- /dev/null
+++ b/3rdparty/assimp/code/Q3BSPFileParser.cpp
@@ -0,0 +1,272 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#include "AssimpPCH.h"
+#include "Q3BSPFileParser.h"
+#include "DefaultIOSystem.h"
+#include "Q3BSPFileData.h"
+#include "Q3BSPZipArchive.h"
+#include <vector>
+
+namespace Assimp
+{
+
+using namespace Q3BSP;
+
+// ------------------------------------------------------------------------------------------------
+Q3BSPFileParser::Q3BSPFileParser( const std::string &rMapName, Q3BSPZipArchive *pZipArchive ) :
+ m_sOffset( 0 ),
+ m_Data(),
+ m_pModel( NULL ),
+ m_pZipArchive( pZipArchive )
+{
+ ai_assert( NULL != m_pZipArchive );
+ ai_assert( !rMapName.empty() );
+
+ if ( !readData( rMapName ) )
+ return;
+
+ m_pModel = new Q3BSPModel;
+ m_pModel->m_ModelName = rMapName;
+ if ( !parseFile() )
+ {
+ delete m_pModel;
+ m_pModel = NULL;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Q3BSPFileParser::~Q3BSPFileParser()
+{
+ delete m_pModel;
+ m_pModel = NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+Q3BSP::Q3BSPModel *Q3BSPFileParser::getModel() const
+{
+ return m_pModel;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Q3BSPFileParser::readData( const std::string &rMapName )
+{
+ if ( !m_pZipArchive->Exists( rMapName.c_str() ) )
+ return false;
+
+ IOStream *pMapFile = m_pZipArchive->Open( rMapName.c_str() );
+ if ( NULL == pMapFile )
+ return false;
+
+ const size_t size = pMapFile->FileSize();
+ m_Data.resize( size );
+
+ const size_t readSize = pMapFile->Read( &m_Data[0], sizeof( char ), size );
+ if ( readSize != size )
+ {
+ m_Data.clear();
+ return false;
+ }
+ m_pZipArchive->Close( pMapFile );
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Q3BSPFileParser::parseFile()
+{
+ if ( m_Data.empty() )
+ {
+ return false;
+ }
+
+ if ( !validateFormat() )
+ {
+ return false;
+ }
+
+ // Imports the dictionary of the level
+ getLumps();
+
+ // Conunt data and prepare model data
+ countLumps();
+
+ // Read in Vertices
+ getVertices();
+
+ // Read in Indices
+ getIndices();
+
+ // Read Faces
+ getFaces();
+
+ // Read Textures
+ getTextures();
+
+ // Read Lightmaps
+ getLightMaps();
+
+ // Load the entities
+ getEntities();
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Q3BSPFileParser::validateFormat()
+{
+ sQ3BSPHeader *pHeader = (sQ3BSPHeader*) &m_Data[ 0 ];
+ m_sOffset += sizeof( sQ3BSPHeader );
+
+ // Version and identify string validation
+ if (pHeader->strID[ 0 ] != 'I' || pHeader->strID[ 1 ] != 'B' || pHeader->strID[ 2 ] != 'S'
+ || pHeader->strID[ 3 ] != 'P')
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getLumps()
+{
+ size_t Offset = m_sOffset;
+ m_pModel->m_Lumps.resize( kMaxLumps );
+ for ( size_t idx=0; idx < kMaxLumps; idx++ )
+ {
+ sQ3BSPLump *pLump = new sQ3BSPLump;
+ memcpy( pLump, &m_Data[ Offset ], sizeof( sQ3BSPLump ) );
+ Offset += sizeof( sQ3BSPLump );
+ m_pModel->m_Lumps[ idx ] = pLump;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::countLumps()
+{
+ m_pModel->m_Vertices.resize( m_pModel->m_Lumps[ kVertices ]->iSize / sizeof( sQ3BSPVertex ) );
+ m_pModel->m_Indices.resize( m_pModel->m_Lumps[ kMeshVerts ]->iSize / sizeof( int ) );
+ m_pModel->m_Faces.resize( m_pModel->m_Lumps[ kFaces ]->iSize / sizeof( sQ3BSPFace ) );
+ m_pModel->m_Textures.resize( m_pModel->m_Lumps[ kTextures ]->iSize / sizeof( sQ3BSPTexture ) );
+ m_pModel->m_Lightmaps.resize( m_pModel->m_Lumps[ kLightmaps ]->iSize / sizeof( sQ3BSPLightmap ) );
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getVertices()
+{
+ size_t Offset = m_pModel->m_Lumps[ kVertices ]->iOffset;
+ for ( size_t idx = 0; idx < m_pModel->m_Vertices.size(); idx++ )
+ {
+ sQ3BSPVertex *pVertex = new sQ3BSPVertex;
+ memcpy( pVertex, &m_Data[ Offset ], sizeof( sQ3BSPVertex ) );
+ Offset += sizeof( sQ3BSPVertex );
+ m_pModel->m_Vertices[ idx ] = pVertex;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getIndices()
+{
+ ai_assert( NULL != m_pModel );
+
+ sQ3BSPLump *lump = m_pModel->m_Lumps[ kMeshVerts ];
+ size_t Offset = (size_t) lump->iOffset;
+ const size_t nIndices = lump->iSize / sizeof( int );
+ m_pModel->m_Indices.resize( nIndices );
+ memcpy( &m_pModel->m_Indices[ 0 ], &m_Data[ Offset ], lump->iSize );
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getFaces()
+{
+ ai_assert( NULL != m_pModel );
+
+ size_t Offset = m_pModel->m_Lumps[ kFaces ]->iOffset;
+ for ( size_t idx = 0; idx < m_pModel->m_Faces.size(); idx++ )
+ {
+ sQ3BSPFace *pFace = new sQ3BSPFace;
+ memcpy( pFace, &m_Data[ Offset ], sizeof( sQ3BSPFace ) );
+ m_pModel->m_Faces[ idx ] = pFace;
+ Offset += sizeof( sQ3BSPFace );
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getTextures()
+{
+ ai_assert( NULL != m_pModel );
+
+ size_t Offset = m_pModel->m_Lumps[ kTextures ]->iOffset;
+ for ( size_t idx=0; idx < m_pModel->m_Textures.size(); idx++ )
+ {
+ sQ3BSPTexture *pTexture = new sQ3BSPTexture;
+ memcpy( pTexture, &m_Data[ Offset ], sizeof(sQ3BSPTexture) );
+ m_pModel->m_Textures[ idx ] = pTexture;
+ Offset += sizeof(sQ3BSPTexture);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getLightMaps()
+{
+ ai_assert( NULL != m_pModel );
+
+ size_t Offset = m_pModel->m_Lumps[kLightmaps]->iOffset;
+ for ( size_t idx=0; idx < m_pModel->m_Lightmaps.size(); idx++ )
+ {
+ sQ3BSPLightmap *pLightmap = new sQ3BSPLightmap;
+ memcpy( pLightmap, &m_Data[ Offset ], sizeof( sQ3BSPLightmap ) );
+ Offset += sizeof( sQ3BSPLightmap );
+ m_pModel->m_Lightmaps[ idx ] = pLightmap;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3BSPFileParser::getEntities()
+{
+ int size = m_pModel->m_Lumps[ kEntities ]->iSize;
+ m_pModel->m_EntityData.resize( size );
+ size_t Offset = m_pModel->m_Lumps[ kEntities ]->iOffset;
+ memcpy( &m_pModel->m_EntityData[ 0 ], &m_Data[ Offset ], sizeof( char ) * size );
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
diff --git a/3rdparty/assimp/code/Q3BSPFileParser.h b/3rdparty/assimp/code/Q3BSPFileParser.h
new file mode 100644
index 000000000..a049dc297
--- /dev/null
+++ b/3rdparty/assimp/code/Q3BSPFileParser.h
@@ -0,0 +1,89 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development 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 Development 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_Q3BSPFILEPARSER_H_INC
+#define ASSIMP_Q3BSPFILEPARSER_H_INC
+
+#include "BaseImporter.h"
+#include <string>
+
+namespace Assimp
+{
+namespace Q3BSP
+{
+
+class Q3BSPZipArchive;
+struct Q3BSPModel;
+class ZipFile;
+
+}
+
+// -------------------------------------------------------------------
+// -------------------------------------------------------------------
+class Q3BSPFileParser
+{
+public:
+ Q3BSPFileParser( const std::string &rMapName, Q3BSP::Q3BSPZipArchive *pZipArchive );
+ ~Q3BSPFileParser();
+ Q3BSP::Q3BSPModel *getModel() const;
+
+protected:
+ bool readData(const std::string &rMapName);
+ bool parseFile();
+ bool validateFormat();
+ void getLumps();
+ void countLumps();
+ void getVertices();
+ void getIndices();
+ void getFaces();
+ void getTextures();
+ void getLightMaps();
+ void getEntities();
+
+private:
+ size_t m_sOffset;
+ std::vector<char> m_Data;
+ Q3BSP::Q3BSPModel *m_pModel;
+ Q3BSP::Q3BSPZipArchive *m_pZipArchive;
+};
+
+} // Namespace Assimp
+
+#endif // ASSIMP_Q3BSPFILEPARSER_H_INC
diff --git a/3rdparty/assimp/code/Q3BSPZipArchive.cpp b/3rdparty/assimp/code/Q3BSPZipArchive.cpp
new file mode 100644
index 000000000..62ef5ffcf
--- /dev/null
+++ b/3rdparty/assimp/code/Q3BSPZipArchive.cpp
@@ -0,0 +1,196 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#include "Q3BSPZipArchive.h"
+#include <algorithm>
+#include <cassert>
+
+namespace Assimp
+{
+namespace Q3BSP
+{
+
+// ------------------------------------------------------------------------------------------------
+// Constructor.
+Q3BSPZipArchive::Q3BSPZipArchive( const std::string& rFile ) :
+ m_ZipFileHandle( NULL ),
+ m_FileList(),
+ m_bDirty( true )
+{
+ if ( !rFile.empty() )
+ {
+ m_ZipFileHandle = unzOpen( rFile.c_str() );
+ if ( NULL != m_ZipFileHandle )
+ {
+ mapArchive();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor.
+Q3BSPZipArchive::~Q3BSPZipArchive()
+{
+ if ( NULL != m_ZipFileHandle )
+ {
+ unzClose( m_ZipFileHandle );
+ }
+ m_ZipFileHandle = NULL;
+ m_FileList.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns true, if the archive is already open.
+bool Q3BSPZipArchive::isOpen() const
+{
+ return ( NULL != m_ZipFileHandle );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns true, if the filename is part of the archive.
+bool Q3BSPZipArchive::Exists( const char* pFile ) const
+{
+ ai_assert( NULL != pFile );
+ if ( NULL == pFile )
+ {
+ return false;
+ }
+
+ std::string rFile( pFile );
+ std::vector<std::string>::const_iterator it = std::find( m_FileList.begin(), m_FileList.end(), rFile );
+ if ( m_FileList.end() == it )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns the separator delimiter.
+char Q3BSPZipArchive::getOsSeparator() const
+{
+ return '/';
+}
+
+// ------------------------------------------------------------------------------------------------
+// Opens a file, which is part of the archive.
+IOStream *Q3BSPZipArchive::Open( const char* pFile, const char* /*pMode*/ )
+{
+ ai_assert( NULL != pFile );
+
+ std::string rItem( pFile );
+ std::vector<std::string>::iterator it = std::find( m_FileList.begin(), m_FileList.end(), rItem );
+ if ( m_FileList.end() == it )
+ return NULL;
+
+ ZipFile *pZipFile = new ZipFile( *it, m_ZipFileHandle );
+ m_ArchiveMap[ rItem ] = pZipFile;
+
+ return pZipFile;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Close a filestream.
+void Q3BSPZipArchive::Close( IOStream *pFile )
+{
+ ai_assert( NULL != pFile );
+
+ std::map<std::string, IOStream*>::iterator it;
+ for ( it = m_ArchiveMap.begin(); it != m_ArchiveMap.end(); ++it )
+ {
+ if ( (*it).second == pFile )
+ {
+ ZipFile *pZipFile = reinterpret_cast<ZipFile*>( (*it).second );
+ delete pZipFile;
+ m_ArchiveMap.erase( it );
+ break;
+ }
+ }
+}
+// ------------------------------------------------------------------------------------------------
+// Returns the file-list of the archive.
+void Q3BSPZipArchive::getFileList( std::vector<std::string> &rFileList )
+{
+ rFileList = m_FileList;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Maps the archive content.
+bool Q3BSPZipArchive::mapArchive()
+{
+ if ( NULL == m_ZipFileHandle )
+ return false;
+
+ if ( !m_bDirty )
+ return true;
+
+ if ( !m_FileList.empty() )
+ m_FileList.resize( 0 );
+
+ // At first ensure file is already open
+ if ( UNZ_OK == unzGoToFirstFile( m_ZipFileHandle ) )
+ {
+ char filename[ FileNameSize ];
+ unzGetCurrentFileInfo( m_ZipFileHandle, NULL, filename, FileNameSize, NULL, 0, NULL, 0 );
+ m_FileList.push_back( filename );
+ unzCloseCurrentFile( m_ZipFileHandle );
+
+ // Loop over all files
+ while ( unzGoToNextFile( m_ZipFileHandle ) != UNZ_END_OF_LIST_OF_FILE )
+ {
+ char filename[ FileNameSize ];
+ unzGetCurrentFileInfo( m_ZipFileHandle, NULL, filename, FileNameSize, NULL, 0, NULL, 0 );
+ m_FileList.push_back( filename );
+ unzCloseCurrentFile( m_ZipFileHandle );
+ }
+ }
+
+ std::sort( m_FileList.begin(), m_FileList.end() );
+ m_bDirty = false;
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Q3BSP
+} // Namespace Assimp
diff --git a/3rdparty/assimp/code/Q3BSPZipArchive.h b/3rdparty/assimp/code/Q3BSPZipArchive.h
new file mode 100644
index 000000000..c0d8b96cf
--- /dev/null
+++ b/3rdparty/assimp/code/Q3BSPZipArchive.h
@@ -0,0 +1,182 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development 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 Development 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 AI_Q3BSP_ZIPARCHIVE_H_INC
+#define AI_Q3BSP_ZIPARCHIVE_H_INC
+
+#include "../contrib/unzip/unzip.h"
+#include "../include/IOStream.h"
+#include "../include/IOSystem.h"
+#include <string>
+#include <vector>
+#include <map>
+#include <cassert>
+
+namespace Assimp
+{
+namespace Q3BSP
+{
+
+// ------------------------------------------------------------------------------------------------
+/// \class ZipFile
+/// \ingroup Assimp::Q3BSP
+///
+/// \brief
+// ------------------------------------------------------------------------------------------------
+class ZipFile : public IOStream
+{
+public:
+ ZipFile( const std::string &rFileName, unzFile zipFile ) :
+ m_Name( rFileName ),
+ m_zipFile( zipFile )
+ {
+ ai_assert( NULL != m_zipFile );
+ }
+
+ ~ZipFile()
+ {
+ m_zipFile = NULL;
+ }
+
+ size_t Read(void* pvBuffer, size_t pSize, size_t pCount )
+ {
+ size_t bytes_read = 0;
+ if ( NULL == m_zipFile )
+ return bytes_read;
+
+ // search file and place file pointer there
+ if ( unzLocateFile( m_zipFile, m_Name.c_str(), 0 ) == UNZ_OK )
+ {
+ // get file size, etc.
+ unz_file_info fileInfo;
+ unzGetCurrentFileInfo( m_zipFile, &fileInfo, 0, 0, 0, 0, 0, 0 );
+ const size_t size = pSize * pCount;
+ assert( size <= fileInfo.uncompressed_size );
+
+ // The file has EXACTLY the size of uncompressed_size. In C
+ // you need to mark the last character with '\0', so add
+ // another character
+ unzOpenCurrentFile( m_zipFile );
+ bytes_read = unzReadCurrentFile( m_zipFile, pvBuffer, fileInfo.uncompressed_size);
+ if ( /*bytes_read < 0 ||*/ bytes_read != static_cast<size_t>( fileInfo.uncompressed_size ) )
+ {
+ return 0;
+ }
+ //size_t filesize = fileInfo.uncompressed_size;
+ unzCloseCurrentFile( m_zipFile );
+ }
+ return bytes_read;
+ }
+
+ size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/)
+ {
+ return 0;
+ }
+
+ size_t FileSize() const
+ {
+ if ( NULL == m_zipFile )
+ return 0;
+ if ( unzLocateFile( m_zipFile, m_Name.c_str(), 0 ) == UNZ_OK )
+ {
+ unz_file_info fileInfo;
+ unzGetCurrentFileInfo( m_zipFile, &fileInfo, 0, 0, 0, 0, 0, 0 );
+ return fileInfo.uncompressed_size;
+ }
+ return 0;
+ }
+
+ aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/)
+ {
+ return aiReturn_FAILURE;
+ }
+
+ size_t Tell() const
+ {
+ return 0;
+ }
+
+ void Flush()
+ {
+ // empty
+ }
+
+private:
+ std::string m_Name;
+ unzFile m_zipFile;
+};
+
+// ------------------------------------------------------------------------------------------------
+/// \class Q3BSPZipArchive
+/// \ingroup Assimp::Q3BSP
+///
+/// \brief IMplements a zip archive like the WinZip archives. Will be also used to import data
+/// from a P3K archive ( Quake level format ).
+// ------------------------------------------------------------------------------------------------
+class Q3BSPZipArchive : public Assimp::IOSystem
+{
+public:
+ static const unsigned int FileNameSize = 256;
+
+public:
+ Q3BSPZipArchive( const std::string & rFile );
+ ~Q3BSPZipArchive();
+ bool Exists( const char* pFile) const;
+ char getOsSeparator() const;
+ IOStream* Open(const char* pFile, const char* pMode = "rb");
+ void Close( IOStream* pFile);
+ bool isOpen() const;
+ void getFileList( std::vector<std::string> &rFileList );
+
+private:
+ bool mapArchive();
+
+private:
+ unzFile m_ZipFileHandle;
+ std::map<std::string, IOStream*> m_ArchiveMap;
+ std::vector<std::string> m_FileList;
+ bool m_bDirty;
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Q3BSP
+} // Namespace Assimp
+
+#endif // AI_Q3BSP_ZIPARCHIVE_H_INC
diff --git a/3rdparty/assimp/code/Q3DLoader.cpp b/3rdparty/assimp/code/Q3DLoader.cpp
new file mode 100644
index 000000000..aaba431c3
--- /dev/null
+++ b/3rdparty/assimp/code/Q3DLoader.cpp
@@ -0,0 +1,600 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Q3DLoader.cpp
+ * @brief Implementation of the Q3D importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
+
+// internal headers
+#include "Q3DLoader.h"
+#include "StreamReader.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+Q3DImporter::Q3DImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+Q3DImporter::~Q3DImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool Q3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "q3s" || extension == "q3o")
+ return true;
+ else if (!extension.length() || checkSig) {
+ if (!pIOHandler)
+ return true;
+ const char* tokens[] = {"quick3Do","quick3Ds"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,2);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Q3DImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("q3o");
+ extensions.insert("q3s");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void Q3DImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ StreamReaderLE stream(pIOHandler->Open(pFile,"rb"));
+
+ // The header is 22 bytes large
+ if (stream.GetRemainingSize() < 22)
+ throw DeadlyImportError("File is either empty or corrupt: " + pFile);
+
+ // Check the file's signature
+ if (ASSIMP_strincmp( (const char*)stream.GetPtr(), "quick3Do", 8 ) &&
+ ASSIMP_strincmp( (const char*)stream.GetPtr(), "quick3Ds", 8 ))
+ {
+ throw DeadlyImportError("Not a Quick3D file. Signature string is: " +
+ std::string((const char*)stream.GetPtr(),8));
+ }
+
+ // Print the file format version
+ DefaultLogger::get()->info("Quick3D File format version: " +
+ std::string(&((const char*)stream.GetPtr())[8],2));
+
+ // ... an store it
+ char major = ((const char*)stream.GetPtr())[8];
+ char minor = ((const char*)stream.GetPtr())[9];
+
+ stream.IncPtr(10);
+ unsigned int numMeshes = (unsigned int)stream.GetI4();
+ unsigned int numMats = (unsigned int)stream.GetI4();
+ unsigned int numTextures = (unsigned int)stream.GetI4();
+
+ std::vector<Material> materials;
+ materials.reserve(numMats);
+
+ std::vector<Mesh> meshes;
+ meshes.reserve(numMeshes);
+
+ // Allocate the scene root node
+ pScene->mRootNode = new aiNode();
+
+ aiColor3D fgColor (0.6f,0.6f,0.6f);
+
+ // Now read all file chunks
+ while (true)
+ {
+ if (stream.GetRemainingSize() < 1)break;
+ char c = stream.GetI1();
+ switch (c)
+ {
+ // Meshes chunk
+ case 'm':
+ {
+ for (unsigned int quak = 0; quak < numMeshes; ++quak)
+ {
+ meshes.push_back(Mesh());
+ Mesh& mesh = meshes.back();
+
+ // read all vertices
+ unsigned int numVerts = (unsigned int)stream.GetI4();
+ if (!numVerts)
+ throw DeadlyImportError("Quick3D: Found mesh with zero vertices");
+
+ std::vector<aiVector3D>& verts = mesh.verts;
+ verts.resize(numVerts);
+
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ verts[i].x = stream.GetF4();
+ verts[i].y = stream.GetF4();
+ verts[i].z = stream.GetF4();
+ }
+
+ // read all faces
+ numVerts = (unsigned int)stream.GetI4();
+ if (!numVerts)
+ throw DeadlyImportError("Quick3D: Found mesh with zero faces");
+
+ std::vector<Face >& faces = mesh.faces;
+ faces.reserve(numVerts);
+
+ // number of indices
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ faces.push_back(Face(stream.GetI2()) );
+ if (faces.back().indices.empty())
+ throw DeadlyImportError("Quick3D: Found face with zero indices");
+ }
+
+ // indices
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ Face& vec = faces[i];
+ for (unsigned int a = 0; a < (unsigned int)vec.indices.size();++a)
+ vec.indices[a] = stream.GetI4();
+ }
+
+ // material indices
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ faces[i].mat = (unsigned int)stream.GetI4();
+ }
+
+ // read all normals
+ numVerts = (unsigned int)stream.GetI4();
+ std::vector<aiVector3D>& normals = mesh.normals;
+ normals.resize(numVerts);
+
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ normals[i].x = stream.GetF4();
+ normals[i].y = stream.GetF4();
+ normals[i].z = stream.GetF4();
+ }
+
+ numVerts = (unsigned int)stream.GetI4();
+ if (numTextures && numVerts)
+ {
+ // read all texture coordinates
+ std::vector<aiVector3D>& uv = mesh.uv;
+ uv.resize(numVerts);
+
+ for (unsigned int i = 0; i < numVerts;++i)
+ {
+ uv[i].x = stream.GetF4();
+ uv[i].y = stream.GetF4();
+ }
+
+ // UV indices
+ for (unsigned int i = 0; i < (unsigned int)faces.size();++i)
+ {
+ Face& vec = faces[i];
+ for (unsigned int a = 0; a < (unsigned int)vec.indices.size();++a)
+ {
+ vec.uvindices[a] = stream.GetI4();
+ if (!i && !a)
+ mesh.prevUVIdx = vec.uvindices[a];
+ else if (vec.uvindices[a] != mesh.prevUVIdx)
+ mesh.prevUVIdx = 0xffffffff;
+ }
+ }
+ }
+
+ // we don't need the rest, but we need to get to the next chunk
+ stream.IncPtr(36);
+ if (minor > '0' && major == '3')
+ stream.IncPtr(mesh.faces.size());
+ }
+ // stream.IncPtr(4); // unknown value here
+ }
+ break;
+
+ // materials chunk
+ case 'c':
+
+ for (unsigned int i = 0; i < numMats; ++i)
+ {
+ materials.push_back(Material());
+ Material& mat = materials.back();
+
+ // read the material name
+ while (( c = stream.GetI1()))
+ mat.name.data[mat.name.length++] = c;
+
+ // add the terminal character
+ mat.name.data[mat.name.length] = '\0';
+
+ // read the ambient color
+ mat.ambient.r = stream.GetF4();
+ mat.ambient.g = stream.GetF4();
+ mat.ambient.b = stream.GetF4();
+
+ // read the diffuse color
+ mat.diffuse.r = stream.GetF4();
+ mat.diffuse.g = stream.GetF4();
+ mat.diffuse.b = stream.GetF4();
+
+ // read the ambient color
+ mat.specular.r = stream.GetF4();
+ mat.specular.g = stream.GetF4();
+ mat.specular.b = stream.GetF4();
+
+ // read the transparency
+ mat.transparency = stream.GetF4();
+
+ // unknown value here
+ // stream.IncPtr(4);
+ // FIX: it could be the texture index ...
+ mat.texIdx = (unsigned int)stream.GetI4();
+ }
+
+ break;
+
+ // texture chunk
+ case 't':
+
+ pScene->mNumTextures = numTextures;
+ if (!numTextures)break;
+ pScene->mTextures = new aiTexture*[pScene->mNumTextures];
+ // to make sure we won't crash if we leave through an exception
+ ::memset(pScene->mTextures,0,sizeof(void*)*pScene->mNumTextures);
+ for (unsigned int i = 0; i < pScene->mNumTextures; ++i)
+ {
+ aiTexture* tex = pScene->mTextures[i] = new aiTexture();
+
+ // skip the texture name
+ while (stream.GetI1()) {};
+
+ // read texture width and height
+ tex->mWidth = (unsigned int)stream.GetI4();
+ tex->mHeight = (unsigned int)stream.GetI4();
+
+ if (!tex->mWidth || !tex->mHeight)
+ throw DeadlyImportError("Quick3D: Invalid texture. Width or height is zero");
+
+ register unsigned int mul = tex->mWidth * tex->mHeight;
+ aiTexel* begin = tex->pcData = new aiTexel[mul];
+ aiTexel* const end = & begin [mul];
+
+ for (;begin != end; ++begin)
+ {
+ begin->r = stream.GetI1();
+ begin->g = stream.GetI1();
+ begin->b = stream.GetI1();
+ begin->a = 0xff;
+ }
+ }
+
+ break;
+
+ // scene chunk
+ case 's':
+ {
+ // skip position and rotation
+ stream.IncPtr(12);
+
+ for (unsigned int i = 0; i < 4;++i)
+ for (unsigned int a = 0; a < 4;++a)
+ pScene->mRootNode->mTransformation[i][a] = stream.GetF4();
+
+ stream.IncPtr(16);
+
+ // now setup a single camera
+ pScene->mNumCameras = 1;
+ pScene->mCameras = new aiCamera*[1];
+ aiCamera* cam = pScene->mCameras[0] = new aiCamera();
+ cam->mPosition.x = stream.GetF4();
+ cam->mPosition.y = stream.GetF4();
+ cam->mPosition.z = stream.GetF4();
+ cam->mName.Set("Q3DCamera");
+
+ // skip eye rotation for the moment
+ stream.IncPtr(12);
+
+ // read the default material color
+ fgColor .r = stream.GetF4();
+ fgColor .g = stream.GetF4();
+ fgColor .b = stream.GetF4();
+
+ // skip some unimportant properties
+ stream.IncPtr(29);
+
+ // setup a single point light with no attenuation
+ pScene->mNumLights = 1;
+ pScene->mLights = new aiLight*[1];
+ aiLight* light = pScene->mLights[0] = new aiLight();
+ light->mName.Set("Q3DLight");
+ light->mType = aiLightSource_POINT;
+
+ light->mAttenuationConstant = 1;
+ light->mAttenuationLinear = 0;
+ light->mAttenuationQuadratic = 0;
+
+ light->mColorDiffuse.r = stream.GetF4();
+ light->mColorDiffuse.g = stream.GetF4();
+ light->mColorDiffuse.b = stream.GetF4();
+
+ light->mColorSpecular = light->mColorDiffuse;
+
+
+ // We don't need the rest, but we need to know where
+ // this fucking chunk ends.
+ unsigned int temp = (unsigned int)(stream.GetI4() * stream.GetI4());
+
+ // skip the background file name
+ while (stream.GetI1()) {};
+
+ // skip background texture data + the remaining fields
+ stream.IncPtr(temp*3 + 20); // 4 bytes of unknown data here
+
+ // TODO
+ goto outer;
+ }
+ break;
+
+ default:
+ throw DeadlyImportError("Quick3D: Unknown chunk");
+ break;
+ };
+ }
+outer:
+
+ // If we have no mesh loaded - break here
+ if (meshes.empty())
+ throw DeadlyImportError("Quick3D: No meshes loaded");
+
+ // If we have no materials loaded - generate a default mat
+ if (materials.empty())
+ {
+ DefaultLogger::get()->info("Quick3D: No material found, generating one");
+ materials.push_back(Material());
+ materials.back().diffuse = fgColor ;
+ }
+
+ // find out which materials we'll need
+ typedef std::pair<unsigned int, unsigned int> FaceIdx;
+ typedef std::vector< FaceIdx > FaceIdxArray;
+ FaceIdxArray* fidx = new FaceIdxArray[materials.size()];
+
+ unsigned int p = 0;
+ for (std::vector<Mesh>::iterator it = meshes.begin(), end = meshes.end();
+ it != end; ++it,++p)
+ {
+ unsigned int q = 0;
+ for (std::vector<Face>::iterator fit = (*it).faces.begin(), fend = (*it).faces.end();
+ fit != fend; ++fit,++q)
+ {
+ if ((*fit).mat >= materials.size())
+ {
+ DefaultLogger::get()->warn("Quick3D: Material index overflow");
+ (*fit).mat = 0;
+ }
+ if (fidx[(*fit).mat].empty())++pScene->mNumMeshes;
+ fidx[(*fit).mat].push_back( FaceIdx(p,q) );
+ }
+ }
+ pScene->mNumMaterials = pScene->mNumMeshes;
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+ pScene->mMeshes = new aiMesh*[pScene->mNumMaterials];
+
+ for (unsigned int i = 0, real = 0; i < (unsigned int)materials.size(); ++i)
+ {
+ if (fidx[i].empty())continue;
+
+ // Allocate a mesh and a material
+ aiMesh* mesh = pScene->mMeshes[real] = new aiMesh();
+ MaterialHelper* mat = new MaterialHelper();
+ pScene->mMaterials[real] = mat;
+
+ mesh->mMaterialIndex = real;
+
+ // Build the output material
+ Material& srcMat = materials[i];
+ mat->AddProperty(&srcMat.diffuse, 1,AI_MATKEY_COLOR_DIFFUSE);
+ mat->AddProperty(&srcMat.specular, 1,AI_MATKEY_COLOR_SPECULAR);
+ mat->AddProperty(&srcMat.ambient, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ // NOTE: Ignore transparency for the moment - it seems
+ // unclear how to interpret the data
+#if 0
+ if (!(minor > '0' && major == '3'))
+ srcMat.transparency = 1.0f - srcMat.transparency;
+ mat->AddProperty(&srcMat.transparency, 1, AI_MATKEY_OPACITY);
+#endif
+
+ // add shininess - Quick3D seems to use it ins its viewer
+ srcMat.transparency = 16.f;
+ mat->AddProperty(&srcMat.transparency, 1, AI_MATKEY_SHININESS);
+
+ int m = (int)aiShadingMode_Phong;
+ mat->AddProperty(&m, 1, AI_MATKEY_SHADING_MODEL);
+
+ if (srcMat.name.length)
+ mat->AddProperty(&srcMat.name,AI_MATKEY_NAME);
+
+ // Add a texture
+ if (srcMat.texIdx < pScene->mNumTextures || real < pScene->mNumTextures)
+ {
+ srcMat.name.data[0] = '*';
+ srcMat.name.length = ASSIMP_itoa10(&srcMat.name.data[1],1000,
+ (srcMat.texIdx < pScene->mNumTextures ? srcMat.texIdx : real));
+ mat->AddProperty(&srcMat.name,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+
+ mesh->mNumFaces = (unsigned int)fidx[i].size();
+ aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces];
+
+ // Now build the output mesh. First find out how many
+ // vertices we'll need
+ for (FaceIdxArray::const_iterator it = fidx[i].begin(),end = fidx[i].end();
+ it != end; ++it)
+ {
+ mesh->mNumVertices += (unsigned int)meshes[(*it).first].faces[
+ (*it).second].indices.size();
+ }
+
+ aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ aiVector3D* norms = mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+ aiVector3D* uv;
+ if (real < pScene->mNumTextures)
+ {
+ uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
+ mesh->mNumUVComponents[0] = 2;
+ }
+ else uv = NULL;
+
+ // Build the final array
+ unsigned int cnt = 0;
+ for (FaceIdxArray::const_iterator it = fidx[i].begin(),end = fidx[i].end();
+ it != end; ++it, ++faces)
+ {
+ Mesh& m = meshes[(*it).first];
+ Face& face = m.faces[(*it).second];
+ faces->mNumIndices = (unsigned int)face.indices.size();
+ faces->mIndices = new unsigned int [faces->mNumIndices];
+
+
+ aiVector3D faceNormal;
+ bool fnOK = false;
+
+ for (unsigned int n = 0; n < faces->mNumIndices;++n, ++cnt, ++norms, ++verts)
+ {
+ if (face.indices[n] >= m.verts.size())
+ {
+ DefaultLogger::get()->warn("Quick3D: Vertex index overflow");
+ face.indices[n] = 0;
+ }
+
+ // copy vertices
+ *verts = m.verts[ face.indices[n] ];
+
+ if (face.indices[n] >= m.normals.size() && faces->mNumIndices >= 3)
+ {
+ // we have no normal here - assign the face normal
+ if (!fnOK)
+ {
+ const aiVector3D& pV1 = m.verts[ face.indices[0] ];
+ const aiVector3D& pV2 = m.verts[ face.indices[1] ];
+ const aiVector3D& pV3 = m.verts[ face.indices.size() - 1 ];
+ faceNormal = (pV2 - pV1) ^ (pV3 - pV1).Normalize();
+ fnOK = true;
+ }
+ *norms = faceNormal;
+ }
+ else *norms = m.normals[ face.indices[n] ];
+
+ // copy texture coordinates
+ if (uv && m.uv.size())
+ {
+ if (m.prevUVIdx != 0xffffffff && m.uv.size() >= m.verts.size()) // workaround
+ {
+ *uv = m.uv[face.indices[n]];
+ }
+ else
+ {
+ if (face.uvindices[n] >= m.uv.size())
+ {
+ DefaultLogger::get()->warn("Quick3D: Texture coordinate index overflow");
+ face.uvindices[n] = 0;
+ }
+ *uv = m.uv[face.uvindices[n]];
+ }
+ uv->y = 1.f - uv->y;
+ ++uv;
+ }
+
+ // setup the new vertex index
+ faces->mIndices[n] = cnt;
+ }
+
+ }
+ ++real;
+ }
+
+ // Delete our nice helper array
+ delete[] fidx;
+
+ // Now we need to attach the meshes to the root node of the scene
+ pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+ pScene->mRootNode->mMeshes = new unsigned int [pScene->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mRootNode->mMeshes[i] = i;
+
+ /*pScene->mRootNode->mTransformation *= aiMatrix4x4(
+ 1.f, 0.f, 0.f, 0.f,
+ 0.f, -1.f,0.f, 0.f,
+ 0.f, 0.f, 1.f, 0.f,
+ 0.f, 0.f, 0.f, 1.f);*/
+
+ // Add cameras and light sources to the scene root node
+ pScene->mRootNode->mNumChildren = pScene->mNumLights+pScene->mNumCameras;
+ if (pScene->mRootNode->mNumChildren)
+ {
+ pScene->mRootNode->mChildren = new aiNode* [ pScene->mRootNode->mNumChildren ];
+
+ // the light source
+ aiNode* nd = pScene->mRootNode->mChildren[0] = new aiNode();
+ nd->mParent = pScene->mRootNode;
+ nd->mName.Set("Q3DLight");
+ nd->mTransformation = pScene->mRootNode->mTransformation;
+ nd->mTransformation.Inverse();
+
+ // camera
+ nd = pScene->mRootNode->mChildren[1] = new aiNode();
+ nd->mParent = pScene->mRootNode;
+ nd->mName.Set("Q3DCamera");
+ nd->mTransformation = pScene->mRootNode->mChildren[0]->mTransformation;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_Q3D_IMPORTER
diff --git a/3rdparty/assimp/code/Q3DLoader.h b/3rdparty/assimp/code/Q3DLoader.h
new file mode 100644
index 000000000..e26c034b3
--- /dev/null
+++ b/3rdparty/assimp/code/Q3DLoader.h
@@ -0,0 +1,135 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Q3DLoader.h
+ * @brief Declaration of the Q3D importer class.
+ */
+#ifndef AI_Q3DLOADER_H_INCLUDED
+#define AI_Q3DLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+#include <vector>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Importer class for the Quick3D Object and Scene formats.
+*/
+class Q3DImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ Q3DImporter();
+
+ /** Destructor, private as well */
+ ~Q3DImporter();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+ struct Material
+ {
+ Material()
+ : diffuse (0.6f,0.6f,0.6f)
+ , transparency (0.f)
+ , texIdx (0xffffffff)
+ {}
+
+ aiString name;
+ aiColor3D ambient, diffuse, specular;
+ float transparency;
+
+ unsigned int texIdx;
+ };
+
+ struct Face
+ {
+ Face(unsigned int s)
+ : indices (s)
+ , uvindices (s)
+ , mat (0)
+ {
+ }
+
+ std::vector<unsigned int> indices;
+ std::vector<unsigned int> uvindices;
+ unsigned int mat;
+ };
+
+ struct Mesh
+ {
+
+ std::vector<aiVector3D> verts;
+ std::vector<aiVector3D> normals;
+ std::vector<aiVector3D> uv;
+ std::vector<Face> faces;
+
+ uint32_t prevUVIdx;
+ };
+};
+
+} // end of namespace Assimp
+
+#endif // AI_Q3DIMPORTER_H_IN
diff --git a/3rdparty/assimp/code/RawLoader.cpp b/3rdparty/assimp/code/RawLoader.cpp
new file mode 100644
index 000000000..c14ecdc10
--- /dev/null
+++ b/3rdparty/assimp/code/RawLoader.cpp
@@ -0,0 +1,312 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 RawLoader.cpp
+ * @brief Implementation of the RAW importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
+
+// internal headers
+#include "RawLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+RAWImporter::RAWImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+RAWImporter::~RAWImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool RAWImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const
+{
+ return SimpleExtensionCheck(pFile,"raw");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get the list of all supported file extensions
+void RAWImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("raw");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void RAWImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open RAW file " + pFile + ".");
+ }
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ // (terminate it with zero)
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+ const char* buffer = &mBuffer2[0];
+
+ // list of groups loaded from the file
+ std::vector< GroupInformation > outGroups(1,GroupInformation("<default>"));
+ std::vector< GroupInformation >::iterator curGroup = outGroups.begin();
+
+ // now read all lines
+ char line[4096];
+ while (GetNextLine(buffer,line))
+ {
+ // if the line starts with a non-numeric identifier, it marks
+ // the beginning of a new group
+ const char* sz = line;SkipSpaces(&sz);
+ if (IsLineEnd(*sz))continue;
+ if (!IsNumeric(*sz))
+ {
+ const char* sz2 = sz;
+ while (!IsSpaceOrNewLine(*sz2))++sz2;
+ const unsigned int length = (unsigned int)(sz2-sz);
+
+ // find an existing group with this name
+ for (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end();
+ it != end;++it)
+ {
+ if (length == (*it).name.length() && !::strcmp(sz,(*it).name.c_str()))
+ {
+ curGroup = it;sz2 = NULL;
+ break;
+ }
+ }
+ if (sz2)
+ {
+ outGroups.push_back(GroupInformation(std::string(sz,length)));
+ curGroup = outGroups.end()-1;
+ }
+ }
+ else
+ {
+ // there can be maximally 12 floats plus an extra texture file name
+ float data[12];
+ unsigned int num;
+ for (num = 0; num < 12;++num)
+ {
+ if (!SkipSpaces(&sz) || !IsNumeric(*sz))break;
+ sz = fast_atof_move(sz,data[num]);
+ }
+ if (num != 12 && num != 9)
+ {
+ DefaultLogger::get()->error("A line may have either 9 or 12 floats and an optional texture");
+ continue;
+ }
+
+ MeshInformation* output = NULL;
+
+ const char* sz2 = sz;
+ unsigned int length;
+ if (!IsLineEnd(*sz))
+ {
+ while (!IsSpaceOrNewLine(*sz2))++sz2;
+ length = (unsigned int)(sz2-sz);
+ }
+ else if (9 == num)
+ {
+ sz = "%default%";
+ length = 9;
+ }
+ else
+ {
+ sz = "";
+ length = 0;
+ }
+
+ // search in the list of meshes whether we have one with this texture
+ for (std::vector< MeshInformation >::iterator it = (*curGroup).meshes.begin(),
+ end = (*curGroup).meshes.end(); it != end; ++it)
+ {
+ if (length == (*it).name.length() && (length ? !::strcmp(sz,(*it).name.c_str()) : true))
+ {
+ output = &(*it);
+ break;
+ }
+ }
+ // if we don't have the mesh, create it
+ if (!output)
+ {
+ (*curGroup).meshes.push_back(MeshInformation(std::string(sz,length)));
+ output = &((*curGroup).meshes.back());
+ }
+ if (12 == num)
+ {
+ aiColor4D v(data[0],data[1],data[2],1.0f);
+ output->colors.push_back(v);
+ output->colors.push_back(v);
+ output->colors.push_back(v);
+
+ output->vertices.push_back(aiVector3D(data[3],data[4],data[5]));
+ output->vertices.push_back(aiVector3D(data[6],data[7],data[8]));
+ output->vertices.push_back(aiVector3D(data[9],data[10],data[11]));
+ }
+ else
+ {
+ output->vertices.push_back(aiVector3D(data[0],data[1],data[2]));
+ output->vertices.push_back(aiVector3D(data[3],data[4],data[5]));
+ output->vertices.push_back(aiVector3D(data[6],data[7],data[8]));
+ }
+ }
+ }
+
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mName.Set("<RawRoot>");
+
+ // count the number of valid groups
+ // (meshes can't be empty)
+ for (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end();
+ it != end;++it)
+ {
+ if (!(*it).meshes.empty())
+ {
+ ++pScene->mRootNode->mNumChildren;
+ pScene->mNumMeshes += (unsigned int)(*it).meshes.size();
+ }
+ }
+
+ if (!pScene->mNumMeshes)
+ {
+ throw DeadlyImportError("RAW: No meshes loaded. The file seems to be corrupt or empty.");
+ }
+
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ aiNode** cc;
+ if (1 == pScene->mRootNode->mNumChildren)
+ {
+ cc = &pScene->mRootNode;
+ pScene->mRootNode->mNumChildren = 0;
+ }
+ else cc = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren];
+
+ pScene->mNumMaterials = pScene->mNumMeshes;
+ aiMaterial** mats = pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
+
+ unsigned int meshIdx = 0;
+ for (std::vector< GroupInformation >::iterator it = outGroups.begin(), end = outGroups.end();
+ it != end;++it)
+ {
+ if ((*it).meshes.empty())continue;
+
+ aiNode* node;
+ if (pScene->mRootNode->mNumChildren)
+ {
+ node = *cc = new aiNode();
+ node->mParent = pScene->mRootNode;
+ }
+ else node = *cc;++cc;
+ node->mName.Set((*it).name);
+
+ // add all meshes
+ node->mNumMeshes = (unsigned int)(*it).meshes.size();
+ unsigned int* pi = node->mMeshes = new unsigned int[ node->mNumMeshes ];
+ for (std::vector< MeshInformation >::iterator it2 = (*it).meshes.begin(),
+ end2 = (*it).meshes.end(); it2 != end2; ++it2)
+ {
+ ai_assert(!(*it2).vertices.empty());
+
+ // allocate the mesh
+ *pi++ = meshIdx;
+ aiMesh* mesh = pScene->mMeshes[meshIdx] = new aiMesh();
+ mesh->mMaterialIndex = meshIdx++;
+
+ mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ // allocate storage for the vertex components and copy them
+ mesh->mNumVertices = (unsigned int)(*it2).vertices.size();
+ mesh->mVertices = new aiVector3D[ mesh->mNumVertices ];
+ ::memcpy(mesh->mVertices,&(*it2).vertices[0],sizeof(aiVector3D)*mesh->mNumVertices);
+
+ if ((*it2).colors.size())
+ {
+ ai_assert((*it2).colors.size() == mesh->mNumVertices);
+
+ mesh->mColors[0] = new aiColor4D[ mesh->mNumVertices ];
+ ::memcpy(mesh->mColors[0],&(*it2).colors[0],sizeof(aiColor4D)*mesh->mNumVertices);
+ }
+
+ // generate triangles
+ ai_assert(0 == mesh->mNumVertices % 3);
+ aiFace* fc = mesh->mFaces = new aiFace[ mesh->mNumFaces = mesh->mNumVertices/3 ];
+ aiFace* const fcEnd = fc + mesh->mNumFaces;
+ unsigned int n = 0;
+ while (fc != fcEnd)
+ {
+ aiFace& f = *fc++;
+ f.mIndices = new unsigned int[f.mNumIndices = 3];
+ for (unsigned int m = 0; m < 3;++m)
+ f.mIndices[m] = n++;
+ }
+
+ // generate a material for the mesh
+ MaterialHelper* mat = new MaterialHelper();
+
+ aiColor4D clr(1.0f,1.0f,1.0f,1.0f);
+ if ("%default%" == (*it2).name) // a gray default material
+ {
+ clr.r = clr.g = clr.b = 0.6f;
+ }
+ else if ((*it2).name.length() > 0) // a texture
+ {
+ aiString s;
+ s.Set((*it2).name);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ mat->AddProperty<aiColor4D>(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+ *mats++ = mat;
+ }
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_RAW_IMPORTER
diff --git a/3rdparty/assimp/code/RawLoader.h b/3rdparty/assimp/code/RawLoader.h
new file mode 100644
index 000000000..6200adee4
--- /dev/null
+++ b/3rdparty/assimp/code/RawLoader.h
@@ -0,0 +1,123 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 RAWLoader.h
+ * @brief Declaration of the RAW importer class.
+ */
+#ifndef AI_RAWLOADER_H_INCLUDED
+#define AI_RAWLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+#include <vector>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Importer class for the PovRay RAW triangle format
+*/
+class RAWImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ RAWImporter();
+
+ /** Destructor, private as well */
+ ~RAWImporter();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+ struct MeshInformation
+ {
+ MeshInformation(const std::string& _name)
+ : name(_name)
+ {
+ vertices.reserve(100);
+ colors.reserve(100);
+ }
+
+ std::string name;
+
+ std::vector<aiVector3D> vertices;
+ std::vector<aiColor4D> colors;
+ };
+
+ struct GroupInformation
+ {
+ GroupInformation(const std::string& _name)
+ : name(_name)
+ {
+ meshes.reserve(10);
+ }
+
+ std::string name;
+ std::vector<MeshInformation> meshes;
+ };
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_IN
diff --git a/3rdparty/assimp/code/RemoveComments.cpp b/3rdparty/assimp/code/RemoveComments.cpp
new file mode 100644
index 000000000..a0a62af8f
--- /dev/null
+++ b/3rdparty/assimp/code/RemoveComments.cpp
@@ -0,0 +1,108 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 RemoveComments.cpp
+ * @brief Defines the CommentRemover utility class
+ */
+
+#include "AssimpPCH.h"
+#include "RemoveComments.h"
+#include "ParsingUtils.h"
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------------------
+// Remove line comments from a file
+void CommentRemover::RemoveLineComments(const char* szComment,
+ char* szBuffer, char chReplacement /* = ' ' */)
+{
+ // validate parameters
+ ai_assert(NULL != szComment && NULL != szBuffer && *szComment);
+
+ const size_t len = strlen(szComment);
+ while (*szBuffer) {
+
+ // skip over quotes
+ if (*szBuffer == '\"' || *szBuffer == '\'')
+ while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\'') {};
+
+ if (!strncmp(szBuffer,szComment,len)) {
+ while (!IsLineEnd(*szBuffer))
+ *szBuffer++ = chReplacement;
+ }
+ ++szBuffer;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Remove multi-line comments from a file
+void CommentRemover::RemoveMultiLineComments(const char* szCommentStart,
+ const char* szCommentEnd,char* szBuffer,
+ char chReplacement)
+{
+ // validate parameters
+ ai_assert(NULL != szCommentStart && NULL != szCommentEnd &&
+ NULL != szBuffer && *szCommentStart && *szCommentEnd);
+
+ const size_t len = strlen(szCommentEnd);
+ const size_t len2 = strlen(szCommentStart);
+
+ while (*szBuffer) {
+ // skip over quotes
+ if (*szBuffer == '\"' || *szBuffer == '\'')
+ while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\'') {};
+
+ if (!strncmp(szBuffer,szCommentStart,len2)) {
+ while (*szBuffer) {
+ if (!::strncmp(szBuffer,szCommentEnd,len)) {
+ for (unsigned int i = 0; i < len;++i)
+ *szBuffer++ = chReplacement;
+
+ break;
+ }
+ *szBuffer++ = chReplacement;
+ }
+ continue;
+ }
+ ++szBuffer;
+ }
+}
+
+} // !! Assimp
diff --git a/3rdparty/assimp/code/RemoveComments.h b/3rdparty/assimp/code/RemoveComments.h
new file mode 100644
index 000000000..6c9099556
--- /dev/null
+++ b/3rdparty/assimp/code/RemoveComments.h
@@ -0,0 +1,88 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Declares a helper class, "CommentRemover", which can be
+ * used to remove comments (single and multi line) from a text file.
+ */
+#ifndef AI_REMOVE_COMMENTS_H_INC
+#define AI_REMOVE_COMMENTS_H_INC
+
+#include "../include/aiAssert.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** \brief Helper class to remove single and multi line comments from a file
+ *
+ * Some mesh formats like MD5 have comments that are quite similar
+ * to those in C or C++ so this code has been moved to a separate
+ * module.
+ */
+class ASSIMP_API CommentRemover
+{
+ // class cannot be instanced
+ CommentRemover() {}
+
+public:
+
+ //! Remove single-line comments. The end of a line is
+ //! expected to be either NL or CR or NLCR.
+ //! \param szComment The start sequence of the comment, e.g. "//"
+ //! \param szBuffer Buffer to work with
+ //! \param chReplacement Character to be used as replacement
+ //! for commented lines. By default this is ' '
+ static void RemoveLineComments(const char* szComment,
+ char* szBuffer, char chReplacement = ' ');
+
+ //! Remove multi-line comments. The end of a line is
+ //! expected to be either NL or CR or NLCR. Multi-line comments
+ //! may not be nested (as in C).
+ //! \param szCommentStart The start sequence of the comment, e.g. "/*"
+ //! \param szCommentEnd The end sequence of the comment, e.g. "*/"
+ //! \param szBuffer Buffer to work with
+ //! \param chReplacement Character to be used as replacement
+ //! for commented lines. By default this is ' '
+ static void RemoveMultiLineComments(const char* szCommentStart,
+ const char* szCommentEnd,char* szBuffer,
+ char chReplacement = ' ');
+};
+} // ! Assimp
+
+#endif // !! AI_REMOVE_COMMENTS_H_INC
diff --git a/3rdparty/assimp/code/RemoveRedundantMaterials.cpp b/3rdparty/assimp/code/RemoveRedundantMaterials.cpp
new file mode 100644
index 000000000..c2d4e9bb1
--- /dev/null
+++ b/3rdparty/assimp/code/RemoveRedundantMaterials.cpp
@@ -0,0 +1,204 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 RemoveRedundantMaterials.cpp
+ * @brief Implementation of the "RemoveRedundantMaterials" post processing step
+*/
+
+// internal headers
+#include "AssimpPCH.h"
+#include "RemoveRedundantMaterials.h"
+#include "ParsingUtils.h"
+#include "ProcessHelper.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+RemoveRedundantMatsProcess::RemoveRedundantMatsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+RemoveRedundantMatsProcess::~RemoveRedundantMatsProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_RemoveRedundantMaterials) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import properties
+void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp)
+{
+ // Get value of AI_CONFIG_PP_RRM_EXCLUDE_LIST
+ configFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,"");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("RemoveRedundantMatsProcess begin");
+
+ unsigned int iCnt = 0, unreferenced = 0;
+ if (pScene->mNumMaterials)
+ {
+ // Find out which materials are referenced by meshes
+ std::vector<bool> abReferenced(pScene->mNumMaterials,false);
+ for (unsigned int i = 0;i < pScene->mNumMeshes;++i)
+ abReferenced[pScene->mMeshes[i]->mMaterialIndex] = true;
+
+ // If a list of materials to be excluded was given, match the list with
+ // our imported materials and 'salt' all positive matches to ensure that
+ // we get unique hashes later.
+ if (configFixedMaterials.length()) {
+
+ std::list<std::string> strings;
+ ConvertListToStrings(configFixedMaterials,strings);
+
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
+ aiMaterial* mat = pScene->mMaterials[i];
+
+ aiString name;
+ mat->Get(AI_MATKEY_NAME,name);
+
+ if (name.length) {
+ std::list<std::string>::const_iterator it = std::find(strings.begin(), strings.end(), name.data);
+ if (it != strings.end()) {
+
+ // Our brilliant 'salt': A single material property with ~ as first
+ // character to mark it as internal and temporary.
+ const int dummy = 1;
+ ((MaterialHelper*)mat)->AddProperty(&dummy,1,"~RRM.UniqueMaterial",0,0);
+
+ // Keep this material even if no mesh references it
+ abReferenced[i] = true;
+ DefaultLogger::get()->debug(std::string("Found positive match in exclusion list: \'") + name.data + "\'");
+ }
+ }
+ }
+ }
+
+
+ // TODO: reimplement this algorithm to work in-place
+
+ unsigned int* aiMappingTable = new unsigned int[pScene->mNumMaterials];
+ unsigned int iNewNum = 0;
+
+ // Iterate through all materials and calculate a hash for them
+ // store all hashes in a list and so a quick search whether
+ // we do already have a specific hash. This allows us to
+ // determine which materials are identical.
+ uint32_t* aiHashes;
+ aiHashes = new uint32_t[pScene->mNumMaterials];
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
+ {
+ // if the material is not referenced ... remove it
+ if (!abReferenced[i]) {
+ ++unreferenced;
+ continue;
+ }
+
+ uint32_t me = aiHashes[i] = ((MaterialHelper*)pScene->mMaterials[i])->ComputeHash();
+ for (unsigned int a = 0; a < i;++a)
+ {
+ if (me == aiHashes[a]) {
+ ++iCnt;
+ me = 0;
+ aiMappingTable[i] = aiMappingTable[a];
+ delete pScene->mMaterials[i];
+ break;
+ }
+ }
+ if (me) {
+ aiMappingTable[i] = iNewNum++;
+ }
+ }
+ if (iCnt) {
+ // build an output material list
+ aiMaterial** ppcMaterials = new aiMaterial*[iNewNum];
+ ::memset(ppcMaterials,0,sizeof(void*)*iNewNum);
+ for (unsigned int p = 0; p < pScene->mNumMaterials;++p)
+ {
+ // if the material is not referenced ... remove it
+ if (!abReferenced[p])
+ continue;
+
+ // generate new names for all modified materials
+ const unsigned int idx = aiMappingTable[p];
+ if (ppcMaterials[idx])
+ {
+ aiString sz;
+ sz.length = ::sprintf(sz.data,"JoinedMaterial_#%i",p);
+ ((MaterialHelper*)ppcMaterials[idx])->AddProperty(&sz,AI_MATKEY_NAME);
+ }
+ else ppcMaterials[idx] = pScene->mMaterials[p];
+ }
+ // update all material indices
+ for (unsigned int p = 0; p < pScene->mNumMeshes;++p) {
+ aiMesh* mesh = pScene->mMeshes[p];
+ mesh->mMaterialIndex = aiMappingTable[mesh->mMaterialIndex];
+ }
+ // delete the old material list
+ delete[] pScene->mMaterials;
+ pScene->mMaterials = ppcMaterials;
+ pScene->mNumMaterials = iNewNum;
+ }
+ // delete temporary storage
+ delete[] aiHashes;
+ delete[] aiMappingTable;
+ }
+ if (!iCnt)DefaultLogger::get()->debug("RemoveRedundantMatsProcess finished ");
+ else
+ {
+ char szBuffer[128]; // should be sufficiently large
+ ::sprintf(szBuffer,"RemoveRedundantMatsProcess finished. %i redundant and %i unused materials",
+ iCnt,unreferenced);
+ DefaultLogger::get()->info(szBuffer);
+ }
+}
diff --git a/3rdparty/assimp/code/RemoveRedundantMaterials.h b/3rdparty/assimp/code/RemoveRedundantMaterials.h
new file mode 100644
index 000000000..97297f5e8
--- /dev/null
+++ b/3rdparty/assimp/code/RemoveRedundantMaterials.h
@@ -0,0 +1,107 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 RemoveRedundantMaterials.h
+ * @brief Defines a post processing step to remove redundant materials
+ */
+#ifndef AI_REMOVEREDUNDANTMATERIALS_H_INC
+#define AI_REMOVEREDUNDANTMATERIALS_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+
+class RemoveRedundantMatsTest;
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** RemoveRedundantMatsProcess: Postprocessing steo to remove redundant
+ * materials from the imported scene.
+ */
+class ASSIMP_API RemoveRedundantMatsProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::RemoveRedundantMatsTest; // grant the unit test full access to us
+
+protected:
+ /** Constructor to be privately used by Importer */
+ RemoveRedundantMatsProcess();
+
+ /** Destructor, private as well */
+ ~RemoveRedundantMatsProcess();
+
+public:
+ // -------------------------------------------------------------------
+ // Check whether step is active
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ // Execute step on a given scene
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ // Setup import settings
+ void SetupProperties(const Importer* pImp);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Set list of fixed (unmutable) materials
+ * @param fixed See #AI_CONFIG_PP_RRM_EXCLUDE_LIST
+ */
+ void SetFixedMaterialsString(const std::string& fixed = "") {
+ configFixedMaterials = fixed;
+ }
+
+ // -------------------------------------------------------------------
+ /** @brief Get list of fixed (unmutable) materials
+ * @return See #AI_CONFIG_PP_RRM_EXCLUDE_LIST
+ */
+ const std::string& GetFixedMaterialsString() const {
+ return configFixedMaterials;
+ }
+
+private:
+
+ //! Configuration option: list of all fixed materials
+ std::string configFixedMaterials;
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_REMOVEREDUNDANTMATERIALS_H_INC
diff --git a/3rdparty/assimp/code/RemoveVCProcess.cpp b/3rdparty/assimp/code/RemoveVCProcess.cpp
new file mode 100644
index 000000000..31ea56bbe
--- /dev/null
+++ b/3rdparty/assimp/code/RemoveVCProcess.cpp
@@ -0,0 +1,328 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the post processing step to remove
+ * any parts of the mesh structure from the imported data.
+*/
+
+#include "AssimpPCH.h"
+#include "RemoveVCProcess.h"
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+RemoveVCProcess::RemoveVCProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+RemoveVCProcess::~RemoveVCProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool RemoveVCProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_RemoveComponent) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Small helper function to delete all elements in a T** aray using delete
+template <typename T>
+inline void ArrayDelete(T**& in, unsigned int& num)
+{
+ for (unsigned int i = 0; i < num; ++i)
+ delete in[i];
+
+ delete[] in;
+ in = NULL;
+ num = 0;
+}
+
+#if 0
+// ------------------------------------------------------------------------------------------------
+// Updates the node graph - removes all nodes which have the "remove" flag set and the
+// "don't remove" flag not set. Nodes with meshes are never deleted.
+bool UpdateNodeGraph(aiNode* node,std::list<aiNode*>& childsOfParent,bool root)
+{
+ register bool b = false;
+
+ std::list<aiNode*> mine;
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ {
+ if (UpdateNodeGraph(node->mChildren[i],mine,false))
+ b = true;
+ }
+
+ // somewhat tricky ... mNumMeshes must be originally 0 and MSB2 may not be set,
+ // so we can do a simple comparison against MSB here
+ if (!root && AI_RC_UINT_MSB == node->mNumMeshes )
+ {
+ // this node needs to be removed
+ if (node->mNumChildren)
+ {
+ childsOfParent.insert(childsOfParent.end(),mine.begin(),mine.end());
+
+ // set all children to NULL to make sure they are not deleted when we delete ourself
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ node->mChildren[i] = NULL;
+ }
+ b = true;
+ delete node;
+ }
+ else
+ {
+ AI_RC_UNMASK(node->mNumMeshes);
+ childsOfParent.push_back(node);
+
+ if (b)
+ {
+ // reallocate the array of our children here
+ node->mNumChildren = (unsigned int)mine.size();
+ aiNode** const children = new aiNode*[mine.size()];
+ aiNode** ptr = children;
+
+ for (std::list<aiNode*>::iterator it = mine.begin(), end = mine.end();
+ it != end; ++it)
+ {
+ *ptr++ = *it;
+ }
+ delete[] node->mChildren;
+ node->mChildren = children;
+ return false;
+ }
+ }
+ return b;
+}
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void RemoveVCProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("RemoveVCProcess begin");
+ bool bHas = false; //,bMasked = false;
+
+ mScene = pScene;
+
+ // handle animations
+ if ( configDeleteFlags & aiComponent_ANIMATIONS)
+ {
+
+ bHas = true;
+ ArrayDelete(pScene->mAnimations,pScene->mNumAnimations);
+ }
+
+ // handle textures
+ if ( configDeleteFlags & aiComponent_TEXTURES)
+ {
+ bHas = true;
+ ArrayDelete(pScene->mTextures,pScene->mNumTextures);
+ }
+
+ // handle materials
+ if ( configDeleteFlags & aiComponent_MATERIALS && pScene->mNumMaterials)
+ {
+ bHas = true;
+ for (unsigned int i = 1;i < pScene->mNumMaterials;++i)
+ delete pScene->mMaterials[i];
+
+ pScene->mNumMaterials = 1;
+ MaterialHelper* helper = (MaterialHelper*) pScene->mMaterials[0];
+ ai_assert(NULL != helper);
+ helper->Clear();
+
+ // gray
+ aiColor3D clr(0.6f,0.6f,0.6f);
+ helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ // add a small ambient color value
+ clr = aiColor3D(0.05f,0.05f,0.05f);
+ helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT);
+
+ aiString s;
+ s.Set("Dummy_MaterialsRemoved");
+ helper->AddProperty(&s,AI_MATKEY_NAME);
+ }
+
+ // handle light sources
+ if ( configDeleteFlags & aiComponent_LIGHTS)
+ {
+ bHas = true;
+ ArrayDelete(pScene->mLights,pScene->mNumLights);
+ }
+
+ // handle camneras
+ if ( configDeleteFlags & aiComponent_CAMERAS)
+ {
+ bHas = true;
+ ArrayDelete(pScene->mCameras,pScene->mNumCameras);
+ }
+
+ // handle meshes
+ if (configDeleteFlags & aiComponent_MESHES)
+ {
+ bHas = true;
+ ArrayDelete(pScene->mMeshes,pScene->mNumMeshes);
+ }
+ else
+ {
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ {
+ if ( ProcessMesh( pScene->mMeshes[a]))
+ bHas = true;
+ }
+ }
+
+
+ // now check whether the result is still a full scene
+ if (!pScene->mNumMeshes || !pScene->mNumMaterials)
+ {
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ DefaultLogger::get()->debug("Setting AI_SCENE_FLAGS_INCOMPLETE flag");
+
+ // If we have no meshes anymore we should also clear another flag ...
+ if (!pScene->mNumMeshes)
+ pScene->mFlags &= ~AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
+ }
+
+ if (bHas)DefaultLogger::get()->info("RemoveVCProcess finished. Data structure cleanup has been done.");
+ else DefaultLogger::get()->debug("RemoveVCProcess finished. Nothing to be done ...");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the step
+void RemoveVCProcess::SetupProperties(const Importer* pImp)
+{
+ configDeleteFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS,0x0);
+ if (!configDeleteFlags)
+ {
+ DefaultLogger::get()->warn("RemoveVCProcess: AI_CONFIG_PP_RVC_FLAGS is zero.");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh)
+{
+ bool ret = false;
+
+ // if all materials have been deleted let the material
+ // index of the mesh point to the created default material
+ if ( configDeleteFlags & aiComponent_MATERIALS)
+ pMesh->mMaterialIndex = 0;
+
+ // handle normals
+ if (configDeleteFlags & aiComponent_NORMALS && pMesh->mNormals)
+ {
+ delete[] pMesh->mNormals;
+ pMesh->mNormals = NULL;
+ ret = true;
+ }
+
+ // handle tangents and bitangents
+ if (configDeleteFlags & aiComponent_TANGENTS_AND_BITANGENTS && pMesh->mTangents)
+ {
+ delete[] pMesh->mTangents;
+ pMesh->mTangents = NULL;
+
+ delete[] pMesh->mBitangents;
+ pMesh->mBitangents = NULL;
+ ret = true;
+ }
+
+ // handle texture coordinates
+ register bool b = (0 != (configDeleteFlags & aiComponent_TEXCOORDS));
+ for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++real)
+ {
+ if (!pMesh->mTextureCoords[i])break;
+ if (configDeleteFlags & aiComponent_TEXCOORDSn(real) || b)
+ {
+ delete pMesh->mTextureCoords[i];
+ pMesh->mTextureCoords[i] = NULL;
+ ret = true;
+
+ if (!b)
+ {
+ // collapse the rest of the array
+ for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a)
+ pMesh->mTextureCoords[a-1] = pMesh->mTextureCoords[a];
+
+ pMesh->mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS-1] = NULL;
+ continue;
+ }
+ }
+ ++i;
+ }
+
+ // handle vertex colors
+ b = (0 != (configDeleteFlags & aiComponent_COLORS));
+ for (unsigned int i = 0, real = 0; real < AI_MAX_NUMBER_OF_COLOR_SETS; ++real)
+ {
+ if (!pMesh->mColors[i])break;
+ if (configDeleteFlags & aiComponent_COLORSn(i) || b)
+ {
+ delete pMesh->mColors[i];
+ pMesh->mColors[i] = NULL;
+ ret = true;
+
+ if (!b)
+ {
+ // collapse the rest of the array
+ for (unsigned int a = i+1; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a)
+ pMesh->mColors[a-1] = pMesh->mColors[a];
+
+ pMesh->mColors[AI_MAX_NUMBER_OF_COLOR_SETS-1] = NULL;
+ continue;
+ }
+ }
+ ++i;
+ }
+
+ // handle bones
+ if (configDeleteFlags & aiComponent_BONEWEIGHTS && pMesh->mBones)
+ {
+ ArrayDelete(pMesh->mBones,pMesh->mNumBones);
+ ret = true;
+ }
+ return ret;
+}
diff --git a/3rdparty/assimp/code/RemoveVCProcess.h b/3rdparty/assimp/code/RemoveVCProcess.h
new file mode 100644
index 000000000..bd3156561
--- /dev/null
+++ b/3rdparty/assimp/code/RemoveVCProcess.h
@@ -0,0 +1,123 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to remove specific parts of the scene */
+#ifndef AI_REMOVEVCPROCESS_H_INCLUDED
+#define AI_REMOVEVCPROCESS_H_INCLUDED
+
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+
+class RemoveVCProcessTest;
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** RemoveVCProcess: Class to exclude specific parts of the data structure
+ * from further processing by removing them,
+*/
+class ASSIMP_API RemoveVCProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::RemoveVCProcessTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ RemoveVCProcess();
+
+ /** Destructor, private as well */
+ ~RemoveVCProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+ // -------------------------------------------------------------------
+ /** Called prior to ExecuteOnScene().
+ * The function is a request to the process to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ virtual void SetupProperties(const Importer* pImp);
+
+ // -------------------------------------------------------------------
+ /** Manually setup the configuration flags for the step
+ *
+ * @param Bitwise combintion of the #aiComponent enumerated values.
+ */
+ void SetDeleteFlags(unsigned int f)
+ {
+ configDeleteFlags = f;
+ }
+
+ // -------------------------------------------------------------------
+ /** Query the current configuration.
+ */
+ unsigned int GetDeleteFlags() const
+ {
+ return configDeleteFlags;
+ }
+
+private:
+
+ bool ProcessMesh (aiMesh* pcMesh);
+
+ /** Configuration flag
+ */
+ unsigned int configDeleteFlags;
+
+ /** The scene we're working with
+ */
+ aiScene* mScene;
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_REMOVEVCPROCESS_H_INCLUDED
diff --git a/3rdparty/assimp/code/SGSpatialSort.cpp b/3rdparty/assimp/code/SGSpatialSort.cpp
new file mode 100644
index 000000000..705dafc93
--- /dev/null
+++ b/3rdparty/assimp/code/SGSpatialSort.cpp
@@ -0,0 +1,169 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the helper class to quickly find
+vertices close to a given position. Special implementation for
+the 3ds loader handling smooth groups correctly */
+
+#include "AssimpPCH.h"
+#include "SGSpatialSort.h"
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+SGSpatialSort::SGSpatialSort()
+{
+ // define the reference plane. We choose some arbitrary vector away from all basic axises
+ // in the hope that no model spreads all its vertices along this plane.
+ mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
+ mPlaneNormal.Normalize();
+}
+// ------------------------------------------------------------------------------------------------
+// Destructor
+SGSpatialSort::~SGSpatialSort()
+{
+ // nothing to do here, everything destructs automatically
+}
+// ------------------------------------------------------------------------------------------------
+void SGSpatialSort::Add(const aiVector3D& vPosition, unsigned int index,
+ unsigned int smoothingGroup)
+{
+ // store position by index and distance
+ float distance = vPosition * mPlaneNormal;
+ mPositions.push_back( Entry( index, vPosition,
+ distance, smoothingGroup));
+}
+// ------------------------------------------------------------------------------------------------
+void SGSpatialSort::Prepare()
+{
+ // now sort the array ascending by distance.
+ std::sort( this->mPositions.begin(), this->mPositions.end());
+}
+// ------------------------------------------------------------------------------------------------
+// Returns an iterator for all positions close to the given position.
+void SGSpatialSort::FindPositions( const aiVector3D& pPosition,
+ uint32_t pSG,
+ float pRadius,
+ std::vector<unsigned int>& poResults,
+ bool exactMatch /*= false*/) const
+{
+ float dist = pPosition * mPlaneNormal;
+ float minDist = dist - pRadius, maxDist = dist + pRadius;
+
+ // clear the array in this strange fashion because a simple clear() would also deallocate
+ // the array which we want to avoid
+ poResults.erase( poResults.begin(), poResults.end());
+
+ // quick check for positions outside the range
+ if ( mPositions.size() == 0)
+ return;
+ if ( maxDist < mPositions.front().mDistance)
+ return;
+ if ( minDist > mPositions.back().mDistance)
+ return;
+
+ // do a binary search for the minimal distance to start the iteration there
+ unsigned int index = (unsigned int)mPositions.size() / 2;
+ unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
+ while ( binaryStepSize > 1)
+ {
+ if ( mPositions[index].mDistance < minDist)
+ index += binaryStepSize;
+ else
+ index -= binaryStepSize;
+
+ binaryStepSize /= 2;
+ }
+
+ // depending on the direction of the last step we need to single step a bit back or forth
+ // to find the actual beginning element of the range
+ while ( index > 0 && mPositions[index].mDistance > minDist)
+ index--;
+ while ( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
+ index++;
+
+ // Mow start iterating from there until the first position lays outside of the distance range.
+ // Add all positions inside the distance range within the given radius to the result aray
+
+ float squareEpsilon = pRadius * pRadius;
+ std::vector<Entry>::const_iterator it = mPositions.begin() + index;
+ std::vector<Entry>::const_iterator end = mPositions.end();
+
+ if (exactMatch)
+ {
+ while ( it->mDistance < maxDist)
+ {
+ if ((it->mPosition - pPosition).SquareLength() < squareEpsilon && it->mSmoothGroups == pSG)
+ {
+ poResults.push_back( it->mIndex);
+ }
+ ++it;
+ if ( end == it )break;
+ }
+ }
+ else
+ {
+ // if the given smoothing group is 0, we'll return all surrounding vertices
+ if (!pSG)
+ {
+ while ( it->mDistance < maxDist)
+ {
+ if ((it->mPosition - pPosition).SquareLength() < squareEpsilon)
+ poResults.push_back( it->mIndex);
+ ++it;
+ if ( end == it)break;
+ }
+ }
+ else while ( it->mDistance < maxDist)
+ {
+ if ((it->mPosition - pPosition).SquareLength() < squareEpsilon &&
+ (it->mSmoothGroups & pSG || !it->mSmoothGroups))
+ {
+ poResults.push_back( it->mIndex);
+ }
+ ++it;
+ if ( end == it)break;
+ }
+ }
+}
+
+
diff --git a/3rdparty/assimp/code/SGSpatialSort.h b/3rdparty/assimp/code/SGSpatialSort.h
new file mode 100644
index 000000000..4c597bc10
--- /dev/null
+++ b/3rdparty/assimp/code/SGSpatialSort.h
@@ -0,0 +1,139 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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.
+
+----------------------------------------------------------------------
+*/
+
+/** Small helper classes to optimise finding vertizes close to a given location
+ */
+#ifndef AI_D3DSSPATIALSORT_H_INC
+#define AI_D3DSSPATIALSORT_H_INC
+
+#include <vector>
+#include "../include/aiTypes.h"
+
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+/** Specialized version of SpatialSort to support smoothing groups
+ * This is used in by the 3DS, ASE and LWO loaders. 3DS and ASE share their
+ * normal computation code in SmoothingGroups.inl, the LWO loader has its own
+ * implementation to handle all details of its file format correctly.
+ */
+// ----------------------------------------------------------------------------------
+class SGSpatialSort
+{
+public:
+
+ SGSpatialSort();
+
+ // -------------------------------------------------------------------
+ /** Construction from a given face array, handling smoothing groups
+ * properly
+ */
+ SGSpatialSort(const std::vector<aiVector3D>& vPositions);
+
+ // -------------------------------------------------------------------
+ /** Add a vertex to the spatial sort
+ * @param vPosition Vertex position to be added
+ * @param index Index of the vrtex
+ * @param smoothingGroup SmoothingGroup for this vertex
+ */
+ void Add(const aiVector3D& vPosition, unsigned int index,
+ unsigned int smoothingGroup);
+
+ // -------------------------------------------------------------------
+ /** Prepare the spatial sorter for use. This step runs in O(logn)
+ */
+ void Prepare();
+
+ /** Destructor */
+ ~SGSpatialSort();
+
+ // -------------------------------------------------------------------
+ /** Returns an iterator for all positions close to the given position.
+ * @param pPosition The position to look for vertices.
+ * @param pSG Only included vertices with at least one shared smooth group
+ * @param pRadius Maximal distance from the position a vertex may have
+ * to be counted in.
+ * @param poResults The container to store the indices of the found
+ * positions. Will be emptied by the call so it may contain anything.
+ * @param exactMatch Specifies whether smoothing groups are bit masks
+ * (false) or integral values (true). In the latter case, a vertex
+ * cannot belong to more than one smoothing group.
+ * @return An iterator to iterate over all vertices in the given area.
+ */
+ // -------------------------------------------------------------------
+ void FindPositions( const aiVector3D& pPosition, uint32_t pSG,
+ float pRadius, std::vector<unsigned int>& poResults,
+ bool exactMatch = false) const;
+
+protected:
+ /** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */
+ aiVector3D mPlaneNormal;
+
+ // -------------------------------------------------------------------
+ /** An entry in a spatially sorted position array. Consists of a
+ * vertex index, its position and its precalculated distance from
+ * the reference plane */
+ // -------------------------------------------------------------------
+ struct Entry
+ {
+ unsigned int mIndex; ///< The vertex referred by this entry
+ aiVector3D mPosition; ///< Position
+ uint32_t mSmoothGroups;
+ float mDistance; ///< Distance of this vertex to the sorting plane
+
+ Entry() { /** intentionally not initialized.*/ }
+ Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance,uint32_t pSG)
+ :
+ mIndex( pIndex),
+ mPosition( pPosition),
+ mSmoothGroups (pSG),
+ mDistance( pDistance)
+ { }
+
+ bool operator < (const Entry& e) const { return mDistance < e.mDistance; }
+ };
+
+ // all positions, sorted by distance to the sorting plane
+ std::vector<Entry> mPositions;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_SPATIALSORT_H_INC
diff --git a/3rdparty/assimp/code/SMDLoader.cpp b/3rdparty/assimp/code/SMDLoader.cpp
new file mode 100644
index 000000000..e36464588
--- /dev/null
+++ b/3rdparty/assimp/code/SMDLoader.cpp
@@ -0,0 +1,1129 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 SMDLoader.cpp
+ * @brief Implementation of the SMD importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
+
+// internal headers
+#include "SMDLoader.h"
+#include "fast_atof.h"
+#include "SkeletonMeshBuilder.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+SMDImporter::SMDImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+SMDImporter::~SMDImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool SMDImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool) const
+{
+ // fixme: auto format detection
+ return SimpleExtensionCheck(pFile,"smd","vta");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get a list of all supported file extensions
+void SMDImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("smd");
+ extensions.insert("vta");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties
+void SMDImporter::SetupProperties(const Importer* pImp)
+{
+ // The
+ // AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_SMD_KEYFRAME,0xffffffff);
+ if (0xffffffff == configFrameID) {
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void SMDImporter::InternReadFile(
+ const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open SMD/VTA file " + pFile + ".");
+ }
+
+ iFileSize = (unsigned int)file->FileSize();
+
+ // Allocate storage and copy the contents of the file to a memory buffer
+ this->pScene = pScene;
+
+ std::vector<char> buff(iFileSize+1);
+ TextFileToBuffer(file.get(),buff);
+ mBuffer = &buff[0];
+
+ iSmallestFrame = (1 << 31);
+ bHasUVs = true;
+ iLineNumber = 1;
+
+ // Reserve enough space for ... hm ... 10 textures
+ aszTextures.reserve(10);
+
+ // Reserve enough space for ... hm ... 1000 triangles
+ asTriangles.reserve(1000);
+
+ // Reserve enough space for ... hm ... 20 bones
+ asBones.reserve(20);
+
+
+ // parse the file ...
+ ParseFile();
+
+ // If there are no triangles it seems to be an animation SMD,
+ // containing only the animation skeleton.
+ if (asTriangles.empty())
+ {
+ if (asBones.empty())
+ {
+ throw DeadlyImportError("SMD: No triangles and no bones have "
+ "been found in the file. This file seems to be invalid.");
+ }
+
+ // Set the flag in the scene structure which indicates
+ // that there is nothing than an animation skeleton
+ pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+
+ if (!asBones.empty())
+ {
+ // Check whether all bones have been initialized
+ for (std::vector<SMD::Bone>::const_iterator
+ i = asBones.begin();
+ i != asBones.end();++i)
+ {
+ if (!(*i).mName.length())
+ {
+ DefaultLogger::get()->warn("SMD: Not all bones have been initialized");
+ break;
+ }
+ }
+
+ // now fix invalid time values and make sure the animation starts at frame 0
+ FixTimeValues();
+
+ // compute absolute bone transformation matrices
+ // ComputeAbsoluteBoneTransformations();
+ }
+
+ if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE))
+ {
+ // create output meshes
+ CreateOutputMeshes();
+
+ // build an output material list
+ CreateOutputMaterials();
+ }
+
+ // build the output animation
+ CreateOutputAnimations();
+
+ // build output nodes (bones are added as empty dummy nodes)
+ CreateOutputNodes();
+
+ if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)
+ {
+ SkeletonMeshBuilder skeleton(pScene);
+ }
+}
+// ------------------------------------------------------------------------------------------------
+// Write an error message with line number to the log file
+void SMDImporter::LogErrorNoThrow(const char* msg)
+{
+ char szTemp[1024];
+ sprintf(szTemp,"Line %i: %s",iLineNumber,msg);
+ DefaultLogger::get()->error(szTemp);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Write a warning with line number to the log file
+void SMDImporter::LogWarning(const char* msg)
+{
+ char szTemp[1024];
+ ai_assert(strlen(msg) < 1000);
+ DefaultLogger::get()->warn(szTemp);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Fix invalid time values in the file
+void SMDImporter::FixTimeValues()
+{
+ double dDelta = (double)iSmallestFrame;
+ double dMax = 0.0f;
+ for (std::vector<SMD::Bone>::iterator
+ iBone = asBones.begin();
+ iBone != asBones.end();++iBone)
+ {
+ for (std::vector<SMD::Bone::Animation::MatrixKey>::iterator
+ iKey = (*iBone).sAnim.asKeys.begin();
+ iKey != (*iBone).sAnim.asKeys.end();++iKey)
+ {
+ (*iKey).dTime -= dDelta;
+ dMax = std::max(dMax, (*iKey).dTime);
+ }
+ }
+ dLengthOfAnim = dMax;
+}
+
+// ------------------------------------------------------------------------------------------------
+// create output meshes
+void SMDImporter::CreateOutputMeshes()
+{
+ if (aszTextures.empty())
+ aszTextures.push_back(std::string());
+
+ // we need to sort all faces by their material index
+ // in opposition to other loaders we can be sure that each
+ // material is at least used once.
+ pScene->mNumMeshes = (unsigned int) aszTextures.size();
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+
+ typedef std::vector<unsigned int> FaceList;
+ FaceList* aaiFaces = new FaceList[pScene->mNumMeshes];
+
+ // approximate the space that will be required
+ unsigned int iNum = (unsigned int)asTriangles.size() / pScene->mNumMeshes;
+ iNum += iNum >> 1;
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ aaiFaces[i].reserve(iNum);
+
+
+ // collect all faces
+ iNum = 0;
+ for (std::vector<SMD::Face>::const_iterator
+ iFace = asTriangles.begin();
+ iFace != asTriangles.end();++iFace,++iNum)
+ {
+ if (0xffffffff == (*iFace).iTexture)aaiFaces[(*iFace).iTexture].push_back( 0 );
+ else if ((*iFace).iTexture >= aszTextures.size())
+ {
+ DefaultLogger::get()->error("[SMD/VTA] Material index overflow in face");
+ aaiFaces[(*iFace).iTexture].push_back((unsigned int)aszTextures.size()-1);
+ }
+ else aaiFaces[(*iFace).iTexture].push_back(iNum);
+ }
+
+ // now create the output meshes
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ {
+ aiMesh*& pcMesh = pScene->mMeshes[i] = new aiMesh();
+ ai_assert(!aaiFaces[i].empty()); // should not be empty ...
+
+ pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+ pcMesh->mNumVertices = (unsigned int)aaiFaces[i].size()*3;
+ pcMesh->mNumFaces = (unsigned int)aaiFaces[i].size();
+ pcMesh->mMaterialIndex = i;
+
+ // storage for bones
+ typedef std::pair<unsigned int,float> TempWeightListEntry;
+ typedef std::vector< TempWeightListEntry > TempBoneWeightList;
+
+ TempBoneWeightList* aaiBones = new TempBoneWeightList[asBones.size()]();
+
+ // try to reserve enough memory without wasting too much
+ for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
+ {
+ aaiBones[iBone].reserve(pcMesh->mNumVertices/asBones.size());
+ }
+
+ // allocate storage
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+ aiVector3D* pcNormals = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
+ aiVector3D* pcVerts = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
+
+ aiVector3D* pcUVs = NULL;
+ if (bHasUVs)
+ {
+ pcUVs = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
+ pcMesh->mNumUVComponents[0] = 2;
+ }
+
+ iNum = 0;
+ for (unsigned int iFace = 0; iFace < pcMesh->mNumFaces;++iFace)
+ {
+ pcMesh->mFaces[iFace].mIndices = new unsigned int[3];
+ pcMesh->mFaces[iFace].mNumIndices = 3;
+
+ // fill the vertices
+ unsigned int iSrcFace = aaiFaces[i][iFace];
+ SMD::Face& face = asTriangles[iSrcFace];
+
+ *pcVerts++ = face.avVertices[0].pos;
+ *pcVerts++ = face.avVertices[1].pos;
+ *pcVerts++ = face.avVertices[2].pos;
+
+ // fill the normals
+ *pcNormals++ = face.avVertices[0].nor;
+ *pcNormals++ = face.avVertices[1].nor;
+ *pcNormals++ = face.avVertices[2].nor;
+
+ // fill the texture coordinates
+ if (pcUVs)
+ {
+ *pcUVs++ = face.avVertices[0].uv;
+ *pcUVs++ = face.avVertices[1].uv;
+ *pcUVs++ = face.avVertices[2].uv;
+ }
+
+ for (unsigned int iVert = 0; iVert < 3;++iVert)
+ {
+ float fSum = 0.0f;
+ for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone)
+ {
+ TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone];
+
+ // FIX: The second check is here just to make sure we won't
+ // assign more than one weight to a single vertex index
+ if (pairval.first >= asBones.size() ||
+ pairval.first == face.avVertices[iVert].iParentNode)
+ {
+ DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. "
+ "The bone index will be ignored, the weight will be assigned "
+ "to the vertex' parent node");
+ continue;
+ }
+ aaiBones[pairval.first].push_back(TempWeightListEntry(iNum,pairval.second));
+ fSum += pairval.second;
+ }
+ // ******************************************************************
+ // If the sum of all vertex weights is not 1.0 we must assign
+ // the rest to the vertex' parent node. Well, at least the doc says
+ // we should ...
+ // FIX: We use 0.975 as limit, floating-point inaccuracies seem to
+ // be very strong in some SMD exporters. Furthermore it is possible
+ // that the parent of a vertex is 0xffffffff (if the corresponding
+ // entry in the file was unreadable)
+ // ******************************************************************
+ if (fSum < 0.975f && face.avVertices[iVert].iParentNode != 0xffffffff)
+ {
+ if (face.avVertices[iVert].iParentNode >= asBones.size())
+ {
+ DefaultLogger::get()->error("[SMD/VTA] Bone index overflow. "
+ "The index of the vertex parent bone is invalid. "
+ "The remaining weights will be normalized to 1.0");
+
+ if (fSum)
+ {
+ fSum = 1 / fSum;
+ for (unsigned int iBone = 0;iBone < face.avVertices[iVert].aiBoneLinks.size();++iBone)
+ {
+ TempWeightListEntry& pairval = face.avVertices[iVert].aiBoneLinks[iBone];
+ if (pairval.first >= asBones.size())continue;
+ aaiBones[pairval.first].back().second *= fSum;
+ }
+ }
+ }
+ else
+ {
+ aaiBones[face.avVertices[iVert].iParentNode].push_back(
+ TempWeightListEntry(iNum,1.0f-fSum));
+ }
+ }
+ pcMesh->mFaces[iFace].mIndices[iVert] = iNum++;
+ }
+ }
+
+ // now build all bones of the mesh
+ iNum = 0;
+ for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
+ if (!aaiBones[iBone].empty())++iNum;
+
+ if (false && iNum)
+ {
+ pcMesh->mNumBones = iNum;
+ pcMesh->mBones = new aiBone*[pcMesh->mNumBones];
+ iNum = 0;
+ for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
+ {
+ if (aaiBones[iBone].empty())continue;
+ aiBone*& bone = pcMesh->mBones[iNum] = new aiBone();
+
+ bone->mNumWeights = (unsigned int)aaiBones[iBone].size();
+ bone->mWeights = new aiVertexWeight[bone->mNumWeights];
+ bone->mOffsetMatrix = asBones[iBone].mOffsetMatrix;
+ bone->mName.Set( asBones[iBone].mName );
+
+ asBones[iBone].bIsUsed = true;
+
+ for (unsigned int iWeight = 0; iWeight < bone->mNumWeights;++iWeight)
+ {
+ bone->mWeights[iWeight].mVertexId = aaiBones[iBone][iWeight].first;
+ bone->mWeights[iWeight].mWeight = aaiBones[iBone][iWeight].second;
+ }
+ ++iNum;
+ }
+ }
+ delete[] aaiBones;
+ }
+ delete[] aaiFaces;
+}
+
+// ------------------------------------------------------------------------------------------------
+// add bone child nodes
+void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent)
+{
+ ai_assert(NULL != pcNode && 0 == pcNode->mNumChildren && NULL == pcNode->mChildren);
+
+ // first count ...
+ for (unsigned int i = 0; i < asBones.size();++i)
+ {
+ SMD::Bone& bone = asBones[i];
+ if (bone.iParent == iParent)++pcNode->mNumChildren;
+ }
+
+ // now allocate the output array
+ pcNode->mChildren = new aiNode*[pcNode->mNumChildren];
+
+ // and fill all subnodes
+ unsigned int qq = 0;
+ for (unsigned int i = 0; i < asBones.size();++i)
+ {
+ SMD::Bone& bone = asBones[i];
+ if (bone.iParent != iParent)continue;
+
+ aiNode* pc = pcNode->mChildren[qq++] = new aiNode();
+ pc->mName.Set(bone.mName);
+
+ // store the local transformation matrix of the bind pose
+ pc->mTransformation = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrix;
+ pc->mParent = pcNode;
+
+ // add children to this node, too
+ AddBoneChildren(pc,i);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// create output nodes
+void SMDImporter::CreateOutputNodes()
+{
+ pScene->mRootNode = new aiNode();
+ if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE))
+ {
+ // create one root node that renders all meshes
+ pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
+ pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ pScene->mRootNode->mMeshes[i] = i;
+ }
+
+ // now add all bones as dummy sub nodes to the graph
+ // AddBoneChildren(pScene->mRootNode,(uint32_t)-1);
+
+ // if we have only one bone we can even remove the root node
+ if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE &&
+ 1 == pScene->mRootNode->mNumChildren)
+ {
+ aiNode* pcOldRoot = pScene->mRootNode;
+ pScene->mRootNode = pcOldRoot->mChildren[0];
+ pcOldRoot->mChildren[0] = NULL;
+ delete pcOldRoot;
+
+ pScene->mRootNode->mParent = NULL;
+ }
+ else
+ {
+ ::strcpy(pScene->mRootNode->mName.data, "<SMD_root>");
+ pScene->mRootNode->mName.length = 10;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// create output animations
+void SMDImporter::CreateOutputAnimations()
+{
+ unsigned int iNumBones = 0;
+ for (std::vector<SMD::Bone>::const_iterator
+ i = asBones.begin();
+ i != asBones.end();++i)
+ {
+ if ((*i).bIsUsed)++iNumBones;
+ }
+ if (!iNumBones)
+ {
+ // just make sure this case doesn't occur ... (it could occur
+ // if the file was invalid)
+ return;
+ }
+
+ pScene->mNumAnimations = 1;
+ pScene->mAnimations = new aiAnimation*[1];
+ aiAnimation*& anim = pScene->mAnimations[0] = new aiAnimation();
+
+ anim->mDuration = dLengthOfAnim;
+ anim->mNumChannels = iNumBones;
+ anim->mTicksPerSecond = 25.0; // FIXME: is this correct?
+
+ aiNodeAnim** pp = anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
+
+ // now build valid keys
+ unsigned int a = 0;
+ for (std::vector<SMD::Bone>::const_iterator
+ i = asBones.begin();
+ i != asBones.end();++i)
+ {
+ if (!(*i).bIsUsed)continue;
+
+ aiNodeAnim* p = pp[a] = new aiNodeAnim();
+
+ // copy the name of the bone
+ p->mNodeName.Set( i->mName);
+
+ p->mNumRotationKeys = (unsigned int) (*i).sAnim.asKeys.size();
+ if (p->mNumRotationKeys)
+ {
+ p->mNumPositionKeys = p->mNumRotationKeys;
+ aiVectorKey* pVecKeys = p->mPositionKeys = new aiVectorKey[p->mNumRotationKeys];
+ aiQuatKey* pRotKeys = p->mRotationKeys = new aiQuatKey[p->mNumRotationKeys];
+
+ for (std::vector<SMD::Bone::Animation::MatrixKey>::const_iterator
+ qq = (*i).sAnim.asKeys.begin();
+ qq != (*i).sAnim.asKeys.end(); ++qq)
+ {
+ pRotKeys->mTime = pVecKeys->mTime = (*qq).dTime;
+
+ // compute the rotation quaternion from the euler angles
+ pRotKeys->mValue = aiQuaternion( (*qq).vRot.x, (*qq).vRot.y, (*qq).vRot.z );
+ pVecKeys->mValue = (*qq).vPos;
+
+ ++pVecKeys; ++pRotKeys;
+ }
+ }
+ ++a;
+
+ // there are no scaling keys ...
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void SMDImporter::ComputeAbsoluteBoneTransformations()
+{
+ // For each bone: determine the key with the lowest time value
+ // theoretically the SMD format should have all keyframes
+ // in order. However, I've seen a file where this wasn't true.
+ for (unsigned int i = 0; i < asBones.size();++i)
+ {
+ SMD::Bone& bone = asBones[i];
+
+ uint32_t iIndex = 0;
+ double dMin = 10e10;
+ for (unsigned int i = 0; i < bone.sAnim.asKeys.size();++i)
+ {
+ double d = std::min(bone.sAnim.asKeys[i].dTime,dMin);
+ if (d < dMin)
+ {
+ dMin = d;
+ iIndex = i;
+ }
+ }
+ bone.sAnim.iFirstTimeKey = iIndex;
+ }
+
+ unsigned int iParent = 0;
+ while (iParent < asBones.size())
+ {
+ for (unsigned int iBone = 0; iBone < asBones.size();++iBone)
+ {
+ SMD::Bone& bone = asBones[iBone];
+
+ if (iParent == bone.iParent)
+ {
+ SMD::Bone& parentBone = asBones[iParent];
+
+
+ uint32_t iIndex = bone.sAnim.iFirstTimeKey;
+ const aiMatrix4x4& mat = bone.sAnim.asKeys[iIndex].matrix;
+ aiMatrix4x4& matOut = bone.sAnim.asKeys[iIndex].matrixAbsolute;
+
+ // The same for the parent bone ...
+ iIndex = parentBone.sAnim.iFirstTimeKey;
+ const aiMatrix4x4& mat2 = parentBone.sAnim.asKeys[iIndex].matrixAbsolute;
+
+ // Compute the absolute transformation matrix
+ matOut = mat * mat2;
+ }
+ }
+ ++iParent;
+ }
+
+ // Store the inverse of the absolute transformation matrix
+ // of the first key as bone offset matrix
+ for (iParent = 0; iParent < asBones.size();++iParent)
+ {
+ SMD::Bone& bone = asBones[iParent];
+ bone.mOffsetMatrix = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrixAbsolute;
+ bone.mOffsetMatrix.Inverse();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// create output materials
+void SMDImporter::CreateOutputMaterials()
+{
+ pScene->mNumMaterials = (unsigned int)aszTextures.size();
+ pScene->mMaterials = new aiMaterial*[std::max(1u, pScene->mNumMaterials)];
+
+ for (unsigned int iMat = 0; iMat < pScene->mNumMaterials;++iMat)
+ {
+ MaterialHelper* pcMat = new MaterialHelper();
+ pScene->mMaterials[iMat] = pcMat;
+
+ aiString szName;
+ szName.length = (size_t)::sprintf(szName.data,"Texture_%i",iMat);
+ pcMat->AddProperty(&szName,AI_MATKEY_NAME);
+
+ if (aszTextures[iMat].length())
+ {
+ ::strcpy(szName.data, aszTextures[iMat].c_str() );
+ szName.length = aszTextures[iMat].length();
+ pcMat->AddProperty(&szName,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ }
+
+ // create a default material if necessary
+ if (0 == pScene->mNumMaterials)
+ {
+ pScene->mNumMaterials = 1;
+
+ MaterialHelper* pcHelper = new MaterialHelper();
+ pScene->mMaterials[0] = pcHelper;
+
+ int iMode = (int)aiShadingMode_Gouraud;
+ pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
+
+ aiColor3D clr;
+ clr.b = clr.g = clr.r = 0.7f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
+
+ clr.b = clr.g = clr.r = 0.05f;
+ pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
+
+ aiString szName;
+ szName.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse the file
+void SMDImporter::ParseFile()
+{
+ const char* szCurrent = mBuffer;
+
+ // read line per line ...
+ while (true)
+ {
+ if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
+
+ // "version <n> \n", <n> should be 1 for hl and hl SMD files
+ if (TokenMatch(szCurrent,"version",7))
+ {
+ if (!SkipSpaces(szCurrent,&szCurrent)) break;
+ if (1 != strtol10(szCurrent,&szCurrent))
+ {
+ DefaultLogger::get()->warn("SMD.version is not 1. This "
+ "file format is not known. Continuing happily ...");
+ }
+ continue;
+ }
+ // "nodes\n" - Starts the node section
+ if (TokenMatch(szCurrent,"nodes",5))
+ {
+ ParseNodesSection(szCurrent,&szCurrent);
+ continue;
+ }
+ // "triangles\n" - Starts the triangle section
+ if (TokenMatch(szCurrent,"triangles",9))
+ {
+ ParseTrianglesSection(szCurrent,&szCurrent);
+ continue;
+ }
+ // "vertexanimation\n" - Starts the vertex animation section
+ if (TokenMatch(szCurrent,"vertexanimation",15))
+ {
+ bHasUVs = false;
+ ParseVASection(szCurrent,&szCurrent);
+ continue;
+ }
+ // "skeleton\n" - Starts the skeleton section
+ if (TokenMatch(szCurrent,"skeleton",8))
+ {
+ ParseSkeletonSection(szCurrent,&szCurrent);
+ continue;
+ }
+ SkipLine(szCurrent,&szCurrent);
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int SMDImporter::GetTextureIndex(const std::string& filename)
+{
+ unsigned int iIndex = 0;
+ for (std::vector<std::string>::const_iterator
+ i = aszTextures.begin();
+ i != aszTextures.end();++i,++iIndex)
+ {
+ // case-insensitive ... it's a path
+ if (0 == ASSIMP_stricmp ( filename.c_str(),(*i).c_str()))return iIndex;
+ }
+ iIndex = (unsigned int)aszTextures.size();
+ aszTextures.push_back(filename);
+ return iIndex;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse the nodes section of the file
+void SMDImporter::ParseNodesSection(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ while (true)
+ {
+ // "end\n" - Ends the nodes section
+ if (0 == ASSIMP_strincmp(szCurrent,"end",3) &&
+ IsSpaceOrNewLine(*(szCurrent+3)))
+ {
+ szCurrent += 4;
+ break;
+ }
+ ParseNodeInfo(szCurrent,&szCurrent);
+ }
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse the triangles section of the file
+void SMDImporter::ParseTrianglesSection(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ // Parse a triangle, parse another triangle, parse the next triangle ...
+ // and so on until we reach a token that looks quite similar to "end"
+ while (true)
+ {
+ if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
+
+ // "end\n" - Ends the triangles section
+ if (TokenMatch(szCurrent,"end",3))
+ break;
+ ParseTriangle(szCurrent,&szCurrent);
+ }
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+ *szCurrentOut = szCurrent;
+}
+// ------------------------------------------------------------------------------------------------
+// Parse the vertex animation section of the file
+void SMDImporter::ParseVASection(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ unsigned int iCurIndex = 0;
+ while (true)
+ {
+ if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
+
+ // "end\n" - Ends the "vertexanimation" section
+ if (TokenMatch(szCurrent,"end",3))
+ break;
+
+ // "time <n>\n"
+ if (TokenMatch(szCurrent,"time",4))
+ {
+ // NOTE: The doc says that time values COULD be negative ...
+ // NOTE2: this is the shape key -> valve docs
+ int iTime = 0;
+ if (!ParseSignedInt(szCurrent,&szCurrent,iTime) || configFrameID != (unsigned int)iTime)break;
+ SkipLine(szCurrent,&szCurrent);
+ }
+ else
+ {
+ if (0 == iCurIndex)
+ {
+ asTriangles.push_back(SMD::Face());
+ }
+ if (++iCurIndex == 3)iCurIndex = 0;
+ ParseVertex(szCurrent,&szCurrent,asTriangles.back().avVertices[iCurIndex],true);
+ }
+ }
+
+ if (iCurIndex != 2 && !asTriangles.empty())
+ {
+ // we want to no degenerates, so throw this triangle away
+ asTriangles.pop_back();
+ }
+
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+ *szCurrentOut = szCurrent;
+}
+// ------------------------------------------------------------------------------------------------
+// Parse the skeleton section of the file
+void SMDImporter::ParseSkeletonSection(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ int iTime = 0;
+ while (true)
+ {
+ if (!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break;
+
+ // "end\n" - Ends the skeleton section
+ if (TokenMatch(szCurrent,"end",3))
+ break;
+
+ // "time <n>\n" - Specifies the current animation frame
+ else if (TokenMatch(szCurrent,"time",4))
+ {
+ // NOTE: The doc says that time values COULD be negative ...
+ if (!ParseSignedInt(szCurrent,&szCurrent,iTime))break;
+
+ iSmallestFrame = std::min(iSmallestFrame,iTime);
+ SkipLine(szCurrent,&szCurrent);
+ }
+ else ParseSkeletonElement(szCurrent,&szCurrent,iTime);
+ }
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+#define SMDI_PARSE_RETURN { \
+ SkipLine(szCurrent,&szCurrent); \
+ *szCurrentOut = szCurrent; \
+ return; \
+}
+// ------------------------------------------------------------------------------------------------
+// Parse a node line
+void SMDImporter::ParseNodeInfo(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ unsigned int iBone = 0;
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+ if (!ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone index");
+ SMDI_PARSE_RETURN;
+ }
+ // add our bone to the list
+ if (iBone >= asBones.size())asBones.resize(iBone+1);
+ SMD::Bone& bone = asBones[iBone];
+
+ bool bQuota = true;
+ if ('\"' != *szCurrent)
+ {
+ LogWarning("Bone name is expcted to be enclosed in "
+ "double quotation marks. ");
+ bQuota = false;
+ }
+ else ++szCurrent;
+
+ const char* szEnd = szCurrent;
+ while (true)
+ {
+ if (bQuota && '\"' == *szEnd)
+ {
+ iBone = (unsigned int)(szEnd - szCurrent);
+ ++szEnd;
+ break;
+ }
+ else if (IsSpaceOrNewLine(*szEnd))
+ {
+ iBone = (unsigned int)(szEnd - szCurrent);
+ break;
+ }
+ else if (!(*szEnd))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone name");
+ SMDI_PARSE_RETURN;
+ }
+ ++szEnd;
+ }
+ bone.mName = std::string(szCurrent,iBone);
+ szCurrent = szEnd;
+
+ // the only negative bone parent index that could occur is -1 AFAIK
+ if (!ParseSignedInt(szCurrent,&szCurrent,(int&)bone.iParent))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone parent index. Assuming -1");
+ SMDI_PARSE_RETURN;
+ }
+
+ // go to the beginning of the next line
+ SMDI_PARSE_RETURN;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a skeleton element
+void SMDImporter::ParseSkeletonElement(const char* szCurrent,
+ const char** szCurrentOut,int iTime)
+{
+ aiVector3D vPos;
+ aiVector3D vRot;
+
+ unsigned int iBone = 0;
+ if (!ParseUnsignedInt(szCurrent,&szCurrent,iBone))
+ {
+ DefaultLogger::get()->error("Unexpected EOF/EOL while parsing bone index");
+ SMDI_PARSE_RETURN;
+ }
+ if (iBone >= asBones.size())
+ {
+ LogErrorNoThrow("Bone index in skeleton section is out of range");
+ SMDI_PARSE_RETURN;
+ }
+ SMD::Bone& bone = asBones[iBone];
+
+ bone.sAnim.asKeys.push_back(SMD::Bone::Animation::MatrixKey());
+ SMD::Bone::Animation::MatrixKey& key = bone.sAnim.asKeys.back();
+
+ key.dTime = (double)iTime;
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vPos.x))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.x");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vPos.y))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.y");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vPos.z))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.pos.z");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vRot.x))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.x");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vRot.y))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.y");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vRot.z))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing bone.rot.z");
+ SMDI_PARSE_RETURN;
+ }
+ // build the transformation matrix of the key
+ key.matrix.FromEulerAnglesXYZ(vRot.x,vRot.y,vRot.z);
+ {
+ aiMatrix4x4 mTemp;
+ mTemp.a4 = vPos.x;
+ mTemp.b4 = vPos.y;
+ mTemp.c4 = vPos.z;
+ key.matrix = key.matrix * mTemp;
+ }
+
+ // go to the beginning of the next line
+ SMDI_PARSE_RETURN;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a triangle
+void SMDImporter::ParseTriangle(const char* szCurrent,
+ const char** szCurrentOut)
+{
+ asTriangles.push_back(SMD::Face());
+ SMD::Face& face = asTriangles.back();
+
+ if (!SkipSpaces(szCurrent,&szCurrent))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing a triangle");
+ return;
+ }
+
+ // read the texture file name
+ const char* szLast = szCurrent;
+ while (!IsSpaceOrNewLine(*szCurrent++)) {};
+
+ // ... and get the index that belongs to this file name
+ face.iTexture = GetTextureIndex(std::string(szLast,(uintptr_t)szCurrent-(uintptr_t)szLast));
+
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+
+ // load three vertices
+ for (unsigned int iVert = 0; iVert < 3;++iVert)
+ {
+ ParseVertex(szCurrent,&szCurrent,
+ face.avVertices[iVert]);
+ }
+ *szCurrentOut = szCurrent;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a float
+bool SMDImporter::ParseFloat(const char* szCurrent,
+ const char** szCurrentOut, float& out)
+{
+ if (!SkipSpaces(&szCurrent))
+ return false;
+
+ *szCurrentOut = fast_atof_move(szCurrent,out);
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse an unsigned int
+bool SMDImporter::ParseUnsignedInt(const char* szCurrent,
+ const char** szCurrentOut, unsigned int& out)
+{
+ if (!SkipSpaces(&szCurrent))
+ return false;
+
+ out = strtol10(szCurrent,szCurrentOut);
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a signed int
+bool SMDImporter::ParseSignedInt(const char* szCurrent,
+ const char** szCurrentOut, int& out)
+{
+ if (!SkipSpaces(&szCurrent))
+ return false;
+
+ out = strtol10s(szCurrent,szCurrentOut);
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Parse a vertex
+void SMDImporter::ParseVertex(const char* szCurrent,
+ const char** szCurrentOut, SMD::Vertex& vertex,
+ bool bVASection /*= false*/)
+{
+ if (SkipSpaces(&szCurrent) && IsLineEnd(*szCurrent))
+ {
+ SkipSpacesAndLineEnd(szCurrent,&szCurrent);
+ return ParseVertex(szCurrent,szCurrentOut,vertex,bVASection);
+ }
+ if (!ParseSignedInt(szCurrent,&szCurrent,(int&)vertex.iParentNode))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.parent");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.x))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.x");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.y))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.y");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.pos.z))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.pos.z");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.x))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.x");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.y))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.y");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.nor.z))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.nor.z");
+ SMDI_PARSE_RETURN;
+ }
+
+ if (bVASection)SMDI_PARSE_RETURN;
+
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.x))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.x");
+ SMDI_PARSE_RETURN;
+ }
+ if (!ParseFloat(szCurrent,&szCurrent,(float&)vertex.uv.y))
+ {
+ LogErrorNoThrow("Unexpected EOF/EOL while parsing vertex.uv.y");
+ SMDI_PARSE_RETURN;
+ }
+
+ // now read the number of bones affecting this vertex
+ // all elements from now are fully optional, we don't need them
+ unsigned int iSize = 0;
+ if (!ParseUnsignedInt(szCurrent,&szCurrent,iSize))SMDI_PARSE_RETURN;
+ vertex.aiBoneLinks.resize(iSize,std::pair<unsigned int, float>(0,0.0f));
+
+ for (std::vector<std::pair<unsigned int, float> >::iterator
+ i = vertex.aiBoneLinks.begin();
+ i != vertex.aiBoneLinks.end();++i)
+ {
+ if (!ParseUnsignedInt(szCurrent,&szCurrent,(*i).first))
+ SMDI_PARSE_RETURN;
+ if (!ParseFloat(szCurrent,&szCurrent,(*i).second))
+ SMDI_PARSE_RETURN;
+ }
+
+ // go to the beginning of the next line
+ SMDI_PARSE_RETURN;
+}
+
+#endif // !! ASSIMP_BUILD_NO_SMD_IMPORTER
diff --git a/3rdparty/assimp/code/SMDLoader.h b/3rdparty/assimp/code/SMDLoader.h
new file mode 100644
index 000000000..0f1cbe8fd
--- /dev/null
+++ b/3rdparty/assimp/code/SMDLoader.h
@@ -0,0 +1,420 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 SMDLoader.h
+ * @brief Defintion of the Valve SMD file format
+ */
+
+#ifndef AI_SMDLOADER_H_INCLUDED
+#define AI_SMDLOADER_H_INCLUDED
+
+// internal headers
+#include "BaseImporter.h"
+#include "ParsingUtils.h"
+
+// public Assimp headers
+#include "../include/aiTypes.h"
+#include "../include/aiTexture.h"
+#include "../include/aiAnim.h"
+#include "../include/aiMaterial.h"
+struct aiNode;
+
+// STL headers
+#include <vector>
+
+namespace Assimp {
+class MaterialHelper;
+
+namespace SMD {
+
+// ---------------------------------------------------------------------------
+/** Data structure for a vertex in a SMD file
+*/
+struct Vertex
+{
+ Vertex() : iParentNode(0xffffffff)
+ {}
+
+ //! Vertex position, normal and texture coordinate
+ aiVector3D pos,nor,uv;
+
+ //! Vertex parent node
+ unsigned int iParentNode;
+
+ //! Links to bones: pair.first is the bone index,
+ //! pair.second is the vertex weight.
+ //! WARN: The remaining weight (to reach 1.0f) is assigned
+ //! to the parent node/bone
+ std::vector<std::pair<unsigned int, float> > aiBoneLinks;
+};
+
+// ---------------------------------------------------------------------------
+/** Data structure for a face in a SMD file
+*/
+struct Face
+{
+ Face() : iTexture(0x0)
+ {}
+
+ //! Texture index for the face
+ unsigned int iTexture;
+
+ //! The three vertices of the face
+ Vertex avVertices[3];
+};
+
+// ---------------------------------------------------------------------------
+/** Data structure for a bone in a SMD file
+*/
+struct Bone
+{
+ //! Default constructor
+ Bone() : iParent(0xffffffff), bIsUsed(false)
+ {
+ }
+
+ //! Destructor
+ ~Bone()
+ {
+ }
+
+ //! Name of the bone
+ std::string mName;
+
+ //! Parent of the bone
+ uint32_t iParent;
+
+ //! Animation of the bone
+ struct Animation
+ {
+ //! Public default constructor
+ Animation()
+ {
+ asKeys.reserve(20);
+ }
+
+ //! Data structure for a matrix key
+ struct MatrixKey
+ {
+ //! Matrix at this time
+ aiMatrix4x4 matrix;
+
+ //! Absolute transformation matrix
+ aiMatrix4x4 matrixAbsolute;
+
+ //! Position
+ aiVector3D vPos;
+
+ //! Rotation (euler angles)
+ aiVector3D vRot;
+
+ //! Current time. may be negative, this
+ //! will be fixed later
+ double dTime;
+ };
+
+ //! Index of the key with the smallest time value
+ uint32_t iFirstTimeKey;
+
+ //! Array of matrix keys
+ std::vector<MatrixKey> asKeys;
+
+ } sAnim;
+
+ //! Offset matrix of the bone
+ aiMatrix4x4 mOffsetMatrix;
+
+ //! true if the bone is referenced by at least one mesh
+ bool bIsUsed;
+};
+
+} //! namespace SMD
+
+// ---------------------------------------------------------------------------
+/** Used to load Half-life 1 and 2 SMD models
+*/
+class SMDImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ SMDImporter();
+
+ /** Destructor, private as well */
+ ~SMDImporter();
+
+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;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ReadFile().
+ * The function is a request to the importer to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ void SetupProperties(const Importer* pImp);
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Parse the SMD file and create the output scene
+ */
+ void ParseFile();
+
+ // -------------------------------------------------------------------
+ /** Parse the triangles section of the SMD file
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives a pointer to the heading line of
+ * the next section (or to EOF)
+ */
+ void ParseTrianglesSection(const char* szCurrent,
+ const char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Parse the vertex animation section in VTA files
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives a pointer to the heading line of
+ * the next section (or to EOF)
+ */
+ void ParseVASection(const char* szCurrent,
+ const char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Parse the nodes section of the SMD file
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives a pointer to the heading line of
+ * the next section (or to EOF)
+ */
+ void ParseNodesSection(const char* szCurrent,
+ const char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Parse the skeleton section of the SMD file
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives a pointer to the heading line of
+ * the next section (or to EOF)
+ */
+ void ParseSkeletonSection(const char* szCurrent,
+ const char** szCurrentOut);
+
+ // -------------------------------------------------------------------
+ /** Parse a single triangle in the SMD file
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives the output cursor position
+ */
+ void ParseTriangle(const char* szCurrent,
+ const char** szCurrentOut);
+
+
+ // -------------------------------------------------------------------
+ /** Parse a single vertex in the SMD file
+ * \param szCurrent Current position in the file. Points to the first
+ * data line of the section.
+ * \param szCurrentOut Receives the output cursor position
+ * \param vertex Vertex to be filled
+ */
+ void ParseVertex(const char* szCurrent,
+ const char** szCurrentOut, SMD::Vertex& vertex,
+ bool bVASection = false);
+
+ // -------------------------------------------------------------------
+ /** Get the index of a texture. If the texture was not yet known
+ * it will be added to the internal texture list.
+ * \param filename Name of the texture
+ * \return Value texture index
+ */
+ unsigned int GetTextureIndex(const std::string& filename);
+
+ // -------------------------------------------------------------------
+ /** Computes absolute bone transformations
+ * All output transformations are in worldspace.
+ */
+ void ComputeAbsoluteBoneTransformations();
+
+
+ // -------------------------------------------------------------------
+ /** Parse a line in the skeleton section
+ */
+ void ParseSkeletonElement(const char* szCurrent,
+ const char** szCurrentOut,int iTime);
+
+ // -------------------------------------------------------------------
+ /** Parse a line in the nodes section
+ */
+ void ParseNodeInfo(const char* szCurrent,
+ const char** szCurrentOut);
+
+
+ // -------------------------------------------------------------------
+ /** Parse a floating-point value
+ */
+ bool ParseFloat(const char* szCurrent,
+ const char** szCurrentOut, float& out);
+
+ // -------------------------------------------------------------------
+ /** Parse an unsigned integer. There may be no sign!
+ */
+ bool ParseUnsignedInt(const char* szCurrent,
+ const char** szCurrentOut, unsigned int& out);
+
+ // -------------------------------------------------------------------
+ /** Parse a signed integer. Signs (+,-) are handled.
+ */
+ bool ParseSignedInt(const char* szCurrent,
+ const char** szCurrentOut, int& out);
+
+ // -------------------------------------------------------------------
+ /** Fix invalid time values in the file
+ */
+ void FixTimeValues();
+
+ // -------------------------------------------------------------------
+ /** Add all children of a bone as subnodes to a node
+ * \param pcNode Parent node
+ * \param iParent Parent bone index
+ */
+ void AddBoneChildren(aiNode* pcNode, uint32_t iParent);
+
+ // -------------------------------------------------------------------
+ /** Build output meshes/materials/nodes/animations
+ */
+ void CreateOutputMeshes();
+ void CreateOutputNodes();
+ void CreateOutputAnimations();
+ void CreateOutputMaterials();
+
+
+ // -------------------------------------------------------------------
+ /** Print a log message together with the current line number
+ */
+ void LogErrorNoThrow(const char* msg);
+ void LogWarning(const char* msg);
+
+
+ // -------------------------------------------------------------------
+ inline bool SkipLine( const char* in, const char** out)
+ {
+ Assimp::SkipLine(in,out);
+ ++iLineNumber;
+ return true;
+ }
+ // -------------------------------------------------------------------
+ inline bool SkipSpacesAndLineEnd( const char* in, const char** out)
+ {
+ ++iLineNumber;
+ return Assimp::SkipSpacesAndLineEnd(in,out);
+ }
+
+private:
+
+ /** Configuration option: frame to be loaded */
+ unsigned int configFrameID;
+
+ /** Buffer to hold the loaded file */
+ const char* mBuffer;
+
+ /** Output scene to be filled
+ */
+ aiScene* pScene;
+
+ /** Size of the input file in bytes
+ */
+ unsigned int iFileSize;
+
+ /** Array of textures found in the file
+ */
+ std::vector<std::string> aszTextures;
+
+ /** Array of triangles found in the file
+ */
+ std::vector<SMD::Face> asTriangles;
+
+ /** Array of bones found in the file
+ */
+ std::vector<SMD::Bone> asBones;
+
+ /** Smallest frame index found in the skeleton
+ */
+ int iSmallestFrame;
+
+ /** Length of the whole animation, in frames
+ */
+ double dLengthOfAnim;
+
+ /** Do we have texture coordinates?
+ */
+ bool bHasUVs;
+
+ /** Current line numer
+ */
+ unsigned int iLineNumber;
+
+};
+
+} // end of namespace Assimp
+
+#endif // AI_SMDIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/STLLoader.cpp b/3rdparty/assimp/code/STLLoader.cpp
new file mode 100644
index 000000000..2c8701dfc
--- /dev/null
+++ b/3rdparty/assimp/code/STLLoader.cpp
@@ -0,0 +1,398 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the STL importer class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_STL_IMPORTER
+
+// internal headers
+#include "STLLoader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+STLImporter::STLImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+STLImporter::~STLImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool STLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ const std::string extension = GetExtension(pFile);
+
+ if (extension == "stl")
+ return true;
+ else if (!extension.length() || checkSig) {
+ if (!pIOHandler)
+ return true;
+ const char* tokens[] = {"STL","solid"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,2);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void STLImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("stl");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void STLImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
+
+ // Check whether we can read from the file
+ if ( file.get() == NULL) {
+ throw DeadlyImportError( "Failed to open STL file " + pFile + ".");
+ }
+
+ fileSize = (unsigned int)file->FileSize();
+
+ // allocate storage and copy the contents of the file to a memory buffer
+ // (terminate it with zero)
+ std::vector<char> mBuffer2;
+ TextFileToBuffer(file.get(),mBuffer2);
+
+ this->pScene = pScene;
+ this->mBuffer = &mBuffer2[0];
+
+ // the default vertex color is white
+ clrColorDefault.r = clrColorDefault.g = clrColorDefault.b = clrColorDefault.a = 1.0f;
+
+ // allocate one mesh
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+ aiMesh* pMesh = pScene->mMeshes[0] = new aiMesh();
+ pMesh->mMaterialIndex = 0;
+
+ // allocate a single node
+ pScene->mRootNode = new aiNode();
+ pScene->mRootNode->mNumMeshes = 1;
+ pScene->mRootNode->mMeshes = new unsigned int[1];
+ pScene->mRootNode->mMeshes[0] = 0;
+
+ bool bMatClr = false;
+
+ // check whether the file starts with 'solid' -
+ // in this case we can simply assume it IS a text file. finished.
+ if (!::strncmp(mBuffer,"solid",5)) {
+ LoadASCIIFile();
+ }
+ else bMatClr = LoadBinaryFile();
+
+ // now copy faces
+ pMesh->mFaces = new aiFace[pMesh->mNumFaces];
+ for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces;++i) {
+
+ aiFace& face = pMesh->mFaces[i];
+ face.mIndices = new unsigned int[face.mNumIndices = 3];
+ for (unsigned int o = 0; o < 3;++o,++p) {
+ face.mIndices[o] = p;
+ }
+ }
+
+ // create a single default material - everything white, as we have vertex colors
+ MaterialHelper* pcMat = new MaterialHelper();
+ aiString s;
+ s.Set(AI_DEFAULT_MATERIAL_NAME);
+ pcMat->AddProperty(&s, AI_MATKEY_NAME);
+
+ aiColor4D clrDiffuse(1.0f,1.0f,1.0f,1.0f);
+ if (bMatClr) {
+ clrDiffuse = clrColorDefault;
+ }
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE);
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR);
+ clrDiffuse = aiColor4D(0.05f,0.05f,0.05f,1.0f);
+ pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT);
+
+ pScene->mNumMaterials = 1;
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = pcMat;
+}
+// ------------------------------------------------------------------------------------------------
+// Read an ASCII STL file
+void STLImporter::LoadASCIIFile()
+{
+ aiMesh* pMesh = pScene->mMeshes[0];
+
+ const char* sz = mBuffer + 5; // skip the "solid"
+ SkipSpaces(&sz);
+ const char* szMe = sz;
+ while (!::IsSpaceOrNewLine(*sz)) {
+ sz++;
+ }
+
+ size_t temp;
+ // setup the name of the node
+ if ((temp = (size_t)(sz-szMe))) {
+
+ pScene->mRootNode->mName.length = temp;
+ memcpy(pScene->mRootNode->mName.data,szMe,temp);
+ pScene->mRootNode->mName.data[temp] = '\0';
+ }
+ else pScene->mRootNode->mName.Set("<STL_ASCII>");
+
+ // try to guess how many vertices we could have
+ // assume we'll need 160 bytes for each face
+ pMesh->mNumVertices = ( pMesh->mNumFaces = fileSize / 160 ) * 3;
+ pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+ pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+
+ unsigned int curFace = 0, curVertex = 3;
+ while (true)
+ {
+ // go to the next token
+ if (!SkipSpacesAndLineEnd(&sz))
+ {
+ // seems we're finished although there was no end marker
+ DefaultLogger::get()->warn("STL: unexpected EOF. \'endsolid\' keyword was expected");
+ break;
+ }
+ // facet normal -0.13 -0.13 -0.98
+ if (!strncmp(sz,"facet",5) && IsSpaceOrNewLine(*(sz+5))) {
+
+ if (3 != curVertex) {
+ DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete");
+ }
+ if (pMesh->mNumFaces == curFace) {
+ // need to resize the arrays, our size estimate was wrong
+ unsigned int iNeededSize = (unsigned int)(sz-mBuffer) / pMesh->mNumFaces;
+ if (iNeededSize <= 160)iNeededSize >>= 1; // prevent endless looping
+ unsigned int add = (unsigned int)((mBuffer+fileSize)-sz) / iNeededSize;
+ add += add >> 3; // add 12.5% as buffer
+ iNeededSize = (pMesh->mNumFaces + add)*3;
+ aiVector3D* pv = new aiVector3D[iNeededSize];
+ memcpy(pv,pMesh->mVertices,pMesh->mNumVertices*sizeof(aiVector3D));
+ delete[] pMesh->mVertices;
+ pMesh->mVertices = pv;
+ pv = new aiVector3D[iNeededSize];
+ memcpy(pv,pMesh->mNormals,pMesh->mNumVertices*sizeof(aiVector3D));
+ delete[] pMesh->mNormals;
+ pMesh->mNormals = pv;
+
+ pMesh->mNumVertices = iNeededSize;
+ pMesh->mNumFaces += add;
+ }
+ aiVector3D* vn = &pMesh->mNormals[curFace++*3];
+
+ sz += 6;
+ curVertex = 0;
+ SkipSpaces(&sz);
+ if (strncmp(sz,"normal",6)) {
+ DefaultLogger::get()->warn("STL: a facet normal vector was expected but not found");
+ }
+ else
+ {
+ sz += 7;
+ SkipSpaces(&sz);
+ sz = fast_atof_move(sz, (float&)vn->x );
+ SkipSpaces(&sz);
+ sz = fast_atof_move(sz, (float&)vn->y );
+ SkipSpaces(&sz);
+ sz = fast_atof_move(sz, (float&)vn->z );
+ *(vn+1) = *vn;
+ *(vn+2) = *vn;
+ }
+ }
+ // vertex 1.50000 1.50000 0.00000
+ else if (!strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6)))
+ {
+ if (3 == curVertex) {
+ DefaultLogger::get()->error("STL: a facet with more than 3 vertices has been found");
+ }
+ else
+ {
+ sz += 7;
+ SkipSpaces(&sz);
+ aiVector3D* vn = &pMesh->mVertices[(curFace-1)*3 + curVertex++];
+ sz = fast_atof_move(sz, (float&)vn->x );
+ SkipSpaces(&sz);
+ sz = fast_atof_move(sz, (float&)vn->y );
+ SkipSpaces(&sz);
+ sz = fast_atof_move(sz, (float&)vn->z );
+ }
+ }
+ else if (!::strncmp(sz,"endsolid",8)) {
+ // finished!
+ break;
+ }
+ // else skip the whole identifier
+ else while (!::IsSpaceOrNewLine(*sz)) {
+ ++sz;
+ }
+ }
+
+ if (!curFace) {
+ pMesh->mNumFaces = 0;
+ throw DeadlyImportError("STL: ASCII file is empty or invalid; no data loaded");
+ }
+ pMesh->mNumFaces = curFace;
+ pMesh->mNumVertices = curFace*3;
+ // we are finished!
+}
+
+// ------------------------------------------------------------------------------------------------
+// Read a binary STL file
+bool STLImporter::LoadBinaryFile()
+{
+ // skip the first 80 bytes
+ if (fileSize < 84) {
+ throw DeadlyImportError("STL: file is too small for the header");
+ }
+ bool bIsMaterialise = false;
+
+ // search for an occurence of "COLOR=" in the header
+ const char* sz2 = (const char*)mBuffer;
+ const char* const szEnd = sz2+80;
+ while (sz2 < szEnd) {
+
+ if ('C' == *sz2++ && 'O' == *sz2++ && 'L' == *sz2++ &&
+ 'O' == *sz2++ && 'R' == *sz2++ && '=' == *sz2++) {
+
+ // read the default vertex color for facets
+ bIsMaterialise = true;
+ DefaultLogger::get()->info("STL: Taking code path for Materialise files");
+ clrColorDefault.r = (*sz2++) / 255.0f;
+ clrColorDefault.g = (*sz2++) / 255.0f;
+ clrColorDefault.b = (*sz2++) / 255.0f;
+ clrColorDefault.a = (*sz2++) / 255.0f;
+ break;
+ }
+ }
+ const unsigned char* sz = (const unsigned char*)mBuffer + 80;
+
+ // now read the number of facets
+ aiMesh* pMesh = pScene->mMeshes[0];
+ pScene->mRootNode->mName.Set("<STL_BINARY>");
+
+ pMesh->mNumFaces = *((uint32_t*)sz);
+ sz += 4;
+
+ if (fileSize < 84 + pMesh->mNumFaces*50) {
+ throw DeadlyImportError("STL: file is too small to hold all facets");
+ }
+
+ if (!pMesh->mNumFaces) {
+ throw DeadlyImportError("STL: file is empty. There are no facets defined");
+ }
+
+ pMesh->mNumVertices = pMesh->mNumFaces*3;
+
+ aiVector3D* vp,*vn;
+ vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
+ vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+
+ for (unsigned int i = 0; i < pMesh->mNumFaces;++i) {
+
+ // NOTE: Blender sometimes writes empty normals ... this is not
+ // our fault ... the RemoveInvalidData helper step should fix that
+ *vn = *((aiVector3D*)sz);
+ sz += sizeof(aiVector3D);
+ *(vn+1) = *vn;
+ *(vn+2) = *vn;
+ vn += 3;
+
+ *vp++ = *((aiVector3D*)sz);
+ sz += sizeof(aiVector3D);
+
+ *vp++ = *((aiVector3D*)sz);
+ sz += sizeof(aiVector3D);
+
+ *vp++ = *((aiVector3D*)sz);
+ sz += sizeof(aiVector3D);
+
+ uint16_t color = *((uint16_t*)sz);
+ sz += 2;
+
+ if (color & (1 << 15))
+ {
+ // seems we need to take the color
+ if (!pMesh->mColors[0])
+ {
+ pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
+ for (unsigned int i = 0; i <pMesh->mNumVertices;++i)
+ *pMesh->mColors[0]++ = this->clrColorDefault;
+ pMesh->mColors[0] -= pMesh->mNumVertices;
+
+ DefaultLogger::get()->info("STL: Mesh has vertex colors");
+ }
+ aiColor4D* clr = &pMesh->mColors[0][pMesh->mNumFaces*3];
+ clr->a = 1.0f;
+ if (bIsMaterialise) // fuck, this is reversed
+ {
+ clr->r = (color & 0x31u) / 31.0f;
+ clr->g = ((color & (0x31u<<5))>>5u) / 31.0f;
+ clr->b = ((color & (0x31u<<10))>>10u) / 31.0f;
+ }
+ else
+ {
+ clr->b = (color & 0x31u) / 31.0f;
+ clr->g = ((color & (0x31u<<5))>>5u) / 31.0f;
+ clr->r = ((color & (0x31u<<10))>>10u) / 31.0f;
+ }
+ // assign the color to all vertices of the face
+ *(clr+1) = *clr;
+ *(clr+2) = *clr;
+ }
+ }
+ if (bIsMaterialise && !pMesh->mColors[0])
+ {
+ // use the color as diffuse material color
+ return true;
+ }
+ return false;
+}
+
+#endif // !! ASSIMP_BUILD_NO_STL_IMPORTER
diff --git a/3rdparty/assimp/code/STLLoader.h b/3rdparty/assimp/code/STLLoader.h
new file mode 100644
index 000000000..bdaacfe1e
--- /dev/null
+++ b/3rdparty/assimp/code/STLLoader.h
@@ -0,0 +1,119 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 STLLoader.h
+ * Declaration of the STL importer class.
+ */
+#ifndef AI_STLLOADER_H_INCLUDED
+#define AI_STLLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/aiTypes.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** Importer class for the sterolithography STL file format
+*/
+class STLImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ STLImporter();
+
+ /** Destructor, private as well */
+ ~STLImporter();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+
+ // -------------------------------------------------------------------
+ /** Loads a binary .stl file
+ * @return true if the default vertex color must be used as material color
+ */
+ bool LoadBinaryFile();
+
+ // -------------------------------------------------------------------
+ /** Loads a ASCII text .stl file
+ */
+ void LoadASCIIFile();
+
+protected:
+
+ /** Buffer to hold the loaded file */
+ const char* mBuffer;
+
+ /** Size of the file, in bytes */
+ unsigned int fileSize;
+
+ /** Output scene */
+ aiScene* pScene;
+
+ /** Default vertex color */
+ aiColor4D clrColorDefault;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_IN
diff --git a/3rdparty/assimp/code/SceneCombiner.cpp b/3rdparty/assimp/code/SceneCombiner.cpp
new file mode 100644
index 000000000..7ee40fd31
--- /dev/null
+++ b/3rdparty/assimp/code/SceneCombiner.cpp
@@ -0,0 +1,1133 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implements Assimp::SceneCombiner. This is a smart utility
+ * class that combines multiple scenes, meshes, ... into one. Currently
+ * these utilities are used by the IRR and LWS loaders and the
+ * OptimizeGraph step.
+ */
+// ----------------------------------------------------------------------------
+#include "AssimpPCH.h"
+#include "SceneCombiner.h"
+#include "fast_atof.h"
+#include "Hash.h"
+#include "time.h"
+
+namespace Assimp {
+
+// ------------------------------------------------------------------------------------------------
+// Add a prefix to a string
+inline void PrefixString(aiString& string,const char* prefix, unsigned int len)
+{
+ // If the string is already prefixed, we won't prefix it a second time
+ if (string.length >= 1 && string.data[0] == '$')
+ return;
+
+ if (len+string.length>=MAXLEN-1) {
+ DefaultLogger::get()->debug("Can't add an unique prefix because the string is too long");
+ ai_assert(false);
+ return;
+ }
+
+ // Add the prefix
+ ::memmove(string.data+len,string.data,string.length+1);
+ ::memcpy (string.data, prefix, len);
+
+ // And update the string's length
+ string.length += len;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add node identifiers to a hashing set
+void SceneCombiner::AddNodeHashes(aiNode* node, std::set<unsigned int>& hashes)
+{
+ // Add node name to hashing set if it is non-empty - empty nodes are allowed
+ // and they can't have any anims assigned so its absolutely safe to duplicate them.
+ if (node->mName.length) {
+ hashes.insert( SuperFastHash(node->mName.data,node->mName.length) );
+ }
+
+ // Process all children recursively
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ AddNodeHashes(node->mChildren[i],hashes);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add a name prefix to all nodes in a hierarchy
+void SceneCombiner::AddNodePrefixes(aiNode* node, const char* prefix, unsigned int len)
+{
+ ai_assert(NULL != prefix);
+ PrefixString(node->mName,prefix,len);
+
+ // Process all children recursively
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ AddNodePrefixes(node->mChildren[i],prefix,len);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Search for matching names
+bool SceneCombiner::FindNameMatch(const aiString& name, std::vector<SceneHelper>& input, unsigned int cur)
+{
+ const unsigned int hash = SuperFastHash(name.data, name.length);
+
+ // Check whether we find a positive match in one of the given sets
+ for (unsigned int i = 0; i < input.size(); ++i) {
+
+ if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add a name prefix to all nodes in a hierarchy if a hash match is found
+void SceneCombiner::AddNodePrefixesChecked(aiNode* node, const char* prefix, unsigned int len,
+ std::vector<SceneHelper>& input, unsigned int cur)
+{
+ ai_assert(NULL != prefix);
+ const unsigned int hash = SuperFastHash(node->mName.data,node->mName.length);
+
+ // Check whether we find a positive match in one of the given sets
+ for (unsigned int i = 0; i < input.size(); ++i) {
+
+ if (cur != i && input[i].hashes.find(hash) != input[i].hashes.end()) {
+ PrefixString(node->mName,prefix,len);
+ break;
+ }
+ }
+
+ // Process all children recursively
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ AddNodePrefixesChecked(node->mChildren[i],prefix,len,input,cur);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Add an offset to all mesh indices in a node graph
+void SceneCombiner::OffsetNodeMeshIndices (aiNode* node, unsigned int offset)
+{
+ for (unsigned int i = 0; i < node->mNumMeshes;++i)
+ node->mMeshes[i] += offset;
+
+ for (unsigned int i = 0; i < node->mNumChildren;++i)
+ OffsetNodeMeshIndices(node->mChildren[i],offset);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Merges two scenes. Currently only used by the LWS loader.
+void SceneCombiner::MergeScenes(aiScene** _dest,std::vector<aiScene*>& src,
+ unsigned int flags)
+{
+ ai_assert(NULL != _dest);
+
+ // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it
+ if (src.empty())
+ {
+ if (*_dest)
+ {
+ (*_dest)->~aiScene();
+ SceneCombiner::CopySceneFlat(_dest,src[0]);
+ }
+ else *_dest = src[0];
+ return;
+ }
+ if (*_dest)(*_dest)->~aiScene();
+ else *_dest = new aiScene();
+
+ // Create a dummy scene to serve as master for the others
+ aiScene* master = new aiScene();
+ master->mRootNode = new aiNode();
+ master->mRootNode->mName.Set("<MergeRoot>");
+
+ std::vector<AttachmentInfo> srcList (src.size());
+ for (unsigned int i = 0; i < srcList.size();++i) {
+ srcList[i] = AttachmentInfo(src[i],master->mRootNode);
+ }
+
+ // 'master' will be deleted afterwards
+ MergeScenes (_dest, master, srcList, flags);
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::AttachToGraph (aiNode* attach, std::vector<NodeAttachmentInfo>& srcList)
+{
+ unsigned int cnt;
+ for (cnt = 0; cnt < attach->mNumChildren;++cnt)
+ AttachToGraph(attach->mChildren[cnt],srcList);
+
+ cnt = 0;
+ for (std::vector<NodeAttachmentInfo>::iterator it = srcList.begin();
+ it != srcList.end(); ++it)
+ {
+ if ((*it).attachToNode == attach && !(*it).resolved)
+ ++cnt;
+ }
+
+ if (cnt) {
+ aiNode** n = new aiNode*[cnt+attach->mNumChildren];
+ if (attach->mNumChildren) {
+ ::memcpy(n,attach->mChildren,sizeof(void*)*attach->mNumChildren);
+ delete[] attach->mChildren;
+ }
+ attach->mChildren = n;
+
+ n += attach->mNumChildren;
+ attach->mNumChildren += cnt;
+
+ for (unsigned int i = 0; i < srcList.size();++i) {
+ NodeAttachmentInfo& att = srcList[i];
+ if (att.attachToNode == attach && !att.resolved) {
+ *n = att.node;
+ (**n).mParent = attach;
+ ++n;
+
+ // mark this attachment as resolved
+ att.resolved = true;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::AttachToGraph ( aiScene* master,
+ std::vector<NodeAttachmentInfo>& src)
+{
+ ai_assert(NULL != master);
+ AttachToGraph(master->mRootNode,src);
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::MergeScenes(aiScene** _dest, aiScene* master,
+ std::vector<AttachmentInfo>& srcList,
+ unsigned int flags)
+{
+ ai_assert(NULL != _dest);
+
+ // if _dest points to NULL allocate a new scene. Otherwise clear the old and reuse it
+ if (srcList.empty())
+ {
+ if (*_dest)
+ {
+ (*_dest)->~aiScene();
+ SceneCombiner::CopySceneFlat(_dest,master);
+ }
+ else *_dest = master;
+ return;
+ }
+ if (*_dest)(*_dest)->~aiScene();
+ else *_dest = new aiScene();
+
+ aiScene* dest = *_dest;
+
+ std::vector<SceneHelper> src (srcList.size()+1);
+ src[0].scene = master;
+ for (unsigned int i = 0; i < srcList.size();++i) {
+ src[i+1] = SceneHelper( srcList[i].scene );
+ }
+
+ // this helper array specifies which scenes are duplicates of others
+ std::vector<unsigned int> duplicates(src.size(),0xffffffff);
+
+ // this helper array is used as lookup table several times
+ std::vector<unsigned int> offset(src.size());
+
+ // Find duplicate scenes
+ for (unsigned int i = 0; i < src.size();++i)
+ {
+ if (duplicates[i] != i && duplicates[i] != 0xffffffff)continue;
+ duplicates[i] = i;
+ for ( unsigned int a = i+1; a < src.size(); ++a)
+ {
+ if (src[i].scene == src[a].scene)
+ duplicates[a] = i;
+ }
+ }
+
+ // Generate unique names for all named stuff?
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES)
+ {
+#if 0
+ // Construct a proper random number generator
+ boost::mt19937 rng( );
+ boost::uniform_int<> dist(1u,1 << 24u);
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<> > rndGen(rng, dist);
+#endif
+ for (unsigned int i = 1; i < src.size();++i)
+ {
+ //if (i != duplicates[i])
+ //{
+ // // duplicate scenes share the same UID
+ // ::strcpy( src[i].id, src[duplicates[i]].id );
+ // src[i].idlen = src[duplicates[i]].idlen;
+
+ // continue;
+ //}
+
+ src[i].idlen = ::sprintf(src[i].id,"$%.6X$_",i);
+
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
+
+ // Compute hashes for all identifiers in this scene and store them
+ // in a sorted table (for convenience I'm using std::set). We hash
+ // just the node and animation channel names, all identifiers except
+ // the material names should be caught by doing this.
+ AddNodeHashes(src[i]->mRootNode,src[i].hashes);
+
+ for (unsigned int a = 0; a < src[i]->mNumAnimations;++a) {
+ aiAnimation* anim = src[i]->mAnimations[a];
+ src[i].hashes.insert(SuperFastHash(anim->mName.data,anim->mName.length));
+ }
+ }
+ }
+ }
+
+ unsigned int cnt;
+
+ // First find out how large the respective output arrays must be
+ for ( unsigned int n = 0; n < src.size();++n )
+ {
+ SceneHelper* cur = &src[n];
+
+ if (n == duplicates[n] || flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
+ dest->mNumTextures += (*cur)->mNumTextures;
+ dest->mNumMaterials += (*cur)->mNumMaterials;
+ dest->mNumMeshes += (*cur)->mNumMeshes;
+ }
+
+ dest->mNumLights += (*cur)->mNumLights;
+ dest->mNumCameras += (*cur)->mNumCameras;
+ dest->mNumAnimations += (*cur)->mNumAnimations;
+
+ // Combine the flags of all scenes
+ // We need to process them flag-by-flag here to get correct results
+ // dest->mFlags ; //|= (*cur)->mFlags;
+ if ((*cur)->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
+ dest->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT;
+ }
+ }
+
+ // generate the output texture list + an offset table for all texture indices
+ if (dest->mNumTextures)
+ {
+ aiTexture** pip = dest->mTextures = new aiTexture*[dest->mNumMaterials];
+ cnt = 0;
+ for ( unsigned int n = 0; n < src.size();++n )
+ {
+ SceneHelper* cur = &src[n];
+ for (unsigned int i = 0; i < (*cur)->mNumTextures;++i)
+ {
+ if (n != duplicates[n])
+ {
+ if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY)
+ Copy(pip,(*cur)->mTextures[i]);
+
+ else continue;
+ }
+ else *pip = (*cur)->mTextures[i];
+ ++pip;
+ }
+
+ offset[n] = cnt;
+ cnt = (unsigned int)(pip - dest->mTextures);
+ }
+ }
+
+ // generate the output material list + an offset table for all material indices
+ if (dest->mNumMaterials)
+ {
+ aiMaterial** pip = dest->mMaterials = new aiMaterial*[dest->mNumMaterials];
+ cnt = 0;
+ for ( unsigned int n = 0; n < src.size();++n ) {
+ SceneHelper* cur = &src[n];
+ for (unsigned int i = 0; i < (*cur)->mNumMaterials;++i)
+ {
+ if (n != duplicates[n])
+ {
+ if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY)
+ Copy(pip,(*cur)->mMaterials[i]);
+
+ else continue;
+ }
+ else *pip = (*cur)->mMaterials[i];
+
+ if ((*cur)->mNumTextures != dest->mNumTextures) {
+ // We need to update all texture indices of the mesh. So we need to search for
+ // a material property called '$tex.file'
+
+ for (unsigned int a = 0; a < (*pip)->mNumProperties;++a)
+ {
+ aiMaterialProperty* prop = (*pip)->mProperties[a];
+ if (!strncmp(prop->mKey.data,"$tex.file",9))
+ {
+ // Check whether this texture is an embedded texture.
+ // In this case the property looks like this: *<n>,
+ // where n is the index of the texture.
+ aiString& s = *((aiString*)prop->mData);
+ if ('*' == s.data[0]) {
+ // Offset the index and write it back ..
+ const unsigned int idx = strtol10(&s.data[1]) + offset[n];
+ ASSIMP_itoa10(&s.data[1],sizeof(s.data)-1,idx);
+ }
+ }
+
+ // Need to generate new, unique material names?
+ else if (!::strcmp( prop->mKey.data,"$mat.name" ) && flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES)
+ {
+ aiString* pcSrc = (aiString*) prop->mData;
+ PrefixString(*pcSrc, (*cur).id, (*cur).idlen);
+ }
+ }
+ }
+ ++pip;
+ }
+
+ offset[n] = cnt;
+ cnt = (unsigned int)(pip - dest->mMaterials);
+ }
+ }
+
+ // generate the output mesh list + again an offset table for all mesh indices
+ if (dest->mNumMeshes)
+ {
+ aiMesh** pip = dest->mMeshes = new aiMesh*[dest->mNumMeshes];
+ cnt = 0;
+ for ( unsigned int n = 0; n < src.size();++n )
+ {
+ SceneHelper* cur = &src[n];
+ for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i)
+ {
+ if (n != duplicates[n]) {
+ if ( flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY)
+ Copy(pip, (*cur)->mMeshes[i]);
+
+ else continue;
+ }
+ else *pip = (*cur)->mMeshes[i];
+
+ // update the material index of the mesh
+ (*pip)->mMaterialIndex += offset[n];
+ ++pip;
+ }
+
+ // reuse the offset array - store now the mesh offset in it
+ offset[n] = cnt;
+ cnt = (unsigned int)(pip - dest->mMeshes);
+ }
+ }
+
+ std::vector <NodeAttachmentInfo> nodes;
+ nodes.reserve(srcList.size());
+
+ // ----------------------------------------------------------------------------
+ // Now generate the output node graph. We need to make those
+ // names in the graph that are referenced by anims or lights
+ // or cameras unique. So we add a prefix to them ... $<rand>_
+ // We could also use a counter, but using a random value allows us to
+ // use just one prefix if we are joining multiple scene hierarchies recursively.
+ // Chances are quite good we don't collide, so we try that ...
+ // ----------------------------------------------------------------------------
+
+ // Allocate space for light sources, cameras and animations
+ aiLight** ppLights = dest->mLights = (dest->mNumLights
+ ? new aiLight*[dest->mNumLights] : NULL);
+
+ aiCamera** ppCameras = dest->mCameras = (dest->mNumCameras
+ ? new aiCamera*[dest->mNumCameras] : NULL);
+
+ aiAnimation** ppAnims = dest->mAnimations = (dest->mNumAnimations
+ ? new aiAnimation*[dest->mNumAnimations] : NULL);
+
+ for ( int n = src.size()-1; n >= 0 ;--n ) /* !!! important !!! */
+ {
+ SceneHelper* cur = &src[n];
+ aiNode* node;
+
+ // To offset or not to offset, this is the question
+ if (n != (int)duplicates[n])
+ {
+ // Get full scenegraph copy
+ Copy( &node, (*cur)->mRootNode );
+ OffsetNodeMeshIndices(node,offset[duplicates[n]]);
+
+ if (flags & AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY) {
+ // (note:) they are already 'offseted' by offset[duplicates[n]]
+ OffsetNodeMeshIndices(node,offset[n] - offset[duplicates[n]]);
+ }
+ }
+ else // if (n == duplicates[n])
+ {
+ node = (*cur)->mRootNode;
+ OffsetNodeMeshIndices(node,offset[n]);
+ }
+ if (n) // src[0] is the master node
+ nodes.push_back(NodeAttachmentInfo( node,srcList[n-1].attachToNode,n ));
+
+ // add name prefixes?
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
+
+ // or the whole scenegraph
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
+ AddNodePrefixesChecked(node,(*cur).id,(*cur).idlen,src,n);
+ }
+ else AddNodePrefixes(node,(*cur).id,(*cur).idlen);
+
+ // meshes
+ for (unsigned int i = 0; i < (*cur)->mNumMeshes;++i) {
+ aiMesh* mesh = (*cur)->mMeshes[i];
+
+ // rename all bones
+ for (unsigned int a = 0; a < mesh->mNumBones;++a) {
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
+ if (!FindNameMatch(mesh->mBones[a]->mName,src,n))
+ continue;
+ }
+ PrefixString(mesh->mBones[a]->mName,(*cur).id,(*cur).idlen);
+ }
+ }
+ }
+
+ // --------------------------------------------------------------------
+ // Copy light sources
+ for (unsigned int i = 0; i < (*cur)->mNumLights;++i,++ppLights)
+ {
+ if (n != (int)duplicates[n]) // duplicate scene?
+ {
+ Copy(ppLights, (*cur)->mLights[i]);
+ }
+ else *ppLights = (*cur)->mLights[i];
+
+
+ // Add name prefixes?
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
+ if (!FindNameMatch((*ppLights)->mName,src,n))
+ continue;
+ }
+
+ PrefixString((*ppLights)->mName,(*cur).id,(*cur).idlen);
+ }
+ }
+
+ // --------------------------------------------------------------------
+ // Copy cameras
+ for (unsigned int i = 0; i < (*cur)->mNumCameras;++i,++ppCameras) {
+ if (n != (int)duplicates[n]) // duplicate scene?
+ {
+ Copy(ppCameras, (*cur)->mCameras[i]);
+ }
+ else *ppCameras = (*cur)->mCameras[i];
+
+ // Add name prefixes?
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
+ if (!FindNameMatch((*ppCameras)->mName,src,n))
+ continue;
+ }
+
+ PrefixString((*ppCameras)->mName,(*cur).id,(*cur).idlen);
+ }
+ }
+
+ // --------------------------------------------------------------------
+ // Copy animations
+ for (unsigned int i = 0; i < (*cur)->mNumAnimations;++i,++ppAnims) {
+ if (n != (int)duplicates[n]) // duplicate scene?
+ {
+ Copy(ppAnims, (*cur)->mAnimations[i]);
+ }
+ else *ppAnims = (*cur)->mAnimations[i];
+
+ // Add name prefixes?
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES) {
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
+ if (!FindNameMatch((*ppAnims)->mName,src,n))
+ continue;
+ }
+
+ PrefixString((*ppAnims)->mName,(*cur).id,(*cur).idlen);
+
+ // don't forget to update all node animation channels
+ for (unsigned int a = 0; a < (*ppAnims)->mNumChannels;++a) {
+ if (flags & AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY) {
+ if (!FindNameMatch((*ppAnims)->mChannels[a]->mNodeName,src,n))
+ continue;
+ }
+
+ PrefixString((*ppAnims)->mChannels[a]->mNodeName,(*cur).id,(*cur).idlen);
+ }
+ }
+ }
+ }
+
+ // Now build the output graph
+ AttachToGraph ( master, nodes);
+ dest->mRootNode = master->mRootNode;
+
+ // Check whether we succeeded at building the output graph
+ for (std::vector <NodeAttachmentInfo> ::iterator it = nodes.begin();
+ it != nodes.end(); ++it)
+ {
+ if (!(*it).resolved) {
+ if (flags & AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS) {
+ // search for this attachment point in all other imported scenes, too.
+ for ( unsigned int n = 0; n < src.size();++n ) {
+ if (n != (*it).src_idx) {
+ AttachToGraph(src[n].scene,nodes);
+ if ((*it).resolved)
+ break;
+ }
+ }
+ }
+ if (!(*it).resolved) {
+ DefaultLogger::get()->error(std::string("SceneCombiner: Failed to resolve attachment ")
+ + (*it).node->mName.data + " " + (*it).attachToNode->mName.data);
+ }
+ }
+ }
+
+ // now delete all input scenes. Make sure duplicate scenes aren't
+ // deleted more than one time
+ for ( unsigned int n = 0; n < src.size();++n ) {
+ if (n != duplicates[n]) // duplicate scene?
+ continue;
+
+ aiScene* deleteMe = src[n].scene;
+
+ // We need to delete the arrays before the destructor is called -
+ // we are reusing the array members
+ delete[] deleteMe->mMeshes; deleteMe->mMeshes = NULL;
+ delete[] deleteMe->mCameras; deleteMe->mCameras = NULL;
+ delete[] deleteMe->mLights; deleteMe->mLights = NULL;
+ delete[] deleteMe->mMaterials; deleteMe->mMaterials = NULL;
+ delete[] deleteMe->mAnimations; deleteMe->mAnimations = NULL;
+
+ deleteMe->mRootNode = NULL;
+
+ // Now we can safely delete the scene
+ delete deleteMe;
+ }
+
+ // Check flags
+ if (!dest->mNumMeshes || !dest->mNumMaterials) {
+ dest->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+ }
+
+ // We're finished
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a list of unique bones
+void SceneCombiner::BuildUniqueBoneList(std::list<BoneWithHash>& asBones,
+ std::vector<aiMesh*>::const_iterator it,
+ std::vector<aiMesh*>::const_iterator end)
+{
+ unsigned int iOffset = 0;
+ for (; it != end;++it) {
+ for (unsigned int l = 0; l < (*it)->mNumBones;++l) {
+ aiBone* p = (*it)->mBones[l];
+ uint32_t itml = SuperFastHash(p->mName.data,(unsigned int)p->mName.length);
+
+ std::list<BoneWithHash>::iterator it2 = asBones.begin();
+ std::list<BoneWithHash>::iterator end2 = asBones.end();
+
+ for (;it2 != end2;++it2) {
+ if ((*it2).first == itml) {
+ (*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset));
+ break;
+ }
+ }
+ if (end2 == it2) {
+ // need to begin a new bone entry
+ asBones.push_back(BoneWithHash());
+ BoneWithHash& btz = asBones.back();
+
+ // setup members
+ btz.first = itml;
+ btz.second = &p->mName;
+ btz.pSrcBones.push_back(BoneSrcIndex(p,iOffset));
+ }
+ }
+ iOffset += (*it)->mNumVertices;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Merge a list of bones
+void SceneCombiner::MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator it,
+ std::vector<aiMesh*>::const_iterator end)
+{
+ ai_assert(NULL != out && !out->mNumBones);
+
+ // find we need to build an unique list of all bones.
+ // we work with hashes to make the comparisons MUCH faster,
+ // at least if we have many bones.
+ std::list<BoneWithHash> asBones;
+ BuildUniqueBoneList(asBones, it,end);
+
+ // now create the output bones
+ out->mNumBones = 0;
+ out->mBones = new aiBone*[asBones.size()];
+
+ for (std::list<BoneWithHash>::const_iterator it = asBones.begin(),end = asBones.end(); it != end;++it) {
+ // Allocate a bone and setup it's name
+ aiBone* pc = out->mBones[out->mNumBones++] = new aiBone();
+ pc->mName = aiString( *((*it).second ));
+
+ std::vector< BoneSrcIndex >::const_iterator wend = (*it).pSrcBones.end();
+
+ // Loop through all bones to be joined for this bone
+ for (std::vector< BoneSrcIndex >::const_iterator wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit) {
+ pc->mNumWeights += (*wmit).first->mNumWeights;
+
+ // NOTE: different offset matrices for bones with equal names
+ // are - at the moment - not handled correctly.
+ if (wmit != (*it).pSrcBones.begin() && pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix) {
+ DefaultLogger::get()->warn("Bones with equal names but different offset matrices can't be joined at the moment");
+ continue;
+ }
+ pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix;
+ }
+
+ // Allocate the vertex weight array
+ aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights];
+
+ // And copy the final weights - adjust the vertex IDs by the
+ // face index offset of the coresponding mesh.
+ for (std::vector< BoneSrcIndex >::const_iterator wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit) {
+ aiBone* pip = (*wmit).first;
+ for (unsigned int mp = 0; mp < pip->mNumWeights;++mp,++avw) {
+ const aiVertexWeight& vfi = pip->mWeights[mp];
+ avw->mWeight = vfi.mWeight;
+ avw->mVertexId = vfi.mVertexId + (*wmit).second;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Merge a list of meshes
+void SceneCombiner::MergeMeshes(aiMesh** _out,unsigned int /*flags*/,
+ std::vector<aiMesh*>::const_iterator begin,
+ std::vector<aiMesh*>::const_iterator end)
+{
+ ai_assert(NULL != _out);
+
+ if (begin == end) {
+ *_out = NULL; // no meshes ...
+ return;
+ }
+
+ // Allocate the output mesh
+ aiMesh* out = *_out = new aiMesh();
+ out->mMaterialIndex = (*begin)->mMaterialIndex;
+
+ // Find out how much output storage we'll need
+ for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) {
+ out->mNumVertices += (*it)->mNumVertices;
+ out->mNumFaces += (*it)->mNumFaces;
+ out->mNumBones += (*it)->mNumBones;
+
+ // combine primitive type flags
+ out->mPrimitiveTypes |= (*it)->mPrimitiveTypes;
+ }
+
+ if (out->mNumVertices) {
+ aiVector3D* pv2;
+
+ // copy vertex positions
+ if ((**begin).HasPositions()) {
+
+ pv2 = out->mVertices = new aiVector3D[out->mNumVertices];
+ for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) {
+ if ((*it)->mVertices) {
+ ::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D));
+ }
+ else DefaultLogger::get()->warn("JoinMeshes: Positions expected but input mesh contains no positions");
+ pv2 += (*it)->mNumVertices;
+ }
+ }
+ // copy normals
+ if ((**begin).HasNormals()) {
+
+ pv2 = out->mNormals = new aiVector3D[out->mNumVertices];
+ for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) {
+ if ((*it)->mNormals) {
+ ::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D));
+ }
+ else DefaultLogger::get()->warn("JoinMeshes: Normals expected but input mesh contains no normals");
+ pv2 += (*it)->mNumVertices;
+ }
+ }
+ // copy tangents and bitangents
+ if ((**begin).HasTangentsAndBitangents()) {
+
+ pv2 = out->mTangents = new aiVector3D[out->mNumVertices];
+ aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices];
+
+ for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) {
+ if ((*it)->mTangents) {
+ ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D));
+ ::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D));
+ }
+ else DefaultLogger::get()->warn("JoinMeshes: Tangents expected but input mesh contains no tangents");
+ pv2 += (*it)->mNumVertices;
+ pv2b += (*it)->mNumVertices;
+ }
+ }
+ // copy texture coordinates
+ unsigned int n = 0;
+ while ((**begin).HasTextureCoords(n)) {
+ out->mNumUVComponents[n] = (*begin)->mNumUVComponents[n];
+
+ pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices];
+ for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) {
+
+ if ((*it)->mTextureCoords[n]) {
+ ::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D));
+ }
+ else DefaultLogger::get()->warn("JoinMeshes: UVs expected but input mesh contains no UVs");
+ pv2 += (*it)->mNumVertices;
+ }
+ ++n;
+ }
+ // copy vertex colors
+ n = 0;
+ while ((**begin).HasVertexColors(n)) {
+ aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices];
+ for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) {
+
+ if ((*it)->mColors[n]) {
+ ::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D));
+ }
+ else DefaultLogger::get()->warn("JoinMeshes: VCs expected but input mesh contains no VCs");
+ pv2 += (*it)->mNumVertices;
+ }
+ ++n;
+ }
+ }
+
+ if (out->mNumFaces) // just for safety
+ {
+ // copy faces
+ out->mFaces = new aiFace[out->mNumFaces];
+ aiFace* pf2 = out->mFaces;
+
+ unsigned int ofs = 0;
+ for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it) {
+ for (unsigned int m = 0; m < (*it)->mNumFaces;++m,++pf2) {
+ aiFace& face = (*it)->mFaces[m];
+ pf2->mNumIndices = face.mNumIndices;
+ pf2->mIndices = face.mIndices;
+
+ if (ofs) {
+ // add the offset to the vertex
+ for (unsigned int q = 0; q < face.mNumIndices; ++q)
+ face.mIndices[q] += ofs;
+ }
+ face.mIndices = NULL;
+ }
+ ofs += (*it)->mNumVertices;
+ }
+ }
+
+ // bones - as this is quite lengthy, I moved the code to a separate function
+ if (out->mNumBones)
+ MergeBones(out,begin,end);
+
+ // delete all source meshes
+ for (std::vector<aiMesh*>::const_iterator it = begin; it != end;++it)
+ delete *it;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename Type>
+inline void CopyPtrArray (Type**& dest, Type** src, unsigned int num)
+{
+ if (!num)
+ {
+ dest = NULL;
+ return;
+ }
+ dest = new Type*[num];
+ for (unsigned int i = 0; i < num;++i)
+ SceneCombiner::Copy(&dest[i],src[i]);
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename Type>
+inline void GetArrayCopy (Type*& dest, unsigned int num )
+{
+ if (!dest)return;
+ Type* old = dest;
+
+ dest = new Type[num];
+ ::memcpy(dest, old, sizeof(Type) * num);
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::CopySceneFlat(aiScene** _dest,aiScene* src)
+{
+ // reuse the old scene or allocate a new?
+ if (*_dest)(*_dest)->~aiScene();
+ else *_dest = new aiScene();
+
+ ::memcpy(*_dest,src,sizeof(aiScene));
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::CopyScene(aiScene** _dest,aiScene* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ aiScene* dest = *_dest = new aiScene();
+
+ // copy animations
+ dest->mNumAnimations = src->mNumAnimations;
+ CopyPtrArray(dest->mAnimations,src->mAnimations,
+ dest->mNumAnimations);
+
+ // copy textures
+ dest->mNumTextures = src->mNumTextures;
+ CopyPtrArray(dest->mTextures,src->mTextures,
+ dest->mNumTextures);
+
+ // copy materials
+ dest->mNumMaterials = src->mNumMaterials;
+ CopyPtrArray(dest->mMaterials,src->mMaterials,
+ dest->mNumMaterials);
+
+ // copy lights
+ dest->mNumLights = src->mNumLights;
+ CopyPtrArray(dest->mLights,src->mLights,
+ dest->mNumLights);
+
+ // copy cameras
+ dest->mNumCameras = src->mNumCameras;
+ CopyPtrArray(dest->mCameras,src->mCameras,
+ dest->mNumCameras);
+
+ // copy meshes
+ dest->mNumMeshes = src->mNumMeshes;
+ CopyPtrArray(dest->mMeshes,src->mMeshes,
+ dest->mNumMeshes);
+
+ // now - copy the root node of the scene (deep copy, too)
+ Copy( &dest->mRootNode, src->mRootNode);
+
+ // and keep the flags ...
+ dest->mFlags = src->mFlags;
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy (aiMesh** _dest, const aiMesh* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ aiMesh* dest = *_dest = new aiMesh();
+
+ // get a flat copy
+ ::memcpy(dest,src,sizeof(aiMesh));
+
+ // and reallocate all arrays
+ GetArrayCopy( dest->mVertices, dest->mNumVertices );
+ GetArrayCopy( dest->mNormals , dest->mNumVertices );
+ GetArrayCopy( dest->mTangents, dest->mNumVertices );
+ GetArrayCopy( dest->mBitangents, dest->mNumVertices );
+
+ unsigned int n = 0;
+ while (dest->HasTextureCoords(n))
+ GetArrayCopy( dest->mTextureCoords[n++], dest->mNumVertices );
+
+ n = 0;
+ while (dest->HasVertexColors(n))
+ GetArrayCopy( dest->mColors[n++], dest->mNumVertices );
+
+ // make a deep copy of all bones
+ CopyPtrArray(dest->mBones,dest->mBones,dest->mNumBones);
+
+ // make a deep copy of all faces
+ GetArrayCopy(dest->mFaces,dest->mNumFaces);
+ for (unsigned int i = 0; i < dest->mNumFaces;++i)
+ {
+ aiFace& f = dest->mFaces[i];
+ GetArrayCopy(f.mIndices,f.mNumIndices);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy (aiMaterial** _dest, const aiMaterial* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ MaterialHelper* dest = (MaterialHelper*) ( *_dest = new MaterialHelper() );
+ dest->mNumAllocated = src->mNumAllocated;
+ dest->mNumProperties = src->mNumProperties;
+ dest->mProperties = new aiMaterialProperty* [dest->mNumAllocated];
+
+ for (unsigned int i = 0; i < dest->mNumProperties;++i)
+ {
+ aiMaterialProperty* prop = dest->mProperties[i] = new aiMaterialProperty();
+ aiMaterialProperty* sprop = src->mProperties[i];
+
+ prop->mDataLength = sprop->mDataLength;
+ prop->mData = new char[prop->mDataLength];
+ ::memcpy(prop->mData,sprop->mData,prop->mDataLength);
+
+ prop->mIndex = sprop->mIndex;
+ prop->mSemantic = sprop->mSemantic;
+ prop->mKey = sprop->mKey;
+ prop->mType = sprop->mType;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy (aiTexture** _dest, const aiTexture* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ aiTexture* dest = *_dest = new aiTexture();
+
+ // get a flat copy
+ ::memcpy(dest,src,sizeof(aiTexture));
+
+ // and reallocate all arrays. We must do it manually here
+ const char* old = (const char*)dest->pcData;
+ if (old)
+ {
+ unsigned int cpy;
+ if (!dest->mHeight)cpy = dest->mWidth;
+ else cpy = dest->mHeight * dest->mWidth * sizeof(aiTexel);
+
+ if (!cpy)
+ {
+ dest->pcData = NULL;
+ return;
+ }
+ // the cast is legal, the aiTexel c'tor does nothing important
+ dest->pcData = (aiTexel*) new char[cpy];
+ ::memcpy(dest->pcData, old, cpy);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy (aiAnimation** _dest, const aiAnimation* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ aiAnimation* dest = *_dest = new aiAnimation();
+
+ // get a flat copy
+ ::memcpy(dest,src,sizeof(aiAnimation));
+
+ // and reallocate all arrays
+ CopyPtrArray( dest->mChannels, src->mChannels, dest->mNumChannels );
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy (aiNodeAnim** _dest, const aiNodeAnim* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ aiNodeAnim* dest = *_dest = new aiNodeAnim();
+
+ // get a flat copy
+ ::memcpy(dest,src,sizeof(aiNodeAnim));
+
+ // and reallocate all arrays
+ GetArrayCopy( dest->mPositionKeys, dest->mNumPositionKeys );
+ GetArrayCopy( dest->mScalingKeys, dest->mNumScalingKeys );
+ GetArrayCopy( dest->mRotationKeys, dest->mNumRotationKeys );
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy (aiCamera** _dest,const aiCamera* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ aiCamera* dest = *_dest = new aiCamera();
+
+ // get a flat copy, that's already OK
+ ::memcpy(dest,src,sizeof(aiCamera));
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy (aiLight** _dest, const aiLight* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ aiLight* dest = *_dest = new aiLight();
+
+ // get a flat copy, that's already OK
+ ::memcpy(dest,src,sizeof(aiLight));
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy (aiBone** _dest, const aiBone* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ aiBone* dest = *_dest = new aiBone();
+
+ // get a flat copy
+ ::memcpy(dest,src,sizeof(aiBone));
+
+ // and reallocate all arrays
+ GetArrayCopy( dest->mWeights, dest->mNumWeights );
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy (aiNode** _dest, const aiNode* src)
+{
+ ai_assert(NULL != _dest && NULL != src);
+
+ aiNode* dest = *_dest = new aiNode();
+
+ // get a flat copy
+ ::memcpy(dest,src,sizeof(aiNode));
+
+ // and reallocate all arrays
+ GetArrayCopy( dest->mMeshes, dest->mNumMeshes );
+ CopyPtrArray( dest->mChildren, src->mChildren,dest->mNumChildren);
+}
+
+
+}
diff --git a/3rdparty/assimp/code/SceneCombiner.h b/3rdparty/assimp/code/SceneCombiner.h
new file mode 100644
index 000000000..8c29c8fd7
--- /dev/null
+++ b/3rdparty/assimp/code/SceneCombiner.h
@@ -0,0 +1,365 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Declares a helper class, "SceneCombiner" providing various
+ * utilities to merge scenes.
+ */
+#ifndef AI_SCENE_COMBINER_H_INC
+#define AI_SCENE_COMBINER_H_INC
+
+#include "../include/aiAssert.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** \brief Helper data structure for SceneCombiner.
+ *
+ * Describes to which node a scene must be attached to.
+ */
+struct AttachmentInfo
+{
+ AttachmentInfo()
+ : scene (NULL)
+ , attachToNode (NULL)
+ {}
+
+ AttachmentInfo(aiScene* _scene, aiNode* _attachToNode)
+ : scene (_scene)
+ , attachToNode (_attachToNode)
+ {}
+
+ aiScene* scene;
+ aiNode* attachToNode;
+};
+
+// ---------------------------------------------------------------------------
+struct NodeAttachmentInfo
+{
+ NodeAttachmentInfo()
+ : node (NULL)
+ , attachToNode (NULL)
+ , resolved (false)
+ , src_idx (0xffffffff)
+ {}
+
+ NodeAttachmentInfo(aiNode* _scene, aiNode* _attachToNode,size_t idx)
+ : node (_scene)
+ , attachToNode (_attachToNode)
+ , resolved (false)
+ , src_idx (idx)
+ {}
+
+ aiNode* node;
+ aiNode* attachToNode;
+ bool resolved;
+ size_t src_idx;
+};
+
+// ---------------------------------------------------------------------------
+/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES
+ * Generate unique names for all named scene items
+ */
+#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES 0x1
+
+/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES
+ * Generate unique names for materials, too.
+ * This is not absolutely required to pass the validation.
+ */
+#define AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES 0x2
+
+/** @def AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY
+ * Use deep copies of duplicate scenes
+ */
+#define AI_INT_MERGE_SCENE_DUPLICATES_DEEP_CPY 0x4
+
+/** @def AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS
+ * If attachment nodes are not found in the given master scene,
+ * search the other imported scenes for them in an any order.
+ */
+#define AI_INT_MERGE_SCENE_RESOLVE_CROSS_ATTACHMENTS 0x8
+
+/** @def AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY
+ * Can be combined with AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES.
+ * Unique names are generated, but only if this is absolutely
+ * required to avoid name conflicts.
+ */
+#define AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY 0x10
+
+
+typedef std::pair<aiBone*,unsigned int> BoneSrcIndex;
+
+// ---------------------------------------------------------------------------
+/** @brief Helper data structure for SceneCombiner::MergeBones.
+ */
+struct BoneWithHash : public std::pair<uint32_t,aiString*> {
+ std::vector<BoneSrcIndex> pSrcBones;
+};
+
+
+// ---------------------------------------------------------------------------
+/** @brief Utility for SceneCombiner
+ */
+struct SceneHelper
+{
+ SceneHelper ()
+ : scene (NULL)
+ , idlen (0)
+ {
+ id[0] = 0;
+ }
+
+ SceneHelper (aiScene* _scene)
+ : scene (_scene)
+ , idlen (0)
+ {
+ id[0] = 0;
+ }
+
+ AI_FORCE_INLINE aiScene* operator-> () const
+ {
+ return scene;
+ }
+
+ // scene we're working on
+ aiScene* scene;
+
+ // prefix to be added to all identifiers in the scene ...
+ char id [32];
+
+ // and its strlen()
+ unsigned int idlen;
+
+ // hash table to quickly check whether a name is contained in the scene
+ std::set<unsigned int> hashes;
+};
+
+// ---------------------------------------------------------------------------
+/** \brief Static helper class providing various utilities to merge two
+ * scenes. It is intended as internal utility and NOT for use by
+ * applications.
+ *
+ * The class is currently being used by various postprocessing steps
+ * and loaders (ie. LWS).
+ */
+class ASSIMP_API SceneCombiner
+{
+ // class cannot be instanced
+ SceneCombiner() {}
+
+public:
+
+ // -------------------------------------------------------------------
+ /** Merges two or more scenes.
+ *
+ * @param dest Receives a pointer to the destination scene. If the
+ * pointer doesn't point to NULL when the function is called, the
+ * existing scene is cleared and refilled.
+ * @param src Non-empty list of scenes to be merged. The function
+ * deletes the input scenes afterwards. There may be duplicate scenes.
+ * @param flags Combination of the AI_INT_MERGE_SCENE flags defined above
+ */
+ static void MergeScenes(aiScene** dest,std::vector<aiScene*>& src,
+ unsigned int flags = 0);
+
+
+ // -------------------------------------------------------------------
+ /** Merges two or more scenes and attaches all sceenes to a specific
+ * position in the node graph of the masteer scene.
+ *
+ * @param dest Receives a pointer to the destination scene. If the
+ * pointer doesn't point to NULL when the function is called, the
+ * existing scene is cleared and refilled.
+ * @param master Master scene. It will be deleted afterwards. All
+ * other scenes will be inserted in its node graph.
+ * @param src Non-empty list of scenes to be merged along with their
+ * corresponding attachment points in the master scene. The function
+ * deletes the input scenes afterwards. There may be duplicate scenes.
+ * @param flags Combination of the AI_INT_MERGE_SCENE flags defined above
+ */
+ static void MergeScenes(aiScene** dest, aiScene* master,
+ std::vector<AttachmentInfo>& src,
+ unsigned int flags = 0);
+
+
+ // -------------------------------------------------------------------
+ /** Merges two or more meshes
+ *
+ * The meshes should have equal vertex formats. Only components
+ * that are provided by ALL meshes will be present in the output mesh.
+ * An exception is made for VColors - they are set to black. The
+ * meshes should have the same material indices, too. The output
+ * material index is always the material index of the first mesh.
+ *
+ * @param dest Destination mesh. Must be empty.
+ * @param flags Currently no parameters
+ * @param begin First mesh to be processed
+ * @param end Points to the mesh after the last mesh to be processed
+ */
+ static void MergeMeshes(aiMesh** dest,unsigned int flags,
+ std::vector<aiMesh*>::const_iterator begin,
+ std::vector<aiMesh*>::const_iterator end);
+
+
+ // -------------------------------------------------------------------
+ /** Merges two or more bones
+ *
+ * @param out Mesh to receive the output bone list
+ * @param flags Currently no parameters
+ * @param begin First mesh to be processed
+ * @param end Points to the mesh after the last mesh to be processed
+ */
+ static void MergeBones(aiMesh* out,std::vector<aiMesh*>::const_iterator it,
+ std::vector<aiMesh*>::const_iterator end);
+
+
+ // -------------------------------------------------------------------
+ /** Builds a list of uniquely named bones in a mesh list
+ *
+ * @param asBones Receives the output list
+ * @param it First mesh to be processed
+ * @param end Last mesh to be processed
+ */
+ static void BuildUniqueBoneList(std::list<BoneWithHash>& asBones,
+ std::vector<aiMesh*>::const_iterator it,
+ std::vector<aiMesh*>::const_iterator end);
+
+ // -------------------------------------------------------------------
+ /** Add a name prefix to all nodes in a scene.
+ *
+ * @param Current node. This function is called recursively.
+ * @param prefix Prefix to be added to all nodes
+ * @param len STring length
+ */
+ static void AddNodePrefixes(aiNode* node, const char* prefix,
+ unsigned int len);
+
+ // -------------------------------------------------------------------
+ /** Add an offset to all mesh indices in a node graph
+ *
+ * @param Current node. This function is called recursively.
+ * @param offset Offset to be added to all mesh indices
+ */
+ static void OffsetNodeMeshIndices (aiNode* node, unsigned int offset);
+
+ // -------------------------------------------------------------------
+ /** Attach a list of node graphs to well-defined nodes in a master
+ * graph. This is a helper for MergeScenes()
+ *
+ * @param master Master scene
+ * @param srcList List of source scenes along with their attachment
+ * points. If an attachment point is NULL (or does not exist in
+ * the master graph), a scene is attached to the root of the master
+ * graph (as an additional child node)
+ * @duplicates List of duplicates. If elem[n] == n the scene is not
+ * a duplicate. Otherwise elem[n] links scene n to its first occurence.
+ */
+ static void AttachToGraph ( aiScene* master,
+ std::vector<NodeAttachmentInfo>& srcList);
+
+ static void AttachToGraph (aiNode* attach,
+ std::vector<NodeAttachmentInfo>& srcList);
+
+
+ // -------------------------------------------------------------------
+ /** Get a deep copy of a scene
+ *
+ * @param dest Receives a pointer to the destination scene
+ * @param src Source scene - remains unmodified.
+ */
+ static void CopyScene(aiScene** dest,aiScene* source);
+
+
+ // -------------------------------------------------------------------
+ /** Get a flat copy of a scene
+ *
+ * Only the first hierarchy layer is copied. All pointer members of
+ * aiScene are shared by source and destination scene. If the
+ * pointer doesn't point to NULL when the function is called, the
+ * existing scene is cleared and refilled.
+ * @param dest Receives a pointer to the destination scene
+ * @param src Source scene - remains unmodified.
+ */
+ static void CopySceneFlat(aiScene** dest,aiScene* source);
+
+
+ // -------------------------------------------------------------------
+ /** Get a deep copy of a mesh
+ *
+ * @param dest Receives a pointer to the destination mesh
+ * @param src Source mesh - remains unmodified.
+ */
+ static void Copy (aiMesh** dest, const aiMesh* src);
+
+ // similar to Copy():
+ static void Copy (aiMaterial** dest, const aiMaterial* src);
+ static void Copy (aiTexture** dest, const aiTexture* src);
+ static void Copy (aiAnimation** dest, const aiAnimation* src);
+ static void Copy (aiCamera** dest, const aiCamera* src);
+ static void Copy (aiBone** dest, const aiBone* src);
+ static void Copy (aiLight** dest, const aiLight* src);
+ static void Copy (aiNodeAnim** dest, const aiNodeAnim* src);
+
+ // recursive, of course
+ static void Copy (aiNode** dest, const aiNode* src);
+
+
+private:
+
+ // -------------------------------------------------------------------
+ // Same as AddNodePrefixes, but with an additional check
+ static void AddNodePrefixesChecked(aiNode* node, const char* prefix,
+ unsigned int len,
+ std::vector<SceneHelper>& input,
+ unsigned int cur);
+
+ // -------------------------------------------------------------------
+ // Add node identifiers to a hashing set
+ static void AddNodeHashes(aiNode* node, std::set<unsigned int>& hashes);
+
+
+ // -------------------------------------------------------------------
+ // Search for duplicate names
+ static bool FindNameMatch(const aiString& name,
+ std::vector<SceneHelper>& input, unsigned int cur);
+};
+
+}
+
+#endif // !! AI_SCENE_COMBINER_H_INC
diff --git a/3rdparty/assimp/code/ScenePreprocessor.cpp b/3rdparty/assimp/code/ScenePreprocessor.cpp
new file mode 100644
index 000000000..682a161a6
--- /dev/null
+++ b/3rdparty/assimp/code/ScenePreprocessor.cpp
@@ -0,0 +1,281 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#include "ScenePreprocessor.h"
+
+using namespace Assimp;
+
+// ---------------------------------------------------------------------------------------------
+void ScenePreprocessor::ProcessScene ()
+{
+ ai_assert(scene != NULL);
+
+ // Process all meshes
+ for (unsigned int i = 0; i < scene->mNumMeshes;++i)
+ ProcessMesh(scene->mMeshes[i]);
+
+ // - nothing to do for nodes for the moment
+ // - nothing to do for textures for the moment
+ // - nothing to do for lights for the moment
+ // - nothing to do for cameras for the moment
+
+ // Process all animations
+ for (unsigned int i = 0; i < scene->mNumAnimations;++i)
+ ProcessAnimation(scene->mAnimations[i]);
+
+ // Generate a default material if none was specified
+ if (!scene->mNumMaterials && scene->mNumMeshes) {
+ scene->mMaterials = new aiMaterial*[2];
+ MaterialHelper* helper;
+
+ aiString name;
+
+ // Check whether there are meshes with at least one set of uv coordinates ... add a dummy texture for them
+ // meshes without texture coordinates receive a boring gray default material.
+ unsigned int mat0 = 0xffffffff, mat1 = 0xffffffff;
+ for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
+ if (scene->mMeshes[i]->mTextureCoords[0]) {
+
+ if (mat0 == 0xffffffff) {
+
+ scene->mMaterials[scene->mNumMaterials] = helper = new MaterialHelper();
+ name.Set("$texture.png");
+ helper->AddProperty(&name,AI_MATKEY_TEXTURE_DIFFUSE(0));
+
+ name.Set(AI_DEFAULT_TEXTURED_MATERIAL_NAME);
+ helper->AddProperty(&name,AI_MATKEY_NAME);
+
+ mat0 = scene->mNumMaterials++;
+ DefaultLogger::get()->debug("ScenePreprocessor: Adding textured material \'" AI_DEFAULT_TEXTURED_MATERIAL_NAME "\'");
+ }
+ scene->mMeshes[i]->mMaterialIndex = mat0;
+ }
+ else
+ {
+ if (mat1 == 0xffffffff) {
+
+ scene->mMaterials[scene->mNumMaterials] = helper = new MaterialHelper();
+ aiColor3D clr(0.6f,0.6f,0.6f);
+ helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
+
+ // setup the default name
+ name.Set(AI_DEFAULT_MATERIAL_NAME);
+ helper->AddProperty(&name,AI_MATKEY_NAME);
+
+ mat1 = scene->mNumMaterials++;
+ DefaultLogger::get()->debug("ScenePreprocessor: Adding grey material \'" AI_DEFAULT_MATERIAL_NAME "\'");
+ }
+ scene->mMeshes[i]->mMaterialIndex = mat1;
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------------------------
+void ScenePreprocessor::ProcessMesh (aiMesh* mesh)
+{
+ // If aiMesh::mNumUVComponents is *not* set assign the default value of 2
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ if (!mesh->mTextureCoords[i])
+ mesh->mNumUVComponents[i] = 0;
+
+ else {
+ if ( !mesh->mNumUVComponents[i])
+ mesh->mNumUVComponents[i] = 2;
+
+ aiVector3D* p = mesh->mTextureCoords[i], *end = p+mesh->mNumVertices;
+
+ // Ensure unsued components are zeroed. This will make 1D texture channels work
+ // as if they were 2D channels .. just in case an application doesn't handle
+ // this case
+ if (2 == mesh->mNumUVComponents[i]) {
+ for (; p != end; ++p)
+ p->z = 0.f;
+ }
+ else if (1 == mesh->mNumUVComponents[i]) {
+ for (; p != end; ++p)
+ p->z = p->y = 0.f;
+ }
+ else if (3 == mesh->mNumUVComponents[i]) {
+
+ // Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element
+ for (; p != end; ++p) {
+ if (p->z != 0)
+ break;
+ }
+ if (p == end) {
+ DefaultLogger::get()->warn("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D.");
+ mesh->mNumUVComponents[i] = 2;
+ }
+ }
+ }
+ }
+
+ // If the information which primitive types are there in the
+ // mesh is currently not available, compute it.
+ if (!mesh->mPrimitiveTypes) {
+ for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
+ aiFace& face = mesh->mFaces[a];
+ switch (face.mNumIndices)
+ {
+ case 3u:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ break;
+
+ case 2u:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ break;
+
+ case 1u:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ break;
+
+ default:
+ mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ break;
+ }
+ }
+ }
+
+ // If tangents and normals are given but no bitangents compute them
+ if (mesh->mTangents && mesh->mNormals && !mesh->mBitangents) {
+
+ mesh->mBitangents = new aiVector3D[mesh->mNumVertices];
+ for (unsigned int i = 0; i < mesh->mNumVertices;++i) {
+ mesh->mBitangents[i] = mesh->mNormals[i] ^ mesh->mTangents[i];
+ }
+ }
+}
+
+// ---------------------------------------------------------------------------------------------
+void ScenePreprocessor::ProcessAnimation (aiAnimation* anim)
+{
+ double first = 10e10, last = -10e10;
+ for (unsigned int i = 0; i < anim->mNumChannels;++i) {
+ aiNodeAnim* channel = anim->mChannels[i];
+
+ /* If the exact duration of the animation is not given
+ * compute it now.
+ */
+ if (anim->mDuration == -1.) {
+
+ // Position keys
+ for (unsigned int i = 0; i < channel->mNumPositionKeys;++i) {
+ aiVectorKey& key = channel->mPositionKeys[i];
+ first = std::min (first, key.mTime);
+ last = std::max (last, key.mTime);
+ }
+
+ // Scaling keys
+ for (unsigned int i = 0; i < channel->mNumScalingKeys;++i) {
+ aiVectorKey& key = channel->mScalingKeys[i];
+ first = std::min (first, key.mTime);
+ last = std::max (last, key.mTime);
+ }
+
+ // Rotation keys
+ for (unsigned int i = 0; i < channel->mNumRotationKeys;++i) {
+ aiQuatKey& key = channel->mRotationKeys[i];
+ first = std::min (first, key.mTime);
+ last = std::max (last, key.mTime);
+ }
+ }
+
+ /* Check whether the animation channel has no rotation
+ * or position tracks. In this case we generate a dummy
+ * track from the information we have in the transformation
+ * matrix of the corresponding node.
+ */
+ if (!channel->mNumRotationKeys || !channel->mNumPositionKeys || !channel->mNumScalingKeys) {
+ // Find the node that belongs to this animation
+ aiNode* node = scene->mRootNode->FindNode(channel->mNodeName);
+ if (node) // ValidateDS will complain later if 'node' is NULL
+ {
+ // Decompose the transformation matrix of the node
+ aiVector3D scaling, position;
+ aiQuaternion rotation;
+
+ node->mTransformation.Decompose(scaling, rotation,position);
+
+ // No rotation keys? Generate a dummy track
+ if (!channel->mNumRotationKeys) {
+ channel->mNumRotationKeys = 1;
+ channel->mRotationKeys = new aiQuatKey[1];
+ aiQuatKey& q = channel->mRotationKeys[0];
+
+ q.mTime = 0.;
+ q.mValue = rotation;
+
+ DefaultLogger::get()->debug("ScenePreprocessor: Dummy rotation track has been generated");
+ }
+
+ // No scaling keys? Generate a dummy track
+ if (!channel->mNumScalingKeys) {
+ channel->mNumScalingKeys = 1;
+ channel->mScalingKeys = new aiVectorKey[1];
+ aiVectorKey& q = channel->mScalingKeys[0];
+
+ q.mTime = 0.;
+ q.mValue = scaling;
+
+ DefaultLogger::get()->debug("ScenePreprocessor: Dummy scaling track has been generated");
+ }
+
+ // No position keys? Generate a dummy track
+ if (!channel->mNumPositionKeys) {
+ channel->mNumPositionKeys = 1;
+ channel->mPositionKeys = new aiVectorKey[1];
+ aiVectorKey& q = channel->mPositionKeys[0];
+
+ q.mTime = 0.;
+ q.mValue = position;
+
+ DefaultLogger::get()->debug("ScenePreprocessor: Dummy position track has been generated");
+ }
+ }
+ }
+ }
+
+ if (anim->mDuration == -1.) {
+ DefaultLogger::get()->debug("ScenePreprocessor: Setting animation duration");
+ anim->mDuration = last - std::min( first, 0. );
+ }
+}
diff --git a/3rdparty/assimp/code/ScenePreprocessor.h b/3rdparty/assimp/code/ScenePreprocessor.h
new file mode 100644
index 000000000..01bbf7dce
--- /dev/null
+++ b/3rdparty/assimp/code/ScenePreprocessor.h
@@ -0,0 +1,116 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to search all meshes for
+ degenerated faces */
+#ifndef AI_SCENE_PREPROCESSOR_H_INC
+#define AI_SCENE_PREPROCESSOR_H_INC
+
+class ScenePreprocessorTest;
+namespace Assimp {
+
+// ----------------------------------------------------------------------------------
+/** ScenePreprocessor: Preprocess a scene before any post-processing
+ * steps are executed.
+ *
+ * The step computes data that needn't necessarily be provided by the
+ * importer, such as aiMesh::mPrimitiveTypes.
+*/
+// ----------------------------------------------------------------------------------
+class ASSIMP_API ScenePreprocessor
+{
+ // Make ourselves a friend of the corresponding test unit.
+ friend class ::ScenePreprocessorTest;
+public:
+
+ // ----------------------------------------------------------------
+ /** Default c'tpr. Use SetScene() to assign a scene to the object.
+ */
+ ScenePreprocessor()
+ : scene (NULL)
+ {}
+
+ /** Constructs the object and assigns a specific scene to it
+ */
+ ScenePreprocessor(aiScene* _scene)
+ : scene (_scene)
+ {}
+
+ // ----------------------------------------------------------------
+ /** Assign a (new) scene to the object.
+ *
+ * One 'SceneProcessor' can be used for multiple scenes.
+ * Call ProcessScene to have the scene preprocessed.
+ * @param sc Scene to be processed.
+ */
+ void SetScene (aiScene* sc) {
+ scene = sc;
+ }
+
+ // ----------------------------------------------------------------
+ /** Preprocess the current scene
+ */
+ void ProcessScene ();
+
+protected:
+
+ // ----------------------------------------------------------------
+ /** Preprocess an animation in the scene
+ * @param anim Anim to be preprocessed.
+ */
+ void ProcessAnimation (aiAnimation* anim);
+
+
+ // ----------------------------------------------------------------
+ /** Preprocess a mesh in the scene
+ * @param mesh Mesh to be preprocessed.
+ */
+ void ProcessMesh (aiMesh* mesh);
+
+protected:
+
+ //! Scene we're currently working on
+ aiScene* scene;
+};
+
+
+} // ! end namespace Assimp
+
+#endif // include guard
diff --git a/3rdparty/assimp/code/SkeletonMeshBuilder.cpp b/3rdparty/assimp/code/SkeletonMeshBuilder.cpp
new file mode 100644
index 000000000..9e42270bc
--- /dev/null
+++ b/3rdparty/assimp/code/SkeletonMeshBuilder.cpp
@@ -0,0 +1,267 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 SkeletonMeshBuilder.cpp
+ * @brief Implementation of a little class to construct a dummy mesh for a skeleton
+ */
+
+#include "AssimpPCH.h"
+#include "../include/aiScene.h"
+#include "SkeletonMeshBuilder.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// The constructor processes the given scene and adds a mesh there.
+SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly)
+{
+ // nothing to do if there's mesh data already present at the scene
+ if ( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL)
+ return;
+
+ if (!root)
+ root = pScene->mRootNode;
+
+ mKnobsOnly = bKnobsOnly;
+
+ // build some faces around each node
+ CreateGeometry( root );
+
+ // create a mesh to hold all the generated faces
+ pScene->mNumMeshes = 1;
+ pScene->mMeshes = new aiMesh*[1];
+ pScene->mMeshes[0] = CreateMesh();
+ // and install it at the root node
+ root->mNumMeshes = 1;
+ root->mMeshes = new unsigned int[1];
+ root->mMeshes[0] = 0;
+
+ // create a dummy material for the mesh
+ pScene->mNumMaterials = 1;
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = CreateMaterial();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively builds a simple mesh representation for the given node
+void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode)
+{
+ // add a joint entry for the node.
+ const unsigned int vertexStartIndex = mVertices.size();
+
+ // now build the geometry.
+ if ( pNode->mNumChildren > 0 && !mKnobsOnly)
+ {
+ // If the node has children, we build little pointers to each of them
+ for ( unsigned int a = 0; a < pNode->mNumChildren; a++)
+ {
+ // find a suitable coordinate system
+ const aiMatrix4x4& childTransform = pNode->mChildren[a]->mTransformation;
+ aiVector3D childpos( childTransform.a4, childTransform.b4, childTransform.c4);
+ float distanceToChild = childpos.Length();
+ if ( distanceToChild < 0.0001f)
+ continue;
+ aiVector3D up = aiVector3D( childpos).Normalize();
+
+ aiVector3D orth( 1.0f, 0.0f, 0.0f);
+ if ( fabs( orth * up) > 0.99f)
+ orth.Set( 0.0f, 1.0f, 0.0f);
+
+ aiVector3D front = (up ^ orth).Normalize();
+ aiVector3D side = (front ^ up).Normalize();
+
+ unsigned int localVertexStart = mVertices.size();
+ mVertices.push_back( -front * distanceToChild * 0.1f);
+ mVertices.push_back( childpos);
+ mVertices.push_back( -side * distanceToChild * 0.1f);
+ mVertices.push_back( -side * distanceToChild * 0.1f);
+ mVertices.push_back( childpos);
+ mVertices.push_back( front * distanceToChild * 0.1f);
+ mVertices.push_back( front * distanceToChild * 0.1f);
+ mVertices.push_back( childpos);
+ mVertices.push_back( side * distanceToChild * 0.1f);
+ mVertices.push_back( side * distanceToChild * 0.1f);
+ mVertices.push_back( childpos);
+ mVertices.push_back( -front * distanceToChild * 0.1f);
+
+ mFaces.push_back( Face( localVertexStart + 0, localVertexStart + 1, localVertexStart + 2));
+ mFaces.push_back( Face( localVertexStart + 3, localVertexStart + 4, localVertexStart + 5));
+ mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8));
+ mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11));
+ }
+ }
+ else
+ {
+ // if the node has no children, it's an end node. Put a little knob there instead
+ aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4);
+ float sizeEstimate = ownpos.Length() * 0.18f;
+
+ mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
+ mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
+ mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
+ mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, -sizeEstimate));
+
+ mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
+ mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
+ mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( sizeEstimate, 0.0f, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
+ mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, -sizeEstimate, 0.0f));
+ mVertices.push_back( aiVector3D( 0.0f, 0.0f, sizeEstimate));
+ mVertices.push_back( aiVector3D( -sizeEstimate, 0.0f, 0.0f));
+
+ mFaces.push_back( Face( vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2));
+ mFaces.push_back( Face( vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5));
+ mFaces.push_back( Face( vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8));
+ mFaces.push_back( Face( vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11));
+ mFaces.push_back( Face( vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14));
+ mFaces.push_back( Face( vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17));
+ mFaces.push_back( Face( vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20));
+ mFaces.push_back( Face( vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23));
+ }
+
+ unsigned int numVertices = mVertices.size() - vertexStartIndex;
+ if ( numVertices > 0)
+ {
+ // create a bone affecting all the newly created vertices
+ aiBone* bone = new aiBone;
+ mBones.push_back( bone);
+ bone->mName = pNode->mName;
+
+ // calculate the bone offset matrix by concatenating the inverse transformations of all parents
+ bone->mOffsetMatrix = aiMatrix4x4( pNode->mTransformation).Inverse();
+ for ( aiNode* parent = pNode->mParent; parent != NULL; parent = parent->mParent)
+ bone->mOffsetMatrix = aiMatrix4x4( parent->mTransformation).Inverse() * bone->mOffsetMatrix;
+
+ // add all the vertices to the bone's influences
+ bone->mNumWeights = numVertices;
+ bone->mWeights = new aiVertexWeight[numVertices];
+ for ( unsigned int a = 0; a < numVertices; a++)
+ bone->mWeights[a] = aiVertexWeight( vertexStartIndex + a, 1.0f);
+
+ // HACK: (thom) transform all vertices to the bone's local space. Should be done before adding
+ // them to the array, but I'm tired now and I'm annoyed.
+ aiMatrix4x4 boneToMeshTransform = aiMatrix4x4( bone->mOffsetMatrix).Inverse();
+ for ( unsigned int a = vertexStartIndex; a < mVertices.size(); a++)
+ mVertices[a] = boneToMeshTransform * mVertices[a];
+ }
+
+ // and finally recurse into the children list
+ for ( unsigned int a = 0; a < pNode->mNumChildren; a++)
+ CreateGeometry( pNode->mChildren[a]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the mesh from the internally accumulated stuff and returns it.
+aiMesh* SkeletonMeshBuilder::CreateMesh()
+{
+ aiMesh* mesh = new aiMesh();
+
+ // add points
+ mesh->mNumVertices = mVertices.size();
+ mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+ std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices);
+
+ mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+
+ // add faces
+ mesh->mNumFaces = mFaces.size();
+ mesh->mFaces = new aiFace[mesh->mNumFaces];
+ for ( unsigned int a = 0; a < mesh->mNumFaces; a++)
+ {
+ const Face& inface = mFaces[a];
+ aiFace& outface = mesh->mFaces[a];
+ outface.mNumIndices = 3;
+ outface.mIndices = new unsigned int[3];
+ outface.mIndices[0] = inface.mIndices[0];
+ outface.mIndices[1] = inface.mIndices[1];
+ outface.mIndices[2] = inface.mIndices[2];
+
+ // Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize
+ // the skeleton, so it's good if there's a visual difference to the rest of the geometry
+ aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^
+ (mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]]));
+
+ if (nor.Length() < 1e-5f) /* ensure that FindInvalidData won't remove us ...*/
+ nor = aiVector3D(1.f,0.f,0.f);
+
+ for (unsigned int n = 0; n < 3; ++n)
+ mesh->mNormals[inface.mIndices[n]] = nor;
+ }
+
+ // add the bones
+ mesh->mNumBones = mBones.size();
+ mesh->mBones = new aiBone*[mesh->mNumBones];
+ std::copy( mBones.begin(), mBones.end(), mesh->mBones);
+
+ // default
+ mesh->mMaterialIndex = 0;
+
+ return mesh;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates a dummy material and returns it.
+aiMaterial* SkeletonMeshBuilder::CreateMaterial()
+{
+ Assimp::MaterialHelper* matHelper = new Assimp::MaterialHelper;
+
+ // Name
+ aiString matName( std::string( "SkeletonMaterial"));
+ matHelper->AddProperty( &matName, AI_MATKEY_NAME);
+
+ // Prevent backface culling
+ const int no_cull = 1;
+ matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED);
+
+ return matHelper;
+}
diff --git a/3rdparty/assimp/code/SkeletonMeshBuilder.h b/3rdparty/assimp/code/SkeletonMeshBuilder.h
new file mode 100644
index 000000000..3989e7cff
--- /dev/null
+++ b/3rdparty/assimp/code/SkeletonMeshBuilder.h
@@ -0,0 +1,122 @@
+/** Helper class to construct a dummy mesh for file formats containing only motion data */
+
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 SkeletonMeshBuilder.h
+ * Declares SkeletonMeshBuilder, a tiny utility to build dummy meshes
+ * for animation skeletons.
+ */
+
+#ifndef AI_SKELETONMESHBUILDER_H_INC
+#define AI_SKELETONMESHBUILDER_H_INC
+
+#include <vector>
+#include "../include/aiMesh.h"
+
+struct aiScene;
+struct aiNode;
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/**
+ * This little helper class constructs a dummy mesh for a given scene
+ * the resembles the node hierarchy. This is useful for file formats
+ * that don't carry any mesh data but only animation data.
+ */
+class ASSIMP_API SkeletonMeshBuilder
+{
+public:
+
+ // -------------------------------------------------------------------
+ /** The constructor processes the given scene and adds a mesh there.
+ *
+ * Does nothing if the scene already has mesh data.
+ * @param pScene The scene for which a skeleton mesh should be constructed.
+ * @param root The node to start with. NULL is the scene root
+ * @param bKnobsOnly Set this to true if you don't want the connectors
+ * between the knobs representing the nodes.
+ */
+ SkeletonMeshBuilder( aiScene* pScene, aiNode* root = NULL,
+ bool bKnobsOnly = false);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Recursively builds a simple mesh representation for the given node
+ * and also creates a joint for the node that affects this part of
+ * the mesh.
+ * @param pNode The node to build geometry for.
+ */
+ void CreateGeometry( const aiNode* pNode);
+
+ // -------------------------------------------------------------------
+ /** Creates the mesh from the internally accumulated stuff and returns it.
+ */
+ aiMesh* CreateMesh();
+
+ // -------------------------------------------------------------------
+ /** Creates a dummy material and returns it. */
+ aiMaterial* CreateMaterial();
+
+protected:
+ /** space to assemble the mesh data: points */
+ std::vector<aiVector3D> mVertices;
+
+ /** faces */
+ struct Face
+ {
+ unsigned int mIndices[3];
+ Face();
+ Face( unsigned int p0, unsigned int p1, unsigned int p2)
+ { mIndices[0] = p0; mIndices[1] = p1; mIndices[2] = p2; }
+ };
+ std::vector<Face> mFaces;
+
+ /** bones */
+ std::vector<aiBone*> mBones;
+
+ bool mKnobsOnly;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_SKELETONMESHBUILDER_H_INC
diff --git a/3rdparty/assimp/code/SmoothingGroups.h b/3rdparty/assimp/code/SmoothingGroups.h
new file mode 100644
index 000000000..50535820c
--- /dev/null
+++ b/3rdparty/assimp/code/SmoothingGroups.h
@@ -0,0 +1,103 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines the helper data structures for importing 3DS files.
+http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */
+
+#ifndef AI_SMOOTHINGGROUPS_H_INC
+#define AI_SMOOTHINGGROUPS_H_INC
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a face with smoothing groups assigned */
+struct FaceWithSmoothingGroup
+{
+ FaceWithSmoothingGroup() : iSmoothGroup(0)
+ {
+ // let the rest uninitialized for performance - in release builds.
+ // in debug builds set all indices to a common magic value
+#ifdef _DEBUG
+ this->mIndices[0] = 0xffffffff;
+ this->mIndices[1] = 0xffffffff;
+ this->mIndices[2] = 0xffffffff;
+#endif
+ }
+
+
+ //! Indices. .3ds is using uint16. However, after
+ //! an unique vrtex set has been generated it might
+ //! be an index becomes > 2^16
+ uint32_t mIndices[3];
+
+ //! specifies to which smoothing group the face belongs to
+ uint32_t iSmoothGroup;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper structure representing a mesh whose faces have smoothing
+ groups assigned. This allows us to reuse the code for normal computations
+ from smoothings groups for several loaders (3DS, ASE). All of them
+ use face structures which inherit from #FaceWithSmoothingGroup,
+ but as they add extra members and need to be copied by value we
+ need to use a template here.
+ */
+template <class T>
+struct MeshWithSmoothingGroups
+{
+ //! Vertex positions
+ std::vector<aiVector3D> mPositions;
+
+ //! Face lists
+ std::vector<T> mFaces;
+
+ //! List of normal vectors
+ std::vector<aiVector3D> mNormals;
+};
+
+// ---------------------------------------------------------------------------
+/** Computes normal vectors for the mesh
+ */
+template <class T>
+void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh);
+
+
+// include implementations
+#include "SmoothingGroups.inl"
+
+#endif // !! AI_SMOOTHINGGROUPS_H_INC
diff --git a/3rdparty/assimp/code/SmoothingGroups.inl b/3rdparty/assimp/code/SmoothingGroups.inl
new file mode 100644
index 000000000..10eb7d25f
--- /dev/null
+++ b/3rdparty/assimp/code/SmoothingGroups.inl
@@ -0,0 +1,138 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Generation of normal vectors basing on smoothing groups */
+
+#ifndef AI_SMOOTHINGGROUPS_INL_INCLUDED
+#define AI_SMOOTHINGGROUPS_INL_INCLUDED
+
+// internal headers
+#include "SGSpatialSort.h"
+
+// CRT header
+#include <algorithm>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups<T>& sMesh)
+{
+ // First generate face normals
+ sMesh.mNormals.resize(sMesh.mPositions.size(),aiVector3D());
+ for ( unsigned int a = 0; a < sMesh.mFaces.size(); a++)
+ {
+ T& face = sMesh.mFaces[a];
+
+ aiVector3D* pV1 = &sMesh.mPositions[face.mIndices[0]];
+ aiVector3D* pV2 = &sMesh.mPositions[face.mIndices[1]];
+ aiVector3D* pV3 = &sMesh.mPositions[face.mIndices[2]];
+
+ aiVector3D pDelta1 = *pV2 - *pV1;
+ aiVector3D pDelta2 = *pV3 - *pV1;
+ aiVector3D vNor = pDelta1 ^ pDelta2;
+
+ for (unsigned int c = 0; c < 3;++c)
+ sMesh.mNormals[face.mIndices[c]] = vNor;
+ }
+
+ // calculate the position bounds so we have a reliable epsilon to check position differences against
+ aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f);
+ for ( unsigned int a = 0; a < sMesh.mPositions.size(); a++)
+ {
+ minVec.x = std::min( minVec.x, sMesh.mPositions[a].x);
+ minVec.y = std::min( minVec.y, sMesh.mPositions[a].y);
+ minVec.z = std::min( minVec.z, sMesh.mPositions[a].z);
+ maxVec.x = std::max( maxVec.x, sMesh.mPositions[a].x);
+ maxVec.y = std::max( maxVec.y, sMesh.mPositions[a].y);
+ maxVec.z = std::max( maxVec.z, sMesh.mPositions[a].z);
+ }
+ const float posEpsilon = (maxVec - minVec).Length() * 1e-5f;
+ std::vector<aiVector3D> avNormals;
+ avNormals.resize(sMesh.mNormals.size());
+
+ // now generate the spatial sort tree
+ SGSpatialSort sSort;
+ for ( typename std::vector<T>::iterator i = sMesh.mFaces.begin();
+ i != sMesh.mFaces.end();++i)
+ {
+ for (unsigned int c = 0; c < 3;++c)
+ sSort.Add(sMesh.mPositions[(*i).mIndices[c]],(*i).mIndices[c],(*i).iSmoothGroup);
+ }
+ sSort.Prepare();
+
+ std::vector<bool> vertexDone(sMesh.mPositions.size(),false);
+ for ( typename std::vector<T>::iterator i = sMesh.mFaces.begin();
+ i != sMesh.mFaces.end();++i)
+ {
+ std::vector<unsigned int> poResult;
+ for (unsigned int c = 0; c < 3;++c)
+ {
+ register unsigned int idx = (*i).mIndices[c];
+ if (vertexDone[idx])continue;
+
+ sSort.FindPositions(sMesh.mPositions[idx],(*i).iSmoothGroup,
+ posEpsilon,poResult);
+
+ aiVector3D vNormals;
+ for (std::vector<unsigned int>::const_iterator
+ a = poResult.begin();
+ a != poResult.end();++a)
+ {
+ vNormals += sMesh.mNormals[(*a)];
+ }
+ vNormals.Normalize();
+
+ // write back into all affected normals
+ for (std::vector<unsigned int>::const_iterator
+ a = poResult.begin();
+ a != poResult.end();++a)
+ {
+ idx = *a;
+ avNormals [idx] = vNormals;
+ vertexDone[idx] = true;
+ }
+ }
+ }
+ sMesh.mNormals = avNormals;
+}
+
+#endif // !! AI_SMOOTHINGGROUPS_INL_INCLUDED
diff --git a/3rdparty/assimp/code/SortByPTypeProcess.cpp b/3rdparty/assimp/code/SortByPTypeProcess.cpp
new file mode 100644
index 000000000..18cd1f874
--- /dev/null
+++ b/3rdparty/assimp/code/SortByPTypeProcess.cpp
@@ -0,0 +1,405 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the DeterminePTypeHelperProcess and
+ * SortByPTypeProcess post-process steps.
+*/
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "ProcessHelper.h"
+#include "SortByPTypeProcess.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+SortByPTypeProcess::SortByPTypeProcess()
+{
+ configRemoveMeshes = 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+SortByPTypeProcess::~SortByPTypeProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool SortByPTypeProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_SortByPType) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+void SortByPTypeProcess::SetupProperties(const Importer* pImp)
+{
+ configRemoveMeshes = pImp->GetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE,0);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Update changed meshes in all nodes
+void UpdateNodes(const std::vector<unsigned int>& replaceMeshIndex, aiNode* node)
+{
+// std::vector<unsigned int>::const_iterator it;
+
+ if (node->mNumMeshes)
+ {
+ unsigned int newSize = 0;
+ for (unsigned int m = 0; m< node->mNumMeshes; ++m)
+ {
+ unsigned int add = node->mMeshes[m]<<2;
+ for (unsigned int i = 0; i < 4;++i)
+ {
+ if (0xffffffff != replaceMeshIndex[add+i])++newSize;
+ }
+ }
+ if (!newSize)
+ {
+ delete[] node->mMeshes;
+ node->mNumMeshes = 0;
+ node->mMeshes = NULL;
+ }
+ else
+ {
+ // Try to reuse the old array if possible
+ unsigned int* newMeshes = (newSize > node->mNumMeshes
+ ? new unsigned int[newSize] : node->mMeshes);
+
+ for (unsigned int m = 0; m< node->mNumMeshes; ++m)
+ {
+ unsigned int add = node->mMeshes[m]<<2;
+ for (unsigned int i = 0; i < 4;++i)
+ {
+ if (0xffffffff != replaceMeshIndex[add+i])
+ *newMeshes++ = replaceMeshIndex[add+i];
+ }
+ }
+ if (newSize > node->mNumMeshes)
+ delete[] node->mMeshes;
+
+ node->mMeshes = newMeshes-(node->mNumMeshes = newSize);
+ }
+ }
+
+ // call all subnodes recursively
+ for (unsigned int m = 0; m < node->mNumChildren; ++m)
+ UpdateNodes(replaceMeshIndex,node->mChildren[m]);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SortByPTypeProcess::Execute( aiScene* pScene)
+{
+ if (!pScene->mNumMeshes)
+ {
+ DefaultLogger::get()->debug("SortByPTypeProcess skipped, there are no meshes");
+ return;
+ }
+
+ DefaultLogger::get()->debug("SortByPTypeProcess begin");
+
+ unsigned int aiNumMeshesPerPType[4] = {0,0,0,0};
+
+ std::vector<aiMesh*> outMeshes;
+ outMeshes.reserve(pScene->mNumMeshes<<1u);
+
+ bool bAnyChanges = false;
+
+ std::vector<unsigned int> replaceMeshIndex(pScene->mNumMeshes*4,0xffffffff);
+ std::vector<unsigned int>::iterator meshIdx = replaceMeshIndex.begin();
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
+ {
+ aiMesh* mesh = pScene->mMeshes[i];
+ ai_assert(0 != mesh->mPrimitiveTypes);
+
+ // if there's just one primitive type in the mesh there's nothing to do for us
+ unsigned int num = 0;
+ if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT)
+ {
+ ++aiNumMeshesPerPType[0];
+ ++num;
+ }
+ if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE)
+ {
+ ++aiNumMeshesPerPType[1];
+ ++num;
+ }
+ if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE)
+ {
+ ++aiNumMeshesPerPType[2];
+ ++num;
+ }
+ if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)
+ {
+ ++aiNumMeshesPerPType[3];
+ ++num;
+ }
+
+ if (1 == num)
+ {
+ if (!(configRemoveMeshes & mesh->mPrimitiveTypes))
+ {
+ *meshIdx = (unsigned int) outMeshes.size();
+ outMeshes.push_back(mesh);
+ }
+ else bAnyChanges = true;
+
+ meshIdx += 4;
+ continue;
+ }
+ bAnyChanges = true;
+
+ // reuse our current mesh arrays for the submesh
+ // with the largest numer of primitives
+ unsigned int aiNumPerPType[4] = {0,0,0,0};
+ aiFace* pFirstFace = mesh->mFaces;
+ aiFace* const pLastFace = pFirstFace + mesh->mNumFaces;
+
+ unsigned int numPolyVerts = 0;
+ for (;pFirstFace != pLastFace; ++pFirstFace)
+ {
+ if (pFirstFace->mNumIndices <= 3)
+ ++aiNumPerPType[pFirstFace->mNumIndices-1];
+ else
+ {
+ ++aiNumPerPType[3];
+ numPolyVerts += pFirstFace-> mNumIndices;
+ }
+ }
+
+ VertexWeightTable* avw = ComputeVertexBoneWeightTable(mesh);
+ for (unsigned int real = 0; real < 4; ++real,++meshIdx)
+ {
+ if ( !aiNumPerPType[real] || configRemoveMeshes & (1u << real))
+ {
+ continue;
+ }
+
+ *meshIdx = (unsigned int) outMeshes.size();
+ outMeshes.push_back(new aiMesh());
+ aiMesh* out = outMeshes.back();
+
+ // the name carries the adjacency information between the meshes
+ out->mName = mesh->mName;
+
+ // copy data members
+ out->mPrimitiveTypes = 1u << real;
+ out->mMaterialIndex = mesh->mMaterialIndex;
+
+ // allocate output storage
+ out->mNumFaces = aiNumPerPType[real];
+ aiFace* outFaces = out->mFaces = new aiFace[out->mNumFaces];
+
+ out->mNumVertices = (3 == real ? numPolyVerts : out->mNumFaces * (real+1));
+
+ aiVector3D *vert(NULL), *nor(NULL), *tan(NULL), *bit(NULL);
+ aiVector3D *uv [AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ aiColor4D *cols [AI_MAX_NUMBER_OF_COLOR_SETS];
+
+ if (mesh->mVertices)
+ vert = out->mVertices = new aiVector3D[out->mNumVertices];
+
+ if (mesh->mNormals)
+ nor = out->mNormals = new aiVector3D[out->mNumVertices];
+
+ if (mesh->mTangents)
+ {
+ tan = out->mTangents = new aiVector3D[out->mNumVertices];
+ bit = out->mBitangents = new aiVector3D[out->mNumVertices];
+ }
+
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
+ {
+ if (mesh->mTextureCoords[i])
+ uv[i] = out->mTextureCoords[i] = new aiVector3D[out->mNumVertices];
+ else uv[i] = NULL;
+
+ out->mNumUVComponents[i] = mesh->mNumUVComponents[i];
+ }
+
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS;++i)
+ {
+ if (mesh->mColors[i])
+ cols[i] = out->mColors[i] = new aiColor4D[out->mNumVertices];
+ else cols[i] = NULL;
+ }
+
+ typedef std::vector< aiVertexWeight > TempBoneInfo;
+ std::vector< TempBoneInfo > tempBones(mesh->mNumBones);
+
+ // try to guess how much storage we'll need
+ for (unsigned int q = 0; q < mesh->mNumBones;++q)
+ {
+ tempBones[q].reserve(mesh->mBones[q]->mNumWeights / (num-1));
+ }
+
+ unsigned int outIdx = 0;
+ for (unsigned int m = 0; m < mesh->mNumFaces; ++m)
+ {
+ aiFace& in = mesh->mFaces[m];
+ if ((real == 3 && in.mNumIndices <= 3) || (real != 3 && in.mNumIndices != real+1))
+ {
+ continue;
+ }
+
+ outFaces->mNumIndices = in.mNumIndices;
+ outFaces->mIndices = in.mIndices;
+
+ for (unsigned int q = 0; q < in.mNumIndices; ++q)
+ {
+ register unsigned int idx = in.mIndices[q];
+
+ // process all bones of this index
+ if (avw)
+ {
+ VertexWeightTable& tbl = avw[idx];
+ for (VertexWeightTable::const_iterator it = tbl.begin(), end = tbl.end();
+ it != end; ++it)
+ {
+ tempBones[ (*it).first ].push_back( aiVertexWeight(outIdx, (*it).second) );
+ }
+ }
+
+ if (vert)
+ {
+ *vert++ = mesh->mVertices[idx];
+ //mesh->mVertices[idx].x = get_qnan();
+ }
+ if (nor )*nor++ = mesh->mNormals[idx];
+ if (tan )
+ {
+ *tan++ = mesh->mTangents[idx];
+ *bit++ = mesh->mBitangents[idx];
+ }
+
+ for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp)
+ {
+ if (!uv[pp])break;
+ *uv[pp]++ = mesh->mTextureCoords[pp][idx];
+ }
+
+ for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp)
+ {
+ if (!cols[pp])break;
+ *cols[pp]++ = mesh->mColors[pp][idx];
+ }
+
+ in.mIndices[q] = outIdx++;
+ }
+
+ in.mIndices = NULL;
+ ++outFaces;
+ }
+ ai_assert(outFaces == out->mFaces + out->mNumFaces);
+
+ // now generate output bones
+ for (unsigned int q = 0; q < mesh->mNumBones;++q)
+ if (!tempBones[q].empty())++out->mNumBones;
+
+ if (out->mNumBones)
+ {
+ out->mBones = new aiBone*[out->mNumBones];
+ for (unsigned int q = 0, real = 0; q < mesh->mNumBones;++q)
+ {
+ TempBoneInfo& in = tempBones[q];
+ if (in.empty())continue;
+
+ aiBone* srcBone = mesh->mBones[q];
+ aiBone* bone = out->mBones[real] = new aiBone();
+
+ bone->mName = srcBone->mName;
+ bone->mOffsetMatrix = srcBone->mOffsetMatrix;
+
+ bone->mNumWeights = (unsigned int)in.size();
+ bone->mWeights = new aiVertexWeight[bone->mNumWeights];
+
+ ::memcpy(bone->mWeights,&in[0],bone->mNumWeights*sizeof(aiVertexWeight));
+
+ ++real;
+ }
+ }
+ }
+
+ // delete the per-vertex bone weights table
+ delete[] avw;
+
+ // delete the input mesh
+ delete mesh;
+ }
+
+ if (outMeshes.empty())
+ {
+ // This should not occur
+ throw DeadlyImportError("No meshes remaining");
+ }
+
+ // If we added at least one mesh process all nodes in the node
+ // graph and update their respective mesh indices.
+ if (bAnyChanges)
+ {
+ UpdateNodes(replaceMeshIndex,pScene->mRootNode);
+ }
+
+ if (outMeshes.size() != pScene->mNumMeshes)
+ {
+ delete[] pScene->mMeshes;
+ pScene->mNumMeshes = (unsigned int)outMeshes.size();
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ }
+ ::memcpy(pScene->mMeshes,&outMeshes[0],pScene->mNumMeshes*sizeof(void*));
+
+ if (!DefaultLogger::isNullLogger())
+ {
+ char buffer[1024];
+ ::sprintf(buffer,"Points: %i%s, Lines: %i%s, Triangles: %i%s, Polygons: %i%s (Meshes, X = removed)",
+ aiNumMeshesPerPType[0], (configRemoveMeshes & aiPrimitiveType_POINT ? "X" : ""),
+ aiNumMeshesPerPType[1], (configRemoveMeshes & aiPrimitiveType_LINE ? "X" : ""),
+ aiNumMeshesPerPType[2], (configRemoveMeshes & aiPrimitiveType_TRIANGLE ? "X" : ""),
+ aiNumMeshesPerPType[3], (configRemoveMeshes & aiPrimitiveType_POLYGON ? "X" : ""));
+ DefaultLogger::get()->info(buffer);
+ DefaultLogger::get()->debug("SortByPTypeProcess finished");
+ }
+}
+
diff --git a/3rdparty/assimp/code/SortByPTypeProcess.h b/3rdparty/assimp/code/SortByPTypeProcess.h
new file mode 100644
index 000000000..835dedc8f
--- /dev/null
+++ b/3rdparty/assimp/code/SortByPTypeProcess.h
@@ -0,0 +1,88 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to sort meshes by the types
+ of primitives they contain */
+#ifndef AI_SORTBYPTYPEPROCESS_H_INC
+#define AI_SORTBYPTYPEPROCESS_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiMesh.h"
+
+class SortByPTypeProcessTest;
+namespace Assimp {
+
+
+// ---------------------------------------------------------------------------
+/** SortByPTypeProcess: Sorts meshes by the types of primitives they contain.
+ * A mesh with 5 lines, 3 points and 145 triangles would be split in 3
+ * submeshes.
+*/
+class ASSIMP_API SortByPTypeProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::SortByPTypeProcessTest; // grant the unit test full access to us
+
+protected:
+ /** Constructor to be privately used by Importer */
+ SortByPTypeProcess();
+
+ /** Destructor, private as well */
+ ~SortByPTypeProcess();
+
+public:
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+ int configRemoveMeshes;
+};
+
+
+} // end of namespace Assimp
+
+#endif // !!AI_SORTBYPTYPEPROCESS_H_INC
diff --git a/3rdparty/assimp/code/SpatialSort.cpp b/3rdparty/assimp/code/SpatialSort.cpp
new file mode 100644
index 000000000..c39426db7
--- /dev/null
+++ b/3rdparty/assimp/code/SpatialSort.cpp
@@ -0,0 +1,342 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the helper class to quickly find vertices close to a given position */
+
+#include "AssimpPCH.h"
+#include "SpatialSort.h"
+
+using namespace Assimp;
+
+// CHAR_BIT seems to be defined under MVSC, but not under GCC. Pray that the correct value is 8.
+#ifndef CHAR_BIT
+# define CHAR_BIT 8
+#endif
+
+// ------------------------------------------------------------------------------------------------
+// Constructs a spatially sorted representation from the given position array.
+SpatialSort::SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions,
+ unsigned int pElementOffset)
+
+ // define the reference plane. We choose some arbitrary vector away from all basic axises
+ // in the hope that no model spreads all its vertices along this plane.
+ : mPlaneNormal(0.8523f, 0.34321f, 0.5736f)
+{
+ mPlaneNormal.Normalize();
+ Fill(pPositions,pNumPositions,pElementOffset);
+}
+
+// ------------------------------------------------------------------------------------------------
+SpatialSort :: SpatialSort()
+: mPlaneNormal(0.8523f, 0.34321f, 0.5736f)
+{
+ mPlaneNormal.Normalize();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor
+SpatialSort::~SpatialSort()
+{
+ // nothing to do here, everything destructs automatically
+}
+
+// ------------------------------------------------------------------------------------------------
+void SpatialSort::Fill( const aiVector3D* pPositions, unsigned int pNumPositions,
+ unsigned int pElementOffset,
+ bool pFinalize /*= true */)
+{
+ mPositions.clear();
+ Append(pPositions,pNumPositions,pElementOffset,pFinalize);
+}
+
+// ------------------------------------------------------------------------------------------------
+void SpatialSort :: Finalize()
+{
+ std::sort( mPositions.begin(), mPositions.end());
+}
+
+// ------------------------------------------------------------------------------------------------
+void SpatialSort::Append( const aiVector3D* pPositions, unsigned int pNumPositions,
+ unsigned int pElementOffset,
+ bool pFinalize /*= true */)
+{
+ // store references to all given positions along with their distance to the reference plane
+ const size_t initial = mPositions.size();
+ mPositions.reserve(initial + (pFinalize?pNumPositions:pNumPositions*2));
+ for ( unsigned int a = 0; a < pNumPositions; a++)
+ {
+ const char* tempPointer = reinterpret_cast<const char*> (pPositions);
+ const aiVector3D* vec = reinterpret_cast<const aiVector3D*> (tempPointer + a * pElementOffset);
+
+ // store position by index and distance
+ float distance = *vec * mPlaneNormal;
+ mPositions.push_back( Entry( a+initial, *vec, distance));
+ }
+
+ if (pFinalize) {
+ // now sort the array ascending by distance.
+ Finalize();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns an iterator for all positions close to the given position.
+void SpatialSort::FindPositions( const aiVector3D& pPosition,
+ float pRadius, std::vector<unsigned int>& poResults) const
+{
+ const float dist = pPosition * mPlaneNormal;
+ const float minDist = dist - pRadius, maxDist = dist + pRadius;
+
+ // clear the array in this strange fashion because a simple clear() would also deallocate
+ // the array which we want to avoid
+ poResults.erase( poResults.begin(), poResults.end());
+
+ // quick check for positions outside the range
+ if ( mPositions.size() == 0)
+ return;
+ if ( maxDist < mPositions.front().mDistance)
+ return;
+ if ( minDist > mPositions.back().mDistance)
+ return;
+
+ // do a binary search for the minimal distance to start the iteration there
+ unsigned int index = (unsigned int)mPositions.size() / 2;
+ unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
+ while ( binaryStepSize > 1)
+ {
+ if ( mPositions[index].mDistance < minDist)
+ index += binaryStepSize;
+ else
+ index -= binaryStepSize;
+
+ binaryStepSize /= 2;
+ }
+
+ // depending on the direction of the last step we need to single step a bit back or forth
+ // to find the actual beginning element of the range
+ while ( index > 0 && mPositions[index].mDistance > minDist)
+ index--;
+ while ( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
+ index++;
+
+ // Mow start iterating from there until the first position lays outside of the distance range.
+ // Add all positions inside the distance range within the given radius to the result aray
+ std::vector<Entry>::const_iterator it = mPositions.begin() + index;
+ const float pSquared = pRadius*pRadius;
+ while ( it->mDistance < maxDist)
+ {
+ if ( (it->mPosition - pPosition).SquareLength() < pSquared)
+ poResults.push_back( it->mIndex);
+ ++it;
+ if ( it == mPositions.end())
+ break;
+ }
+
+ // that's it
+}
+
+namespace {
+
+ // Binary, signed-integer representation of a single-precision floating-point value.
+ // IEEE 754 says: "If two floating-point numbers in the same format are ordered then they are
+ // ordered the same way when their bits are reinterpreted as sign-magnitude integers."
+ // This allows us to convert all floating-point numbers to signed integers of arbitrary size
+ // and then use them to work with ULPs (Units in the Last Place, for high-precision
+ // computations) or to compare them (integer comparisons are faster than floating-point
+ // comparisons on many platforms).
+ typedef signed int BinFloat;
+
+ // --------------------------------------------------------------------------------------------
+ // Converts the bit pattern of a floating-point number to its signed integer representation.
+ BinFloat ToBinary( const float & pValue) {
+
+ // If this assertion fails, signed int is not big enough to store a float on your platform.
+ // Please correct the declaration of BinFloat a few lines above - but do it in a portable,
+ // #ifdef'd manner!
+ BOOST_STATIC_ASSERT( sizeof(BinFloat) >= sizeof(float));
+
+ #if defined( _MSC_VER)
+ // If this assertion fails, Visual C++ has finally moved to ILP64. This means that this
+ // code has just become legacy code! Find out the current value of _MSC_VER and modify
+ // the #if above so it evaluates false on the current and all upcoming VC versions (or
+ // on the current platform, if LP64 or LLP64 are still used on other platforms).
+ BOOST_STATIC_ASSERT( sizeof(BinFloat) == sizeof(float));
+
+ // This works best on Visual C++, but other compilers have their problems with it.
+ const BinFloat binValue = reinterpret_cast<BinFloat const &>(pValue);
+ #else
+ // On many compilers, reinterpreting a float address as an integer causes aliasing
+ // problems. This is an ugly but more or less safe way of doing it.
+ union {
+ float asFloat;
+ BinFloat asBin;
+ } conversion;
+ conversion.asBin = 0; // zero empty space in case sizeof(BinFloat) > sizeof(float)
+ conversion.asFloat = pValue;
+ const BinFloat binValue = conversion.asBin;
+ #endif
+
+ // floating-point numbers are of sign-magnitude format, so find out what signed number
+ // representation we must convert negative values to.
+ // See http://en.wikipedia.org/wiki/Signed_number_representations.
+
+ // Two's complement?
+ if ( (-42 == (~42 + 1)) && (binValue & 0x80000000))
+ return BinFloat(1 << (CHAR_BIT * sizeof(BinFloat) - 1)) - binValue;
+ // One's complement?
+ else if ( (-42 == ~42) && (binValue & 0x80000000))
+ return BinFloat(-0) - binValue;
+ // Sign-magnitude?
+ else if ( (-42 == (42 | (-0))) && (binValue & 0x80000000)) // -0 = 1000... binary
+ return binValue;
+ else
+ return binValue;
+ }
+
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+// Fills an array with indices of all positions indentical to the given position. In opposite to
+// FindPositions(), not an epsilon is used but a (very low) tolerance of four floating-point units.
+void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition,
+ std::vector<unsigned int>& poResults) const
+{
+ // Epsilons have a huge disadvantage: they are of constant precision, while floating-point
+ // values are of log2 precision. If you apply e=0.01 to 100, the epsilon is rather small, but
+ // if you apply it to 0.001, it is enormous.
+
+ // The best way to overcome this is the unit in the last place (ULP). A precision of 2 ULPs
+ // tells us that a float does not differ more than 2 bits from the "real" value. ULPs are of
+ // logarithmic precision - around 1, they are 1(2^24) and around 10000, they are 0.00125.
+
+ // For standard C math, we can assume a precision of 0.5 ULPs according to IEEE 754. The
+ // incoming vertex positions might have already been transformed, probably using rather
+ // inaccurate SSE instructions, so we assume a tolerance of 4 ULPs to safely identify
+ // identical vertex positions.
+ static const int toleranceInULPs = 4;
+ // An interesting point is that the inaccuracy grows linear with the number of operations:
+ // multiplying to numbers, each inaccurate to four ULPs, results in an inaccuracy of four ULPs
+ // plus 0.5 ULPs for the multiplication.
+ // To compute the distance to the plane, a dot product is needed - that is a multiplication and
+ // an addition on each number.
+ static const int distanceToleranceInULPs = toleranceInULPs + 1;
+ // The squared distance between two 3D vectors is computed the same way, but with an additional
+ // subtraction.
+ static const int distance3DToleranceInULPs = distanceToleranceInULPs + 1;
+
+ // Convert the plane distance to its signed integer representation so the ULPs tolerance can be
+ // applied. For some reason, VC won't optimize two calls of the bit pattern conversion.
+ const BinFloat minDistBinary = ToBinary( pPosition * mPlaneNormal) - distanceToleranceInULPs;
+ const BinFloat maxDistBinary = minDistBinary + 2 * distanceToleranceInULPs;
+
+ // clear the array in this strange fashion because a simple clear() would also deallocate
+ // the array which we want to avoid
+ poResults.erase( poResults.begin(), poResults.end());
+
+ // do a binary search for the minimal distance to start the iteration there
+ unsigned int index = (unsigned int)mPositions.size() / 2;
+ unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
+ while ( binaryStepSize > 1)
+ {
+ // Ugly, but conditional jumps are faster with integers than with floats
+ if ( minDistBinary > ToBinary(mPositions[index].mDistance))
+ index += binaryStepSize;
+ else
+ index -= binaryStepSize;
+
+ binaryStepSize /= 2;
+ }
+
+ // depending on the direction of the last step we need to single step a bit back or forth
+ // to find the actual beginning element of the range
+ while ( index > 0 && minDistBinary < ToBinary(mPositions[index].mDistance) )
+ index--;
+ while ( index < (mPositions.size() - 1) && minDistBinary > ToBinary(mPositions[index].mDistance))
+ index++;
+
+ // Now start iterating from there until the first position lays outside of the distance range.
+ // Add all positions inside the distance range within the tolerance to the result aray
+ std::vector<Entry>::const_iterator it = mPositions.begin() + index;
+ while ( ToBinary(it->mDistance) < maxDistBinary)
+ {
+ if ( distance3DToleranceInULPs >= ToBinary((it->mPosition - pPosition).SquareLength()))
+ poResults.push_back(it->mIndex);
+ ++it;
+ if ( it == mPositions.end())
+ break;
+ }
+
+ // that's it
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int SpatialSort::GenerateMappingTable(std::vector<unsigned int>& fill,float pRadius) const
+{
+ fill.resize(mPositions.size(),0xffffffff);
+ float dist, maxDist;
+
+ unsigned int t=0;
+ const float pSquared = pRadius*pRadius;
+ for (size_t i = 0; i < mPositions.size();) {
+ dist = mPositions[i].mPosition * mPlaneNormal;
+ maxDist = dist + pRadius;
+
+ fill[mPositions[i].mIndex] = t;
+ const aiVector3D& oldpos = mPositions[i].mPosition;
+ for (++i; i < fill.size() && mPositions[i].mDistance < maxDist
+ && (mPositions[i].mPosition - oldpos).SquareLength() < pSquared; ++i)
+ {
+ fill[mPositions[i].mIndex] = t;
+ }
+ ++t;
+ }
+
+#ifdef _DEBUG
+
+ // debug invariant: mPositions[i].mIndex values must range from 0 to mPositions.size()-1
+ for (size_t i = 0; i < fill.size(); ++i) {
+ ai_assert(fill[i]<mPositions.size());
+ }
+
+#endif
+ return t;
+}
+
diff --git a/3rdparty/assimp/code/SpatialSort.h b/3rdparty/assimp/code/SpatialSort.h
new file mode 100644
index 000000000..032b30d89
--- /dev/null
+++ b/3rdparty/assimp/code/SpatialSort.h
@@ -0,0 +1,170 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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.
+
+----------------------------------------------------------------------
+*/
+
+/** Small helper classes to optimise finding vertizes close to a given location */
+#ifndef AI_SPATIALSORT_H_INC
+#define AI_SPATIALSORT_H_INC
+
+#include <vector>
+#include "../include/aiTypes.h"
+
+namespace Assimp
+{
+
+// ------------------------------------------------------------------------------------------------
+/** A little helper class to quickly find all vertices in the epsilon environment of a given
+ * position. Construct an instance with an array of positions. The class stores the given positions
+ * by their indices and sorts them by their distance to an arbitrary chosen plane.
+ * You can then query the instance for all vertices close to a given position in an average O(log n)
+ * time, with O(n) worst case complexity when all vertices lay on the plane. The plane is chosen
+ * so that it avoids common planes in usual data sets. */
+// ------------------------------------------------------------------------------------------------
+class ASSIMP_API SpatialSort
+{
+public:
+
+ SpatialSort();
+
+ // ------------------------------------------------------------------------------------
+ /** Constructs a spatially sorted representation from the given position array.
+ * Supply the positions in its layout in memory, the class will only refer to them
+ * by index.
+ * @param pPositions Pointer to the first position vector of the array.
+ * @param pNumPositions Number of vectors to expect in that array.
+ * @param pElementOffset Offset in bytes from the beginning of one vector in memory
+ * to the beginning of the next vector. */
+ SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions,
+ unsigned int pElementOffset);
+
+ /** Destructor */
+ ~SpatialSort();
+
+public:
+
+ // ------------------------------------------------------------------------------------
+ /** Sets the input data for the SpatialSort. This replaces existing data, if any.
+ * The new data receives new indices in ascending order.
+ *
+ * @param pPositions Pointer to the first position vector of the array.
+ * @param pNumPositions Number of vectors to expect in that array.
+ * @param pElementOffset Offset in bytes from the beginning of one vector in memory
+ * to the beginning of the next vector.
+ * @param pFinalize Specifies whether the SpatialSort's internal representation
+ * is finalized after the new data has been added. Finalization is
+ * required in order to use #FindPosition() or #GenerateMappingTable().
+ * If you don't finalize yet, you can use #Append() to add data from
+ * other sources.*/
+ void Fill( const aiVector3D* pPositions, unsigned int pNumPositions,
+ unsigned int pElementOffset,
+ bool pFinalize = true);
+
+
+ // ------------------------------------------------------------------------------------
+ /** Same as #Fill(), except the method appends to existing data in the #SpatialSort. */
+ void Append( const aiVector3D* pPositions, unsigned int pNumPositions,
+ unsigned int pElementOffset,
+ bool pFinalize = true);
+
+
+ // ------------------------------------------------------------------------------------
+ /** Finalize the spatial hash data structure. This can be useful after
+ * multiple calls to #Append() with the pFinalize parameter set to false.
+ * This is finally required before one of #FindPositions() and #GenerateMappingTable()
+ * can be called to query the spatial sort.*/
+ void Finalize();
+
+ // ------------------------------------------------------------------------------------
+ /** Returns an iterator for all positions close to the given position.
+ * @param pPosition The position to look for vertices.
+ * @param pRadius Maximal distance from the position a vertex may have to be counted in.
+ * @param poResults The container to store the indices of the found positions.
+ * Will be emptied by the call so it may contain anything.
+ * @return An iterator to iterate over all vertices in the given area.*/
+ void FindPositions( const aiVector3D& pPosition, float pRadius,
+ std::vector<unsigned int>& poResults) const;
+
+ // ------------------------------------------------------------------------------------
+ /** Fills an array with indices of all positions indentical to the given position. In
+ * opposite to FindPositions(), not an epsilon is used but a (very low) tolerance of
+ * four floating-point units.
+ * @param pPosition The position to look for vertices.
+ * @param poResults The container to store the indices of the found positions.
+ * Will be emptied by the call so it may contain anything.*/
+ void FindIdenticalPositions( const aiVector3D& pPosition,
+ std::vector<unsigned int>& poResults) const;
+
+ // ------------------------------------------------------------------------------------
+ /** Compute a table that maps each vertex ID referring to a spatially close
+ * enough position to the same output ID. Output IDs are assigned in ascending order
+ * from 0...n.
+ * @param fill Will be filled with numPositions entries.
+ * @param pRadius Maximal distance from the position a vertex may have to
+ * be counted in.
+ * @return Number of unique vertices (n). */
+ unsigned int GenerateMappingTable(std::vector<unsigned int>& fill,
+ float pRadius) const;
+
+protected:
+ /** Normal of the sorting plane, normalized. The center is always at (0, 0, 0) */
+ aiVector3D mPlaneNormal;
+
+ /** An entry in a spatially sorted position array. Consists of a vertex index,
+ * its position and its precalculated distance from the reference plane */
+ struct Entry
+ {
+ unsigned int mIndex; ///< The vertex referred by this entry
+ aiVector3D mPosition; ///< Position
+ float mDistance; ///< Distance of this vertex to the sorting plane
+
+ Entry() { /** intentionally not initialized.*/ }
+ Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance)
+ : mIndex( pIndex), mPosition( pPosition), mDistance( pDistance)
+ { }
+
+ bool operator < (const Entry& e) const { return mDistance < e.mDistance; }
+ };
+
+ // all positions, sorted by distance to the sorting plane
+ std::vector<Entry> mPositions;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_SPATIALSORT_H_INC
diff --git a/3rdparty/assimp/code/SplitLargeMeshes.cpp b/3rdparty/assimp/code/SplitLargeMeshes.cpp
new file mode 100644
index 000000000..f729d469c
--- /dev/null
+++ b/3rdparty/assimp/code/SplitLargeMeshes.cpp
@@ -0,0 +1,677 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the SplitLargeMeshes postprocessing step
+*/
+
+#include "AssimpPCH.h"
+
+// internal headers of the post-processing framework
+#include "SplitLargeMeshes.h"
+#include "ProcessHelper.h"
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+SplitLargeMeshesProcess_Triangle::SplitLargeMeshesProcess_Triangle()
+{
+ LIMIT = AI_SLM_DEFAULT_MAX_TRIANGLES;
+}
+
+// ------------------------------------------------------------------------------------------------
+SplitLargeMeshesProcess_Triangle::~SplitLargeMeshesProcess_Triangle()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool SplitLargeMeshesProcess_Triangle::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_SplitLargeMeshes) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene)
+{
+ if (0xffffffff == this->LIMIT)return;
+
+ DefaultLogger::get()->debug("SplitLargeMeshesProcess_Triangle begin");
+ std::vector<std::pair<aiMesh*, unsigned int> > avList;
+
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ this->SplitMesh(a, pScene->mMeshes[a],avList);
+
+ if (avList.size() != pScene->mNumMeshes)
+ {
+ // it seems something has been splitted. rebuild the mesh list
+ delete[] pScene->mMeshes;
+ pScene->mNumMeshes = (unsigned int)avList.size();
+ pScene->mMeshes = new aiMesh*[avList.size()];
+
+ for (unsigned int i = 0; i < avList.size();++i)
+ pScene->mMeshes[i] = avList[i].first;
+
+ // now we need to update all nodes
+ this->UpdateNode(pScene->mRootNode,avList);
+ DefaultLogger::get()->info("SplitLargeMeshesProcess_Triangle finished. Meshes have been splitted");
+ }
+ else DefaultLogger::get()->debug("SplitLargeMeshesProcess_Triangle finished. There was nothing to do");
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties
+void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp)
+{
+ // get the current value of the split property
+ this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Update a node after some meshes have been split
+void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode,
+ const std::vector<std::pair<aiMesh*, unsigned int> >& avList)
+{
+ // for every index in out list build a new entry
+ std::vector<unsigned int> aiEntries;
+ aiEntries.reserve(pcNode->mNumMeshes + 1);
+ for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
+ {
+ for (unsigned int a = 0; a < avList.size();++a)
+ {
+ if (avList[a].second == pcNode->mMeshes[i])
+ {
+ aiEntries.push_back(a);
+ }
+ }
+ }
+
+ // now build the new list
+ delete pcNode->mMeshes;
+ pcNode->mNumMeshes = (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];
+
+ // recusively update all other nodes
+ for (unsigned int i = 0; i < pcNode->mNumChildren;++i)
+ {
+ UpdateNode ( pcNode->mChildren[i], avList );
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SplitLargeMeshesProcess_Triangle::SplitMesh(
+ unsigned int a,
+ aiMesh* pMesh,
+ std::vector<std::pair<aiMesh*, unsigned int> >& avList)
+{
+ if (pMesh->mNumFaces > SplitLargeMeshesProcess_Triangle::LIMIT)
+ {
+ DefaultLogger::get()->info("Mesh exceeds the triangle limit. It will be split ...");
+
+ // we need to split this mesh into sub meshes
+ // determine the size of a submesh
+ const unsigned int iSubMeshes = (pMesh->mNumFaces / LIMIT) + 1;
+
+ const unsigned int iOutFaceNum = pMesh->mNumFaces / iSubMeshes;
+ const unsigned int iOutVertexNum = iOutFaceNum * 3;
+
+ // now generate all submeshes
+ for (unsigned int i = 0; i < iSubMeshes;++i)
+ {
+ aiMesh* pcMesh = new aiMesh;
+ pcMesh->mNumFaces = iOutFaceNum;
+ pcMesh->mMaterialIndex = pMesh->mMaterialIndex;
+
+ // the name carries the adjacency information between the meshes
+ pcMesh->mName = pMesh->mName;
+
+ if (i == iSubMeshes-1)
+ {
+ pcMesh->mNumFaces = iOutFaceNum + (
+ pMesh->mNumFaces - iOutFaceNum * iSubMeshes);
+ }
+ // copy the list of faces
+ pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
+
+ const unsigned int iBase = iOutFaceNum * i;
+
+ // get the total number of indices
+ unsigned int iCnt = 0;
+ for (unsigned int p = iBase; p < pcMesh->mNumFaces + iBase;++p)
+ {
+ iCnt += pMesh->mFaces[p].mNumIndices;
+ }
+ pcMesh->mNumVertices = iCnt;
+
+ // allocate storage
+ if (pMesh->mVertices != NULL)
+ pcMesh->mVertices = new aiVector3D[iCnt];
+
+ if (pMesh->HasNormals())
+ pcMesh->mNormals = new aiVector3D[iCnt];
+
+ if (pMesh->HasTangentsAndBitangents())
+ {
+ pcMesh->mTangents = new aiVector3D[iCnt];
+ pcMesh->mBitangents = new aiVector3D[iCnt];
+ }
+
+ // texture coordinates
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
+ {
+ pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c];
+ if (pMesh->HasTextureCoords( c))
+ {
+ pcMesh->mTextureCoords[c] = new aiVector3D[iCnt];
+ }
+ }
+
+ // vertex colors
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c)
+ {
+ if (pMesh->HasVertexColors( c))
+ {
+ pcMesh->mColors[c] = new aiColor4D[iCnt];
+ }
+ }
+
+ if (pMesh->HasBones())
+ {
+ // assume the number of bones won't change in most cases
+ pcMesh->mBones = new aiBone*[pMesh->mNumBones];
+
+ // iterate through all bones of the mesh and find those which
+ // need to be copied to the splitted mesh
+ std::vector<aiVertexWeight> avTempWeights;
+ for (unsigned int p = 0; p < pcMesh->mNumBones;++p)
+ {
+ aiBone* const bone = pcMesh->mBones[p];
+ avTempWeights.clear();
+ avTempWeights.reserve(bone->mNumWeights / iSubMeshes);
+
+ for (unsigned int q = 0; q < bone->mNumWeights;++q)
+ {
+ aiVertexWeight& weight = bone->mWeights[q];
+ if (weight.mVertexId >= iBase && weight.mVertexId < iBase + iOutVertexNum)
+ {
+ avTempWeights.push_back(weight);
+ weight = avTempWeights.back();
+ weight.mVertexId -= iBase;
+ }
+ }
+
+ if (!avTempWeights.empty())
+ {
+ // we'll need this bone. Copy it ...
+ aiBone* pc = new aiBone();
+ pcMesh->mBones[pcMesh->mNumBones++] = pc;
+ pc->mName = aiString(bone->mName);
+ pc->mNumWeights = (unsigned int)avTempWeights.size();
+ pc->mOffsetMatrix = bone->mOffsetMatrix;
+
+ // no need to reallocate the array for the last submesh.
+ // Here we can reuse the (large) source array, although
+ // we'll waste some memory
+ if (iSubMeshes-1 == i)
+ {
+ pc->mWeights = bone->mWeights;
+ bone->mWeights = NULL;
+ }
+ else pc->mWeights = new aiVertexWeight[pc->mNumWeights];
+
+ // copy the weights
+ ::memcpy(pc->mWeights,&avTempWeights[0],sizeof(aiVertexWeight)*pc->mNumWeights);
+ }
+ }
+ }
+
+ // (we will also need to copy the array of indices)
+ unsigned int iCurrent = 0;
+ for (unsigned int p = 0; p < pcMesh->mNumFaces;++p)
+ {
+ pcMesh->mFaces[p].mNumIndices = 3;
+ // allocate a new array
+ const unsigned int iTemp = p + iBase;
+ const unsigned int iNumIndices = pMesh->mFaces[iTemp].mNumIndices;
+
+ // setup face type and number of indices
+ pcMesh->mFaces[p].mNumIndices = iNumIndices;
+ unsigned int* pi = pMesh->mFaces[iTemp].mIndices;
+ unsigned int* piOut = pcMesh->mFaces[p].mIndices = new unsigned int[iNumIndices];
+
+ // need to update the output primitive types
+ switch (iNumIndices)
+ {
+ case 1:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ break;
+ case 2:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ break;
+ case 3:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ break;
+ default:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ }
+
+ // and copy the contents of the old array, offset by current base
+ for (unsigned int v = 0; v < iNumIndices;++v)
+ {
+ unsigned int iIndex = pi[v];
+ unsigned int iIndexOut = iCurrent++;
+ piOut[v] = iIndexOut;
+
+ // copy positions
+ if (pMesh->mVertices != NULL)
+ pcMesh->mVertices[iIndexOut] = pMesh->mVertices[iIndex];
+
+ // copy normals
+ if (pMesh->HasNormals())
+ pcMesh->mNormals[iIndexOut] = pMesh->mNormals[iIndex];
+
+ // copy tangents/bitangents
+ if (pMesh->HasTangentsAndBitangents())
+ {
+ pcMesh->mTangents[iIndexOut] = pMesh->mTangents[iIndex];
+ pcMesh->mBitangents[iIndexOut] = pMesh->mBitangents[iIndex];
+ }
+
+ // texture coordinates
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
+ {
+ if (pMesh->HasTextureCoords( c))
+ pcMesh->mTextureCoords[c][iIndexOut] = pMesh->mTextureCoords[c][iIndex];
+ }
+ // vertex colors
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c)
+ {
+ if (pMesh->HasVertexColors( c))
+ pcMesh->mColors[c][iIndexOut] = pMesh->mColors[c][iIndex];
+ }
+ }
+ }
+
+ // add the newly created mesh to the list
+ avList.push_back(std::pair<aiMesh*, unsigned int>(pcMesh,a));
+ }
+
+ // now delete the old mesh data
+ delete pMesh;
+ }
+ else avList.push_back(std::pair<aiMesh*, unsigned int>(pMesh,a));
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+SplitLargeMeshesProcess_Vertex::SplitLargeMeshesProcess_Vertex()
+{
+ LIMIT = AI_SLM_DEFAULT_MAX_VERTICES;
+}
+
+// ------------------------------------------------------------------------------------------------
+SplitLargeMeshesProcess_Vertex::~SplitLargeMeshesProcess_Vertex()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool SplitLargeMeshesProcess_Vertex::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_SplitLargeMeshes) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene)
+{
+ std::vector<std::pair<aiMesh*, unsigned int> > avList;
+
+ if (0xffffffff == this->LIMIT)return;
+
+ DefaultLogger::get()->debug("SplitLargeMeshesProcess_Vertex begin");
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ this->SplitMesh(a, pScene->mMeshes[a],avList);
+
+ if (avList.size() != pScene->mNumMeshes)
+ {
+ // it seems something has been splitted. rebuild the mesh list
+ delete[] pScene->mMeshes;
+ pScene->mNumMeshes = (unsigned int)avList.size();
+ pScene->mMeshes = new aiMesh*[avList.size()];
+
+ for (unsigned int i = 0; i < avList.size();++i)
+ pScene->mMeshes[i] = avList[i].first;
+
+ // now we need to update all nodes
+ SplitLargeMeshesProcess_Triangle::UpdateNode(pScene->mRootNode,avList);
+ DefaultLogger::get()->info("SplitLargeMeshesProcess_Vertex finished. Meshes have been splitted");
+ }
+ else DefaultLogger::get()->debug("SplitLargeMeshesProcess_Vertex finished. There was nothing to do");
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties
+void SplitLargeMeshesProcess_Vertex::SetupProperties( const Importer* pImp)
+{
+ this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void SplitLargeMeshesProcess_Vertex::SplitMesh(
+ unsigned int a,
+ aiMesh* pMesh,
+ std::vector<std::pair<aiMesh*, unsigned int> >& avList)
+{
+ if (pMesh->mNumVertices > SplitLargeMeshesProcess_Vertex::LIMIT)
+ {
+ typedef std::vector< std::pair<unsigned int,float> > VertexWeightTable;
+
+ // build a per-vertex weight list if necessary
+ VertexWeightTable* avPerVertexWeights = ComputeVertexBoneWeightTable(pMesh);
+
+ // we need to split this mesh into sub meshes
+ // determine the estimated size of a submesh
+ // (this could be too large. Max waste is a single digit percentage)
+ const unsigned int iSubMeshes = (pMesh->mNumVertices / SplitLargeMeshesProcess_Vertex::LIMIT) + 1;
+ //const unsigned int iOutVertexNum2 = pMesh->mNumVertices /iSubMeshes;
+
+ // create a std::vector<unsigned int> to indicate which vertices
+ // have already been copied
+ std::vector<unsigned int> avWasCopied;
+ avWasCopied.resize(pMesh->mNumVertices,0xFFFFFFFF);
+
+ // try to find a good estimate for the number of output faces
+ // per mesh. Add 12.5% as buffer
+ unsigned int iEstimatedSize = pMesh->mNumFaces / iSubMeshes;
+ iEstimatedSize += iEstimatedSize >> 3;
+
+ // now generate all submeshes
+ unsigned int iBase = 0;
+ while (true)
+ {
+ const unsigned int iOutVertexNum = SplitLargeMeshesProcess_Vertex::LIMIT;
+
+ aiMesh* pcMesh = new aiMesh;
+ pcMesh->mNumVertices = 0;
+ pcMesh->mMaterialIndex = pMesh->mMaterialIndex;
+
+ // the name carries the adjacency information between the meshes
+ pcMesh->mName = pMesh->mName;
+
+ typedef std::vector<aiVertexWeight> BoneWeightList;
+ if (pMesh->HasBones())
+ {
+ pcMesh->mBones = new aiBone*[pMesh->mNumBones];
+ ::memset(pcMesh->mBones,0,sizeof(void*)*pMesh->mNumBones);
+ }
+
+ // clear the temporary helper array
+ if (iBase)
+ {
+ // we can't use memset here we unsigned int needn' be 32 bits
+ for (std::vector<unsigned int>::iterator
+ iter = avWasCopied.begin(),end = avWasCopied.end();
+ iter != end;++iter)
+ {
+ (*iter) = 0xffffffff;
+ }
+ }
+
+ // output vectors
+ std::vector<aiFace> vFaces;
+
+ // reserve enough storage for most cases
+ if (pMesh->HasPositions())
+ {
+ pcMesh->mVertices = new aiVector3D[iOutVertexNum];
+ }
+ if (pMesh->HasNormals())
+ {
+ pcMesh->mNormals = new aiVector3D[iOutVertexNum];
+ }
+ if (pMesh->HasTangentsAndBitangents())
+ {
+ pcMesh->mTangents = new aiVector3D[iOutVertexNum];
+ pcMesh->mBitangents = new aiVector3D[iOutVertexNum];
+ }
+ for (unsigned int c = 0; pMesh->HasVertexColors(c);++c)
+ {
+ pcMesh->mColors[c] = new aiColor4D[iOutVertexNum];
+ }
+ for (unsigned int c = 0; pMesh->HasTextureCoords(c);++c)
+ {
+ pcMesh->mNumUVComponents[c] = pMesh->mNumUVComponents[c];
+ pcMesh->mTextureCoords[c] = new aiVector3D[iOutVertexNum];
+ }
+ vFaces.reserve(iEstimatedSize);
+
+ // (we will also need to copy the array of indices)
+ while (iBase < pMesh->mNumFaces)
+ {
+ // allocate a new array
+ const unsigned int iNumIndices = pMesh->mFaces[iBase].mNumIndices;
+
+ // doesn't catch degenerates but is quite fast
+ unsigned int iNeed = 0;
+ for (unsigned int v = 0; v < iNumIndices;++v)
+ {
+ unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v];
+
+ // check whether we do already have this vertex
+ if (0xFFFFFFFF == avWasCopied[iIndex])
+ {
+ iNeed++;
+ }
+ }
+ if (pcMesh->mNumVertices + iNeed > iOutVertexNum)
+ {
+ // 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:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+ break;
+ case 2:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+ break;
+ case 3:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ break;
+ default:
+ pcMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+ }
+
+ // and copy the contents of the old array, offset by current base
+ for (unsigned int v = 0; v < iNumIndices;++v)
+ {
+ unsigned int iIndex = pMesh->mFaces[iBase].mIndices[v];
+
+ // check whether we do already have this vertex
+ if (0xFFFFFFFF != avWasCopied[iIndex])
+ {
+ rFace.mIndices[v] = avWasCopied[iIndex];
+ continue;
+ }
+
+ // copy positions
+ pcMesh->mVertices[pcMesh->mNumVertices] = (pMesh->mVertices[iIndex]);
+
+ // copy normals
+ if (pMesh->HasNormals())
+ {
+ pcMesh->mNormals[pcMesh->mNumVertices] = (pMesh->mNormals[iIndex]);
+ }
+
+ // copy tangents/bitangents
+ if (pMesh->HasTangentsAndBitangents())
+ {
+ pcMesh->mTangents[pcMesh->mNumVertices] = (pMesh->mTangents[iIndex]);
+ pcMesh->mBitangents[pcMesh->mNumVertices] = (pMesh->mBitangents[iIndex]);
+ }
+
+ // texture coordinates
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c)
+ {
+ if (pMesh->HasTextureCoords( c))
+ {
+ pcMesh->mTextureCoords[c][pcMesh->mNumVertices] = pMesh->mTextureCoords[c][iIndex];
+ }
+ }
+ // vertex colors
+ for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c)
+ {
+ if (pMesh->HasVertexColors( c))
+ {
+ pcMesh->mColors[c][pcMesh->mNumVertices] = pMesh->mColors[c][iIndex];
+ }
+ }
+ // check whether we have bone weights assigned to this vertex
+ rFace.mIndices[v] = pcMesh->mNumVertices;
+ if (avPerVertexWeights)
+ {
+ VertexWeightTable& table = avPerVertexWeights[ pcMesh->mNumVertices ];
+ if ( !table.empty() )
+ {
+ for (VertexWeightTable::const_iterator
+ iter = table.begin();
+ iter != table.end();++iter)
+ {
+ // allocate the bone weight array if necessary
+ BoneWeightList* pcWeightList = (BoneWeightList*)pcMesh->mBones[(*iter).first];
+ if (!pcWeightList)
+ {
+ pcMesh->mBones[(*iter).first] = (aiBone*)(pcWeightList = new BoneWeightList());
+ }
+ pcWeightList->push_back(aiVertexWeight(pcMesh->mNumVertices,(*iter).second));
+ }
+ }
+ }
+
+ avWasCopied[iIndex] = pcMesh->mNumVertices;
+ pcMesh->mNumVertices++;
+ }
+ iBase++;
+ if (pcMesh->mNumVertices == iOutVertexNum)
+ {
+ // break here. The face is only added if it was complete
+ break;
+ }
+ }
+
+ // check which bones we'll need to create for this submesh
+ if (pMesh->HasBones())
+ {
+ aiBone** ppCurrent = pcMesh->mBones;
+ for (unsigned int k = 0; k < pMesh->mNumBones;++k)
+ {
+ // check whether the bone is existing
+ BoneWeightList* pcWeightList;
+ if ((pcWeightList = (BoneWeightList*)pcMesh->mBones[k]))
+ {
+ aiBone* pcOldBone = pMesh->mBones[k];
+ aiBone* pcOut;
+ *ppCurrent++ = pcOut = new aiBone();
+ pcOut->mName = aiString(pcOldBone->mName);
+ pcOut->mOffsetMatrix = pcOldBone->mOffsetMatrix;
+ pcOut->mNumWeights = (unsigned int)pcWeightList->size();
+ pcOut->mWeights = new aiVertexWeight[pcOut->mNumWeights];
+
+ // copy the vertex weights
+ ::memcpy(pcOut->mWeights,&pcWeightList->operator[](0),
+ pcOut->mNumWeights * sizeof(aiVertexWeight));
+
+ // delete the temporary bone weight list
+ delete pcWeightList;
+ pcMesh->mNumBones++;
+ }
+ }
+ }
+
+ // copy the face list to the mesh
+ pcMesh->mFaces = new aiFace[vFaces.size()];
+ pcMesh->mNumFaces = (unsigned int)vFaces.size();
+
+ for (unsigned int p = 0; p < pcMesh->mNumFaces;++p)
+ pcMesh->mFaces[p] = vFaces[p];
+
+ // add the newly created mesh to the list
+ avList.push_back(std::pair<aiMesh*, unsigned int>(pcMesh,a));
+
+ if (iBase == pMesh->mNumFaces)
+ {
+ // have all faces ... finish the outer loop, too
+ break;
+ }
+ }
+
+ // delete the per-vertex weight list again
+ delete[] avPerVertexWeights;
+
+ // now delete the old mesh data
+ delete pMesh;
+ return;
+ }
+ avList.push_back(std::pair<aiMesh*, unsigned int>(pMesh,a));
+ return;
+}
diff --git a/3rdparty/assimp/code/SplitLargeMeshes.h b/3rdparty/assimp/code/SplitLargeMeshes.h
new file mode 100644
index 000000000..fd5aec56c
--- /dev/null
+++ b/3rdparty/assimp/code/SplitLargeMeshes.h
@@ -0,0 +1,216 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to split large meshes into submeshes
+ */
+#ifndef AI_SPLITLARGEMESHES_H_INC
+#define AI_SPLITLARGEMESHES_H_INC
+
+#include <vector>
+#include "BaseProcess.h"
+
+#include "../include/aiMesh.h"
+#include "../include/aiScene.h"
+
+class SplitLargeMeshesTest;
+namespace Assimp
+{
+
+class SplitLargeMeshesProcess_Triangle;
+class SplitLargeMeshesProcess_Vertex;
+
+// NOTE: If you change these limits, don't forget to change the
+// corresponding values in all Assimp ports
+
+// **********************************************************
+// Java: ConfigProperty.java,
+// ConfigProperty.DEFAULT_VERTEX_SPLIT_LIMIT
+// ConfigProperty.DEFAULT_TRIANGLE_SPLIT_LIMIT
+// **********************************************************
+
+// default limit for vertices
+#if (!defined AI_SLM_DEFAULT_MAX_VERTICES)
+# define AI_SLM_DEFAULT_MAX_VERTICES 1000000
+#endif
+
+// default limit for triangles
+#if (!defined AI_SLM_DEFAULT_MAX_TRIANGLES)
+# define AI_SLM_DEFAULT_MAX_TRIANGLES 1000000
+#endif
+
+// ---------------------------------------------------------------------------
+/** Postprocessing filter to split large meshes into submeshes
+ *
+ * Applied BEFORE the JoinVertices-Step occurs.
+ * Returns NON-UNIQUE vertices, splits by triangle number.
+*/
+class ASSIMP_API SplitLargeMeshesProcess_Triangle : public BaseProcess
+{
+ friend class Importer;
+ friend class SplitLargeMeshesProcess_Vertex;
+ friend class ::SplitLargeMeshesTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ SplitLargeMeshesProcess_Triangle();
+
+ /** Destructor, private as well */
+ ~SplitLargeMeshesProcess_Triangle();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag.
+ * @param pFlags The processing flags the importer was called with. A
+ * bitwise combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields,
+ * false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+
+ // -------------------------------------------------------------------
+ /** Called prior to ExecuteOnScene().
+ * The function is a request to the process to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ virtual void SetupProperties(const Importer* pImp);
+
+
+ //! Set the split limit - needed for unit testing
+ inline void SetLimit(unsigned int l)
+ {LIMIT = l;}
+
+ //! Get the split limit
+ inline unsigned int GetLimit() const
+ {return LIMIT;}
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+ // -------------------------------------------------------------------
+ //! Apply the algorithm to a given mesh
+ void SplitMesh (unsigned int a, aiMesh* pcMesh,
+ std::vector<std::pair<aiMesh*, unsigned int> >& avList);
+
+ // -------------------------------------------------------------------
+ //! Update a node in the asset after a few of its meshes
+ //! have been split
+ static void UpdateNode(aiNode* pcNode,
+ const std::vector<std::pair<aiMesh*, unsigned int> >& avList);
+
+public:
+ //! Triangle limit
+ unsigned int LIMIT;
+};
+
+
+// ---------------------------------------------------------------------------
+/** Postprocessing filter to split large meshes into submeshes
+ *
+ * Applied AFTER the JoinVertices-Step occurs.
+ * Returns UNIQUE vertices, splits by vertex number.
+*/
+class ASSIMP_API SplitLargeMeshesProcess_Vertex : public BaseProcess
+{
+ friend class Importer;
+ friend class ::SplitLargeMeshesTest;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ SplitLargeMeshesProcess_Vertex();
+
+ /** Destructor, private as well */
+ ~SplitLargeMeshesProcess_Vertex();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** Called prior to ExecuteOnScene().
+ * The function is a request to the process to update its configuration
+ * basing on the Importer's configuration property list.
+ */
+ virtual void SetupProperties(const Importer* pImp);
+
+
+ //! Set the split limit - needed for unit testing
+ inline void SetLimit(unsigned int l)
+ {LIMIT = l;}
+
+ //! Get the split limit
+ inline unsigned int GetLimit() const
+ {return LIMIT;}
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+ // -------------------------------------------------------------------
+ //! Apply the algorithm to a given mesh
+ void SplitMesh (unsigned int a, aiMesh* pcMesh,
+ std::vector<std::pair<aiMesh*, unsigned int> >& avList);
+
+ // NOTE: Reuse SplitLargeMeshesProcess_Triangle::UpdateNode()
+
+public:
+ //! Triangle limit
+ unsigned int LIMIT;
+};
+
+} // end of namespace Assimp
+
+#endif // !!AI_SPLITLARGEMESHES_H_INC
diff --git a/3rdparty/assimp/code/StandardShapes.cpp b/3rdparty/assimp/code/StandardShapes.cpp
new file mode 100644
index 000000000..028db569e
--- /dev/null
+++ b/3rdparty/assimp/code/StandardShapes.cpp
@@ -0,0 +1,502 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 StandardShapes.cpp
+ * @brief Implementation of the StandardShapes class
+ *
+ * The primitive geometry data comes from
+ * http://geometrictools.com/Documentation/PlatonicSolids.pdf.
+ */
+
+#include "AssimpPCH.h"
+#include "StandardShapes.h"
+
+namespace Assimp {
+
+
+# define ADD_TRIANGLE(n0,n1,n2) \
+ positions.push_back(n0); \
+ positions.push_back(n1); \
+ positions.push_back(n2);
+
+# define ADD_PENTAGON(n0,n1,n2,n3,n4) \
+ if (polygons) \
+ { \
+ positions.push_back(n0); \
+ positions.push_back(n1); \
+ positions.push_back(n2); \
+ positions.push_back(n3); \
+ positions.push_back(n4); \
+ } \
+ else \
+ { \
+ ADD_TRIANGLE(n0, n1, n2) \
+ ADD_TRIANGLE(n0, n2, n3) \
+ ADD_TRIANGLE(n0, n3, n4) \
+ }
+
+# define ADD_QUAD(n0,n1,n2,n3) \
+ if (polygons) \
+ { \
+ positions.push_back(n0); \
+ positions.push_back(n1); \
+ positions.push_back(n2); \
+ positions.push_back(n3); \
+ } \
+ else \
+ { \
+ ADD_TRIANGLE(n0, n1, n2) \
+ ADD_TRIANGLE(n0, n2, n3) \
+ }
+
+
+// ------------------------------------------------------------------------------------------------
+// Fast subdivision for a mesh whose verts have a magnitude of 1
+void Subdivide(std::vector<aiVector3D>& positions)
+{
+ // assume this to be constant - (fixme: must be 1.0? I think so)
+ const float fl1 = positions[0].Length();
+
+ unsigned int origSize = (unsigned int)positions.size();
+ for (unsigned int i = 0 ; i < origSize ; i+=3)
+ {
+ aiVector3D& tv0 = positions[i];
+ aiVector3D& tv1 = positions[i+1];
+ aiVector3D& tv2 = positions[i+2];
+
+ aiVector3D a = tv0, b = tv1, c = tv2;
+ aiVector3D v1 = aiVector3D(a.x+b.x, a.y+b.y, a.z+b.z).Normalize()*fl1;
+ aiVector3D v2 = aiVector3D(a.x+c.x, a.y+c.y, a.z+c.z).Normalize()*fl1;
+ aiVector3D v3 = aiVector3D(b.x+c.x, b.y+c.y, b.z+c.z).Normalize()*fl1;
+
+ tv0 = v1; tv1 = v3; tv2 = v2; // overwrite the original
+ ADD_TRIANGLE(v1, v2, a);
+ ADD_TRIANGLE(v2, v3, c);
+ ADD_TRIANGLE(v3, v1, b);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Construct a mesh from given vertex positions
+aiMesh* StandardShapes::MakeMesh(const std::vector<aiVector3D>& positions,
+ unsigned int numIndices)
+{
+ if (positions.size() & numIndices || positions.empty() || !numIndices)
+ return NULL;
+
+ // Determine which kinds of primitives the mesh consists of
+ aiMesh* out = new aiMesh();
+ switch (numIndices)
+ {
+ case 1:
+ out->mPrimitiveTypes = aiPrimitiveType_POINT;
+ break;
+ case 2:
+ out->mPrimitiveTypes = aiPrimitiveType_LINE;
+ break;
+ case 3:
+ out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+ break;
+ default:
+ out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
+ break;
+ };
+
+ out->mNumFaces = (unsigned int)positions.size() / numIndices;
+ out->mFaces = new aiFace[out->mNumFaces];
+ for (unsigned int i = 0, a = 0; i < out->mNumFaces;++i)
+ {
+ aiFace& f = out->mFaces[i];
+ f.mNumIndices = numIndices;
+ f.mIndices = new unsigned int[numIndices];
+ for (unsigned int i = 0; i < numIndices;++i,++a)
+ f.mIndices[i] = a;
+ }
+ out->mNumVertices = (unsigned int)positions.size();
+ out->mVertices = new aiVector3D[out->mNumVertices];
+ ::memcpy(out->mVertices,&positions[0],out->mNumVertices*sizeof(aiVector3D));
+ return out;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Construct a mesh with a specific shape (callback)
+aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)(
+ std::vector<aiVector3D>&))
+{
+ std::vector<aiVector3D> temp;
+ unsigned num = (*GenerateFunc)(temp);
+ return MakeMesh(temp,num);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Construct a mesh with a specific shape (callback)
+aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)(
+ std::vector<aiVector3D>&, bool))
+{
+ std::vector<aiVector3D> temp;
+ unsigned num = (*GenerateFunc)(temp,true);
+ return MakeMesh(temp,num);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Construct a mesh with a specific shape (callback)
+aiMesh* StandardShapes::MakeMesh (unsigned int num, void (*GenerateFunc)(
+ unsigned int,std::vector<aiVector3D>&))
+{
+ std::vector<aiVector3D> temp;
+ (*GenerateFunc)(num,temp);
+ return MakeMesh(temp,3);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build an incosahedron with points.magnitude == 1
+unsigned int StandardShapes::MakeIcosahedron(std::vector<aiVector3D>& positions)
+{
+ positions.reserve(positions.size()+60);
+
+ const float t = (1.f + 2.236067977f)/2.f;
+ const float s = sqrt(1.f + t*t);
+
+ const aiVector3D v0 = aiVector3D(t,1.f, 0.f)/s;
+ const aiVector3D v1 = aiVector3D(-t,1.f, 0.f)/s;
+ const aiVector3D v2 = aiVector3D(t,-1.f, 0.f)/s;
+ const aiVector3D v3 = aiVector3D(-t,-1.f, 0.f)/s;
+ const aiVector3D v4 = aiVector3D(1.f, 0.f, t)/s;
+ const aiVector3D v5 = aiVector3D(1.f, 0.f,-t)/s;
+ const aiVector3D v6 = aiVector3D(-1.f, 0.f,t)/s;
+ const aiVector3D v7 = aiVector3D(-1.f, 0.f,-t)/s;
+ const aiVector3D v8 = aiVector3D(0.f, t, 1.f)/s;
+ const aiVector3D v9 = aiVector3D(0.f,-t, 1.f)/s;
+ const aiVector3D v10 = aiVector3D(0.f, t,-1.f)/s;
+ const aiVector3D v11 = aiVector3D(0.f,-t,-1.f)/s;
+
+ ADD_TRIANGLE(v0,v8,v4);
+ ADD_TRIANGLE(v0,v5,v10);
+ ADD_TRIANGLE(v2,v4,v9);
+ ADD_TRIANGLE(v2,v11,v5);
+
+ ADD_TRIANGLE(v1,v6,v8);
+ ADD_TRIANGLE(v1,v10,v7);
+ ADD_TRIANGLE(v3,v9,v6);
+ ADD_TRIANGLE(v3,v7,v11);
+
+ ADD_TRIANGLE(v0,v10,v8);
+ ADD_TRIANGLE(v1,v8,v10);
+ ADD_TRIANGLE(v2,v9,v11);
+ ADD_TRIANGLE(v3,v11,v9);
+
+ ADD_TRIANGLE(v4,v2,v0);
+ ADD_TRIANGLE(v5,v0,v2);
+ ADD_TRIANGLE(v6,v1,v3);
+ ADD_TRIANGLE(v7,v3,v1);
+
+ ADD_TRIANGLE(v8,v6,v4);
+ ADD_TRIANGLE(v9,v4,v6);
+ ADD_TRIANGLE(v10,v5,v7);
+ ADD_TRIANGLE(v11,v7,v5);
+ return 3;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a dodecahedron with points.magnitude == 1
+unsigned int StandardShapes::MakeDodecahedron(std::vector<aiVector3D>& positions,
+ bool polygons /*= false*/)
+{
+ positions.reserve(positions.size()+108);
+
+ const float a = 1.f / 1.7320508f;
+ const float b = sqrt((3.f-2.23606797f)/6.f);
+ const float c = sqrt((3.f+2.23606797f)/6.f);
+
+ const aiVector3D v0 = aiVector3D(a,a,a);
+ const aiVector3D v1 = aiVector3D(a,a,-a);
+ const aiVector3D v2 = aiVector3D(a,-a,a);
+ const aiVector3D v3 = aiVector3D(a,-a,-a);
+ const aiVector3D v4 = aiVector3D(-a,a,a);
+ const aiVector3D v5 = aiVector3D(-a,a,-a);
+ const aiVector3D v6 = aiVector3D(-a,-a,a);
+ const aiVector3D v7 = aiVector3D(-a,-a,-a);
+ const aiVector3D v8 = aiVector3D(b,c,0.f);
+ const aiVector3D v9 = aiVector3D(-b,c,0.f);
+ const aiVector3D v10 = aiVector3D(b,-c,0.f);
+ const aiVector3D v11 = aiVector3D(-b,-c,0.f);
+ const aiVector3D v12 = aiVector3D(c, 0.f, b);
+ const aiVector3D v13 = aiVector3D(c, 0.f, -b);
+ const aiVector3D v14 = aiVector3D(-c, 0.f, b);
+ const aiVector3D v15 = aiVector3D(-c, 0.f, -b);
+ const aiVector3D v16 = aiVector3D(0.f, b, c);
+ const aiVector3D v17 = aiVector3D(0.f, -b, c);
+ const aiVector3D v18 = aiVector3D(0.f, b, -c);
+ const aiVector3D v19 = aiVector3D(0.f, -b, -c);
+
+ ADD_PENTAGON(v0, v8, v9, v4, v16);
+ ADD_PENTAGON(v0, v12, v13, v1, v8);
+ ADD_PENTAGON(v0, v16, v17, v2, v12);
+ ADD_PENTAGON(v8, v1, v18, v5, v9);
+ ADD_PENTAGON(v12, v2, v10, v3, v13);
+ ADD_PENTAGON(v16, v4, v14, v6, v17);
+ ADD_PENTAGON(v9, v5, v15, v14, v4);
+
+ ADD_PENTAGON(v6, v11, v10, v2, v17);
+ ADD_PENTAGON(v3, v19, v18, v1, v13);
+ ADD_PENTAGON(v7, v15, v5, v18, v19);
+ ADD_PENTAGON(v7, v11, v6, v14, v15);
+ ADD_PENTAGON(v7, v19, v3, v10, v11);
+ return (polygons ? 5 : 3);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build an octahedron with points.magnitude == 1
+unsigned int StandardShapes::MakeOctahedron(std::vector<aiVector3D>& positions)
+{
+ positions.reserve(positions.size()+24);
+
+ const aiVector3D v0 = aiVector3D(1.0f, 0.f, 0.f) ;
+ const aiVector3D v1 = aiVector3D(-1.0f, 0.f, 0.f);
+ const aiVector3D v2 = aiVector3D(0.f, 1.0f, 0.f);
+ const aiVector3D v3 = aiVector3D(0.f, -1.0f, 0.f);
+ const aiVector3D v4 = aiVector3D(0.f, 0.f, 1.0f);
+ const aiVector3D v5 = aiVector3D(0.f, 0.f, -1.0f);
+
+ ADD_TRIANGLE(v4,v0,v2);
+ ADD_TRIANGLE(v4,v2,v1);
+ ADD_TRIANGLE(v4,v1,v3);
+ ADD_TRIANGLE(v4,v3,v0);
+
+ ADD_TRIANGLE(v5,v2,v0);
+ ADD_TRIANGLE(v5,v1,v2);
+ ADD_TRIANGLE(v5,v3,v1);
+ ADD_TRIANGLE(v5,v0,v3);
+ return 3;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a tetrahedron with points.magnitude == 1
+unsigned int StandardShapes::MakeTetrahedron(std::vector<aiVector3D>& positions)
+{
+ positions.reserve(positions.size()+9);
+
+ const float a = 1.41421f/3.f;
+ const float b = 2.4494f/3.f;
+
+ const aiVector3D v0 = aiVector3D(0.f,0.f,1.f);
+ const aiVector3D v1 = aiVector3D(2*a,0,-1.f/3.f);
+ const aiVector3D v2 = aiVector3D(-a,b,-1.f/3.f);
+ const aiVector3D v3 = aiVector3D(-a,-b,-1.f/3.f);
+
+ ADD_TRIANGLE(v0,v1,v2);
+ ADD_TRIANGLE(v0,v2,v3);
+ ADD_TRIANGLE(v0,v3,v1);
+ ADD_TRIANGLE(v1,v3,v2);
+ return 3;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a hexahedron with points.magnitude == 1
+unsigned int StandardShapes::MakeHexahedron(std::vector<aiVector3D>& positions,
+ bool polygons /*= false*/)
+{
+ positions.reserve(positions.size()+36);
+ const float length = 1.f/1.73205080f;
+
+ const aiVector3D v0 = aiVector3D(-1.f,-1.f,-1.f)*length;
+ const aiVector3D v1 = aiVector3D(1.f,-1.f,-1.f)*length;
+ const aiVector3D v2 = aiVector3D(1.f,1.f,-1.f)*length;
+ const aiVector3D v3 = aiVector3D(-1.f,1.f,-1.f)*length;
+ const aiVector3D v4 = aiVector3D(-1.f,-1.f,1.f)*length;
+ const aiVector3D v5 = aiVector3D(1.f,-1.f,1.f)*length;
+ const aiVector3D v6 = aiVector3D(1.f,1.f,1.f)*length;
+ const aiVector3D v7 = aiVector3D(-1.f,1.f,1.f)*length;
+
+ ADD_QUAD(v0,v3,v2,v1);
+ ADD_QUAD(v0,v1,v5,v4);
+ ADD_QUAD(v0,v4,v7,v3);
+ ADD_QUAD(v6,v5,v1,v2);
+ ADD_QUAD(v6,v2,v3,v7);
+ ADD_QUAD(v6,v7,v4,v5);
+ return (polygons ? 4 : 3);
+}
+
+// Cleanup ...
+#undef ADD_TRIANGLE
+#undef ADD_QUAD
+#undef ADD_PENTAGON
+
+// ------------------------------------------------------------------------------------------------
+// Create a subdivision sphere
+void StandardShapes::MakeSphere(unsigned int tess,
+ std::vector<aiVector3D>& positions)
+{
+ // Reserve enough storage. Every subdivision
+ // splits each triangle in 4, the icosahedron consists of 60 verts
+ positions.reserve(positions.size()+60 * integer_pow(4, tess));
+
+ // Construct an icosahedron to start with
+ MakeIcosahedron(positions);
+
+ // ... and subdivide it until the requested output
+ // tesselation is reached
+ for (unsigned int i = 0; i<tess;++i)
+ Subdivide(positions);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a cone
+void StandardShapes::MakeCone(float height,float radius1,
+ float radius2,unsigned int tess,
+ std::vector<aiVector3D>& positions,bool bOpen /*= false */)
+{
+ // Sorry, a cone with less than 3 segments makes ABSOLUTELY NO SENSE
+ if (tess < 3 || !height)
+ return;
+
+ size_t old = positions.size();
+
+ // No negative radii
+ radius1 = ::fabs(radius1);
+ radius2 = ::fabs(radius2);
+
+ float halfHeight = height / 2;
+
+ // radius1 is always the smaller one
+ if (radius2 > radius1)
+ {
+ std::swap(radius2,radius1);
+ halfHeight = -halfHeight;
+ }
+ else old = 0xffffffff;
+
+ // Use a large epsilon to check whether the cone is pointy
+ if (radius1 < (radius2-radius1)*10e-3f)radius1 = 0.f;
+
+ // We will need 3*2 verts per segment + 3*2 verts per segment
+ // if the cone is closed
+ const unsigned int mem = tess*6 + (!bOpen ? tess*3 * (radius1 ? 2 : 1) : 0);
+ positions.reserve(positions.size () + mem);
+
+ // Now construct all segments
+ const float angle_delta = (float)AI_MATH_TWO_PI / tess;
+ const float angle_max = (float)AI_MATH_TWO_PI;
+
+ float s = 1.f; // cos(angle == 0);
+ float t = 0.f; // sin(angle == 0);
+
+ for (float angle = 0.f; angle < angle_max; )
+ {
+ const aiVector3D v1 = aiVector3D (s * radius1, -halfHeight, t * radius1 );
+ const aiVector3D v2 = aiVector3D (s * radius2, halfHeight, t * radius2 );
+
+ const float next = angle + angle_delta;
+ float s2 = ::cos(next);
+ float t2 = ::sin(next);
+
+ const aiVector3D v3 = aiVector3D (s2 * radius2, halfHeight, t2 * radius2 );
+ const aiVector3D v4 = aiVector3D (s2 * radius1, -halfHeight, t2 * radius1 );
+
+ positions.push_back(v1);
+ positions.push_back(v2);
+ positions.push_back(v3);
+ positions.push_back(v4);
+ positions.push_back(v1);
+ positions.push_back(v3);
+
+ if (!bOpen)
+ {
+ // generate the end 'cap'
+ positions.push_back(aiVector3D(s * radius2, halfHeight, t * radius2 ));
+ positions.push_back(aiVector3D(s2 * radius2, halfHeight, t2 * radius2 ));
+ positions.push_back(aiVector3D(0.f, halfHeight, 0.f));
+
+
+ if (radius1)
+ {
+ // generate the other end 'cap'
+ positions.push_back(aiVector3D(s * radius1, -halfHeight, t * radius1 ));
+ positions.push_back(aiVector3D(s2 * radius1, -halfHeight, t2 * radius1 ));
+ positions.push_back(aiVector3D(0.f, -halfHeight, 0.f));
+
+ }
+ }
+ s = s2;
+ t = t2;
+ angle = next;
+ }
+
+ // Need to flip face order?
+ if (0xffffffff != old )
+ {
+ for (size_t s = old; s < positions.size();s += 3)
+ std::swap(positions[s],positions[s+1]);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a circle
+void StandardShapes::MakeCircle(float radius, unsigned int tess,
+ std::vector<aiVector3D>& positions)
+{
+ // Sorry, a circle with less than 3 segments makes ABSOLUTELY NO SENSE
+ if (tess < 3 || !radius)
+ return;
+
+ radius = ::fabs(radius);
+
+ // We will need 3 vertices per segment
+ positions.reserve(positions.size()+tess*3);
+
+ const float angle_delta = (float)AI_MATH_TWO_PI / tess;
+ const float angle_max = (float)AI_MATH_TWO_PI;
+
+ float s = 1.f; // cos(angle == 0);
+ float t = 0.f; // sin(angle == 0);
+
+ for (float angle = 0.f; angle < angle_max; )
+ {
+ positions.push_back(aiVector3D(s * radius,0.f,t * radius));
+ angle += angle_delta;
+ s = ::cos(angle);
+ t = ::sin(angle);
+ positions.push_back(aiVector3D(s * radius,0.f,t * radius));
+
+ positions.push_back(aiVector3D(0.f,0.f,0.f));
+ }
+}
+
+} // ! Assimp
diff --git a/3rdparty/assimp/code/StandardShapes.h b/3rdparty/assimp/code/StandardShapes.h
new file mode 100644
index 000000000..e5b550e36
--- /dev/null
+++ b/3rdparty/assimp/code/StandardShapes.h
@@ -0,0 +1,196 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Declares a helper class, "StandardShapes" which generates
+ * vertices for standard shapes, such as cylnders, cones, spheres ..
+ */
+#ifndef AI_STANDARD_SHAPES_H_INC
+#define AI_STANDARD_SHAPES_H_INC
+
+#include <vector>
+
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** \brief Helper class to generate vertex buffers for standard geometric
+ * shapes, such as cylinders, cones, boxes, spheres, elipsoids ... .
+ */
+class ASSIMP_API StandardShapes
+{
+ // class cannot be instanced
+ StandardShapes() {}
+
+public:
+
+
+ // ----------------------------------------------------------------
+ /** Generates a mesh from an array of vertex positions.
+ *
+ * @param positions List of vertex positions
+ * @param numIndices Number of indices per primitive
+ * @return Output mesh
+ */
+ static aiMesh* MakeMesh(const std::vector<aiVector3D>& positions,
+ unsigned int numIndices);
+
+
+ static aiMesh* MakeMesh ( unsigned int (*GenerateFunc)
+ (std::vector<aiVector3D>&));
+
+ static aiMesh* MakeMesh ( unsigned int (*GenerateFunc)
+ (std::vector<aiVector3D>&, bool));
+
+ static aiMesh* MakeMesh ( unsigned int n, void (*GenerateFunc)
+ (unsigned int,std::vector<aiVector3D>&));
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a hexahedron (cube)
+ *
+ * Hexahedrons can be scaled on all axes.
+ * @param positions Receives output triangles.
+ * @param polygons If you pass true here quads will be returned
+ * @return Number of vertices per face
+ */
+ static unsigned int MakeHexahedron(
+ std::vector<aiVector3D>& positions,
+ bool polygons = false);
+
+ // ----------------------------------------------------------------
+ /** @brief Generates an icosahedron
+ *
+ * @param positions Receives output triangles.
+ * @return Number of vertices per face
+ */
+ static unsigned int MakeIcosahedron(
+ std::vector<aiVector3D>& positions);
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a dodecahedron
+ *
+ * @param positions Receives output triangles
+ * @param polygons If you pass true here pentagons will be returned
+ * @return Number of vertices per face
+ */
+ static unsigned int MakeDodecahedron(
+ std::vector<aiVector3D>& positions,
+ bool polygons = false);
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates an octahedron
+ *
+ * @param positions Receives output triangles.
+ * @return Number of vertices per face
+ */
+ static unsigned int MakeOctahedron(
+ std::vector<aiVector3D>& positions);
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a tetrahedron
+ *
+ * @param positions Receives output triangles.
+ * @return Number of vertices per face
+ */
+ static unsigned int MakeTetrahedron(
+ std::vector<aiVector3D>& positions);
+
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a sphere
+ *
+ * @param tess Number of subdivions - 0 generates a octahedron
+ * @param positions Receives output triangles.
+ */
+ static void MakeSphere(unsigned int tess,
+ std::vector<aiVector3D>& positions);
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a cone or a cylinder, either open or closed.
+ *
+ * @code
+ *
+ * |-----| <- radius 1
+ *
+ * __x__ <- ] ^
+ * / \ | height |
+ * / \ | Y
+ * / \ |
+ * / \ |
+ * /______x______\ <- ] <- end cap
+ *
+ * |-------------| <- radius 2
+ *
+ * @endcode
+ *
+ * @param height Height of the cone
+ * @param radius1 First radius
+ * @param radius2 Second radius
+ * @param tess Number of triangles.
+ * @param bOpened true for an open cone/cylinder. An open shape has
+ * no 'end caps'
+ * @param positions Receives output triangles
+ */
+ static void MakeCone(float height,float radius1,
+ float radius2,unsigned int tess,
+ std::vector<aiVector3D>& positions,bool bOpen= false);
+
+
+ // ----------------------------------------------------------------
+ /** @brief Generates a flat circle
+ *
+ * The circle is constructed in the planed formed by the x,z
+ * axes of the cartesian coordinate system.
+ *
+ * @param radius Radius of the circle
+ * @param tess Number of segments.
+ * @param positions Receives output triangles.
+ */
+ static void MakeCircle(float radius, unsigned int tess,
+ std::vector<aiVector3D>& positions);
+
+};
+} // ! Assimp
+
+#endif // !! AI_STANDARD_SHAPES_H_INC
diff --git a/3rdparty/assimp/code/StdOStreamLogStream.h b/3rdparty/assimp/code/StdOStreamLogStream.h
new file mode 100644
index 000000000..b80451f04
--- /dev/null
+++ b/3rdparty/assimp/code/StdOStreamLogStream.h
@@ -0,0 +1,52 @@
+#ifndef AI_STROSTREAMLOGSTREAM_H_INC
+#define AI_STROSTREAMLOGSTREAM_H_INC
+
+#include "../include/LogStream.h"
+#include <ostream>
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** @class StdOStreamLogStream
+ * @brief Logs into a std::ostream
+ */
+class StdOStreamLogStream : public LogStream
+{
+public:
+ /** @brief Construction from an existing std::ostream
+ * @param _ostream Output stream to be used
+ */
+ StdOStreamLogStream(std::ostream& _ostream);
+
+ /** @brief Destructor */
+ ~StdOStreamLogStream();
+
+ /** @brief Writer */
+ void write(const char* message);
+private:
+ std::ostream& ostream;
+};
+
+// ---------------------------------------------------------------------------
+// Default constructor
+inline StdOStreamLogStream::StdOStreamLogStream(std::ostream& _ostream)
+ : ostream (_ostream)
+{}
+
+// ---------------------------------------------------------------------------
+// Default constructor
+inline StdOStreamLogStream::~StdOStreamLogStream()
+{}
+
+// ---------------------------------------------------------------------------
+// Write method
+inline void StdOStreamLogStream::write(const char* message)
+{
+ ostream << message;
+ ostream.flush();
+}
+
+// ---------------------------------------------------------------------------
+} // Namespace Assimp
+
+#endif // guard
diff --git a/3rdparty/assimp/code/StreamReader.h b/3rdparty/assimp/code/StreamReader.h
new file mode 100644
index 000000000..08880d10d
--- /dev/null
+++ b/3rdparty/assimp/code/StreamReader.h
@@ -0,0 +1,384 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines the StreamReader class which reads data from
+ * a binary stream with a well-defined endianess. */
+
+#ifndef AI_STREAMREADER_H_INCLUDED
+#define AI_STREAMREADER_H_INCLUDED
+
+#include "ByteSwap.h"
+namespace Assimp {
+ 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
+
+// --------------------------------------------------------------------------------------------
+/** Wrapper class around IOStream to allow for consistent reading of binary data in both
+ * little and big endian format. Don't attempt to instance the template directly. Use
+ * StreamReaderLE to read from a little-endian stream and StreamReaderBE to read from a
+ * BE stream. The class expects that the endianess of any input data is known at
+ * compile-time, which should usually be true (#BaseImporter::ConvertToUTF8 implements
+ * runtime endianess conversions for text files).
+ *
+ * XXX switch from unsigned int for size types to size_t? or ptrdiff_t?*/
+// --------------------------------------------------------------------------------------------
+template <bool SwapEndianess = false, bool RuntimeSwitch = false>
+class StreamReader
+{
+
+public:
+
+ // FIXME: use these data types throughout the whole library,
+ // then change them to 64 bit values :-)
+
+ typedef int diff;
+ typedef unsigned int pos;
+
+public:
+
+
+ // ---------------------------------------------------------------------
+ /** Construction from a given stream with a well-defined endianess.
+ *
+ * The StreamReader holds a permanent strong reference to the
+ * stream, which is released upon destruction.
+ * @param stream Input stream. The stream is not restarted if
+ * its file pointer is not at 0. Instead, the stream reader
+ * reads from the current position to the end of the stream.
+ * @param le If @c RuntimeSwitch is true: specifies whether the
+ * stream is in little endian byte order. Otherwise the
+ * endianess information is contained in the @c SwapEndianess
+ * template parameter and this parameter is meaningless. */
+ StreamReader(boost::shared_ptr<IOStream> stream, bool le = false)
+ : stream(stream)
+ , le(le)
+ {
+ _Begin();
+ }
+
+ // ---------------------------------------------------------------------
+ StreamReader(IOStream* stream, bool le = false)
+ : stream(boost::shared_ptr<IOStream>(stream))
+ , le(le)
+ {
+ _Begin();
+ }
+
+ // ---------------------------------------------------------------------
+ ~StreamReader() {
+ delete[] buffer;
+ }
+
+public:
+
+ // deprecated, use overloaded operator>> instead
+
+ // ---------------------------------------------------------------------
+ /** Read a float from the stream */
+ float GetF4()
+ {
+ return Get<float>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a double from the stream */
+ double GetF8() {
+ return Get<double>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a signed 16 bit integer from the stream */
+ int16_t GetI2() {
+ return Get<int16_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a signed 8 bit integer from the stream */
+ int8_t GetI1() {
+ return Get<int8_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read an signed 32 bit integer from the stream */
+ int32_t GetI4() {
+ return Get<int32_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a signed 64 bit integer from the stream */
+ int64_t GetI8() {
+ return Get<int64_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a unsigned 16 bit integer from the stream */
+ uint16_t GetU2() {
+ return Get<uint16_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a unsigned 8 bit integer from the stream */
+ uint8_t GetU1() {
+ return Get<uint8_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read an unsigned 32 bit integer from the stream */
+ uint32_t GetU4() {
+ return Get<uint32_t>();
+ }
+
+ // ---------------------------------------------------------------------
+ /** Read a unsigned 64 bit integer from the stream */
+ uint64_t GetU8() {
+ return Get<uint64_t>();
+ }
+
+public:
+
+ // ---------------------------------------------------------------------
+ /** Get the remaining stream size (to the end of the srream) */
+ unsigned int GetRemainingSize() const {
+ return (unsigned int)(end - current);
+ }
+
+
+ // ---------------------------------------------------------------------
+ /** Get the remaining stream size (to the current read limit). The
+ * return value is the remaining size of the stream if no custom
+ * read limit has been set. */
+ unsigned int GetRemainingSizeToLimit() const {
+ return (unsigned int)(limit - current);
+ }
+
+
+ // ---------------------------------------------------------------------
+ /** Increase the file pointer (relative seeking) */
+ void IncPtr(int plus) {
+ current += plus;
+ if (current > limit) {
+ throw DeadlyImportError("End of file or read limit was reached");
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ /** Get the current file pointer */
+ int8_t* GetPtr() const {
+ return current;
+ }
+
+
+ // ---------------------------------------------------------------------
+ /** Set current file pointer (Get it from #GetPtr). This is if you
+ * prefer to do pointer arithmetics on your own or want to copy
+ * large chunks of data at once.
+ * @param p The new pointer, which is validated against the size
+ * limit and buffer boundaries. */
+ void SetPtr(int8_t* p) {
+
+ current = p;
+ if (current > limit || current < buffer) {
+ throw DeadlyImportError("End of file or read limit was reached");
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ /** Copy n bytes to an external buffer
+ * @param out Destination for copying
+ * @param bytes Number of bytes to copy */
+ void CopyAndAdvance(void* out, size_t bytes) {
+
+ int8_t* ur = GetPtr();
+ SetPtr(ur+bytes); // fire exception if eof
+
+ memcpy(out,ur,bytes);
+ }
+
+
+ // ---------------------------------------------------------------------
+ /** Get the current offset from the beginning of the file */
+ int GetCurrentPos() const {
+ return (unsigned int)(current - buffer);
+ }
+
+ void SetCurrentPos(size_t pos) {
+ SetPtr(buffer + pos);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Setup a temporary read limit
+ *
+ * @param limit Maximum number of bytes to be read from
+ * the beginning of the file. Passing 0xffffffff
+ * resets the limit to the original end of the stream. */
+ void SetReadLimit(unsigned int _limit) {
+
+ if (0xffffffff == _limit) {
+ limit = end;
+ return;
+ }
+
+ limit = buffer + _limit;
+ if (limit > end) {
+ throw DeadlyImportError("StreamReader: Invalid read limit");
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ /** Get the current read limit in bytes. Reading over this limit
+ * accidentially raises an exception. */
+ int GetReadLimit() const {
+ return (unsigned int)(limit - buffer);
+ }
+
+ // ---------------------------------------------------------------------
+ /** Skip to the read limit in bytes. Reading over this limit
+ * accidentially raises an exception. */
+ void SkipToReadLimit() {
+ current = limit;
+ }
+
+ // ---------------------------------------------------------------------
+ /** overload operator>> and allow chaining of >> ops. */
+ template <typename T>
+ StreamReader& operator >> (T& f) {
+ f = Get<T>();
+ return *this;
+ }
+
+private:
+
+ // ---------------------------------------------------------------------
+ /** Generic read method. ByteSwap::Swap(T*) *must* be defined */
+ template <typename T>
+ T Get() {
+ if (current + sizeof(T) > limit) {
+ throw DeadlyImportError("End of file or stream limit was reached");
+ }
+
+ T f = *((const T*)current);
+ Intern :: Getter<SwapEndianess,T,RuntimeSwitch>() (&f,le);
+
+ current += sizeof(T);
+ return f;
+ }
+
+ // ---------------------------------------------------------------------
+ void _Begin() {
+ if (!stream) {
+ throw DeadlyImportError("StreamReader: Unable to open file");
+ }
+
+ const size_t s = stream->FileSize() - stream->Tell();
+ if (!s) {
+ throw DeadlyImportError("StreamReader: File is empty or EOF is already reached");
+ }
+
+ current = buffer = new int8_t[s];
+ stream->Read(current,s,1);
+ end = limit = &buffer[s];
+ }
+
+private:
+
+
+ boost::shared_ptr<IOStream> stream;
+ int8_t *buffer, *current, *end, *limit;
+ bool le;
+};
+
+
+// --------------------------------------------------------------------------------------------
+// `static` StreamReaders. Their byte order is fixed and they might be a little bit faster.
+#ifdef AI_BUILD_BIG_ENDIAN
+ typedef StreamReader<true> StreamReaderLE;
+ typedef StreamReader<false> StreamReaderBE;
+#else
+ typedef StreamReader<true> StreamReaderBE;
+ typedef StreamReader<false> StreamReaderLE;
+#endif
+
+// `dynamic` StreamReader. The byte order of the input data is specified in the
+// c'tor. This involves runtime branching and might be a little bit slower.
+typedef StreamReader<true,true> StreamReaderAny;
+
+} // end namespace Assimp
+
+#endif // !! AI_STREAMREADER_H_INCLUDED
diff --git a/3rdparty/assimp/code/StringComparison.h b/3rdparty/assimp/code/StringComparison.h
new file mode 100644
index 000000000..b885606ac
--- /dev/null
+++ b/3rdparty/assimp/code/StringComparison.h
@@ -0,0 +1,217 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 platform independent string workers:
+
+ ASSIMP_itoa10
+ ASSIMP_stricmp
+ ASSIMP_strincmp
+
+ These functions are not consistently available on all platforms,
+ or the provided implementations behave too differently.
+*/
+#ifndef INCLUDED_AI_STRING_WORKERS_H
+#define INCLUDED_AI_STRING_WORKERS_H
+
+namespace Assimp {
+
+// -------------------------------------------------------------------------------
+/** @brief itoa with a fixed base 10
+ * 'itoa' is not consistently available on all platforms so it is quite useful
+ * to have a small replacement function here. No need to use a full sprintf()
+ * if we just want to print a number ...
+ * @param out Output buffer
+ * @param max Maximum number of characters to be written, including '\0'.
+ * This parameter may not be 0.
+ * @param number Number to be written
+ * @return Length of the output string, excluding the '\0'
+ */
+inline unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number)
+{
+ ai_assert(NULL != out);
+
+ // write the unary minus to indicate we have a negative number
+ unsigned int written = 1u;
+ if (number < 0 && written < max) {
+ *out++ = '-';
+ ++written;
+ number = -number;
+ }
+
+ // We begin with the largest number that is not zero.
+ int32_t cur = 1000000000; // 2147483648
+ bool mustPrint = false;
+ while (written < max) {
+
+ const unsigned int digit = number / cur;
+ if (mustPrint || digit > 0 || 1 == cur) {
+ // print all future zeroes from now
+ mustPrint = true;
+
+ *out++ = '0'+digit;
+
+ ++written;
+ number -= digit*cur;
+ if (1 == cur) {
+ break;
+ }
+ }
+ cur /= 10;
+ }
+
+ // append a terminal zero
+ *out++ = '\0';
+ return written-1;
+}
+
+// -------------------------------------------------------------------------------
+/** @brief itoa with a fixed base 10 (Secure template overload)
+ * The compiler should choose this function if he or she is able to determine the
+ * size of the array automatically.
+ */
+template <size_t length>
+inline unsigned int ASSIMP_itoa10( char(& out)[length], int32_t number)
+{
+ return ASSIMP_itoa10(out,length,number);
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Helper function to do platform independent string comparison.
+ *
+ * This is required since stricmp() is not consistently available on
+ * all platforms. Some platforms use the '_' prefix, others don't even
+ * have such a function.
+ *
+ * @param s1 First input string
+ * @param s2 Second input string
+ * @return 0 if the given strings are identical
+ */
+inline int ASSIMP_stricmp(const char *s1, const char *s2)
+{
+ ai_assert(NULL != s1 && NULL != s2);
+
+#if (defined _MSC_VER)
+
+ return ::_stricmp(s1,s2);
+#elif defined( __GNUC__ )
+
+ return ::strcasecmp(s1,s2);
+#else
+
+ register char c1, c2;
+ do {
+ c1 = tolower(*s1++);
+ c2 = tolower(*s2++);
+ }
+ while ( c1 && (c1 == c2) );
+ return c1 - c2;
+#endif
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Case independent comparison of two std::strings
+ *
+ * @param a First string
+ * @param b Second string
+ * @return 0 if a == b
+ */
+inline int ASSIMP_stricmp(const std::string& a, const std::string& b)
+{
+ register int i = (int)b.length()-(int)a.length();
+ return (i ? i : ASSIMP_stricmp(a.c_str(),b.c_str()));
+}
+
+// -------------------------------------------------------------------------------
+/** @brief Helper function to do platform independent string comparison.
+ *
+ * This is required since strincmp() is not consistently available on
+ * all platforms. Some platforms use the '_' prefix, others don't even
+ * have such a function.
+ *
+ * @param s1 First input string
+ * @param s2 Second input string
+ * @param n Macimum number of characters to compare
+ * @return 0 if the given strings are identical
+ */
+inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n)
+{
+ ai_assert(NULL != s1 && NULL != s2);
+ if (!n)return 0;
+
+#if (defined _MSC_VER)
+
+ return ::_strnicmp(s1,s2,n);
+
+#elif defined( __GNUC__ )
+
+ return ::strncasecmp(s1,s2, n);
+
+#else
+ register char c1, c2;
+ unsigned int p = 0;
+ do
+ {
+ if (p++ >= n)return 0;
+ c1 = tolower(*s1++);
+ c2 = tolower(*s2++);
+ }
+ while ( c1 && (c1 == c2) );
+
+ return c1 - c2;
+#endif
+}
+
+
+// -------------------------------------------------------------------------------
+/** @brief Evaluates an integer power
+ *
+ * todo: move somewhere where it fits better in than here
+ */
+inline unsigned int integer_pow (unsigned int base, unsigned int power)
+{
+ unsigned int res = 1;
+ for (unsigned int i = 0; i < power;++i)
+ res *= base;
+
+ return res;
+}
+} // end of namespace
+
+#endif // ! AI_STRINGCOMPARISON_H_INC
diff --git a/3rdparty/assimp/code/Subdivision.cpp b/3rdparty/assimp/code/Subdivision.cpp
new file mode 100644
index 000000000..1535813d5
--- /dev/null
+++ b/3rdparty/assimp/code/Subdivision.cpp
@@ -0,0 +1,589 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+
+#include "Subdivision.h"
+#include "SceneCombiner.h"
+#include "SpatialSort.h"
+#include "ProcessHelper.h"
+#include "Vertex.h"
+
+using namespace Assimp;
+void mydummy() {}
+
+// ------------------------------------------------------------------------------------------------
+/** Subdivider stub class to implement the Catmull-Clarke subdivision algorithm. The
+ * implementation is basing on recursive refinement. Directly evaluating the result is also
+ * possibel and much quicker, but it depends on lengthy matrix lookup tables. */
+// ------------------------------------------------------------------------------------------------
+class CatmullClarkSubdivider : public Subdivider
+{
+
+public:
+
+ void Subdivide (const aiMesh* mesh, aiMesh*& out, unsigned int num, bool discard_input);
+ void Subdivide (const aiMesh* const * smesh, size_t nmesh,
+ aiMesh** out, unsigned int num, bool discard_input);
+
+ // ---------------------------------------------------------------------------
+ /** Intermediate description of an edge between two corners of a polygon*/
+ // ---------------------------------------------------------------------------
+ struct Edge
+ {
+ Edge()
+ : ref(0)
+ {}
+ Vertex edge_point, midpoint;
+ unsigned int ref;
+ };
+
+
+
+ typedef std::vector<unsigned int> UIntVector;
+ typedef std::map<uint64_t,Edge> EdgeMap;
+
+ // ---------------------------------------------------------------------------
+ // Hashing function to derive an index into an #EdgeMap from two given
+ // 'unsigned int' vertex coordinates (!!distinct coordinates - same
+ // vertex position == same index!!).
+ // NOTE - this leads to rare hash collisions if a) sizeof(unsigned int)>4
+ // and (id[0]>2^32-1 or id[0]>2^32-1).
+ // MAKE_EDGE_HASH() uses temporaries, so INIT_EDGE_HASH() needs to be put
+ // at the head of every function which is about to use MAKE_EDGE_HASH().
+ // Reason is that the hash is that hash construction needs to hold the
+ // invariant id0<id1 to identify an edge - else two hashes would refer
+ // to the same edge.
+ // ---------------------------------------------------------------------------
+#define MAKE_EDGE_HASH(id0,id1) (eh_tmp0__=id0,eh_tmp1__=id1,\
+ (eh_tmp0__<eh_tmp1__?std::swap(eh_tmp0__,eh_tmp1__):mydummy()),(uint64_t)eh_tmp0__^((uint64_t)eh_tmp1__<<32u))
+
+
+#define INIT_EDGE_HASH_TEMPORARIES()\
+ unsigned int eh_tmp0__, eh_tmp1__;
+
+private:
+
+ void InternSubdivide (const aiMesh* const * smesh,
+ size_t nmesh,aiMesh** out, unsigned int num);
+};
+
+
+// ------------------------------------------------------------------------------------------------
+// Construct a subdivider of a specific type
+Subdivider* Subdivider::Create (Algorithm algo)
+{
+ switch (algo)
+ {
+ case CATMULL_CLARKE:
+ return new CatmullClarkSubdivider();
+ };
+
+ ai_assert(false);
+ return NULL; // shouldn't happen
+}
+
+// ------------------------------------------------------------------------------------------------
+// Call the Catmull Clark subdivision algorithm for one mesh
+void CatmullClarkSubdivider::Subdivide (
+ const aiMesh* mesh,
+ aiMesh*& out,
+ unsigned int num,
+ bool discard_input
+ )
+{
+ assert(mesh != out);
+ Subdivide(&mesh,1,&out,num,discard_input);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Call the Catmull Clark subdivision algorithm for multiple meshes
+void CatmullClarkSubdivider::Subdivide (
+ const aiMesh* const * smesh,
+ size_t nmesh,
+ aiMesh** out,
+ unsigned int num,
+ bool discard_input
+ )
+{
+ ai_assert(NULL != smesh && NULL != out);
+
+ // course, both regions may not overlap
+ assert(smesh<out || smesh+nmesh>out+nmesh);
+ if (!num) {
+
+ // No subdivision at all. Need to copy all the meshes .. argh.
+ if (discard_input) {
+ for (size_t s = 0; s < nmesh; ++s) {
+ out[s] = const_cast<aiMesh*>( smesh[s] );
+ const_cast<aiMesh*&>( smesh[s] ) = NULL;
+ }
+ }
+ else {
+ for (size_t s = 0; s < nmesh; ++s) {
+ SceneCombiner::Copy(out+s,smesh[s]);
+ }
+ }
+ return;
+ }
+
+ std::vector<const aiMesh*> inmeshes;
+ std::vector<aiMesh*> outmeshes;
+ std::vector<unsigned int> maptbl;
+
+ inmeshes.reserve(nmesh);
+ outmeshes.reserve(nmesh);
+ maptbl.reserve(nmesh);
+
+ // Remove pure line and point meshes from the working set to reduce the
+ // number of edge cases the subdivider is forced to deal with. Line and
+ // point meshes are simply passed through.
+ for (size_t s = 0; s < nmesh; ++s) {
+ const aiMesh* i = smesh[s];
+ // FIX - mPrimitiveTypes might not yet be initialized
+ if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE|aiPrimitiveType_POINT))==i->mPrimitiveTypes) {
+ DefaultLogger::get()->debug("Catmull-Clark Subdivider: Skipping pure line/point mesh");
+
+ if (discard_input) {
+ out[s] = const_cast<aiMesh*>( i );
+ const_cast<aiMesh*&>( smesh[s] ) = NULL;
+ }
+ else {
+ SceneCombiner::Copy(out+s,i);
+ }
+ continue;
+ }
+
+ outmeshes.push_back(NULL);inmeshes.push_back(i);
+ maptbl.push_back(s);
+ }
+
+ // Do the actual subdivision on the preallocated storage. InternSubdivide
+ // *always* assumes that enough storage is available, it does not bother
+ // checking any ranges.
+ ai_assert(inmeshes.size()==outmeshes.size()&&inmeshes.size()==maptbl.size());
+ if (inmeshes.empty()) {
+ DefaultLogger::get()->warn("Catmull-Clark Subdivider: Pure point/line scene, I can't do anything");
+ return;
+ }
+ InternSubdivide(&inmeshes.front(),inmeshes.size(),&outmeshes.front(),num);
+ for (unsigned int i = 0; i < maptbl.size(); ++i) {
+ ai_assert(outmeshes[i]);
+ out[maptbl[i]] = outmeshes[i];
+ }
+
+ if (discard_input) {
+ for (size_t s = 0; s < nmesh; ++s) {
+ delete smesh[s];
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Note - this is an implementation of the standard (recursive) Cm-Cl algorithm without further
+// optimizations (except we're using some nice LUTs). A description of the algorithm can be found
+// here: http://en.wikipedia.org/wiki/Catmull-Clark_subdivision_surface
+//
+// The code is mostly O(n), however parts are O(nlogn) which is therefore the algorithm's
+// expected total runtime complexity. The implementation is able to work in-place on the same
+// mesh arrays. Calling #InternSubdivide() directly is not encouraged. The code can operate
+// in-place unless 'smesh' and 'out' are equal (no strange overlaps or reorderings).
+// Previous data is replaced/deleted then.
+// ------------------------------------------------------------------------------------------------
+void CatmullClarkSubdivider::InternSubdivide (
+ const aiMesh* const * smesh,
+ size_t nmesh,
+ aiMesh** out,
+ unsigned int num
+ )
+{
+ ai_assert(NULL != smesh && NULL != out);
+ INIT_EDGE_HASH_TEMPORARIES();
+
+ // no subdivision requested or end of recursive refinement
+ if (!num) {
+ return;
+ }
+
+ UIntVector maptbl;
+ SpatialSort spatial;
+
+ // ---------------------------------------------------------------------
+ // 0. Offset table to index all meshes continously , generate a spatially
+ // sorted representation of all vertices in all meshes.
+ // ---------------------------------------------------------------------
+ typedef std::pair<unsigned int,unsigned int> IntPair;
+ std::vector<IntPair> moffsets(nmesh);
+ unsigned int totfaces = 0, totvert = 0;
+ for (size_t t = 0; t < nmesh; ++t) {
+ const aiMesh* mesh = smesh[t];
+
+ spatial.Append(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D),false);
+ moffsets[t] = IntPair(totfaces,totvert);
+
+ totfaces += mesh->mNumFaces;
+ totvert += mesh->mNumVertices;
+ }
+
+ spatial.Finalize();
+ const unsigned int num_unique = spatial.GenerateMappingTable(maptbl,ComputePositionEpsilon(smesh,nmesh));
+
+
+#define FLATTEN_VERTEX_IDX(mesh_idx, vert_idx) (moffsets[mesh_idx].second+vert_idx)
+#define FLATTEN_FACE_IDX(mesh_idx, face_idx) (moffsets[mesh_idx].first+face_idx)
+
+ // ---------------------------------------------------------------------
+ // 1. Compute the centroid point for all faces
+ // ---------------------------------------------------------------------
+ std::vector<Vertex> centroids(totfaces);
+ unsigned int nfacesout = 0;
+ for (size_t t = 0, n = 0; t < nmesh; ++t) {
+ const aiMesh* mesh = smesh[t];
+ for (unsigned int i = 0; i < mesh->mNumFaces;++i,++n)
+ {
+ const aiFace& face = mesh->mFaces[i];
+ Vertex& c = centroids[n];
+
+ for (unsigned int a = 0; a < face.mNumIndices;++a) {
+ c += Vertex(mesh,face.mIndices[a]);
+ }
+
+ c /= static_cast<float>(face.mNumIndices);
+ nfacesout += face.mNumIndices;
+ }
+ }
+
+ EdgeMap edges;
+
+ // ---------------------------------------------------------------------
+ // 2. Set each edge point to be the average of all neighbouring
+ // face points and original points. Every edge exists twice
+ // if there is a neighboring face.
+ // ---------------------------------------------------------------------
+ for (size_t t = 0; t < nmesh; ++t) {
+ const aiMesh* mesh = smesh[t];
+
+ for (unsigned int i = 0; i < mesh->mNumFaces;++i) {
+ const aiFace& face = mesh->mFaces[i];
+
+ for (unsigned int p =0; p< face.mNumIndices; ++p) {
+ const unsigned int id[] = {
+ face.mIndices[p],
+ face.mIndices[p==face.mNumIndices-1?0:p+1]
+ };
+ const unsigned int mp[] = {
+ maptbl[FLATTEN_VERTEX_IDX(t,id[0])],
+ maptbl[FLATTEN_VERTEX_IDX(t,id[1])]
+ };
+
+ Edge& e = edges[MAKE_EDGE_HASH(mp[0],mp[1])];
+ e.ref++;
+ if (e.ref<=2) {
+ if (e.ref==1) { // original points (end points) - add only once
+ e.edge_point = e.midpoint = Vertex(mesh,id[0])+Vertex(mesh,id[1]);
+ e.midpoint *= 0.5f;
+ }
+ e.edge_point += centroids[FLATTEN_FACE_IDX(t,i)];
+ }
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ // 3. Normalize edge points
+ // ---------------------------------------------------------------------
+ {unsigned int bad_cnt = 0;
+ for (EdgeMap::iterator it = edges.begin(); it != edges.end(); ++it) {
+ if ((*it).second.ref < 2) {
+ ai_assert((*it).second.ref);
+ ++bad_cnt;
+ }
+ (*it).second.edge_point *= 1.f/((*it).second.ref+2.f);
+ }
+
+ if (bad_cnt) {
+ // Report the number of bad edges. bad edges are referenced by less than two
+ // faces in the mesh. They occur at outer model boundaries in non-closed
+ // shapes.
+ char tmp[512];
+ sprintf(tmp,"Catmull-Clark Subdivider: got %u bad edges touching only one face (totally %u edges). ",
+ bad_cnt,static_cast<unsigned int>(edges.size()));
+
+ DefaultLogger::get()->debug(tmp);
+ }}
+
+ // ---------------------------------------------------------------------
+ // 4. Compute a vertex-face adjacency table. We can't reuse the code
+ // from VertexTriangleAdjacency because we need the table for multiple
+ // meshes and out vertex indices need to be mapped to distinct values
+ // first.
+ // ---------------------------------------------------------------------
+ UIntVector faceadjac(nfacesout), cntadjfac(maptbl.size(),0), ofsadjvec(maptbl.size()+1,0); {
+ for (size_t t = 0; t < nmesh; ++t) {
+ const aiMesh* const minp = smesh[t];
+ for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
+
+ const aiFace& f = minp->mFaces[i];
+ for (unsigned int n = 0; n < f.mNumIndices; ++n) {
+ ++cntadjfac[maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]];
+ }
+ }
+ }
+ unsigned int cur = 0;
+ for (size_t i = 0; i < cntadjfac.size(); ++i) {
+ ofsadjvec[i+1] = cur;
+ cur += cntadjfac[i];
+ }
+ for (size_t t = 0; t < nmesh; ++t) {
+ const aiMesh* const minp = smesh[t];
+ for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
+
+ const aiFace& f = minp->mFaces[i];
+ for (unsigned int n = 0; n < f.mNumIndices; ++n) {
+ faceadjac[ofsadjvec[1+maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]++] = FLATTEN_FACE_IDX(t,i);
+ }
+ }
+ }
+
+ // check the other way round for consistency
+#ifdef _DEBUG
+
+ for (size_t t = 0; t < ofsadjvec.size()-1; ++t) {
+ for (unsigned int m = 0; m < cntadjfac[t]; ++m) {
+ const unsigned int fidx = faceadjac[ofsadjvec[t]+m];
+ ai_assert(fidx < totfaces);
+ for (size_t n = 1; n < nmesh; ++n) {
+
+ if (moffsets[n].first > fidx) {
+ const aiMesh* msh = smesh[--n];
+ const aiFace& f = msh->mFaces[fidx-moffsets[n].first];
+
+ bool haveit = false;
+ for (unsigned int i = 0; i < f.mNumIndices; ++i) {
+ if (maptbl[FLATTEN_VERTEX_IDX(n,f.mIndices[i])]==(unsigned int)t) {
+ haveit = true; break;
+ }
+ }
+ ai_assert(haveit);
+ break;
+ }
+ }
+ }
+ }
+
+#endif
+ }
+
+#define GET_ADJACENT_FACES_AND_CNT(vidx,fstartout,numout) \
+ fstartout = &faceadjac[ofsadjvec[vidx]], numout = cntadjfac[vidx]
+
+ typedef std::pair<bool,Vertex> TouchedOVertex;
+ std::vector<TouchedOVertex > new_points(num_unique,TouchedOVertex(false,Vertex()));
+ // ---------------------------------------------------------------------
+ // 5. Spawn a quad from each face point to the corresponding edge points
+ // the original points being the fourth quad points.
+ // ---------------------------------------------------------------------
+ for (size_t t = 0; t < nmesh; ++t) {
+ const aiMesh* const minp = smesh[t];
+ aiMesh* const mout = out[t] = new aiMesh();
+
+ for (unsigned int a = 0; a < minp->mNumFaces; ++a) {
+ mout->mNumFaces += minp->mFaces[a].mNumIndices;
+ }
+
+ // We need random access to the old face buffer, so reuse is not possible.
+ mout->mFaces = new aiFace[mout->mNumFaces];
+
+ mout->mNumVertices = mout->mNumFaces*4;
+ mout->mVertices = new aiVector3D[mout->mNumVertices];
+
+ // quads only, keep material index
+ mout->mPrimitiveTypes = aiPrimitiveType_POLYGON;
+ mout->mMaterialIndex = minp->mMaterialIndex;
+
+ if (minp->HasNormals()) {
+ mout->mNormals = new aiVector3D[mout->mNumVertices];
+ }
+
+ if (minp->HasTangentsAndBitangents()) {
+ mout->mTangents = new aiVector3D[mout->mNumVertices];
+ mout->mBitangents = new aiVector3D[mout->mNumVertices];
+ }
+
+ for (unsigned int i = 0; minp->HasTextureCoords(i); ++i) {
+ mout->mTextureCoords[i] = new aiVector3D[mout->mNumVertices];
+ mout->mNumUVComponents[i] = minp->mNumUVComponents[i];
+ }
+
+ for (unsigned int i = 0; minp->HasVertexColors(i); ++i) {
+ mout->mColors[i] = new aiColor4D[mout->mNumVertices];
+ }
+
+ mout->mNumVertices = mout->mNumFaces<<2u;
+ for (unsigned int i = 0, v = 0, n = 0; i < minp->mNumFaces;++i) {
+
+ const aiFace& face = minp->mFaces[i];
+ for (unsigned int a = 0; a < face.mNumIndices;++a) {
+
+ // Get a clean new face.
+ aiFace& faceOut = mout->mFaces[n++];
+ faceOut.mIndices = new unsigned int [faceOut.mNumIndices = 4];
+
+ // Spawn a new quadrilateral (ccw winding) for this original point between:
+ // a) face centroid
+ centroids[FLATTEN_FACE_IDX(t,i)].SortBack(mout,faceOut.mIndices[0]=v++);
+
+ // b) adjacent edge on the left, seen from the centroid
+ const Edge& e0 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])],
+ maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a==face.mNumIndices-1?0:a+1])
+ ])]; // fixme: replace with mod face.mNumIndices?
+
+ // c) adjacent edge on the right, seen from the centroid
+ const Edge& e1 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])],
+ maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[!a?face.mNumIndices-1:a-1])
+ ])]; // fixme: replace with mod face.mNumIndices?
+
+ e0.edge_point.SortBack(mout,faceOut.mIndices[3]=v++);
+ e1.edge_point.SortBack(mout,faceOut.mIndices[1]=v++);
+
+ // d= original point P with distinct index i
+ // F := 0
+ // R := 0
+ // n := 0
+ // for each face f containing i
+ // F := F+ centroid of f
+ // R := R+ midpoint of edge of f from i to i+1
+ // n := n+1
+ //
+ // (F+2R+(n-3)P)/n
+ const unsigned int org = maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])];
+ TouchedOVertex& ov = new_points[org];
+
+ if (!ov.first) {
+ ov.first = true;
+
+ const unsigned int* adj; unsigned int cnt;
+ GET_ADJACENT_FACES_AND_CNT(org,adj,cnt);
+
+ if (cnt < 3) {
+ ov.second = Vertex(minp,face.mIndices[a]);
+ }
+ else {
+
+ Vertex F,R;
+ for (unsigned int o = 0; o < cnt; ++o) {
+ ai_assert(adj[o] < totfaces);
+ F += centroids[adj[o]];
+
+ // adj[0] is a global face index - search the face in the mesh list
+ const aiMesh* mp = NULL;
+ size_t nidx;
+
+ if (adj[o] < moffsets[0].first) {
+ mp = smesh[nidx=0];
+ }
+ else {
+ for (nidx = 1; nidx<= nmesh; ++nidx) {
+ if (nidx == nmesh ||moffsets[nidx].first > adj[o]) {
+ mp = smesh[--nidx];
+ break;
+ }
+ }
+ }
+
+ ai_assert(adj[o]-moffsets[nidx].first < mp->mNumFaces);
+ const aiFace& f = mp->mFaces[adj[o]-moffsets[nidx].first];
+# ifdef _DEBUG
+ bool haveit = false;
+# endif
+
+ // find our original point in the face
+ for (unsigned int m = 0; m < f.mNumIndices; ++m) {
+ if (maptbl[FLATTEN_VERTEX_IDX(nidx,f.mIndices[m])] == org) {
+
+ // add *both* edges. this way, we can be sure that we add
+ // *all* adjacent edges to R. In a closed shape, every
+ // edge is added twice - so we simply leave out the
+ // factor 2.f in the amove formula and get the right
+ // result.
+
+ const Edge& c0 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX(
+ nidx,f.mIndices[!m?f.mNumIndices-1:m-1])])];
+ // fixme: replace with mod face.mNumIndices?
+
+ const Edge& c1 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX(
+ nidx,f.mIndices[m==f.mNumIndices-1?0:m+1])])];
+ // fixme: replace with mod face.mNumIndices?
+ R += c0.midpoint+c1.midpoint;
+
+# ifdef _DEBUG
+ haveit = true;
+# endif
+ break;
+ }
+ }
+
+ // this invariant *must* hold if the vertex-to-face adjacency table is valid
+#ifdef _DEBUG
+ ai_assert(haveit);
+#endif
+ }
+
+ const float div = static_cast<float>(cnt), divsq = 1.f/(div*div);
+ ov.second = Vertex(minp,face.mIndices[a])*((div-3.f) / div) + R*divsq + F*divsq;
+ }
+ }
+ ov.second.SortBack(mout,faceOut.mIndices[2]=v++);
+ }
+ }
+ }
+
+ // ---------------------------------------------------------------------
+ // 7. Apply the next subdivision step.
+ // ---------------------------------------------------------------------
+ if (num != 1) {
+ std::vector<aiMesh*> tmp(nmesh);
+ InternSubdivide (out,nmesh,&tmp.front(),num-1);
+ for (size_t i = 0; i < nmesh; ++i) {
+ delete out[i];
+ out[i] = tmp[i];
+ }
+ }
+}
diff --git a/3rdparty/assimp/code/Subdivision.h b/3rdparty/assimp/code/Subdivision.h
new file mode 100644
index 000000000..fb67001db
--- /dev/null
+++ b/3rdparty/assimp/code/Subdivision.h
@@ -0,0 +1,124 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a helper class to evaluate subdivision surfaces.*/
+#ifndef AI_SUBDISIVION_H_INC
+#define AI_SUBDISIVION_H_INC
+namespace Assimp {
+
+// ------------------------------------------------------------------------------
+/** Helper class to evaluate subdivision surfaces. Different algorithms
+ * are provided for choice. */
+// ------------------------------------------------------------------------------
+class ASSIMP_API Subdivider
+{
+public:
+
+ /** Enumerates all supported subvidision algorithms */
+ enum Algorithm {
+ CATMULL_CLARKE = 0x1
+ };
+
+public:
+
+ virtual ~Subdivider() {
+ }
+
+public:
+
+ // ---------------------------------------------------------------
+ /** Create a subdivider of a specific type
+ *
+ * @param algo Algorithm to be used for subdivision
+ * @return Subdivider instance. */
+ static Subdivider* Create (Algorithm algo);
+
+ // ---------------------------------------------------------------
+ /** Subdivide a mesh using the selected algorithm
+ *
+ * @param mesh First mesh to be subdivided. Must be in verbose
+ * format.
+ * @param out Receives the output mesh, allocated by me.
+ * @param num Number of subdivisions to perform.
+ * @param discard_input If true is passed, the input mesh is
+ * deleted after the subdivision is complete. This can
+ * improve performance because it allows the optimization
+ * to reuse the existing mesh for intermediate results.
+ * @pre out!=mesh*/
+ virtual void Subdivide (const aiMesh* mesh,
+ aiMesh*& out, unsigned int num,
+ bool discard_input = false) = 0;
+
+ // ---------------------------------------------------------------
+ /** Subdivide multiple meshes using the selected algorithm. This
+ * avoids erroneous smoothing on objects consisting of multiple
+ * per-material meshes. Usually, most 3d modellers smooth on a
+ * per-object base, regardless the materials assigned to the
+ * meshes.
+ *
+ * @param smesh Array of meshes to be subdivided. Must be in
+ * verbose format.
+ * @param nmesh Number of meshes in smesh.
+ * @param out Receives the output meshes. The array must be
+ * sufficiently large (at least @c nmesh elements) and may not
+ * overlap the input array. Output meshes map one-to-one to
+ * their corresponding input meshes. The meshes are allocated
+ * by the function.
+ * @param discard_input If true is passed, input meshes are
+ * deleted after the subdivision is complete. This can
+ * improve performance because it allows the optimization
+ * of reusing existing meshes for intermediate results.
+ * @param num Number of subdivisions to perform.
+ * @pre nmesh != 0, smesh and out may not overlap*/
+ virtual void Subdivide (
+ const aiMesh* const * smesh,
+ size_t nmesh,
+ aiMesh** out,
+ unsigned int num,
+ bool discard_input = false) = 0;
+
+private:
+};
+
+} // end namespace Assimp
+
+
+#endif // !! AI_SUBDISIVION_H_INC
+
diff --git a/3rdparty/assimp/code/TargetAnimation.cpp b/3rdparty/assimp/code/TargetAnimation.cpp
new file mode 100644
index 000000000..cdb350ffc
--- /dev/null
+++ b/3rdparty/assimp/code/TargetAnimation.cpp
@@ -0,0 +1,246 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#include "TargetAnimation.h"
+#include <algorithm>
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+KeyIterator::KeyIterator(const std::vector<aiVectorKey>* _objPos,
+ const std::vector<aiVectorKey>* _targetObjPos,
+ const aiVector3D* defaultObjectPos /*= NULL*/,
+ const aiVector3D* defaultTargetPos /*= NULL*/)
+
+ : reachedEnd (false)
+ , curTime (-1.)
+ , objPos (_objPos)
+ , targetObjPos (_targetObjPos)
+ , nextObjPos (0)
+ , nextTargetObjPos(0)
+{
+ // Generate default transformation tracks if necessary
+ if (!objPos || objPos->empty())
+ {
+ defaultObjPos.resize(1);
+ defaultObjPos.front().mTime = 10e10;
+
+ if (defaultObjectPos)
+ defaultObjPos.front().mValue = *defaultObjectPos;
+
+ objPos = & defaultObjPos;
+ }
+ if (!targetObjPos || targetObjPos->empty())
+ {
+ defaultTargetObjPos.resize(1);
+ defaultTargetObjPos.front().mTime = 10e10;
+
+ if (defaultTargetPos)
+ defaultTargetObjPos.front().mValue = *defaultTargetPos;
+
+ targetObjPos = & defaultTargetObjPos;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <class T>
+inline T Interpolate(const T& one, const T& two, float val)
+{
+ return one + (two-one)*val;
+}
+
+// ------------------------------------------------------------------------------------------------
+void KeyIterator::operator ++()
+{
+ // If we are already at the end of all keyframes, return
+ if (reachedEnd) {
+ return;
+ }
+
+ // Now search in all arrays for the time value closest
+ // to our current position on the time line
+ double d0,d1;
+
+ d0 = objPos->at ( std::min<unsigned int> ( nextObjPos, objPos->size()-1) ).mTime;
+ d1 = targetObjPos->at( std::min<unsigned int> ( nextTargetObjPos, targetObjPos->size()-1) ).mTime;
+
+ // Easiest case - all are identical. In this
+ // case we don't need to interpolate so we can
+ // return earlier
+ if ( d0 == d1 )
+ {
+ curTime = d0;
+ curPosition = objPos->at(nextObjPos).mValue;
+ curTargetPosition = targetObjPos->at(nextTargetObjPos).mValue;
+
+ // increment counters
+ if (objPos->size() != nextObjPos-1)
+ ++nextObjPos;
+
+ if (targetObjPos->size() != nextTargetObjPos-1)
+ ++nextTargetObjPos;
+ }
+
+ // An object position key is closest to us
+ else if (d0 < d1)
+ {
+ curTime = d0;
+
+ // interpolate the other
+ if (1 == targetObjPos->size() || !nextTargetObjPos) {
+ curTargetPosition = targetObjPos->at(0).mValue;
+ }
+ else
+ {
+ const aiVectorKey& last = targetObjPos->at(nextTargetObjPos);
+ const aiVectorKey& first = targetObjPos->at(nextTargetObjPos-1);
+
+ curTargetPosition = Interpolate(first.mValue, last.mValue, (float) (
+ (curTime-first.mTime) / (last.mTime-first.mTime) ));
+ }
+
+ if (objPos->size() != nextObjPos-1)
+ ++nextObjPos;
+ }
+ // A target position key is closest to us
+ else
+ {
+ curTime = d1;
+
+ // interpolate the other
+ if (1 == objPos->size() || !nextObjPos) {
+ curPosition = objPos->at(0).mValue;
+ }
+ else
+ {
+ const aiVectorKey& last = objPos->at(nextObjPos);
+ const aiVectorKey& first = objPos->at(nextObjPos-1);
+
+ curPosition = Interpolate(first.mValue, last.mValue, (float) (
+ (curTime-first.mTime) / (last.mTime-first.mTime)));
+ }
+
+ if (targetObjPos->size() != nextTargetObjPos-1)
+ ++nextTargetObjPos;
+ }
+
+ if (nextObjPos >= objPos->size()-1 &&
+ nextTargetObjPos >= targetObjPos->size()-1)
+ {
+ // We reached the very last keyframe
+ reachedEnd = true;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void TargetAnimationHelper::SetTargetAnimationChannel (
+ const std::vector<aiVectorKey>* _targetPositions)
+{
+ ai_assert(NULL != _targetPositions);
+ targetPositions = _targetPositions;
+}
+
+// ------------------------------------------------------------------------------------------------
+void TargetAnimationHelper::SetMainAnimationChannel (
+ const std::vector<aiVectorKey>* _objectPositions)
+{
+ ai_assert(NULL != _objectPositions);
+ objectPositions = _objectPositions;
+}
+
+// ------------------------------------------------------------------------------------------------
+void TargetAnimationHelper::SetFixedMainAnimationChannel(
+ const aiVector3D& fixed)
+{
+ objectPositions = NULL; // just to avoid confusion
+ fixedMain = fixed;
+}
+
+// ------------------------------------------------------------------------------------------------
+void TargetAnimationHelper::Process(std::vector<aiVectorKey>* distanceTrack)
+{
+ ai_assert(NULL != targetPositions && NULL != distanceTrack);
+
+ // TODO: in most cases we won't need the extra array
+ std::vector<aiVectorKey> real;
+
+ std::vector<aiVectorKey>* fill = (distanceTrack == objectPositions ? &real : distanceTrack);
+ fill->reserve(std::max( objectPositions->size(), targetPositions->size() ));
+
+ // Iterate through all object keys and interpolate their values if necessary.
+ // Then get the corresponding target position, compute the difference
+ // vector between object and target position. Then compute a rotation matrix
+ // that rotates the base vector of the object coordinate system at that time
+ // to match the diff vector.
+
+ KeyIterator iter(objectPositions,targetPositions,&fixedMain);
+ for (;!iter.Finished();++iter)
+ {
+ const aiVector3D& position = iter.GetCurPosition();
+ const aiVector3D& tposition = iter.GetCurTargetPosition();
+
+ // diff vector
+ aiVector3D diff = tposition - position;
+ float f = diff.Length();
+
+ // output distance vector
+ if (f)
+ {
+ fill->push_back(aiVectorKey());
+ aiVectorKey& v = fill->back();
+ v.mTime = iter.GetCurTime();
+ v.mValue = diff;
+
+ diff /= f;
+ }
+ else
+ {
+ // FIXME: handle this
+ }
+
+ // diff is now the vector in which our camera is pointing
+ }
+
+ if (real.size()) {
+ *distanceTrack = real;
+ }
+}
diff --git a/3rdparty/assimp/code/TargetAnimation.h b/3rdparty/assimp/code/TargetAnimation.h
new file mode 100644
index 000000000..049946b39
--- /dev/null
+++ b/3rdparty/assimp/code/TargetAnimation.h
@@ -0,0 +1,179 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a helper class for the ASE and 3DS loaders to
+ help them compute camera and spot light animation channels */
+#ifndef AI_TARGET_ANIMATION_H_INC
+#define AI_TARGET_ANIMATION_H_INC
+
+
+namespace Assimp {
+
+
+
+// ---------------------------------------------------------------------------
+/** Helper class to iterate through all keys in an animation channel.
+ *
+ * Missing tracks are interpolated. This is a helper class for
+ * TargetAnimationHelper, but it can be freely used for other purposes.
+*/
+class ASSIMP_API KeyIterator
+{
+public:
+
+
+ // ------------------------------------------------------------------
+ /** Constructs a new key iterator
+ *
+ * @param _objPos Object position track. May be NULL.
+ * @param _targetObjPos Target object position track. May be NULL.
+ * @param defaultObjectPos Default object position to be used if
+ * no animated track is available. May be NULL.
+ * @param defaultTargetPos Default target position to be used if
+ * no animated track is available. May be NULL.
+ */
+ KeyIterator(const std::vector<aiVectorKey>* _objPos,
+ const std::vector<aiVectorKey>* _targetObjPos,
+ const aiVector3D* defaultObjectPos = NULL,
+ const aiVector3D* defaultTargetPos = NULL);
+
+ // ------------------------------------------------------------------
+ /** Returns true if all keys have been processed
+ */
+ bool Finished() const
+ {return reachedEnd;}
+
+ // ------------------------------------------------------------------
+ /** Increment the iterator
+ */
+ void operator++();
+ inline void operator++(int)
+ {return ++(*this);}
+
+
+
+ // ------------------------------------------------------------------
+ /** Getters to retrieve the current state of the iterator
+ */
+ inline const aiVector3D& GetCurPosition() const
+ {return curPosition;}
+
+ inline const aiVector3D& GetCurTargetPosition() const
+ {return curTargetPosition;}
+
+ inline double GetCurTime() const
+ {return curTime;}
+
+private:
+
+ //! Did we reach the end?
+ bool reachedEnd;
+
+ //! Represents the current position of the iterator
+ aiVector3D curPosition, curTargetPosition;
+
+ double curTime;
+
+ //! Input tracks and the next key to process
+ const std::vector<aiVectorKey>* objPos,*targetObjPos;
+
+ unsigned int nextObjPos, nextTargetObjPos;
+ std::vector<aiVectorKey> defaultObjPos,defaultTargetObjPos;
+};
+
+// ---------------------------------------------------------------------------
+/** Helper class for the 3DS and ASE loaders to compute camera and spot light
+ * animations.
+ *
+ * 3DS and ASE store the differently to Assimp - there is an animation
+ * channel for the camera/spot light itself and a separate position
+ * animation channels specifying the position of the camera/spot light
+ * look-at target */
+class ASSIMP_API TargetAnimationHelper
+{
+public:
+
+ TargetAnimationHelper()
+ : targetPositions (NULL)
+ , objectPositions (NULL)
+ {}
+
+
+ // ------------------------------------------------------------------
+ /** Sets the target animation channel
+ *
+ * This channel specifies the position of the camera/spot light
+ * target at a specific position.
+ *
+ * @param targetPositions Translation channel*/
+ void SetTargetAnimationChannel (const
+ std::vector<aiVectorKey>* targetPositions);
+
+
+ // ------------------------------------------------------------------
+ /** Sets the main animation channel
+ *
+ * @param objectPositions Translation channel */
+ void SetMainAnimationChannel ( const
+ std::vector<aiVectorKey>* objectPositions);
+
+ // ------------------------------------------------------------------
+ /** Sets the main animation channel to a fixed value
+ *
+ * @param fixed Fixed value for the main animation channel*/
+ void SetFixedMainAnimationChannel(const aiVector3D& fixed);
+
+
+ // ------------------------------------------------------------------
+ /** Computes final animation channels
+ * @param distanceTrack Receive camera translation keys ... != NULL. */
+ void Process( std::vector<aiVectorKey>* distanceTrack );
+
+
+private:
+
+ const std::vector<aiVectorKey>* targetPositions,*objectPositions;
+ aiVector3D fixedMain;
+};
+
+
+} // ! end namespace Assimp
+
+#endif // include guard
diff --git a/3rdparty/assimp/code/TerragenLoader.cpp b/3rdparty/assimp/code/TerragenLoader.cpp
new file mode 100644
index 000000000..ed9762e08
--- /dev/null
+++ b/3rdparty/assimp/code/TerragenLoader.cpp
@@ -0,0 +1,254 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the Terragen importer class */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
+#include "TerragenLoader.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+TerragenImporter::TerragenImporter()
+: configComputeUVs (false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+TerragenImporter::~TerragenImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool TerragenImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ // check file extension
+ std::string extension = GetExtension(pFile);
+
+ if ( extension == "ter")
+ return true;
+
+ if ( !extension.length() || checkSig) {
+ /* If CanRead() is called in order to check whether we
+ * support a specific file extension in general pIOHandler
+ * might be NULL and it's our duty to return true here.
+ */
+ if (!pIOHandler)return true;
+ const char* tokens[] = {"terragen"};
+ return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a string of all file extensions supported
+void TerragenImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("ter");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup import properties
+void TerragenImporter::SetupProperties(const Importer* pImp)
+{
+ // AI_CONFIG_IMPORT_TER_MAKE_UVS
+ configComputeUVs = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_TER_MAKE_UVS,0) );
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void TerragenImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ IOStream* file = pIOHandler->Open( pFile, "rb");
+
+ // Check whether we can read from the file
+ if ( file == NULL)
+ throw DeadlyImportError( "Failed to open TERRAGEN TERRAIN file " + pFile + ".");
+
+ // Construct a stream reader to read all data in the correct endianess
+ StreamReaderLE reader(file);
+ if (reader.GetRemainingSize() < 16)
+ throw DeadlyImportError( "TER: file is too small" );
+
+ // Check for the existence of the two magic strings 'TERRAGEN' and 'TERRAIN '
+ if (::strncmp((const char*)reader.GetPtr(),AI_TERR_BASE_STRING,8))
+ throw DeadlyImportError( "TER: Magic string \'TERRAGEN\' not found" );
+
+ if (::strncmp((const char*)reader.GetPtr()+8,AI_TERR_TERRAIN_STRING,8))
+ throw DeadlyImportError( "TER: Magic string \'TERRAIN\' not found" );
+
+ unsigned int x = 0,y = 0,mode = 0;
+ float rad = 6370.f;
+
+
+ aiNode* root = pScene->mRootNode = new aiNode();
+ root->mName.Set("<TERRAGEN.TERRAIN>");
+
+ // Default scaling is 30
+ root->mTransformation.a1 = root->mTransformation.b2 = root->mTransformation.c3 = 30.f;
+
+ // Now read all chunks until we're finished or an EOF marker is encountered
+ reader.IncPtr(16);
+ while (reader.GetRemainingSize() >= 4)
+ {
+ const char* head = (const char*)reader.GetPtr();
+ reader.IncPtr(4);
+
+ // EOF, break in every case
+ if (!::strncmp(head,AI_TERR_EOF_STRING,4))
+ break;
+
+ // Number of x-data points
+ if (!::strncmp(head,AI_TERR_CHUNK_XPTS,4))
+ {
+ x = (uint16_t)reader.GetI2();
+ }
+ // Number of y-data points
+ else if (!::strncmp(head,AI_TERR_CHUNK_YPTS,4))
+ {
+ y = (uint16_t)reader.GetI2();
+ }
+ // Squared terrains width-1.
+ else if (!::strncmp(head,AI_TERR_CHUNK_SIZE,4))
+ {
+ x = y = (uint16_t)reader.GetI2()+1;
+ }
+ // terrain scaling
+ else if (!::strncmp(head,AI_TERR_CHUNK_SCAL,4))
+ {
+ root->mTransformation.a1 = reader.GetF4();
+ root->mTransformation.b2 = reader.GetF4();
+ root->mTransformation.c3 = reader.GetF4();
+ }
+ // mapping == 1: earth radius
+ else if (!::strncmp(head,AI_TERR_CHUNK_CRAD,4))
+ {
+ rad = reader.GetF4();
+ }
+ // mapping mode
+ else if (!::strncmp(head,AI_TERR_CHUNK_CRVM,4))
+ {
+ mode = reader.GetI1();
+ if (0 != mode)
+ DefaultLogger::get()->error("TER: Unsupported mapping mode, a flat terrain is returned");
+ }
+ // actual terrain data
+ else if (!::strncmp(head,AI_TERR_CHUNK_ALTW,4))
+ {
+ float hscale = (float)reader.GetI2() / 65536;
+ float bheight = (float)reader.GetI2();
+
+ if (!hscale)hscale = 1;
+
+ // Ensure we have enough data
+ if (reader.GetRemainingSize() < x*y*2)
+ throw DeadlyImportError("TER: ALTW chunk is too small");
+
+ if (x <= 1 || y <= 1)
+ throw DeadlyImportError("TER: Invalid terrain size");
+
+ // Allocate the output mesh
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes = 1];
+ aiMesh* m = pScene->mMeshes[0] = new aiMesh();
+
+ // We return quads
+ aiFace* f = m->mFaces = new aiFace[m->mNumFaces = (x-1)*(y-1)];
+ aiVector3D* pv = m->mVertices = new aiVector3D[m->mNumVertices = m->mNumFaces*4];
+
+ aiVector3D *uv( NULL );
+ float step_y( 0.0f ), step_x( 0.0f );
+ if (configComputeUVs) {
+ uv = m->mTextureCoords[0] = new aiVector3D[m->mNumVertices];
+ step_y = 1.f/y;
+ step_x = 1.f/x;
+ }
+ const int16_t* data = (const int16_t*)reader.GetPtr();
+
+ for (unsigned int yy = 0, t = 0; yy < y-1;++yy) {
+ for (unsigned int xx = 0; xx < x-1;++xx,++f) {
+
+ // make verts
+ const float fy = (float)yy, fx = (float)xx;
+ register unsigned tmp,tmp2;
+ *pv++ = aiVector3D(fx,fy, (float)data[(tmp2=x*yy) + xx] * hscale + bheight);
+ *pv++ = aiVector3D(fx,fy+1, (float)data[(tmp=x*(yy+1)) + xx] * hscale + bheight);
+ *pv++ = aiVector3D(fx+1,fy+1,(float)data[tmp + xx+1] * hscale + bheight);
+ *pv++ = aiVector3D(fx+1,fy, (float)data[tmp2 + xx+1] * hscale + bheight);
+
+ // also make texture coordinates, if necessary
+ if (configComputeUVs) {
+ *uv++ = aiVector3D( step_x*xx, step_y*yy, 0.f );
+ *uv++ = aiVector3D( step_x*xx, step_y*(yy+1), 0.f );
+ *uv++ = aiVector3D( step_x*(xx+1), step_y*(yy+1), 0.f );
+ *uv++ = aiVector3D( step_x*(xx+1), step_y*yy, 0.f );
+ }
+
+ // make indices
+ f->mIndices = new unsigned int[f->mNumIndices = 4];
+ for (unsigned int i = 0; i < 4;++i)
+ f->mIndices[i] = t++;
+ }
+ }
+
+ // Add the mesh to the root node
+ root->mMeshes = new unsigned int[root->mNumMeshes = 1];
+ root->mMeshes[0] = 0;
+ }
+
+ // Get to the next chunk (4 byte aligned)
+ unsigned dtt;
+ if ((dtt = reader.GetCurrentPos() & 0x3))
+ reader.IncPtr(4-dtt);
+ }
+
+ // Check whether we have a mesh now
+ if (pScene->mNumMeshes != 1)
+ throw DeadlyImportError("TER: Unable to load terrain");
+
+ // Set the AI_SCENE_FLAGS_TERRAIN bit
+ pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN;
+}
+
+#endif // !! ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
diff --git a/3rdparty/assimp/code/TerragenLoader.h b/3rdparty/assimp/code/TerragenLoader.h
new file mode 100644
index 000000000..ddcc2bfc8
--- /dev/null
+++ b/3rdparty/assimp/code/TerragenLoader.h
@@ -0,0 +1,107 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 TerragenLoader.h
+ * @brief Declaration of the .ter importer class.
+ */
+#ifndef INCLUDED_AI_TERRAGEN_TERRAIN_LOADER_H
+#define INCLUDED_AI_TERRAGEN_TERRAIN_LOADER_H
+
+#include "BaseImporter.h"
+namespace Assimp {
+
+// Magic strings
+#define AI_TERR_BASE_STRING "TERRAGEN"
+#define AI_TERR_TERRAIN_STRING "TERRAIN "
+#define AI_TERR_EOF_STRING "EOF "
+
+// Chunka
+#define AI_TERR_CHUNK_XPTS "XPTS"
+#define AI_TERR_CHUNK_YPTS "YPTS"
+#define AI_TERR_CHUNK_SIZE "SIZE"
+#define AI_TERR_CHUNK_SCAL "SCAL"
+#define AI_TERR_CHUNK_CRAD "CRAD"
+#define AI_TERR_CHUNK_CRVM "CRVM"
+#define AI_TERR_CHUNK_ALTW "ALTW"
+
+// ---------------------------------------------------------------------------
+/** @brief Importer class to load Terragen (0.9) terrain files.
+ *
+ * The loader is basing on the information found here:
+ * http://www.planetside.co.uk/terragen/dev/tgterrain.html#chunks
+*/
+class TerragenImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ TerragenImporter();
+
+ /** Destructor, private as well */
+ ~TerragenImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+private:
+
+ bool configComputeUVs;
+
+}; //! class TerragenImporter
+
+} // end of namespace Assimp
+
+#endif // AI_AC3DIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/TextureTransform.cpp b/3rdparty/assimp/code/TextureTransform.cpp
new file mode 100644
index 000000000..597b34cb5
--- /dev/null
+++ b/3rdparty/assimp/code/TextureTransform.cpp
@@ -0,0 +1,563 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 A helper class that processes texture transformations */
+
+
+#include "AssimpPCH.h"
+#include "TextureTransform.h"
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+TextureTransformStep::TextureTransformStep()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+TextureTransformStep::~TextureTransformStep()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool TextureTransformStep::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_TransformUVCoords) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup properties
+void TextureTransformStep::SetupProperties(const Importer* pImp)
+{
+ configFlags = pImp->GetPropertyInteger(AI_CONFIG_PP_TUV_EVALUATE,AI_UVTRAFO_ALL);
+}
+
+// ------------------------------------------------------------------------------------------------
+void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info)
+{
+ /* This function tries to simplify the input UV transformation.
+ * That's very important as it allows us to reduce the number
+ * of output UV channels. The oder in which the transformations
+ * are applied is - as always - scaling, rotation, translation.
+ */
+
+ char szTemp[512];
+ int rounded = 0;
+
+
+ /* Optimize the rotation angle. That's slightly difficult as
+ * we have an inprecise floating-point number (when comparing
+ * UV transformations we'll take that into account by using
+ * an epsilon of 5 degrees). If there is a rotation value, we can't
+ * perform any further optimizations.
+ */
+ if (info.mRotation)
+ {
+ float out = info.mRotation;
+ if ((rounded = (int)(info.mRotation / (float)AI_MATH_TWO_PI)))
+ {
+ out -= rounded*(float)AI_MATH_PI;
+
+ sprintf(szTemp,"Texture coordinate rotation %f can be simplified to %f",info.mRotation,out);
+ DefaultLogger::get()->info(szTemp);
+ }
+
+ // Next step - convert negative rotation angles to positives
+ if (out < 0.f)
+ out = (float)AI_MATH_TWO_PI * 2 + out;
+
+ info.mRotation = out;
+ return;
+ }
+
+
+ /* Optimize UV translation in the U direction. To determine whether
+ * or not we can optimize we need to look at the requested mapping
+ * type (e.g. if mirroring is active there IS a difference between
+ * offset 2 and 3)
+ */
+ if ((rounded = (int)info.mTranslation.x)) {
+ float out;
+ szTemp[0] = 0;
+ if (aiTextureMapMode_Wrap == info.mapU) {
+ // Wrap - simple take the fraction of the field
+ out = info.mTranslation.x-(float)rounded;
+ sprintf(szTemp,"[w] UV U offset %f can be simplified to %f",info.mTranslation.x,out);
+ }
+ else if (aiTextureMapMode_Mirror == info.mapU && 1 != rounded) {
+ // Mirror
+ if (rounded % 2)
+ rounded--;
+ out = info.mTranslation.x-(float)rounded;
+
+ sprintf(szTemp,"[m/d] UV U offset %f can be simplified to %f",info.mTranslation.x,out);
+ }
+ else if (aiTextureMapMode_Clamp == info.mapU || aiTextureMapMode_Decal == info.mapU) {
+ // Clamp - translations beyond 1,1 are senseless
+ sprintf(szTemp,"[c] UV U offset %f can be clamped to 1.0f",info.mTranslation.x);
+
+ out = 1.f;
+ }
+ if (szTemp[0]) {
+ DefaultLogger::get()->info(szTemp);
+ info.mTranslation.x = out;
+ }
+ }
+
+ /* Optimize UV translation in the V direction. To determine whether
+ * or not we can optimize we need to look at the requested mapping
+ * type (e.g. if mirroring is active there IS a difference between
+ * offset 2 and 3)
+ */
+ if ((rounded = (int)info.mTranslation.y)) {
+ float out;
+ szTemp[0] = 0;
+ if (aiTextureMapMode_Wrap == info.mapV) {
+ // Wrap - simple take the fraction of the field
+ out = info.mTranslation.y-(float)rounded;
+ sprintf(szTemp,"[w] UV V offset %f can be simplified to %f",info.mTranslation.y,out);
+ }
+ else if (aiTextureMapMode_Mirror == info.mapV && 1 != rounded) {
+ // Mirror
+ if (rounded % 2)
+ rounded--;
+ out = info.mTranslation.x-(float)rounded;
+
+ sprintf(szTemp,"[m/d] UV V offset %f can be simplified to %f",info.mTranslation.y,out);
+ }
+ else if (aiTextureMapMode_Clamp == info.mapV || aiTextureMapMode_Decal == info.mapV) {
+ // Clamp - translations beyond 1,1 are senseless
+ sprintf(szTemp,"[c] UV V offset %f canbe clamped to 1.0f",info.mTranslation.y);
+
+ out = 1.f;
+ }
+ if (szTemp[0]) {
+ DefaultLogger::get()->info(szTemp);
+ info.mTranslation.y = out;
+ }
+ }
+ return;
+}
+
+// ------------------------------------------------------------------------------------------------
+void UpdateUVIndex(const std::list<TTUpdateInfo>& l, unsigned int n)
+{
+ // Don't set if == 0 && wasn't set before
+ for (std::list<TTUpdateInfo>::const_iterator it = l.begin();it != l.end(); ++it) {
+ const TTUpdateInfo& info = *it;
+
+ if (info.directShortcut)
+ *info.directShortcut = n;
+ else if (!n)
+ {
+ info.mat->AddProperty<int>((int*)&n,1,AI_MATKEY_UVWSRC(info.semantic,info.index));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+inline const char* MappingModeToChar(aiTextureMapMode map)
+{
+ if (aiTextureMapMode_Wrap == map)
+ return "-w";
+
+ if (aiTextureMapMode_Mirror == map)
+ return "-m";
+
+ return "-c";
+}
+
+// ------------------------------------------------------------------------------------------------
+void TextureTransformStep::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("TransformUVCoordsProcess begin");
+
+
+ /* We build a per-mesh list of texture transformations we'll need
+ * to apply. To achieve this, we iterate through all materials,
+ * find all textures and get their transformations and UV indices.
+ * Then we search for all meshes using this material.
+ */
+ typedef std::list<STransformVecInfo> MeshTrafoList;
+ std::vector<MeshTrafoList> meshLists(pScene->mNumMeshes);
+
+ for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
+
+ aiMaterial* mat = pScene->mMaterials[i];
+ for (unsigned int a = 0; a < mat->mNumProperties;++a) {
+
+ aiMaterialProperty* prop = mat->mProperties[a];
+ if (!::strcmp( prop->mKey.data, "$tex.file")) {
+ STransformVecInfo info;
+
+ // Setup a shortcut structure to allow for a fast updating
+ // of the UV index later
+ TTUpdateInfo update;
+ update.mat = (MaterialHelper*) mat;
+ update.semantic = prop->mSemantic;
+ update.index = prop->mIndex;
+
+ // Get textured properties and transform
+ for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2)
+ {
+ aiMaterialProperty* prop2 = mat->mProperties[a2];
+ if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex)
+ continue;
+
+ if ( !::strcmp( prop2->mKey.data, "$tex.uvwsrc"))
+ {
+ info.uvIndex = *((int*)prop2->mData);
+
+ // Store a direct pointer for later use
+ update.directShortcut = (unsigned int*) prop2->mData;
+ }
+
+ else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodeu"))
+ info.mapU = *((aiTextureMapMode*)prop2->mData);
+
+ else if ( !::strcmp( prop2->mKey.data, "$tex.mapmodev"))
+ info.mapV = *((aiTextureMapMode*)prop2->mData);
+
+ else if ( !::strcmp( prop2->mKey.data, "$tex.uvtrafo"))
+ {
+ // ValidateDS should check this
+ ai_assert(prop2->mDataLength >= 20);
+ ::memcpy(&info.mTranslation.x,prop2->mData,sizeof(float)*5);
+ delete[] prop2->mData;
+
+ // Directly remove this property from the list
+ mat->mNumProperties--;
+ for (unsigned int a3 = a2; a3 < mat->mNumProperties;++a3)
+ mat->mProperties[a3] = mat->mProperties[a3+1];
+
+ // Warn: could be an underflow, but nevertheless it should work
+ --a2;
+ }
+ }
+
+ // Find out which transformations are to be evaluated
+ if (!(configFlags & AI_UVTRAFO_ROTATION))
+ info.mRotation = 0.f;
+
+ if (!(configFlags & AI_UVTRAFO_SCALING))
+ info.mScaling = aiVector2D(1.f,1.f);
+
+ if (!(configFlags & AI_UVTRAFO_TRANSLATION))
+ info.mTranslation = aiVector2D(0.f,0.f);
+
+ // Do some preprocessing
+ PreProcessUVTransform(info);
+ info.uvIndex = std::min(info.uvIndex,AI_MAX_NUMBER_OF_TEXTURECOORDS -1u);
+
+ // Find out whether this material is used by more than
+ // one mesh. This will make our task much, much more difficult!
+ unsigned int cnt = 0;
+ for (unsigned int n = 0; n < pScene->mNumMeshes;++n) {
+ if (pScene->mMeshes[n]->mMaterialIndex == i)
+ ++cnt;
+ }
+
+ if (!cnt)
+ continue;
+ else if (1 != cnt) {
+ // This material is referenced by more than one mesh!
+ // So we need to make sure the UV index for the texture
+ // is identical for each of it ...
+ info.lockedPos = AI_TT_UV_IDX_LOCK_TBD;
+ }
+
+ // Get all coresponding meshes
+ for (unsigned int n = 0; n < pScene->mNumMeshes;++n) {
+ aiMesh* mesh = pScene->mMeshes[n];
+ if (mesh->mMaterialIndex != i || !mesh->mTextureCoords[0])
+ continue;
+
+ unsigned int uv = info.uvIndex;
+ if (!mesh->mTextureCoords[uv]) {
+ // If the requested UV index is not available, take the first one instead.
+ uv = 0;
+ }
+
+ if (mesh->mNumUVComponents[info.uvIndex] >= 3){
+ DefaultLogger::get()->warn("UV transformations on 3D mapping channels are not supported");
+ continue;
+ }
+
+ MeshTrafoList::iterator it;
+
+ // Check whether we have this transform setup already
+ for (it = meshLists[n].begin();it != meshLists[n].end(); ++it) {
+
+ if ((*it) == info && (*it).uvIndex == uv) {
+ (*it).updateList.push_back(update);
+ break;
+ }
+ }
+
+ if (it == meshLists[n].end()) {
+ meshLists[n].push_back(info);
+ meshLists[n].back().uvIndex = uv;
+ meshLists[n].back().updateList.push_back(update);
+ }
+ }
+ }
+ }
+ }
+
+ char buffer[1024]; // should be sufficiently large
+ unsigned int outChannels = 0, inChannels = 0, transformedChannels = 0;
+
+ // Now process all meshes. Important: we don't remove unreferenced UV channels.
+ // This is a job for the RemoveUnreferencedData-Step.
+ for (unsigned int q = 0; q < pScene->mNumMeshes;++q) {
+
+ aiMesh* mesh = pScene->mMeshes[q];
+ MeshTrafoList& trafo = meshLists[q];
+
+ inChannels += mesh->GetNumUVChannels();
+
+ if (!mesh->mTextureCoords[0] || trafo.empty() || (trafo.size() == 1 && trafo.begin()->IsUntransformed())) {
+ outChannels += mesh->GetNumUVChannels();
+ continue;
+ }
+
+ // Move untransformed UV channels to the first position in the list ....
+ // except if we need a new locked index which should be as small as possible
+ bool veto = false, need = false;
+ unsigned int cnt = 0;
+ unsigned int untransformed = 0;
+
+ MeshTrafoList::iterator it,it2;
+ for (it = trafo.begin();it != trafo.end(); ++it,++cnt) {
+
+ if (!(*it).IsUntransformed()) {
+ need = true;
+ }
+
+ if ((*it).lockedPos == AI_TT_UV_IDX_LOCK_TBD) {
+ // Lock this index and make sure it won't be changed
+ (*it).lockedPos = cnt;
+ veto = true;
+ continue;
+ }
+
+ if (!veto && it != trafo.begin() && (*it).IsUntransformed()) {
+ for (it2 = trafo.begin();it2 != it; ++it2) {
+ if (!(*it2).IsUntransformed())
+ break;
+ }
+ trafo.insert(it2,*it);
+ trafo.erase(it);
+ break;
+ }
+ }
+ if (!need)
+ continue;
+
+ // Find all that are not at their 'locked' position and move them to it.
+ // Conflicts are possible but quite unlikely.
+ cnt = 0;
+ for (it = trafo.begin();it != trafo.end(); ++it,++cnt) {
+ if ((*it).lockedPos != AI_TT_UV_IDX_LOCK_NONE && (*it).lockedPos != cnt) {
+ it2 = trafo.begin();unsigned int t = 0;
+ while (t != (*it).lockedPos)
+ ++it2;
+
+ if ((*it2).lockedPos != AI_TT_UV_IDX_LOCK_NONE) {
+ DefaultLogger::get()->error("Channel mismatch, can't compute all transformations properly [design bug]");
+ continue;
+ }
+
+ std::swap(*it2,*it);
+ if ((*it).lockedPos == untransformed)
+ untransformed = cnt;
+ }
+ }
+
+ // ... and add dummies for all unreferenced channels
+ // at the end of the list
+ bool ref[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n)
+ ref[n] = (!mesh->mTextureCoords[n] ? true : false);
+
+ for (it = trafo.begin();it != trafo.end(); ++it)
+ ref[(*it).uvIndex] = true;
+
+ for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
+ if (ref[n])
+ continue;
+ trafo.push_back(STransformVecInfo());
+ trafo.back().uvIndex = n;
+ }
+
+ // Then check whether this list breaks the channel limit.
+ // The unimportant ones are at the end of the list, so
+ // it shouldn't be too worse if we remove them.
+ unsigned int size = (unsigned int)trafo.size();
+ if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) {
+
+ if (!DefaultLogger::isNullLogger()) {
+ ::sprintf(buffer,"%u UV channels required but just %u available",
+ static_cast<unsigned int>(trafo.size()),AI_MAX_NUMBER_OF_TEXTURECOORDS);
+
+ DefaultLogger::get()->error(buffer);
+ }
+ size = AI_MAX_NUMBER_OF_TEXTURECOORDS;
+ }
+
+
+ aiVector3D* old[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n)
+ old[n] = mesh->mTextureCoords[n];
+
+ // Now continue and generate the output channels. Channels
+ // that we're not going to need later can be overridden.
+ it = trafo.begin();
+ for (unsigned int n = 0; n < trafo.size();++n,++it) {
+
+ if (n >= size) {
+ // Try to use an untransformed channel for all channels we threw over board
+ UpdateUVIndex((*it).updateList,untransformed);
+ continue;
+ }
+
+ outChannels++;
+
+ // Write to the log
+ if (!DefaultLogger::isNullLogger()) {
+ sprintf(buffer,"Mesh %u, channel %u: t(%.3f,%.3f), s(%.3f,%.3f), r(%.3f), %s%s",
+ q,n,
+ (*it).mTranslation.x,
+ (*it).mTranslation.y,
+ (*it).mScaling.x,
+ (*it).mScaling.y,
+ AI_RAD_TO_DEG( (*it).mRotation),
+ MappingModeToChar ((*it).mapU),
+ MappingModeToChar ((*it).mapV));
+
+ DefaultLogger::get()->info(buffer);
+ }
+
+ // Check whether we need a new buffer here
+ if (mesh->mTextureCoords[n]) {
+
+ it2 = it;++it2;
+ for (unsigned int m = n+1; m < size;++m, ++it2) {
+
+ if ((*it2).uvIndex == n){
+ it2 = trafo.begin();
+ break;
+ }
+ }
+ if (it2 == trafo.begin()){
+ mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices];
+ }
+ }
+ else mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices];
+
+ aiVector3D* src = old[(*it).uvIndex];
+ aiVector3D* dest, *end;
+ dest = mesh->mTextureCoords[n];
+
+ ai_assert(NULL != src);
+
+ // Copy the data to the destination array
+ if (dest != src)
+ ::memcpy(dest,src,sizeof(aiVector3D)*mesh->mNumVertices);
+
+ end = dest + mesh->mNumVertices;
+
+ // Build a transformation matrix and transform all UV coords with it
+ if (!(*it).IsUntransformed()) {
+ const aiVector2D& trl = (*it).mTranslation;
+ const aiVector2D& scl = (*it).mScaling;
+
+ // fixme: simplify ..
+ ++transformedChannels;
+ aiMatrix3x3 matrix;
+
+ aiMatrix3x3 m2,m3,m4,m5;
+
+ m4.a1 = scl.x;
+ m4.b2 = scl.y;
+
+ m2.a3 = m2.b3 = 0.5f;
+ m3.a3 = m3.b3 = -0.5f;
+
+ if ((*it).mRotation > AI_TT_ROTATION_EPSILON )
+ aiMatrix3x3::RotationZ((*it).mRotation,matrix);
+
+ m5.a3 += trl.x; m5.b3 += trl.y;
+ matrix = m2 * m4 * matrix * m3 * m5;
+
+ for (src = dest; src != end; ++src) { /* manual homogenious divide */
+ src->z = 1.f;
+ *src = matrix * *src;
+ src->x /= src->z;
+ src->y /= src->z;
+ src->z = 0.f;
+ }
+ }
+
+ // Update all UV indices
+ UpdateUVIndex((*it).updateList,n);
+ }
+ }
+
+ // Print some detailled statistics into the log
+ if (!DefaultLogger::isNullLogger()) {
+
+ if (transformedChannels) {
+ ::sprintf(buffer,"TransformUVCoordsProcess end: %u output channels (in: %u, modified: %u)",
+ outChannels,inChannels,transformedChannels);
+
+ DefaultLogger::get()->info(buffer);
+ }
+ else DefaultLogger::get()->debug("TransformUVCoordsProcess finished");
+ }
+}
+
+
diff --git a/3rdparty/assimp/code/TextureTransform.h b/3rdparty/assimp/code/TextureTransform.h
new file mode 100644
index 000000000..a9d711f33
--- /dev/null
+++ b/3rdparty/assimp/code/TextureTransform.h
@@ -0,0 +1,225 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 a helper step that processes texture transformations */
+#ifndef AI_TEXTURE_TRANSFORM_H_INCLUDED
+#define AI_TEXTURE_TRANSFORM_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "BaseProcess.h"
+
+struct aiNode;
+
+namespace Assimp {
+
+#define AI_TT_UV_IDX_LOCK_TBD 0xffffffff
+#define AI_TT_UV_IDX_LOCK_NONE 0xeeeeeeee
+
+
+#define AI_TT_ROTATION_EPSILON ((float)AI_DEG_TO_RAD(0.5))
+
+// ---------------------------------------------------------------------------
+/** Small helper structure representing a shortcut into the material list
+ * to be able to update some values quickly.
+*/
+struct TTUpdateInfo
+{
+ TTUpdateInfo() :
+ directShortcut (NULL)
+ , mat (NULL)
+ , semantic (0)
+ , index (0)
+ {}
+
+ //! Direct shortcut, if available
+ unsigned int* directShortcut;
+
+ //! Material
+ MaterialHelper *mat;
+
+ //! Texture type and index
+ unsigned int semantic, index;
+};
+
+
+// ---------------------------------------------------------------------------
+/** Helper class representing texture coordinate transformations
+*/
+struct STransformVecInfo : public aiUVTransform
+{
+
+ STransformVecInfo()
+ : uvIndex (0)
+ , mapU (aiTextureMapMode_Wrap)
+ , mapV (aiTextureMapMode_Wrap)
+ , lockedPos (AI_TT_UV_IDX_LOCK_NONE)
+ {}
+
+ //! Source texture coordinate index
+ unsigned int uvIndex;
+
+ //! Texture mapping mode in the u, v direction
+ aiTextureMapMode mapU,mapV;
+
+ //! Locked destination UV index
+ //! AI_TT_UV_IDX_LOCK_TBD - to be determined
+ //! AI_TT_UV_IDX_LOCK_NONE - none (default)
+ unsigned int lockedPos;
+
+ //! Update info - shortcuts into all materials
+ //! that are referencing this transform setup
+ std::list<TTUpdateInfo> updateList;
+
+
+ // -------------------------------------------------------------------
+ /** Compare two transform setups
+ */
+ inline bool operator== (const STransformVecInfo& other) const
+ {
+ // We use a small epsilon here
+ const static float epsilon = 0.05f;
+
+ if (fabs( mTranslation.x - other.mTranslation.x ) > epsilon ||
+ fabs( mTranslation.y - other.mTranslation.y ) > epsilon)
+ {
+ return false;
+ }
+
+ if (fabs( mScaling.x - other.mScaling.x ) > epsilon ||
+ fabs( mScaling.y - other.mScaling.y ) > epsilon)
+ {
+ return false;
+ }
+
+ if (fabs( mRotation - other.mRotation) > epsilon)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ inline bool operator!= (const STransformVecInfo& other) const
+ {
+ return !(*this == other);
+ }
+
+
+ // -------------------------------------------------------------------
+ /** Returns whether this is an untransformed texture coordinate set
+ */
+ inline bool IsUntransformed() const
+ {
+ return (1.0f == mScaling.x && 1.f == mScaling.y &&
+ !mTranslation.x && !mTranslation.y &&
+ mRotation < AI_TT_ROTATION_EPSILON);
+ }
+
+ // -------------------------------------------------------------------
+ /** Build a 3x3 matrix from the transformations
+ */
+ inline void GetMatrix(aiMatrix3x3& mOut)
+ {
+ mOut = aiMatrix3x3();
+
+ if (1.0f != mScaling.x || 1.0f != mScaling.y)
+ {
+ aiMatrix3x3 mScale;
+ mScale.a1 = mScaling.x;
+ mScale.b2 = mScaling.y;
+ mOut = mScale;
+ }
+ if (mRotation)
+ {
+ aiMatrix3x3 mRot;
+ mRot.a1 = mRot.b2 = cos(mRotation);
+ mRot.a2 = mRot.b1 = sin(mRotation);
+ mRot.a2 = -mRot.a2;
+ mOut *= mRot;
+ }
+ if (mTranslation.x || mTranslation.y)
+ {
+ aiMatrix3x3 mTrans;
+ mTrans.a3 = mTranslation.x;
+ mTrans.b3 = mTranslation.y;
+ mOut *= mTrans;
+ }
+ }
+};
+
+
+// ---------------------------------------------------------------------------
+/** Helper step to compute final UV coordinate sets if there are scalings
+ * or rotations in the original data read from the file.
+*/
+class ASSIMP_API TextureTransformStep : public BaseProcess
+{
+public:
+
+ TextureTransformStep();
+ ~TextureTransformStep();
+
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+ // -------------------------------------------------------------------
+ void SetupProperties(const Importer* pImp);
+
+
+protected:
+
+
+ // -------------------------------------------------------------------
+ /** Preprocess a specific UV transformation setup
+ *
+ * @param info Transformation setup to be preprocessed.
+ */
+ void PreProcessUVTransform(STransformVecInfo& info);
+
+private:
+
+ unsigned int configFlags;
+};
+
+}
+
+#endif //! AI_TEXTURE_TRANSFORM_H_INCLUDED
diff --git a/3rdparty/assimp/code/TinyFormatter.h b/3rdparty/assimp/code/TinyFormatter.h
new file mode 100644
index 000000000..c9e64fc5e
--- /dev/null
+++ b/3rdparty/assimp/code/TinyFormatter.h
@@ -0,0 +1,141 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 TinyFormatter.h
+ * @brief Utility to format log messages more easily. Introduced
+ * to get rid of the boost::format dependency. Much slinker,
+ * basically just extends stringstream.
+ */
+#ifndef INCLUDED_TINY_FORMATTER_H
+#define INCLUDED_TINY_FORMATTER_H
+
+#include <sstream>
+
+namespace Assimp {
+ namespace Formatter {
+
+// ------------------------------------------------------------------------------------------------
+/** stringstream utility. Usage:
+ * @code
+ * void writelog(const std::string&s);
+ * void writelog(const std::wstring&s);
+ * ...
+ * writelog(format()<< "hi! this is a number: " << 4);
+ * writelog(wformat()<< L"hi! this is a number: " << 4);
+ *
+ * @endcode */
+template < typename T,
+ typename CharTraits = std::char_traits<T>,
+ typename Allocator = std::allocator<T>
+>
+class basic_formatter
+{
+
+public:
+
+ typedef class std::basic_string<
+ T,CharTraits,Allocator
+ > string;
+
+ typedef class std::basic_ostringstream<
+ T,CharTraits,Allocator
+ > stringstream;
+
+public:
+
+ basic_formatter() {}
+
+ /* Allow basic_formatter<T>'s to be used almost interchangeably
+ * with std::(w)string or const (w)char* arguments because the
+ * conversion c'tor is called implicitly. */
+ template <typename TT>
+ basic_formatter(const TT& sin) {
+ underlying << sin;
+ }
+
+public:
+
+ operator string () const {
+ return underlying.str();
+ }
+
+
+ /* note - this is declared const because binding temporaries does only
+ * work for const references, so many function prototypes will
+ * include const basic_formatter<T>& s but might still want to
+ * modify the formatted string without the need for a full copy.*/
+ template <typename TToken>
+ const basic_formatter& operator << (const TToken& s) const {
+ underlying << s;
+ return *this;
+ }
+
+ template <typename TToken>
+ basic_formatter& operator << (const TToken& s) {
+ underlying << s;
+ return *this;
+ }
+
+
+ // comma operator overloaded as well, choose your preferred way.
+ template <typename TToken>
+ const basic_formatter& operator, (const TToken& s) const {
+ underlying << s;
+ return *this;
+ }
+
+ template <typename TToken>
+ basic_formatter& operator, (const TToken& s) {
+ underlying << s;
+ return *this;
+ }
+
+private:
+ mutable stringstream underlying;
+};
+
+
+typedef basic_formatter< char > format;
+typedef basic_formatter< wchar_t > wformat;
+
+} // ! namespace Formatter
+
+} // ! namespace Assimp
+#endif
diff --git a/3rdparty/assimp/code/TriangulateProcess.cpp b/3rdparty/assimp/code/TriangulateProcess.cpp
new file mode 100644
index 000000000..d9899a0fb
--- /dev/null
+++ b/3rdparty/assimp/code/TriangulateProcess.cpp
@@ -0,0 +1,401 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2009, ASSIMP Development 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 Development 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 TriangulateProcess.cpp
+ * @brief Implementation of the post processing step to split up
+ * all faces with more than three indices into triangles.
+ *
+ *
+ * The triangulation algorithm will handle concave or convex polygons.
+ * Self-intersecting or non-planar polygons are not rejected, but
+ * they're probably not triangulated correctly.
+ *
+ * AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
+ * - generates vertex colors to represent the face winding order.
+ * the first vertex of a polygon becomes red, the last blue.
+ */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
+#include "TriangulateProcess.h"
+#include "ProcessHelper.h"
+
+//#define AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+TriangulateProcess::TriangulateProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+TriangulateProcess::~TriangulateProcess()
+{
+ // nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool TriangulateProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_Triangulate) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void TriangulateProcess::Execute( aiScene* pScene)
+{
+ DefaultLogger::get()->debug("TriangulateProcess begin");
+
+ bool bHas = false;
+ for ( unsigned int a = 0; a < pScene->mNumMeshes; a++)
+ {
+ if ( TriangulateMesh( pScene->mMeshes[a]))
+ bHas = true;
+ }
+ if (bHas)DefaultLogger::get()->info ("TriangulateProcess finished. All polygons have been triangulated.");
+ else DefaultLogger::get()->debug("TriangulateProcess finished. There was nothing to be done.");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Test whether a point p2 is on the left side of the line formed by p0-p1
+inline bool OnLeftSideOfLine(const aiVector2D& p0, const aiVector2D& p1,const aiVector2D& p2)
+{
+ return ( (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y) ) > 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Test whether a point is inside a given triangle in R2
+inline bool PointInTriangle2D(const aiVector2D& p0, const aiVector2D& p1,const aiVector2D& p2, const aiVector2D& pp)
+{
+ // Point in triangle test using baryzentric coordinates
+ const aiVector2D v0 = p1 - p0;
+ const aiVector2D v1 = p2 - p0;
+ const aiVector2D v2 = pp - p0;
+
+ float dot00 = v0 * v0;
+ float dot01 = v0 * v1;
+ float dot02 = v0 * v2;
+ float dot11 = v1 * v1;
+ float dot12 = v1 * v2;
+
+ const float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
+ dot11 = (dot11 * dot02 - dot01 * dot12) * invDenom;
+ dot00 = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+ return (dot11 > 0) && (dot00 > 0) && (dot11 + dot00 < 1);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Triangulates the given mesh.
+bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
+{
+ // Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases
+ if (!pMesh->mPrimitiveTypes) {
+ bool bNeed = false;
+
+ for ( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
+ const aiFace& face = pMesh->mFaces[a];
+
+ if ( face.mNumIndices != 3) {
+ bNeed = true;
+ }
+ }
+ if (!bNeed)
+ return false;
+ }
+ else if (!(pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON)) {
+ return false;
+ }
+
+ // the output mesh will contain triangles, but no polys anymore
+ pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+ pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
+
+ // Find out how many output faces we'll get
+ unsigned int numOut = 0, max_out = 0;
+ for ( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
+ aiFace& face = pMesh->mFaces[a];
+ if ( face.mNumIndices <= 3)
+ numOut++;
+
+ else {
+ numOut += face.mNumIndices-2;
+ max_out = std::max(max_out,face.mNumIndices);
+ }
+ }
+
+ // Just another check whether aiMesh::mPrimitiveTypes is correct
+ assert(numOut != pMesh->mNumFaces);
+
+ aiVector3D* nor_out = NULL;
+ if (!pMesh->mNormals && pMesh->mPrimitiveTypes == aiPrimitiveType_POLYGON) {
+ nor_out = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
+ }
+
+ aiFace* out = new aiFace[numOut], *curOut = out;
+ std::vector<aiVector3D> temp_verts(max_out+2); /* temporary storage for vertices */
+
+ // Apply vertex colors to represent the face winding?
+#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
+ if (!pMesh->mColors[0])
+ pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
+ else
+ new(pMesh->mColors[0]) aiColor4D[pMesh->mNumVertices];
+
+ aiColor4D* clr = pMesh->mColors[0];
+#endif
+
+ // use boost::scoped_array to avoid slow std::vector<bool> specialiations
+ boost::scoped_array<bool> done(new bool[max_out]);
+ for ( unsigned int a = 0; a < pMesh->mNumFaces; a++) {
+ aiFace& face = pMesh->mFaces[a];
+
+ unsigned int* idx = face.mIndices;
+ int num = (int)face.mNumIndices, ear = 0, tmp, prev = num-1, next = 0, max = num;
+
+ // Apply vertex colors to represent the face winding?
+#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
+ for (unsigned int i = 0; i < face.mNumIndices; ++i) {
+ aiColor4D& c = clr[idx[i]];
+ c.r = (i+1) / (float)max;
+ c.b = 1.f - c.r;
+ }
+#endif
+
+ // if it's a simple point,line or triangle: just copy it
+ if ( face.mNumIndices <= 3)
+ {
+ aiFace& nface = *curOut++;
+ nface.mNumIndices = face.mNumIndices;
+ nface.mIndices = face.mIndices;
+ }
+ // quadrilaterals can't have ears. trifanning will always work
+ else if ( face.mNumIndices == 4) {
+ aiFace& nface = *curOut++;
+ nface.mNumIndices = 3;
+ nface.mIndices = face.mIndices;
+
+ aiFace& sface = *curOut++;
+ sface.mNumIndices = 3;
+ sface.mIndices = new unsigned int[3];
+
+ sface.mIndices[0] = face.mIndices[0];
+ sface.mIndices[1] = face.mIndices[2];
+ sface.mIndices[2] = face.mIndices[3];
+ }
+ else
+ {
+ // A polygon with more than 3 vertices can be either concave or convex.
+ // Usually everything we're getting is convex and we could easily
+ // triangulate by trifanning. However, LightWave is probably the only
+ // modeller to make extensive use of highly concave monster polygons ...
+ // so we need to apply the full 'ear cutting' algorithm.
+
+ // RERQUIREMENT: polygon is expected to be simple and *nearly* planar.
+ // We project it onto a plane to get 2d data. Working in R3 would
+ // also be possible but it's more difficult to implement.
+
+ // Collect all vertices of of the polygon.
+ aiVector3D* verts = pMesh->mVertices;
+ for (tmp = 0; tmp < max; ++tmp) {
+ temp_verts[tmp] = verts[idx[tmp]];
+ }
+
+ // Get newell normal of the polygon. Store it for future use if it's a polygon-only mesh
+ aiVector3D n;
+ NewellNormal<3,3,3>(n,max,&temp_verts.front().x,&temp_verts.front().y,&temp_verts.front().z);
+ if (nor_out) {
+ for (tmp = 0; tmp < max; ++tmp)
+ nor_out[idx[tmp]] = n;
+ }
+
+ // Select largest normal coordinate to ignore for projection
+ const float ax = (n.x>0 ? n.x : -n.x);
+ const float ay = (n.y>0 ? n.y : -n.y);
+ const float az = (n.z>0 ? n.z : -n.z);
+
+ unsigned int ac = 0, bc = 1; /* no z coord. projection to xy */
+ float inv = n.z;
+ if (ax > ay) {
+ if (ax > az) { /* no x coord. projection to yz */
+ ac = 1; bc = 2;
+ inv = n.x;
+ }
+ }
+ else if (ay > az) { /* no y coord. projection to zy */
+ ac = 2; bc = 0;
+ inv = n.y;
+ }
+
+ // Swap projection axes to take the negated projection vector into account
+ if (inv < 0.f) {
+ std::swap(ac,bc);
+ }
+
+ for (tmp =0; tmp < max; ++tmp) {
+ temp_verts[tmp].x = verts[idx[tmp]][ac];
+ temp_verts[tmp].y = verts[idx[tmp]][bc];
+ done[tmp] = false;
+ }
+
+ //
+ // FIXME: currently this is the slow O(kn) variant with a worst case
+ // complexity of O(n^2) (I think). Can be done in O(n).
+ while (num > 3) {
+
+ // Find the next ear of the polygon
+ int num_found = 0;
+ for (ear = next;;prev = ear,ear = next) {
+
+ // break after we looped two times without a positive match
+ for (next=ear+1;done[(next>max-1?next=0:next)];++next) {};
+ if (next < ear) {
+ if (++num_found == 2) {
+ break;
+ }
+ }
+ const aiVector2D* pnt1 = (const aiVector2D*)&temp_verts[ear],
+ *pnt0 = (const aiVector2D*)&temp_verts[prev],
+ *pnt2 = (const aiVector2D*)&temp_verts[next];
+
+ // Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1.
+ if (OnLeftSideOfLine (*pnt0,*pnt2,*pnt1)) {
+ continue;
+ }
+
+ // and no other point may be contained in this triangle
+ for ( tmp = 0; tmp < max; ++tmp) {
+
+ // We need to compare the actual values because it's possible that multiple indexes in
+ // the polygon are refering to the same position. concave_polygon.obj is a sample
+ //
+ // FIXME: Use 'epsiloned' comparisons instead? Due to numeric inaccuracies in
+ // PointInTriangle() I'm guessing that it's actually possible to construct
+ // input data that would cause us to end up with no ears. The problem is,
+ // which epsilon? If we chose a too large value, we'd get wrong results
+ const aiVector2D& vtmp = * ((aiVector2D*) &temp_verts[tmp] );
+ if ( vtmp != *pnt1 && vtmp != *pnt2 && vtmp != *pnt0 && PointInTriangle2D(*pnt0,*pnt1,*pnt2,vtmp))
+ break;
+
+ }
+ if (tmp != max) {
+ continue;
+ }
+
+ // this vertex is an ear
+ break;
+ }
+ if (num_found == 2) {
+
+ // Due to the 'two ear theorem', every simple polygon with more than three points must
+ // have 2 'ears'. Here's definitely someting wrong ... but we don't give up yet.
+ //
+
+ // Instead we're continuting with the standard trifanning algorithm which we'd
+ // use if we had only convex polygons. That's life.
+ DefaultLogger::get()->error("Failed to triangulate polygon (no ear found). Probably not a simple polygon?");
+
+ curOut -= (max-num); /* undo all previous work */
+ for (tmp = 0; tmp < max-2; ++tmp) {
+ aiFace& nface = *curOut++;
+
+ nface.mNumIndices = 3;
+ if (!nface.mIndices)
+ nface.mIndices = new unsigned int[3];
+
+ nface.mIndices[0] = idx[0];
+ nface.mIndices[1] = idx[tmp+1];
+ nface.mIndices[2] = idx[tmp+2];
+ }
+ num = 0;
+ break;
+ }
+
+ aiFace& nface = *curOut++;
+ nface.mNumIndices = 3;
+
+ if (!nface.mIndices) {
+ nface.mIndices = new unsigned int[3];
+ }
+
+ // setup indices for the new triangle ...
+ nface.mIndices[0] = idx[prev];
+ nface.mIndices[1] = idx[ear];
+ nface.mIndices[2] = idx[next];
+
+ // exclude the ear from most further processing
+ done[ear] = true;
+ --num;
+ }
+ if (num > 0) {
+ // We have three indices forming the last 'ear' remaining. Collect them.
+ aiFace& nface = *curOut++;
+ nface.mNumIndices = 3;
+ nface.mIndices = face.mIndices;
+
+ for (tmp = 0; done[tmp]; ++tmp) {};
+ idx[0] = idx[tmp];
+
+ for (++tmp; done[tmp]; ++tmp) {};
+ idx[1] = idx[tmp];
+
+ for (++tmp; done[tmp]; ++tmp) {};
+ idx[2] = idx[tmp];
+ }
+ }
+ face.mIndices = NULL; /* prevent unintended deletion of our awesome results. would be a pity */
+ }
+
+ // kill the old faces
+ delete [] pMesh->mFaces;
+
+ // ... and store the new ones
+ pMesh->mFaces = out;
+ pMesh->mNumFaces = (unsigned int)(curOut-out); /* not necessarily equal to numOut */
+ return true;
+}
+
+#endif // !! ASSIMP_BUILD_NO_TRIANGULATE_PROCESS
diff --git a/3rdparty/assimp/code/TriangulateProcess.h b/3rdparty/assimp/code/TriangulateProcess.h
new file mode 100644
index 000000000..7a680e5aa
--- /dev/null
+++ b/3rdparty/assimp/code/TriangulateProcess.h
@@ -0,0 +1,98 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a post processing step to triangulate all faces
+ with more than three vertices.
+ */
+#ifndef AI_TRIANGULATEPROCESS_H_INC
+#define AI_TRIANGULATEPROCESS_H_INC
+
+#include "BaseProcess.h"
+
+struct aiMesh;
+
+class TriangulateProcessTest;
+namespace Assimp
+{
+
+// ---------------------------------------------------------------------------
+/** The TriangulateProcess splits up all faces with more than three indices
+ * into triangles. You usually want this to happen because the graphics cards
+ * need their data as triangles.
+ */
+class ASSIMP_API TriangulateProcess : public BaseProcess
+{
+ friend class Importer;
+ friend class ::TriangulateProcessTest; // grant the unit test full access to us
+
+protected:
+ /** Constructor to be privately used by Importer */
+ TriangulateProcess();
+
+ /** Destructor, private as well */
+ ~TriangulateProcess();
+
+public:
+ // -------------------------------------------------------------------
+ /** Returns whether the processing step is present in the given flag field.
+ * @param pFlags The processing flags the importer was called with. A bitwise
+ * combination of #aiPostProcessSteps.
+ * @return true if the process is present in this flag fields, false if not.
+ */
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ /** 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);
+
+protected:
+ // -------------------------------------------------------------------
+ /** Triangulates the given mesh.
+ * @param pMesh The mesh to triangulate.
+ */
+ bool TriangulateMesh( aiMesh* pMesh);
+};
+
+} // end of namespace Assimp
+
+#endif // AI_TRIANGULATEPROCESS_H_INC
diff --git a/3rdparty/assimp/code/UnrealLoader.cpp b/3rdparty/assimp/code/UnrealLoader.cpp
new file mode 100644
index 000000000..4985e7a9b
--- /dev/null
+++ b/3rdparty/assimp/code/UnrealLoader.cpp
@@ -0,0 +1,426 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 UnrealLoader.cpp
+ * @brief Implementation of the UNREAL (*.3D) importer class
+ *
+ * Sources:
+ * http://local.wasp.uwa.edu.au/~pbourke/dataformats/unreal/
+ */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_3D_IMPORTER
+
+#include "UnrealLoader.h"
+#include "StreamReader.h"
+#include "ParsingUtils.h"
+#include "fast_atof.h"
+#include "ConvertToLHProcess.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+UnrealImporter::UnrealImporter()
+: configFrameID (0)
+, configHandleFlags (true)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+UnrealImporter::~UnrealImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool UnrealImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const
+{
+ return SimpleExtensionCheck(pFile,"3d","uc");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Build a string of all file extensions supported
+void UnrealImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("3d");
+ extensions.insert("uc");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void UnrealImporter::SetupProperties(const Importer* pImp)
+{
+ // The
+ // AI_CONFIG_IMPORT_UNREAL_KEYFRAME option overrides the
+ // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_UNREAL_KEYFRAME,0xffffffff);
+ if (0xffffffff == configFrameID) {
+ configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
+ }
+
+ // AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS, default is true
+ configHandleFlags = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_UNREAL_HANDLE_FLAGS,1));
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void UnrealImporter::InternReadFile( const std::string& pFile,
+ aiScene* pScene, IOSystem* pIOHandler)
+{
+ // For any of the 3 files being passed get the three correct paths
+ // First of all, determine file extension
+ std::string::size_type pos = pFile.find_last_of('.');
+ std::string extension = GetExtension(pFile);
+
+ std::string d_path,a_path,uc_path;
+ if (extension == "3d") {
+ // jjjj_d.3d
+ // jjjj_a.3d
+ pos = pFile.find_last_of('_');
+ if (std::string::npos == pos) {
+ throw DeadlyImportError("UNREAL: Unexpected naming scheme");
+ }
+ extension = pFile.substr(0,pos);
+ }
+ else {
+ extension = pFile.substr(0,pos);
+ }
+
+ // build proper paths
+ d_path = extension+"_d.3d";
+ a_path = extension+"_a.3d";
+ uc_path = extension+".uc";
+
+ DefaultLogger::get()->debug("UNREAL: data file is " + d_path);
+ DefaultLogger::get()->debug("UNREAL: aniv file is " + a_path);
+ DefaultLogger::get()->debug("UNREAL: uc file is " + uc_path);
+
+ // and open the files ... we can't live without them
+ IOStream* p = pIOHandler->Open(d_path);
+ if (!p)
+ throw DeadlyImportError("UNREAL: Unable to open _d file");
+ StreamReaderLE d_reader(pIOHandler->Open(d_path));
+
+ const uint16_t numTris = d_reader.GetI2();
+ const uint16_t numVert = d_reader.GetI2();
+ d_reader.IncPtr(44);
+ if (!numTris || numVert < 3)
+ throw DeadlyImportError("UNREAL: Invalid number of vertices/triangles");
+
+ // maximum texture index
+ unsigned int maxTexIdx = 0;
+
+ // collect triangles
+ std::vector<Unreal::Triangle> triangles(numTris);
+ for (std::vector<Unreal::Triangle>::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) {
+ Unreal::Triangle& tri = *it;
+
+ for (unsigned int i = 0; i < 3;++i) {
+
+ tri.mVertex[i] = d_reader.GetI2();
+ if (tri.mVertex[i] >= numTris) {
+ DefaultLogger::get()->warn("UNREAL: vertex index out of range");
+ tri.mVertex[i] = 0;
+ }
+ }
+ tri.mType = d_reader.GetI1();
+
+ // handle mesh flagss?
+ if (configHandleFlags)
+ tri.mType = Unreal::MF_NORMAL_OS;
+ else {
+ // ignore MOD and MASKED for the moment, treat them as two-sided
+ if (tri.mType == Unreal::MF_NORMAL_MOD_TS || tri.mType == Unreal::MF_NORMAL_MASKED_TS)
+ tri.mType = Unreal::MF_NORMAL_TS;
+ }
+ d_reader.IncPtr(1);
+
+ for (unsigned int i = 0; i < 3;++i)
+ for (unsigned int i2 = 0; i2 < 2;++i2)
+ tri.mTex[i][i2] = d_reader.GetI1();
+
+ tri.mTextureNum = d_reader.GetI1();
+ maxTexIdx = std::max(maxTexIdx,(unsigned int)tri.mTextureNum);
+ d_reader.IncPtr(1);
+ }
+
+ p = pIOHandler->Open(a_path);
+ if (!p)
+ throw DeadlyImportError("UNREAL: Unable to open _a file");
+ StreamReaderLE a_reader(pIOHandler->Open(a_path));
+
+ // read number of frames
+ const uint32_t numFrames = a_reader.GetI2();
+ if (configFrameID >= numFrames)
+ throw DeadlyImportError("UNREAL: The requested frame does not exist");
+
+ uint32_t st = a_reader.GetI2();
+ if (st != numVert*4u)
+ throw DeadlyImportError("UNREAL: Unexpected aniv file length");
+
+ // skip to our frame
+ a_reader.IncPtr(configFrameID *numVert*4);
+
+ // collect vertices
+ std::vector<aiVector3D> vertices(numVert);
+ for (std::vector<aiVector3D>::iterator it = vertices.begin(), end = vertices.end(); it != end; ++it) {
+ int32_t val = a_reader.GetI4();
+ Unreal::DecompressVertex(*it,val);
+ }
+
+ // list of textures.
+ std::vector< std::pair<unsigned int, std::string> > textures;
+
+ // allocate the output scene
+ aiNode* nd = pScene->mRootNode = new aiNode();
+ nd->mName.Set("<UnrealRoot>");
+
+ // we can live without the uc file if necessary
+ boost::scoped_ptr<IOStream> pb (pIOHandler->Open(uc_path));
+ if (pb.get()) {
+
+ std::vector<char> _data;
+ TextFileToBuffer(pb.get(),_data);
+ const char* data = &_data[0];
+
+ std::vector< std::pair< std::string,std::string > > tempTextures;
+
+ // do a quick search in the UC file for some known, usually texture-related, tags
+ for (;*data;++data) {
+ if (TokenMatchI(data,"#exec",5)) {
+ SkipSpacesAndLineEnd(&data);
+
+ // #exec TEXTURE IMPORT [...] NAME=jjjjj [...] FILE=jjjj.pcx [...]
+ if (TokenMatchI(data,"TEXTURE",7)) {
+ SkipSpacesAndLineEnd(&data);
+
+ if (TokenMatchI(data,"IMPORT",6)) {
+ tempTextures.push_back(std::pair< std::string,std::string >());
+ std::pair< std::string,std::string >& me = tempTextures.back();
+ for (;!IsLineEnd(*data);++data) {
+ if (!::ASSIMP_strincmp(data,"NAME=",5)) {
+ const char *d = data+=5;
+ for (;!IsSpaceOrNewLine(*data);++data) {};
+ me.first = std::string(d,(size_t)(data-d));
+ }
+ else if (!::ASSIMP_strincmp(data,"FILE=",5)) {
+ const char *d = data+=5;
+ for (;!IsSpaceOrNewLine(*data);++data) {};
+ me.second = std::string(d,(size_t)(data-d));
+ }
+ }
+ if (!me.first.length() || !me.second.length())
+ tempTextures.pop_back();
+ }
+ }
+ // #exec MESHMAP SETTEXTURE MESHMAP=box NUM=1 TEXTURE=Jtex1
+ // #exec MESHMAP SCALE MESHMAP=box X=0.1 Y=0.1 Z=0.2
+ else if (TokenMatchI(data,"MESHMAP",7)) {
+ SkipSpacesAndLineEnd(&data);
+
+ if (TokenMatchI(data,"SETTEXTURE",10)) {
+
+ textures.push_back(std::pair<unsigned int, std::string>());
+ std::pair<unsigned int, std::string>& me = textures.back();
+
+ for (;!IsLineEnd(*data);++data) {
+ if (!::ASSIMP_strincmp(data,"NUM=",4)) {
+ data += 4;
+ me.first = strtol10(data,&data);
+ }
+ else if (!::ASSIMP_strincmp(data,"TEXTURE=",8)) {
+ data += 8;
+ const char *d = data;
+ for (;!IsSpaceOrNewLine(*data);++data) {};
+ me.second = std::string(d,(size_t)(data-d));
+
+ // try to find matching path names, doesn't care if we don't find them
+ for (std::vector< std::pair< std::string,std::string > >::const_iterator it = tempTextures.begin();
+ it != tempTextures.end(); ++it) {
+ if ((*it).first == me.second) {
+ me.second = (*it).second;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else if (TokenMatchI(data,"SCALE",5)) {
+
+ for (;!IsLineEnd(*data);++data) {
+ if (data[0] == 'X' && data[1] == '=') {
+ data = fast_atof_move(data+2,(float&)nd->mTransformation.a1);
+ }
+ else if (data[0] == 'Y' && data[1] == '=') {
+ data = fast_atof_move(data+2,(float&)nd->mTransformation.b2);
+ }
+ else if (data[0] == 'Z' && data[1] == '=') {
+ data = fast_atof_move(data+2,(float&)nd->mTransformation.c3);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ DefaultLogger::get()->error("Unable to open .uc file");
+ }
+
+ std::vector<Unreal::TempMat> materials;
+ materials.reserve(textures.size()*2+5);
+
+ // find out how many output meshes and materials we'll have and build material indices
+ for (std::vector<Unreal::Triangle>::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) {
+ Unreal::Triangle& tri = *it;
+ Unreal::TempMat mat(tri);
+ std::vector<Unreal::TempMat>::iterator nt = std::find(materials.begin(),materials.end(),mat);
+ if (nt == materials.end()) {
+ // add material
+ tri.matIndex = materials.size();
+ mat.numFaces = 1;
+ materials.push_back(mat);
+
+ ++pScene->mNumMeshes;
+ }
+ else {
+ tri.matIndex = static_cast<unsigned int>(nt-materials.begin());
+ ++nt->numFaces;
+ }
+ }
+
+ if (!pScene->mNumMeshes) {
+ throw DeadlyImportError("UNREAL: Unable to find valid mesh data");
+ }
+
+ // allocate meshes and bind them to the node graph
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = pScene->mNumMeshes];
+
+ nd->mNumMeshes = pScene->mNumMeshes;
+ nd->mMeshes = new unsigned int[nd->mNumMeshes];
+ for (unsigned int i = 0; i < pScene->mNumMeshes;++i) {
+ aiMesh* m = pScene->mMeshes[i] = new aiMesh();
+ m->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+
+ const unsigned int num = materials[i].numFaces;
+ m->mFaces = new aiFace [num];
+ m->mVertices = new aiVector3D [num*3];
+ m->mTextureCoords[0] = new aiVector3D [num*3];
+
+ nd->mMeshes[i] = i;
+
+ // create materials, too
+ MaterialHelper* mat = new MaterialHelper();
+ pScene->mMaterials[i] = mat;
+
+ // all white by default - texture rulez
+ aiColor3D color(1.f,1.f,1.f);
+
+ aiString s;
+ ::sprintf(s.data,"mat%i_tx%i_",i,materials[i].tex);
+
+ // set the two-sided flag
+ if (materials[i].type == Unreal::MF_NORMAL_TS) {
+ const int twosided = 1;
+ mat->AddProperty(&twosided,1,AI_MATKEY_TWOSIDED);
+ ::strcat(s.data,"ts_");
+ }
+ else ::strcat(s.data,"os_");
+
+ // make TRANS faces 90% opaque that RemRedundantMaterials won't catch us
+ if (materials[i].type == Unreal::MF_NORMAL_TRANS_TS) {
+ const float opac = 0.9f;
+ mat->AddProperty(&opac,1,AI_MATKEY_OPACITY);
+ ::strcat(s.data,"tran_");
+ }
+ else ::strcat(s.data,"opaq_");
+
+ // a special name for the weapon attachment point
+ if (materials[i].type == Unreal::MF_WEAPON_PLACEHOLDER) {
+ s.length = ::sprintf(s.data,"$WeaponTag$");
+ color = aiColor3D(0.f,0.f,0.f);
+ }
+
+ // set color and name
+ mat->AddProperty(&color,1,AI_MATKEY_COLOR_DIFFUSE);
+ s.length = ::strlen(s.data);
+ mat->AddProperty(&s,AI_MATKEY_NAME);
+
+ // set texture, if any
+ const unsigned int tex = materials[i].tex;
+ for (std::vector< std::pair< unsigned int, std::string > >::const_iterator it = textures.begin();it != textures.end();++it) {
+ if ((*it).first == tex) {
+ s.Set((*it).second);
+ mat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0));
+ break;
+ }
+ }
+ }
+
+ // fill them.
+ for (std::vector<Unreal::Triangle>::iterator it = triangles.begin(), end = triangles.end();it != end; ++it) {
+ Unreal::Triangle& tri = *it;
+ Unreal::TempMat mat(tri);
+ std::vector<Unreal::TempMat>::iterator nt = std::find(materials.begin(),materials.end(),mat);
+
+ aiMesh* mesh = pScene->mMeshes[nt-materials.begin()];
+ aiFace& f = mesh->mFaces[mesh->mNumFaces++];
+ f.mIndices = new unsigned int[f.mNumIndices = 3];
+
+ for (unsigned int i = 0; i < 3;++i,mesh->mNumVertices++) {
+ f.mIndices[i] = mesh->mNumVertices;
+
+ mesh->mVertices[mesh->mNumVertices] = vertices[ tri.mVertex[i] ];
+ mesh->mTextureCoords[0][mesh->mNumVertices] = aiVector3D( tri.mTex[i][0] / 255.f, 1.f - tri.mTex[i][1] / 255.f, 0.f);
+ }
+ }
+
+ // convert to RH
+ MakeLeftHandedProcess hero;
+ hero.Execute(pScene);
+
+ FlipWindingOrderProcess flipper;
+ flipper.Execute(pScene);
+}
+
+#endif // !! ASSIMP_BUILD_NO_3D_IMPORTER
diff --git a/3rdparty/assimp/code/UnrealLoader.h b/3rdparty/assimp/code/UnrealLoader.h
new file mode 100644
index 000000000..876fe83a1
--- /dev/null
+++ b/3rdparty/assimp/code/UnrealLoader.h
@@ -0,0 +1,204 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 UnrealLoader.h
+ * @brief Declaration of the .3d (UNREAL) importer class.
+ */
+#ifndef INCLUDED_AI_3D_LOADER_H
+#define INCLUDED_AI_3D_LOADER_H
+
+#include "BaseImporter.h"
+namespace Assimp {
+namespace Unreal {
+
+ /*
+ 0 = Normal one-sided
+ 1 = Normal two-sided
+ 2 = Translucent two-sided
+ 3 = Masked two-sided
+ 4 = Modulation blended two-sided
+ 8 = Placeholder triangle for weapon positioning (invisible)
+ */
+enum MeshFlags {
+ MF_NORMAL_OS = 0,
+ MF_NORMAL_TS = 1,
+ MF_NORMAL_TRANS_TS = 2,
+ MF_NORMAL_MASKED_TS = 3,
+ MF_NORMAL_MOD_TS = 4,
+ MF_WEAPON_PLACEHOLDER = 8
+};
+
+ // a single triangle
+struct Triangle {
+ uint16_t mVertex[3]; // Vertex indices
+ char mType; // James' Mesh Type
+ char mColor; // Color for flat and Gourand Shaded
+ unsigned char mTex[3][2]; // Texture UV coordinates
+ unsigned char mTextureNum; // Source texture offset
+ char mFlags; // Unreal Mesh Flags (unused)
+
+ unsigned int matIndex;
+};
+
+// temporary representation for a material
+struct TempMat {
+ TempMat()
+ : numFaces (0)
+ {}
+
+ TempMat(const Triangle& in)
+ : type ((Unreal::MeshFlags)in.mType)
+ , tex (in.mTextureNum)
+ , numFaces (0)
+ {}
+
+ // type of mesh
+ Unreal::MeshFlags type;
+
+ // index of texture
+ unsigned int tex;
+
+ // number of faces using us
+ unsigned int numFaces;
+
+ // for std::find
+ bool operator == (const TempMat& o ) {
+ return (tex == o.tex && type == o.type);
+ }
+};
+
+struct Vertex
+{
+ int32_t X : 11;
+ int32_t Y : 11;
+ int32_t Z : 10;
+};
+
+ // UNREAL vertex compression
+inline void CompressVertex(const aiVector3D& v, uint32_t& out)
+{
+ union {
+ Vertex n;
+ int32_t t;
+ };
+ n.X = (int32_t)v.x;
+ n.Y = (int32_t)v.y;
+ n.Z = (int32_t)v.z;
+ out = t;
+}
+
+ // UNREAL vertex decompression
+inline void DecompressVertex(aiVector3D& v, int32_t in)
+{
+ union {
+ Vertex n;
+ int32_t i;
+ };
+ i = in;
+
+ v.x = (float)n.X;
+ v.y = (float)n.Y;
+ v.z = (float)n.Z;
+}
+
+} // end namespace Unreal
+
+// ---------------------------------------------------------------------------
+/** @brief Importer class to load UNREAL files (*.3d)
+*/
+class UnrealImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ UnrealImporter();
+
+ /** Destructor, private as well */
+ ~UnrealImporter();
+
+public:
+
+ // -------------------------------------------------------------------
+ /** @brief Returns whether we can handle the format of the given file
+ *
+ * See BaseImporter::CanRead() for details.
+ **/
+ bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+ bool checkSig) const;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** @brief Called by Importer::GetExtensionList()
+ *
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Setup properties for the importer
+ *
+ * See BaseImporter::SetupProperties() for details
+ */
+ void SetupProperties(const Importer* pImp);
+
+
+ // -------------------------------------------------------------------
+ /** @brief Imports the given file into the given scene structure.
+ *
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+private:
+
+ //! frame to be loaded
+ uint32_t configFrameID;
+
+ //! process surface flags
+ bool configHandleFlags;
+
+}; // !class UnrealImporter
+
+} // end of namespace Assimp
+#endif // AI_UNREALIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/ValidateDataStructure.cpp b/3rdparty/assimp/code/ValidateDataStructure.cpp
new file mode 100644
index 000000000..9fc5f4206
--- /dev/null
+++ b/3rdparty/assimp/code/ValidateDataStructure.cpp
@@ -0,0 +1,968 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 ValidateDataStructure.cpp
+ * @brief Implementation of the post processing step to validate
+ * the data structure returned by Assimp.
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "ValidateDataStructure.h"
+#include "BaseImporter.h"
+#include "fast_atof.h"
+#include "ProcessHelper.h"
+
+// CRT headers
+#include <stdarg.h>
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ValidateDSProcess::ValidateDSProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+ValidateDSProcess::~ValidateDSProcess()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool ValidateDSProcess::IsActive( unsigned int pFlags) const
+{
+ return (pFlags & aiProcess_ValidateDataStructure) != 0;
+}
+// ------------------------------------------------------------------------------------------------
+AI_WONT_RETURN void ValidateDSProcess::ReportError(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);
+#ifdef _DEBUG
+ aiAssert( szBuffer,__LINE__,__FILE__ );
+#endif
+ throw DeadlyImportError("Validation failed: " + std::string(szBuffer,iLen));
+}
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::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);
+ DefaultLogger::get()->warn("Validation warning: " + std::string(szBuffer,iLen));
+}
+
+// ------------------------------------------------------------------------------------------------
+inline int HasNameMatch(const aiString& in, aiNode* node)
+{
+ int result = (node->mName == in ? 1 : 0 );
+ for (unsigned int i = 0; i < node->mNumChildren;++i) {
+ result += HasNameMatch(in,node->mChildren[i]);
+ }
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline void ValidateDSProcess::DoValidation(T** parray, unsigned int size,
+ const char* firstName, const char* secondName)
+{
+ // validate all entries
+ if (size)
+ {
+ if (!parray)
+ {
+ ReportError("aiScene::%s is NULL (aiScene::%s is %i)",
+ firstName, secondName, size);
+ }
+ for (unsigned int i = 0; i < size;++i)
+ {
+ if (!parray[i])
+ {
+ ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)",
+ firstName,i,secondName,size);
+ }
+ Validate(parray[i]);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size,
+ const char* firstName, const char* secondName)
+{
+ // validate all entries
+ if (size)
+ {
+ if (!parray) {
+ ReportError("aiScene::%s is NULL (aiScene::%s is %i)",
+ firstName, secondName, size);
+ }
+ for (unsigned int i = 0; i < size;++i)
+ {
+ if (!parray[i])
+ {
+ ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)",
+ firstName,i,secondName,size);
+ }
+ Validate(parray[i]);
+
+ // check whether there are duplicate names
+ for (unsigned int a = i+1; a < size;++a)
+ {
+ if (parray[i]->mName == parray[a]->mName)
+ {
+ this->ReportError("aiScene::%s[%i] has the same name as "
+ "aiScene::%s[%i]",firstName, i,secondName, a);
+ }
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline void ValidateDSProcess::DoValidationWithNameCheck(T** array,
+ unsigned int size, const char* firstName,
+ const char* secondName)
+{
+ // validate all entries
+ DoValidationEx(array,size,firstName,secondName);
+
+ for (unsigned int i = 0; i < size;++i)
+ {
+ int res = HasNameMatch(array[i]->mName,mScene->mRootNode);
+ if (!res) {
+ ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)",
+ firstName,i,array[i]->mName.data);
+ }
+ else if (1 != res) {
+ ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name",
+ firstName,i,array[i]->mName.data);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void ValidateDSProcess::Execute( aiScene* pScene)
+{
+ this->mScene = pScene;
+ DefaultLogger::get()->debug("ValidateDataStructureProcess begin");
+
+ // validate the node graph of the scene
+ Validate(pScene->mRootNode);
+
+ // validate all meshes
+ if (pScene->mNumMeshes) {
+ DoValidation(pScene->mMeshes,pScene->mNumMeshes,"mMeshes","mNumMeshes");
+ }
+ else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) {
+ ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there");
+ }
+ else if (pScene->mMeshes) {
+ ReportError("aiScene::mMeshes is non-null although there are no meshes");
+ }
+
+ // validate all animations
+ if (pScene->mNumAnimations) {
+ DoValidation(pScene->mAnimations,pScene->mNumAnimations,
+ "mAnimations","mNumAnimations");
+ }
+ else if (pScene->mAnimations) {
+ ReportError("aiScene::mAnimations is non-null although there are no animations");
+ }
+
+ // validate all cameras
+ if (pScene->mNumCameras) {
+ DoValidationWithNameCheck(pScene->mCameras,pScene->mNumCameras,
+ "mCameras","mNumCameras");
+ }
+ else if (pScene->mCameras) {
+ ReportError("aiScene::mCameras is non-null although there are no cameras");
+ }
+
+ // validate all lights
+ if (pScene->mNumLights) {
+ DoValidationWithNameCheck(pScene->mLights,pScene->mNumLights,
+ "mLights","mNumLights");
+ }
+ else if (pScene->mLights) {
+ ReportError("aiScene::mLights is non-null although there are no lights");
+ }
+
+ // validate all textures
+ if (pScene->mNumTextures) {
+ DoValidation(pScene->mTextures,pScene->mNumTextures,
+ "mTextures","mNumTextures");
+ }
+ else if (pScene->mTextures) {
+ ReportError("aiScene::mTextures is non-null although there are no textures");
+ }
+
+ // validate all materials
+ if (pScene->mNumMaterials) {
+ DoValidation(pScene->mMaterials,pScene->mNumMaterials,"mMaterials","mNumMaterials");
+ }
+#if 0
+ // NOTE: ScenePreprocessor generates a default material if none is there
+ else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) {
+ ReportError("aiScene::mNumMaterials is 0. At least one material must be there");
+ }
+#endif
+ else if (pScene->mMaterials) {
+ ReportError("aiScene::mMaterials is non-null although there are no materials");
+ }
+
+// if (!has)ReportError("The aiScene data structure is empty");
+ DefaultLogger::get()->debug("ValidateDataStructureProcess end");
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiLight* pLight)
+{
+ if (pLight->mType == aiLightSource_UNDEFINED)
+ ReportWarning("aiLight::mType is aiLightSource_UNDEFINED");
+
+ if (!pLight->mAttenuationConstant &&
+ !pLight->mAttenuationLinear &&
+ !pLight->mAttenuationQuadratic) {
+ ReportWarning("aiLight::mAttenuationXXX - all are zero");
+ }
+
+ if (pLight->mAngleInnerCone > pLight->mAngleOuterCone)
+ ReportError("aiLight::mAngleInnerCone is larger than aiLight::mAngleOuterCone");
+
+ if (pLight->mColorDiffuse.IsBlack() && pLight->mColorAmbient.IsBlack()
+ && pLight->mColorSpecular.IsBlack())
+ {
+ ReportWarning("aiLight::mColorXXX - all are black and won't have any influence");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiCamera* pCamera)
+{
+ if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear)
+ ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear");
+
+ // FIX: there are many 3ds files with invalid FOVs. No reason to
+ // reject them at all ... a warning is appropriate.
+ if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI)
+ ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV",pCamera->mHorizontalFOV);
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiMesh* pMesh)
+{
+ // validate the material index of the mesh
+ if (mScene->mNumMaterials && pMesh->mMaterialIndex >= mScene->mNumMaterials)
+ {
+ ReportError("aiMesh::mMaterialIndex is invalid (value: %i maximum: %i)",
+ pMesh->mMaterialIndex,mScene->mNumMaterials-1);
+ }
+
+ Validate(&pMesh->mName);
+
+ for (unsigned int i = 0; i < pMesh->mNumFaces; ++i)
+ {
+ aiFace& face = pMesh->mFaces[i];
+
+ if (pMesh->mPrimitiveTypes)
+ {
+ switch (face.mNumIndices)
+ {
+ case 0:
+ ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i);
+ case 1:
+ if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT))
+ {
+ ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimtiveTypes "
+ "does not report the POINT flag",i);
+ }
+ break;
+ case 2:
+ if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_LINE))
+ {
+ ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimtiveTypes "
+ "does not report the LINE flag",i);
+ }
+ break;
+ case 3:
+ if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE))
+ {
+ ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimtiveTypes "
+ "does not report the TRIANGLE flag",i);
+ }
+ break;
+ default:
+ if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POLYGON))
+ {
+ this->ReportError("aiMesh::mFaces[%i] is a POLYGON but aiMesh::mPrimtiveTypes "
+ "does not report the POLYGON flag",i);
+ }
+ break;
+ };
+ }
+
+ if (!face.mIndices)
+ ReportError("aiMesh::mFaces[%i].mIndices is NULL",i);
+ }
+
+ // positions must always be there ...
+ if (!pMesh->mNumVertices || (!pMesh->mVertices && !mScene->mFlags)) {
+ ReportError("The mesh contains no vertices");
+ }
+
+ if (pMesh->mNumVertices > AI_MAX_VERTICES) {
+ ReportError("Mesh has too many vertices: %u, but the limit is %u",pMesh->mNumVertices,AI_MAX_VERTICES);
+ }
+ if (pMesh->mNumFaces > AI_MAX_FACES) {
+ ReportError("Mesh has too many faces: %u, but the limit is %u",pMesh->mNumFaces,AI_MAX_FACES);
+ }
+
+ // if tangents are there there must also be bitangent vectors ...
+ if ((pMesh->mTangents != NULL) != (pMesh->mBitangents != NULL)) {
+ ReportError("If there are tangents, bitangent vectors must be present as well");
+ }
+
+ // faces, too
+ if (!pMesh->mNumFaces || (!pMesh->mFaces && !mScene->mFlags)) {
+ ReportError("Mesh contains no faces");
+ }
+
+ // now check whether the face indexing layout is correct:
+ // unique vertices, pseudo-indexed.
+ std::vector<bool> abRefList;
+ abRefList.resize(pMesh->mNumVertices,false);
+ for (unsigned int i = 0; i < pMesh->mNumFaces;++i)
+ {
+ aiFace& face = pMesh->mFaces[i];
+ if (face.mNumIndices > AI_MAX_FACE_INDICES) {
+ ReportError("Face %u has too many faces: %u, but the limit is %u",i,face.mNumIndices,AI_MAX_FACE_INDICES);
+ }
+
+ for (unsigned int a = 0; a < face.mNumIndices;++a)
+ {
+ if (face.mIndices[a] >= pMesh->mNumVertices) {
+ ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a);
+ }
+ // the MSB flag is temporarily used by the extra verbose
+ // mode to tell us that the JoinVerticesProcess might have
+ // been executed already.
+ if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && abRefList[face.mIndices[a]])
+ {
+ ReportError("aiMesh::mVertices[%i] is referenced twice - second "
+ "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a);
+ }
+ abRefList[face.mIndices[a]] = true;
+ }
+ }
+
+ // check whether there are vertices that aren't referenced by a face
+ bool b = false;
+ for (unsigned int i = 0; i < pMesh->mNumVertices;++i) {
+ if (!abRefList[i])b = true;
+ }
+ abRefList.clear();
+ if (b)ReportWarning("There are unreferenced vertices");
+
+ // texture channel 2 may not be set if channel 1 is zero ...
+ {
+ unsigned int i = 0;
+ for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
+ {
+ if (!pMesh->HasTextureCoords(i))break;
+ }
+ for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
+ if (pMesh->HasTextureCoords(i))
+ {
+ ReportError("Texture coordinate channel %i exists "
+ "although the previous channel was NULL.",i);
+ }
+ }
+ // the same for the vertex colors
+ {
+ unsigned int i = 0;
+ for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i)
+ {
+ if (!pMesh->HasVertexColors(i))break;
+ }
+ for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i)
+ if (pMesh->HasVertexColors(i))
+ {
+ ReportError("Vertex color channel %i is exists "
+ "although the previous channel was NULL.",i);
+ }
+ }
+
+
+ // now validate all bones
+ if (pMesh->mNumBones)
+ {
+ if (!pMesh->mBones)
+ {
+ ReportError("aiMesh::mBones is NULL (aiMesh::mNumBones is %i)",
+ pMesh->mNumBones);
+ }
+ boost::scoped_array<float> afSum(NULL);
+ if (pMesh->mNumVertices)
+ {
+ afSum.reset(new float[pMesh->mNumVertices]);
+ for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
+ afSum[i] = 0.0f;
+ }
+
+ // check whether there are duplicate bone names
+ for (unsigned int i = 0; i < pMesh->mNumBones;++i)
+ {
+ const aiBone* bone = pMesh->mBones[i];
+ if (bone->mNumWeights > AI_MAX_BONE_WEIGHTS) {
+ ReportError("Bone %u has too many weights: %u, but the limit is %u",i,bone->mNumWeights,AI_MAX_BONE_WEIGHTS);
+ }
+
+ if (!pMesh->mBones[i])
+ {
+ ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)",
+ i,pMesh->mNumBones);
+ }
+ Validate(pMesh,pMesh->mBones[i],afSum.get());
+
+ for (unsigned int a = i+1; a < pMesh->mNumBones;++a)
+ {
+ if (pMesh->mBones[i]->mName == pMesh->mBones[a]->mName)
+ {
+ ReportError("aiMesh::mBones[%i] has the same name as "
+ "aiMesh::mBones[%i]",i,a);
+ }
+ }
+ }
+ // check whether all bone weights for a vertex sum to 1.0 ...
+ for (unsigned int i = 0; i < pMesh->mNumVertices;++i)
+ {
+ if (afSum[i] && (afSum[i] <= 0.94 || afSum[i] >= 1.05)) {
+ ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]);
+ }
+ }
+ }
+ else if (pMesh->mBones)
+ {
+ ReportError("aiMesh::mBones is non-null although there are no bones");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiMesh* pMesh,
+ const aiBone* pBone,float* afSum)
+{
+ this->Validate(&pBone->mName);
+
+ if (!pBone->mNumWeights) {
+ ReportError("aiBone::mNumWeights is zero");
+ }
+
+ // check whether all vertices affected by this bone are valid
+ for (unsigned int i = 0; i < pBone->mNumWeights;++i)
+ {
+ if (pBone->mWeights[i].mVertexId >= pMesh->mNumVertices) {
+ ReportError("aiBone::mWeights[%i].mVertexId is out of range",i);
+ }
+ else if (!pBone->mWeights[i].mWeight || pBone->mWeights[i].mWeight > 1.0f) {
+ ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value",i);
+ }
+ afSum[pBone->mWeights[i].mVertexId] += pBone->mWeights[i].mWeight;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
+{
+ Validate(&pAnimation->mName);
+
+ // validate all materials
+ if (pAnimation->mNumChannels)
+ {
+ if (!pAnimation->mChannels) {
+ ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)",
+ pAnimation->mNumChannels);
+ }
+ for (unsigned int i = 0; i < pAnimation->mNumChannels;++i)
+ {
+ if (!pAnimation->mChannels[i])
+ {
+ ReportError("aiAnimation::mChannels[%i] is NULL (aiAnimation::mNumChannels is %i)",
+ i, pAnimation->mNumChannels);
+ }
+ Validate(pAnimation, pAnimation->mChannels[i]);
+ }
+ }
+ else ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
+
+ // Animation duration is allowed to be zero in cases where the anim contains only a single key frame.
+ // if (!pAnimation->mDuration)this->ReportError("aiAnimation::mDuration is zero");
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
+ aiTextureType type)
+{
+ const char* szType = TextureTypeToString(type);
+
+ // ****************************************************************************
+ // Search all keys of the material ...
+ // textures must be specified with ascending indices
+ // (e.g. diffuse #2 may not be specified if diffuse #1 is not there ...)
+ // ****************************************************************************
+
+ int iNumIndices = 0;
+ int iIndex = -1;
+ for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
+ {
+ aiMaterialProperty* prop = pMaterial->mProperties[i];
+ if (!::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == type) {
+ iIndex = std::max(iIndex, (int) prop->mIndex);
+ ++iNumIndices;
+
+ if (aiPTI_String != prop->mType)
+ ReportError("Material property %s is expected to be a string",prop->mKey.data);
+ }
+ }
+ if (iIndex +1 != iNumIndices) {
+ ReportError("%s #%i is set, but there are only %i %s textures",
+ szType,iIndex,iNumIndices,szType);
+ }
+ if (!iNumIndices)return;
+ std::vector<aiTextureMapping> mappings(iNumIndices);
+
+ // Now check whether all UV indices are valid ...
+ bool bNoSpecified = true;
+ for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
+ {
+ aiMaterialProperty* prop = pMaterial->mProperties[i];
+ if (prop->mSemantic != type)continue;
+
+ if ((int)prop->mIndex >= iNumIndices)
+ {
+ ReportError("Found texture property with index %i, although there "
+ "are only %i textures of type %s",
+ prop->mIndex, iNumIndices, szType);
+ }
+
+ if (!::strcmp(prop->mKey.data,"$tex.mapping")) {
+ if (aiPTI_Integer != prop->mType || prop->mDataLength < sizeof(aiTextureMapping))
+ {
+ ReportError("Material property %s%i is expected to be an integer (size is %i)",
+ prop->mKey.data,prop->mIndex,prop->mDataLength);
+ }
+ mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData);
+ }
+ else if (!::strcmp(prop->mKey.data,"$tex.uvtrafo")) {
+ if (aiPTI_Float != prop->mType || prop->mDataLength < sizeof(aiUVTransform))
+ {
+ ReportError("Material property %s%i is expected to be 5 floats large (size is %i)",
+ prop->mKey.data,prop->mIndex, prop->mDataLength);
+ }
+ mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData);
+ }
+ else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) {
+ if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength)
+ {
+ ReportError("Material property %s%i is expected to be an integer (size is %i)",
+ prop->mKey.data,prop->mIndex,prop->mDataLength);
+ }
+ bNoSpecified = false;
+
+ // Ignore UV indices for texture channels that are not there ...
+
+ // Get the value
+ iIndex = *((unsigned int*)prop->mData);
+
+ // Check whether there is a mesh using this material
+ // which has not enough UV channels ...
+ for (unsigned int a = 0; a < mScene->mNumMeshes;++a)
+ {
+ aiMesh* mesh = this->mScene->mMeshes[a];
+ if (mesh->mMaterialIndex == (unsigned int)i)
+ {
+ int iChannels = 0;
+ while (mesh->HasTextureCoords(iChannels))++iChannels;
+ if (iIndex >= iChannels)
+ {
+ ReportWarning("Invalid UV index: %i (key %s). Mesh %i has only %i UV channels",
+ iIndex,prop->mKey.data,a,iChannels);
+ }
+ }
+ }
+ }
+ }
+ if (bNoSpecified)
+ {
+ // Assume that all textures are using the first UV channel
+ for (unsigned int a = 0; a < mScene->mNumMeshes;++a)
+ {
+ aiMesh* mesh = mScene->mMeshes[a];
+ if (mesh->mMaterialIndex == (unsigned int)iIndex && mappings[0] == aiTextureMapping_UV)
+ {
+ if (!mesh->mTextureCoords[0])
+ {
+ // This is a special case ... it could be that the
+ // original mesh format intended the use of a special
+ // mapping here.
+ ReportWarning("UV-mapped texture, but there are no UV coords");
+ }
+ }
+ }
+ }
+}
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
+{
+ // check whether there are material keys that are obviously not legal
+ for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
+ {
+ const aiMaterialProperty* prop = pMaterial->mProperties[i];
+ if (!prop) {
+ ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)",
+ i,pMaterial->mNumProperties);
+ }
+ if (!prop->mDataLength || !prop->mData) {
+ ReportError("aiMaterial::mProperties[%i].mDataLength or "
+ "aiMaterial::mProperties[%i].mData is 0",i,i);
+ }
+ // check all predefined types
+ if (aiPTI_String == prop->mType) {
+ // FIX: strings are now stored in a less expensive way, but we can't use the
+ // validation routine for 'normal' aiStrings
+ uint32_t len;
+ if (prop->mDataLength < 5 || prop->mDataLength < 4 + (len=*reinterpret_cast<uint32_t*>(prop->mData)) + 1) {
+ ReportError("aiMaterial::mProperties[%i].mDataLength is "
+ "too small to contain a string (%i, needed: %i)",
+ i,prop->mDataLength,sizeof(aiString));
+ }
+ if (prop->mData[prop->mDataLength-1]) {
+ ReportError("Missing null-terminator in string material property");
+ }
+ // Validate((const aiString*)prop->mData);
+ }
+ else if (aiPTI_Float == prop->mType) {
+ if (prop->mDataLength < sizeof(float)) {
+ ReportError("aiMaterial::mProperties[%i].mDataLength is "
+ "too small to contain a float (%i, needed: %i)",
+ i,prop->mDataLength,sizeof(float));
+ }
+ }
+ else if (aiPTI_Integer == prop->mType) {
+ if (prop->mDataLength < sizeof(int)) {
+ ReportError("aiMaterial::mProperties[%i].mDataLength is "
+ "too small to contain an integer (%i, needed: %i)",
+ i,prop->mDataLength,sizeof(int));
+ }
+ }
+ // TODO: check whether there is a key with an unknown name ...
+ }
+
+ // make some more specific tests
+ float fTemp;
+ int iShading;
+ if (AI_SUCCESS == aiGetMaterialInteger( pMaterial,AI_MATKEY_SHADING_MODEL,&iShading)) {
+ switch ((aiShadingMode)iShading)
+ {
+ case aiShadingMode_Blinn:
+ case aiShadingMode_CookTorrance:
+ case aiShadingMode_Phong:
+
+ if (AI_SUCCESS != aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS,&fTemp)) {
+ ReportWarning("A specular shading model is specified but there is no "
+ "AI_MATKEY_SHININESS key");
+ }
+ if (AI_SUCCESS == aiGetMaterialFloat(pMaterial,AI_MATKEY_SHININESS_STRENGTH,&fTemp) && !fTemp) {
+ ReportWarning("A specular shading model is specified but the value of the "
+ "AI_MATKEY_SHININESS_STRENGTH key is 0.0");
+ }
+ break;
+ default: ;
+ };
+ }
+
+ if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01f)) {
+ ReportWarning("Invalid opacity value (must be 0 < opacity < 1.0)");
+ }
+
+ // Check whether there are invalid texture keys
+ // TODO: that's a relict of the past, where texture type and index were baked
+ // into the material string ... we could do that in one single pass.
+ SearchForInvalidTextures(pMaterial,aiTextureType_DIFFUSE);
+ SearchForInvalidTextures(pMaterial,aiTextureType_SPECULAR);
+ SearchForInvalidTextures(pMaterial,aiTextureType_AMBIENT);
+ SearchForInvalidTextures(pMaterial,aiTextureType_EMISSIVE);
+ SearchForInvalidTextures(pMaterial,aiTextureType_OPACITY);
+ SearchForInvalidTextures(pMaterial,aiTextureType_SHININESS);
+ SearchForInvalidTextures(pMaterial,aiTextureType_HEIGHT);
+ SearchForInvalidTextures(pMaterial,aiTextureType_NORMALS);
+ SearchForInvalidTextures(pMaterial,aiTextureType_DISPLACEMENT);
+ SearchForInvalidTextures(pMaterial,aiTextureType_LIGHTMAP);
+ SearchForInvalidTextures(pMaterial,aiTextureType_REFLECTION);
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiTexture* pTexture)
+{
+ // the data section may NEVER be NULL
+ if (!pTexture->pcData) {
+ ReportError("aiTexture::pcData is NULL");
+ }
+ if (pTexture->mHeight)
+ {
+ if (!pTexture->mWidth)ReportError("aiTexture::mWidth is zero "
+ "(aiTexture::mHeight is %i, uncompressed texture)",pTexture->mHeight);
+ }
+ else
+ {
+ if (!pTexture->mWidth) {
+ ReportError("aiTexture::mWidth is zero (compressed texture)");
+ }
+ if ('\0' != pTexture->achFormatHint[3]) {
+ ReportWarning("aiTexture::achFormatHint must be zero-terminated");
+ }
+ else if ('.' == pTexture->achFormatHint[0]) {
+ ReportWarning("aiTexture::achFormatHint should contain a file extension "
+ "without a leading dot (format hint: %s).",pTexture->achFormatHint);
+ }
+ }
+
+ const char* sz = pTexture->achFormatHint;
+ if ((sz[0] >= 'A' && sz[0] <= 'Z') ||
+ (sz[1] >= 'A' && sz[1] <= 'Z') ||
+ (sz[2] >= 'A' && sz[2] <= 'Z') ||
+ (sz[3] >= 'A' && sz[3] <= 'Z')) {
+ ReportError("aiTexture::achFormatHint contains non-lowercase letters");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
+ const aiNodeAnim* pNodeAnim)
+{
+ Validate(&pNodeAnim->mNodeName);
+
+ if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys)
+ ReportError("Empty node animation channel");
+
+ // otherwise check whether one of the keys exceeds the total duration of the animation
+ if (pNodeAnim->mNumPositionKeys)
+ {
+ if (!pNodeAnim->mPositionKeys)
+ {
+ this->ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)",
+ pNodeAnim->mNumPositionKeys);
+ }
+ double dLast = -10e10;
+ for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys;++i)
+ {
+ // ScenePreprocessor will compute the duration if still the default value
+ // (Aramis) Add small epsilon, comparison tended to fail if max_time == duration,
+ // seems to be due the compilers register usage/width.
+ if (pAnimation->mDuration > 0. && pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration+0.001)
+ {
+ ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger "
+ "than aiAnimation::mDuration (which is %.5f)",i,
+ (float)pNodeAnim->mPositionKeys[i].mTime,
+ (float)pAnimation->mDuration);
+ }
+ if (i && pNodeAnim->mPositionKeys[i].mTime <= dLast)
+ {
+ ReportWarning("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is smaller "
+ "than aiAnimation::mPositionKeys[%i] (which is %.5f)",i,
+ (float)pNodeAnim->mPositionKeys[i].mTime,
+ i-1, (float)dLast);
+ }
+ dLast = pNodeAnim->mPositionKeys[i].mTime;
+ }
+ }
+ // rotation keys
+ if (pNodeAnim->mNumRotationKeys)
+ {
+ if (!pNodeAnim->mRotationKeys)
+ {
+ this->ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)",
+ pNodeAnim->mNumRotationKeys);
+ }
+ double dLast = -10e10;
+ for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys;++i)
+ {
+ if (pAnimation->mDuration > 0. && pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration+0.001)
+ {
+ ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger "
+ "than aiAnimation::mDuration (which is %.5f)",i,
+ (float)pNodeAnim->mRotationKeys[i].mTime,
+ (float)pAnimation->mDuration);
+ }
+ if (i && pNodeAnim->mRotationKeys[i].mTime <= dLast)
+ {
+ ReportWarning("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is smaller "
+ "than aiAnimation::mRotationKeys[%i] (which is %.5f)",i,
+ (float)pNodeAnim->mRotationKeys[i].mTime,
+ i-1, (float)dLast);
+ }
+ dLast = pNodeAnim->mRotationKeys[i].mTime;
+ }
+ }
+ // scaling keys
+ if (pNodeAnim->mNumScalingKeys)
+ {
+ if (!pNodeAnim->mScalingKeys) {
+ ReportError("aiNodeAnim::mScalingKeys is NULL (aiNodeAnim::mNumScalingKeys is %i)",
+ pNodeAnim->mNumScalingKeys);
+ }
+ double dLast = -10e10;
+ for (unsigned int i = 0; i < pNodeAnim->mNumScalingKeys;++i)
+ {
+ if (pAnimation->mDuration > 0. && pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration+0.001)
+ {
+ ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger "
+ "than aiAnimation::mDuration (which is %.5f)",i,
+ (float)pNodeAnim->mScalingKeys[i].mTime,
+ (float)pAnimation->mDuration);
+ }
+ if (i && pNodeAnim->mScalingKeys[i].mTime <= dLast)
+ {
+ ReportWarning("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is smaller "
+ "than aiAnimation::mScalingKeys[%i] (which is %.5f)",i,
+ (float)pNodeAnim->mScalingKeys[i].mTime,
+ i-1, (float)dLast);
+ }
+ dLast = pNodeAnim->mScalingKeys[i].mTime;
+ }
+ }
+
+ if (!pNodeAnim->mNumScalingKeys && !pNodeAnim->mNumRotationKeys &&
+ !pNodeAnim->mNumPositionKeys)
+ {
+ ReportError("A node animation channel must have at least one subtrack");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiNode* pNode)
+{
+ if (!pNode)ReportError("A node of the scenegraph is NULL");
+ if (pNode != mScene->mRootNode && !pNode->mParent)
+ this->ReportError("A node has no valid parent (aiNode::mParent is NULL)");
+
+ this->Validate(&pNode->mName);
+
+ // validate all meshes
+ if (pNode->mNumMeshes)
+ {
+ if (!pNode->mMeshes)
+ {
+ ReportError("aiNode::mMeshes is NULL (aiNode::mNumMeshes is %i)",
+ pNode->mNumMeshes);
+ }
+ std::vector<bool> abHadMesh;
+ abHadMesh.resize(mScene->mNumMeshes,false);
+ for (unsigned int i = 0; i < pNode->mNumMeshes;++i)
+ {
+ if (pNode->mMeshes[i] >= mScene->mNumMeshes)
+ {
+ ReportError("aiNode::mMeshes[%i] is out of range (maximum is %i)",
+ pNode->mMeshes[i],mScene->mNumMeshes-1);
+ }
+ if (abHadMesh[pNode->mMeshes[i]])
+ {
+ ReportError("aiNode::mMeshes[%i] is already referenced by this node (value: %i)",
+ i,pNode->mMeshes[i]);
+ }
+ abHadMesh[pNode->mMeshes[i]] = true;
+ }
+ }
+ if (pNode->mNumChildren)
+ {
+ if (!pNode->mChildren) {
+ ReportError("aiNode::mChildren is NULL (aiNode::mNumChildren is %i)",
+ pNode->mNumChildren);
+ }
+ for (unsigned int i = 0; i < pNode->mNumChildren;++i) {
+ Validate(pNode->mChildren[i]);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDSProcess::Validate( const aiString* pString)
+{
+ if (pString->length > MAXLEN)
+ {
+ this->ReportError("aiString::length is too large (%i, maximum is %i)",
+ pString->length,MAXLEN);
+ }
+ const char* sz = pString->data;
+ while (true)
+ {
+ if ('\0' == *sz)
+ {
+ if (pString->length != (unsigned int)(sz-pString->data))
+ ReportError("aiString::data is invalid: the terminal zero is at a wrong offset");
+ break;
+ }
+ else if (sz >= &pString->data[MAXLEN])
+ ReportError("aiString::data is invalid. There is no terminal character");
+ ++sz;
+ }
+}
diff --git a/3rdparty/assimp/code/ValidateDataStructure.h b/3rdparty/assimp/code/ValidateDataStructure.h
new file mode 100644
index 000000000..48d12b8f0
--- /dev/null
+++ b/3rdparty/assimp/code/ValidateDataStructure.h
@@ -0,0 +1,187 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a (dummy) post processing step to validate the loader's
+ * output data structure (for debugging)
+ */
+#ifndef AI_VALIDATEPROCESS_H_INC
+#define AI_VALIDATEPROCESS_H_INC
+
+#include "../include/aiTypes.h"
+#include "BaseProcess.h"
+
+struct aiBone;
+struct aiMesh;
+struct aiAnimation;
+struct aiNodeAnim;
+struct aiTexture;
+struct aiMaterial;
+struct aiNode;
+struct aiString;
+
+namespace Assimp {
+
+// --------------------------------------------------------------------------------------
+/** Validates the whole ASSIMP scene data structure for correctness.
+ * ImportErrorException is thrown of the scene is corrupt.*/
+// --------------------------------------------------------------------------------------
+class ASSIMP_API ValidateDSProcess : public BaseProcess
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ ValidateDSProcess();
+
+ /** Destructor, private as well */
+ ~ValidateDSProcess();
+
+public:
+ // -------------------------------------------------------------------
+ bool IsActive( unsigned int pFlags) const;
+
+ // -------------------------------------------------------------------
+ void Execute( aiScene* pScene);
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Report a validation error. This will throw an exception,
+ * control won't return.
+ * @param msg Format string for sprintf().*/
+ AI_WONT_RETURN void ReportError(const char* msg,...);
+
+
+ // -------------------------------------------------------------------
+ /** Report a validation warning. This won't throw an exception,
+ * control will return to the callera.
+ * @param msg Format string for sprintf().*/
+ void ReportWarning(const char* msg,...);
+
+
+ // -------------------------------------------------------------------
+ /** Validates a mesh
+ * @param pMesh Input mesh*/
+ void Validate( const aiMesh* pMesh);
+
+ // -------------------------------------------------------------------
+ /** Validates a bone
+ * @param pMesh Input mesh
+ * @param pBone Input bone*/
+ void Validate( const aiMesh* pMesh,const aiBone* pBone,float* afSum);
+
+ // -------------------------------------------------------------------
+ /** Validates an animation
+ * @param pAnimation Input animation*/
+ void Validate( const aiAnimation* pAnimation);
+
+ // -------------------------------------------------------------------
+ /** Validates a material
+ * @param pMaterial Input material*/
+ void Validate( const aiMaterial* pMaterial);
+
+ // -------------------------------------------------------------------
+ /** Search the material data structure for invalid or corrupt
+ * texture keys.
+ * @param pMaterial Input material
+ * @param type Type of the texture*/
+ void SearchForInvalidTextures(const aiMaterial* pMaterial,
+ aiTextureType type);
+
+ // -------------------------------------------------------------------
+ /** Validates a texture
+ * @param pTexture Input texture*/
+ void Validate( const aiTexture* pTexture);
+
+ // -------------------------------------------------------------------
+ /** Validates a light source
+ * @param pLight Input light
+ */
+ void Validate( const aiLight* pLight);
+
+ // -------------------------------------------------------------------
+ /** Validates a camera
+ * @param pCamera Input camera*/
+ void Validate( const aiCamera* pCamera);
+
+ // -------------------------------------------------------------------
+ /** Validates a bone animation channel
+ * @param pAnimation Animation channel.
+ * @param pBoneAnim Input bone animation */
+ void Validate( const aiAnimation* pAnimation,
+ const aiNodeAnim* pBoneAnim);
+
+ // -------------------------------------------------------------------
+ /** Validates a node and all of its subnodes
+ * @param Node Input node*/
+ void Validate( const aiNode* pNode);
+
+ // -------------------------------------------------------------------
+ /** Validates a string
+ * @param pString Input string*/
+ void Validate( const aiString* pString);
+
+private:
+
+ // template to validate one of the aiScene::mXXX arrays
+ template <typename T>
+ inline void DoValidation(T** array, unsigned int size,
+ const char* firstName, const char* secondName);
+
+ // extended version: checks whethr T::mName occurs twice
+ template <typename T>
+ inline void DoValidationEx(T** array, unsigned int size,
+ const char* firstName, const char* secondName);
+
+ // extension to the first template which does also search
+ // the nodegraph for an item with the same name
+ template <typename T>
+ inline void DoValidationWithNameCheck(T** array, unsigned int size,
+ const char* firstName, const char* secondName);
+
+ aiScene* mScene;
+};
+
+
+
+
+} // end of namespace Assimp
+
+#endif // AI_VALIDATEPROCESS_H_INC
diff --git a/3rdparty/assimp/code/Vertex.h b/3rdparty/assimp/code/Vertex.h
new file mode 100644
index 000000000..6fa33dc75
--- /dev/null
+++ b/3rdparty/assimp/code/Vertex.h
@@ -0,0 +1,301 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a helper class to represent an interleaved vertex */
+#ifndef AI_VERTEX_H_INC
+#define AI_VERTEX_H_INC
+namespace Assimp {
+
+ ///////////////////////////////////////////////////////////////////////////
+ // std::plus-family operates on operands with identical types - we need to
+ // support all the (vectype op float) combinations in vector maths.
+ // Providing T(float) would open the way to endless implicit conversions.
+ ///////////////////////////////////////////////////////////////////////////
+ namespace Intern {
+ template <typename T0, typename T1, typename TRES = T0> struct plus {
+ TRES operator() (const T0& t0, const T1& t1) const {
+ return t0+t1;
+ }
+ };
+ template <typename T0, typename T1, typename TRES = T0> struct minus {
+ TRES operator() (const T0& t0, const T1& t1) const {
+ return t0-t1;
+ }
+ };
+ template <typename T0, typename T1, typename TRES = T0> struct multiplies {
+ TRES operator() (const T0& t0, const T1& t1) const {
+ return t0*t1;
+ }
+ };
+ template <typename T0, typename T1, typename TRES = T0> struct divides {
+ TRES operator() (const T0& t0, const T1& t1) const {
+ return t0/t1;
+ }
+ };
+ }
+
+// ------------------------------------------------------------------------------------------------
+/** Intermediate description a vertex with all possible components. Defines a full set of
+ * operators, so you may use such a 'Vertex' in basic arithmetics. All operators are applied
+ * to *all* vertex components equally. This is useful for stuff like interpolation
+ * or subdivision, but won't work if special handling is required for some vertex components. */
+// ------------------------------------------------------------------------------------------------
+class Vertex
+{
+ friend Vertex operator + (const Vertex&,const Vertex&);
+ friend Vertex operator - (const Vertex&,const Vertex&);
+
+ friend Vertex operator + (const Vertex&,float);
+ friend Vertex operator - (const Vertex&,float);
+ friend Vertex operator * (const Vertex&,float);
+ friend Vertex operator / (const Vertex&,float);
+
+ friend Vertex operator + (float, const Vertex&);
+ friend Vertex operator - (float, const Vertex&);
+ friend Vertex operator * (float, const Vertex&);
+ friend Vertex operator / (float, const Vertex&);
+
+public:
+
+ Vertex() {}
+
+ // ----------------------------------------------------------------------------
+ /** Extract a particular vertex from a mesh and interleave all components */
+ explicit Vertex(const aiMesh* msh, unsigned int idx) {
+ ai_assert(idx < msh->mNumVertices);
+ position = msh->mVertices[idx];
+
+ if (msh->HasNormals()) {
+ normal = msh->mNormals[idx];
+ }
+
+ if (msh->HasTangentsAndBitangents()) {
+ tangent = msh->mTangents[idx];
+ bitangent = msh->mBitangents[idx];
+ }
+
+ for (unsigned int i = 0; msh->HasTextureCoords(i); ++i) {
+ texcoords[i] = msh->mTextureCoords[i][idx];
+ }
+
+ for (unsigned int i = 0; msh->HasVertexColors(i); ++i) {
+ colors[i] = msh->mColors[i][idx];
+ }
+ }
+
+public:
+
+ Vertex& operator += (const Vertex& v) {
+ *this = *this+v;
+ return *this;
+ }
+
+ Vertex& operator -= (const Vertex& v) {
+ *this = *this-v;
+ return *this;
+ }
+
+
+
+ Vertex& operator += (float v) {
+ *this = *this+v;
+ return *this;
+ }
+
+ Vertex& operator -= (float v) {
+ *this = *this-v;
+ return *this;
+ }
+
+ Vertex& operator *= (float v) {
+ *this = *this*v;
+ return *this;
+ }
+
+ Vertex& operator /= (float v) {
+ *this = *this/v;
+ return *this;
+ }
+
+public:
+
+ // ----------------------------------------------------------------------------
+ /** Convert back to non-interleaved storage */
+ void SortBack(aiMesh* out, unsigned int idx) const {
+
+ ai_assert(idx<out->mNumVertices);
+ out->mVertices[idx] = position;
+
+ if (out->HasNormals()) {
+ out->mNormals[idx] = normal;
+ }
+
+ if (out->HasTangentsAndBitangents()) {
+ out->mTangents[idx] = tangent;
+ out->mBitangents[idx] = bitangent;
+ }
+
+ for (unsigned int i = 0; out->HasTextureCoords(i); ++i) {
+ out->mTextureCoords[i][idx] = texcoords[i];
+ }
+
+ for (unsigned int i = 0; out->HasVertexColors(i); ++i) {
+ out->mColors[i][idx] = colors[i];
+ }
+ }
+
+private:
+
+ // ----------------------------------------------------------------------------
+ /** Construct from two operands and a binary operation to combine them */
+ template <template <typename t> class op> static Vertex BinaryOp(const Vertex& v0, const Vertex& v1) {
+ // this is a heavy task for the compiler to optimize ... *pray*
+
+ Vertex res;
+ res.position = op<aiVector3D>()(v0.position,v1.position);
+ res.normal = op<aiVector3D>()(v0.normal,v1.normal);
+ res.tangent = op<aiVector3D>()(v0.tangent,v1.tangent);
+ res.bitangent = op<aiVector3D>()(v0.bitangent,v1.bitangent);
+
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ res.texcoords[i] = op<aiVector3D>()(v0.texcoords[i],v1.texcoords[i]);
+ }
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
+ res.colors[i] = op<aiColor4D>()(v0.colors[i],v1.colors[i]);
+ }
+ return res;
+ }
+
+ // ----------------------------------------------------------------------------
+ /** This time binary arithmetics of v0 with a floating-point number */
+ template <template <typename, typename, typename> class op> static Vertex BinaryOp(const Vertex& v0, float f) {
+ // this is a heavy task for the compiler to optimize ... *pray*
+
+ Vertex res;
+ res.position = op<aiVector3D,float,aiVector3D>()(v0.position,f);
+ res.normal = op<aiVector3D,float,aiVector3D>()(v0.normal,f);
+ res.tangent = op<aiVector3D,float,aiVector3D>()(v0.tangent,f);
+ res.bitangent = op<aiVector3D,float,aiVector3D>()(v0.bitangent,f);
+
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ res.texcoords[i] = op<aiVector3D,float,aiVector3D>()(v0.texcoords[i],f);
+ }
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
+ res.colors[i] = op<aiColor4D,float,aiColor4D>()(v0.colors[i],f);
+ }
+ return res;
+ }
+
+ // ----------------------------------------------------------------------------
+ /** This time binary arithmetics of v0 with a floating-point number */
+ template <template <typename, typename, typename> class op> static Vertex BinaryOp(float f, const Vertex& v0) {
+ // this is a heavy task for the compiler to optimize ... *pray*
+
+ Vertex res;
+ res.position = op<float,aiVector3D,aiVector3D>()(f,v0.position);
+ res.normal = op<float,aiVector3D,aiVector3D>()(f,v0.normal);
+ res.tangent = op<float,aiVector3D,aiVector3D>()(f,v0.tangent);
+ res.bitangent = op<float,aiVector3D,aiVector3D>()(f,v0.bitangent);
+
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+ res.texcoords[i] = op<float,aiVector3D,aiVector3D>()(f,v0.texcoords[i]);
+ }
+ for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
+ res.colors[i] = op<float,aiColor4D,aiColor4D>()(f,v0.colors[i]);
+ }
+ return res;
+ }
+
+public:
+
+ aiVector3D position;
+ aiVector3D normal;
+ aiVector3D tangent, bitangent;
+
+ aiVector3D texcoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ aiColor4D colors[AI_MAX_NUMBER_OF_COLOR_SETS];
+};
+
+
+
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE Vertex operator + (const Vertex& v0,const Vertex& v1) {
+ return Vertex::BinaryOp<std::plus>(v0,v1);
+}
+
+AI_FORCE_INLINE Vertex operator - (const Vertex& v0,const Vertex& v1) {
+ return Vertex::BinaryOp<std::minus>(v0,v1);
+}
+
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE Vertex operator + (const Vertex& v0,float f) {
+ return Vertex::BinaryOp<Intern::plus>(v0,f);
+}
+
+AI_FORCE_INLINE Vertex operator - (const Vertex& v0,float f) {
+ return Vertex::BinaryOp<Intern::minus>(v0,f);
+}
+
+AI_FORCE_INLINE Vertex operator * (const Vertex& v0,float f) {
+ return Vertex::BinaryOp<Intern::multiplies>(v0,f);
+}
+
+AI_FORCE_INLINE Vertex operator / (const Vertex& v0,float f) {
+ return Vertex::BinaryOp<Intern::divides>(v0,f);
+}
+
+// ------------------------------------------------------------------------------------------------
+AI_FORCE_INLINE Vertex operator + (float f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::plus>(f,v0);
+}
+
+AI_FORCE_INLINE Vertex operator - (float f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::minus>(f,v0);
+}
+
+AI_FORCE_INLINE Vertex operator * (float f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::multiplies>(f,v0);
+}
+
+AI_FORCE_INLINE Vertex operator / (float f,const Vertex& v0) {
+ return Vertex::BinaryOp<Intern::divides>(f,v0);
+}
+
+}
+#endif
diff --git a/3rdparty/assimp/code/VertexTriangleAdjacency.cpp b/3rdparty/assimp/code/VertexTriangleAdjacency.cpp
new file mode 100644
index 000000000..3db254613
--- /dev/null
+++ b/3rdparty/assimp/code/VertexTriangleAdjacency.cpp
@@ -0,0 +1,132 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the VertexTriangleAdjacency helper class
+ */
+
+#include "AssimpPCH.h"
+
+// internal headers
+#include "VertexTriangleAdjacency.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces,
+ unsigned int iNumFaces,
+ unsigned int iNumVertices /*= 0*/,
+ bool bComputeNumTriangles /*= false*/)
+{
+ // compute the number of referenced vertices if it wasn't specified by the caller
+ const aiFace* const pcFaceEnd = pcFaces + iNumFaces;
+ if (!iNumVertices) {
+
+ for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) {
+ ai_assert(3 == pcFace->mNumIndices);
+ iNumVertices = std::max(iNumVertices,pcFace->mIndices[0]);
+ iNumVertices = std::max(iNumVertices,pcFace->mIndices[1]);
+ iNumVertices = std::max(iNumVertices,pcFace->mIndices[2]);
+ }
+ }
+ unsigned int* pi;
+
+ // allocate storage
+ if (bComputeNumTriangles) {
+ pi = mLiveTriangles = new unsigned int[iNumVertices+1];
+ memset(mLiveTriangles,0,sizeof(unsigned int)*(iNumVertices+1));
+ mOffsetTable = new unsigned int[iNumVertices+2]+1;
+ }
+ else {
+ pi = mOffsetTable = new unsigned int[iNumVertices+2]+1;
+ memset(mOffsetTable,0,sizeof(unsigned int)*(iNumVertices+1));
+ mLiveTriangles = NULL; // important, otherwise the d'tor would crash
+ }
+
+ // get a pointer to the end of the buffer
+ unsigned int* piEnd = pi+iNumVertices;
+ *piEnd++ = 0u;
+
+ // first pass: compute the number of faces referencing each vertex
+ for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace)
+ {
+ pi[pcFace->mIndices[0]]++;
+ pi[pcFace->mIndices[1]]++;
+ pi[pcFace->mIndices[2]]++;
+ }
+
+ // second pass: compute the final offset table
+ unsigned int iSum = 0;
+ unsigned int* piCurOut = this->mOffsetTable;
+ for (unsigned int* piCur = pi; piCur != piEnd;++piCur,++piCurOut) {
+
+ unsigned int iLastSum = iSum;
+ iSum += *piCur;
+ *piCurOut = iLastSum;
+ }
+ pi = this->mOffsetTable;
+
+ // third pass: compute the final table
+ this->mAdjacencyTable = new unsigned int[iSum];
+ iSum = 0;
+ for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace,++iSum) {
+
+ unsigned int idx = pcFace->mIndices[0];
+ mAdjacencyTable[pi[idx]++] = iSum;
+
+ idx = pcFace->mIndices[1];
+ mAdjacencyTable[pi[idx]++] = iSum;
+
+ idx = pcFace->mIndices[2];
+ mAdjacencyTable[pi[idx]++] = iSum;
+ }
+ // fourth pass: undo the offset computations made during the third pass
+ // We could do this in a separate buffer, but this would be TIMES slower.
+ --mOffsetTable;
+ *mOffsetTable = 0u;
+}
+// ------------------------------------------------------------------------------------------------
+VertexTriangleAdjacency::~VertexTriangleAdjacency()
+{
+ // delete allocated storage
+ delete[] mOffsetTable;
+ delete[] mAdjacencyTable;
+ delete[] mLiveTriangles;
+}
diff --git a/3rdparty/assimp/code/VertexTriangleAdjacency.h b/3rdparty/assimp/code/VertexTriangleAdjacency.h
new file mode 100644
index 000000000..2929b0c0e
--- /dev/null
+++ b/3rdparty/assimp/code/VertexTriangleAdjacency.h
@@ -0,0 +1,124 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines a helper class to compute a vertex-triangle adjacency map */
+#ifndef AI_VTADJACENCY_H_INC
+#define AI_VTADJACENCY_H_INC
+
+#include "BaseProcess.h"
+#include "../include/aiTypes.h"
+#include "../include/aiAssert.h"
+
+struct aiMesh;
+namespace Assimp {
+
+// --------------------------------------------------------------------------------------------
+/** @brief The VertexTriangleAdjacency class computes a vertex-triangle
+ * adjacency map from a given index buffer.
+ *
+ * @note Although it is called #VertexTriangleAdjacency, the current version does also
+ * support arbitrary polygons. */
+// --------------------------------------------------------------------------------------------
+class ASSIMP_API VertexTriangleAdjacency
+{
+public:
+
+ // ----------------------------------------------------------------------------
+ /** @brief Construction from an existing index buffer
+ * @param pcFaces Index buffer
+ * @param iNumFaces Number of faces in the buffer
+ * @param iNumVertices Number of referenced vertices. This value
+ * is computed automatically if 0 is specified.
+ * @param bComputeNumTriangles If you want the class to compute
+ * a list containing the number of referenced triangles per vertex
+ * per vertex - pass true. */
+ VertexTriangleAdjacency(aiFace* pcFaces,unsigned int iNumFaces,
+ unsigned int iNumVertices = 0,
+ bool bComputeNumTriangles = true);
+
+
+ // ----------------------------------------------------------------------------
+ /** @brief Destructor */
+ ~VertexTriangleAdjacency();
+
+
+public:
+
+ // ----------------------------------------------------------------------------
+ /** @brief Get all triangles adjacent to a vertex
+ * @param iVertIndex Index of the vertex
+ * @return A pointer to the adjacency list. */
+ unsigned int* GetAdjacentTriangles(unsigned int iVertIndex) const
+ {
+ ai_assert(iVertIndex < iNumVertices);
+ return &mAdjacencyTable[ mOffsetTable[iVertIndex]];
+ }
+
+
+ // ----------------------------------------------------------------------------
+ /** @brief Get the number of triangles that are referenced by
+ * a vertex. This function returns a reference that can be modified
+ * @param iVertIndex Index of the vertex
+ * @return Number of referenced triangles */
+ unsigned int& GetNumTrianglesPtr(unsigned int iVertIndex)
+ {
+ ai_assert(iVertIndex < iNumVertices && NULL != mLiveTriangles);
+ return mLiveTriangles[iVertIndex];
+ }
+
+
+public:
+
+ //! Offset table
+ unsigned int* mOffsetTable;
+
+ //! Adjacency table
+ unsigned int* mAdjacencyTable;
+
+ //! Table containing the number of referenced triangles per vertex
+ unsigned int* mLiveTriangles;
+
+ //! Debug: Number of referenced vertices
+ unsigned int iNumVertices;
+
+};
+}
+
+#endif // !! AI_VTADJACENCY_H_INC
diff --git a/3rdparty/assimp/code/Win32DebugLogStream.h b/3rdparty/assimp/code/Win32DebugLogStream.h
new file mode 100644
index 000000000..db19108cd
--- /dev/null
+++ b/3rdparty/assimp/code/Win32DebugLogStream.h
@@ -0,0 +1,50 @@
+#ifndef AI_WIN32DEBUGLOGSTREAM_H_INC
+#define AI_WIN32DEBUGLOGSTREAM_H_INC
+
+#ifdef WIN32
+
+#include "../include/LogStream.h"
+#include "Windows.h"
+
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/** @class Win32DebugLogStream
+ * @brief Logs into the debug stream from win32.
+ */
+class Win32DebugLogStream :
+ public LogStream
+{
+public:
+ /** @brief Default constructor */
+ Win32DebugLogStream();
+
+ /** @brief Destructor */
+ ~Win32DebugLogStream();
+
+ /** @brief Writer */
+ void write(const char* messgae);
+};
+
+// ---------------------------------------------------------------------------
+// Default constructor
+inline Win32DebugLogStream::Win32DebugLogStream()
+{}
+
+// ---------------------------------------------------------------------------
+// Default constructor
+inline Win32DebugLogStream::~Win32DebugLogStream()
+{}
+
+// ---------------------------------------------------------------------------
+// Write method
+inline void Win32DebugLogStream::write(const char* message)
+{
+ OutputDebugStringA( message);
+}
+
+// ---------------------------------------------------------------------------
+} // Namespace Assimp
+
+#endif // ! WIN32
+#endif // guard
diff --git a/3rdparty/assimp/code/XFileHelper.h b/3rdparty/assimp/code/XFileHelper.h
new file mode 100644
index 000000000..23b8f77ce
--- /dev/null
+++ b/3rdparty/assimp/code/XFileHelper.h
@@ -0,0 +1,200 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Defines the helper data structures for importing XFiles */
+#ifndef AI_XFILEHELPER_H_INC
+#define AI_XFILEHELPER_H_INC
+
+#include <string>
+#include <vector>
+
+#include "../include/aiTypes.h"
+#include "../include/aiQuaternion.h"
+#include "../include/aiMesh.h"
+#include "../include/aiAnim.h"
+
+namespace Assimp
+{
+namespace XFile
+{
+
+/** Helper structure representing a XFile mesh face */
+struct Face
+{
+ std::vector<unsigned int> mIndices;
+};
+
+/** Helper structure representing a texture filename inside a material and its potential source */
+struct TexEntry
+{
+ std::string mName;
+ bool mIsNormalMap; // true if the texname was specified in a NormalmapFilename tag
+
+ TexEntry() { mIsNormalMap = false; }
+ TexEntry( const std::string& pName, bool pIsNormalMap = false)
+ : mName( pName), mIsNormalMap( pIsNormalMap)
+ { /* done */ }
+};
+
+/** Helper structure representing a XFile material */
+struct Material
+{
+ std::string mName;
+ bool mIsReference; // if true, mName holds a name by which the actual material can be found in the material list
+ aiColor4D mDiffuse;
+ float mSpecularExponent;
+ aiColor3D mSpecular;
+ aiColor3D mEmissive;
+ std::vector<TexEntry> mTextures;
+
+ Material() { mIsReference = false; }
+};
+
+/** Helper structure to represent a bone weight */
+struct BoneWeight
+{
+ unsigned int mVertex;
+ float mWeight;
+};
+
+/** Helper structure to represent a bone in a mesh */
+struct Bone
+{
+ std::string mName;
+ std::vector<BoneWeight> mWeights;
+ aiMatrix4x4 mOffsetMatrix;
+};
+
+/** Helper structure to represent an XFile mesh */
+struct Mesh
+{
+ std::vector<aiVector3D> mPositions;
+ std::vector<Face> mPosFaces;
+ std::vector<aiVector3D> mNormals;
+ std::vector<Face> mNormFaces;
+ unsigned int mNumTextures;
+ std::vector<aiVector2D> mTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
+ unsigned int mNumColorSets;
+ std::vector<aiColor4D> mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
+
+ std::vector<unsigned int> mFaceMaterials;
+ std::vector<Material> mMaterials;
+
+ std::vector<Bone> mBones;
+
+ Mesh() { mNumTextures = 0; mNumColorSets = 0; }
+};
+
+/** Helper structure to represent a XFile frame */
+struct Node
+{
+ std::string mName;
+ aiMatrix4x4 mTrafoMatrix;
+ Node* mParent;
+ std::vector<Node*> mChildren;
+ std::vector<Mesh*> mMeshes;
+
+ Node() { mParent = NULL; }
+ Node( Node* pParent) { mParent = pParent; }
+ ~Node()
+ {
+ for ( unsigned int a = 0; a < mChildren.size(); a++)
+ delete mChildren[a];
+ for ( unsigned int a = 0; a < mMeshes.size(); a++)
+ delete mMeshes[a];
+ }
+};
+
+struct MatrixKey
+{
+ double mTime;
+ aiMatrix4x4 mMatrix;
+};
+
+/** Helper structure representing a single animated bone in a XFile */
+struct AnimBone
+{
+ std::string mBoneName;
+ std::vector<aiVectorKey> mPosKeys; // either three separate key sequences for position, rotation, scaling
+ std::vector<aiQuatKey> mRotKeys;
+ std::vector<aiVectorKey> mScaleKeys;
+ std::vector<MatrixKey> mTrafoKeys; // or a combined key sequence of transformation matrices.
+};
+
+/** Helper structure to represent an animation set in a XFile */
+struct Animation
+{
+ std::string mName;
+ std::vector<AnimBone*> mAnims;
+
+ ~Animation()
+ {
+ for ( unsigned int a = 0; a < mAnims.size(); a++)
+ delete mAnims[a];
+ }
+};
+
+/** Helper structure analogue to aiScene */
+struct Scene
+{
+ Node* mRootNode;
+
+ std::vector<Mesh*> mGlobalMeshes; // global meshes found outside of any frames
+ std::vector<Material> mGlobalMaterials; // global materials found outside of any meshes.
+
+ std::vector<Animation*> mAnims;
+ unsigned int mAnimTicksPerSecond;
+
+ Scene() { mRootNode = NULL; mAnimTicksPerSecond = 0; }
+ ~Scene()
+ {
+ delete mRootNode;
+ for ( unsigned int a = 0; a < mGlobalMeshes.size(); a++)
+ delete mGlobalMeshes[a];
+ for ( unsigned int a = 0; a < mAnims.size(); a++)
+ delete mAnims[a];
+ }
+};
+
+} // end of namespace XFile
+} // end of namespace Assimp
+
+#endif // AI_XFILEHELPER_H_INC
diff --git a/3rdparty/assimp/code/XFileImporter.cpp b/3rdparty/assimp/code/XFileImporter.cpp
new file mode 100644
index 000000000..1abfb1f2b
--- /dev/null
+++ b/3rdparty/assimp/code/XFileImporter.cpp
@@ -0,0 +1,672 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 XFileImporter.cpp
+ * @brief Implementation of the XFile importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_X_IMPORTER
+
+#include "XFileImporter.h"
+#include "XFileParser.h"
+#include "ConvertToLHProcess.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+XFileImporter::XFileImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+XFileImporter::~XFileImporter()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the class can handle the format of the given file.
+bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
+{
+ std::string extension = GetExtension(pFile);
+ if (extension == "x") {
+ return true;
+ }
+ if (!extension.length() || checkSig) {
+ uint32_t token[1];
+ token[0] = AI_MAKE_MAGIC("xof ");
+ return CheckMagicToken(pIOHandler,pFile,token,1,0);
+ }
+ return false;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+ extensions.insert("x");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure.
+void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
+{
+ // read file into memory
+ boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+ if ( file.get() == NULL)
+ throw DeadlyImportError( "Failed to open file " + pFile + ".");
+
+ size_t fileSize = file->FileSize();
+ if ( fileSize < 16)
+ throw DeadlyImportError( "XFile is too small.");
+
+ // need to clear members - this method might be called multiple
+ // times on a single XFileImporter instance.
+ mImportedMats.clear();
+
+ // in the hope that binary files will never start with a BOM ...
+ mBuffer.resize( fileSize);
+ file->Read( &mBuffer.front(), 1, fileSize);
+ ConvertToUTF8(mBuffer);
+
+ // parse the file into a temporary representation
+ XFileParser parser( mBuffer);
+
+ // and create the proper return structures out of it
+ CreateDataRepresentationFromImport( pScene, parser.GetImportedData());
+
+ // if nothing came from it, report it as error
+ if ( !pScene->mRootNode)
+ throw DeadlyImportError( "XFile is ill-formatted - no content imported.");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Constructs the return data structure out of the imported data.
+void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, const XFile::Scene* pData)
+{
+ // Read the global materials first so that meshes referring to them can find them later
+ ConvertMaterials( pScene, pData->mGlobalMaterials);
+
+ // copy nodes, extracting meshes and materials on the way
+ pScene->mRootNode = CreateNodes( pScene, NULL, pData->mRootNode);
+
+ // extract animations
+ CreateAnimations( pScene, pData);
+
+ // read the global meshes that were stored outside of any node
+ if ( pData->mGlobalMeshes.size() > 0)
+ {
+ // create a root node to hold them if there isn't any, yet
+ if ( pScene->mRootNode == NULL)
+ {
+ pScene->mRootNode = new aiNode;
+ pScene->mRootNode->mName.Set( "$dummy_node");
+ }
+
+ // convert all global meshes and store them in the root node.
+ // If there was one before, the global meshes now suddenly have its transformation matrix...
+ // Don't know what to do there, I don't want to insert another node under the present root node
+ // just to avoid this.
+ CreateMeshes( pScene, pScene->mRootNode, pData->mGlobalMeshes);
+ }
+
+ // Convert everything to OpenGL space... it's the same operation as the conversion back, so we can reuse the step directly
+ MakeLeftHandedProcess convertProcess;
+ convertProcess.Execute( pScene);
+
+ FlipWindingOrderProcess flipper;
+ flipper.Execute(pScene);
+
+ // finally: create a dummy material if not material was imported
+ if ( pScene->mNumMaterials == 0)
+ {
+ pScene->mNumMaterials = 1;
+ // create the Material
+ Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+ int shadeMode = (int) aiShadingMode_Gouraud;
+ mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
+ // material colours
+ int specExp = 1;
+
+ aiColor3D clr = aiColor3D( 0, 0, 0);
+ mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_EMISSIVE);
+ mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_SPECULAR);
+
+ clr = aiColor3D( 0.5f, 0.5f, 0.5f);
+ mat->AddProperty( &clr, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat->AddProperty( &specExp, 1, AI_MATKEY_SHININESS);
+
+ pScene->mMaterials = new aiMaterial*[1];
+ pScene->mMaterials[0] = mat;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively creates scene nodes from the imported hierarchy.
+aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode)
+{
+ if ( !pNode)
+ return NULL;
+
+ // create node
+ aiNode* node = new aiNode;
+ node->mName.length = pNode->mName.length();
+ node->mParent = pParent;
+ memcpy( node->mName.data, pNode->mName.c_str(), pNode->mName.length());
+ node->mName.data[node->mName.length] = 0;
+ node->mTransformation = pNode->mTrafoMatrix;
+
+ // convert meshes from the source node
+ CreateMeshes( pScene, node, pNode->mMeshes);
+
+ // handle childs
+ if ( pNode->mChildren.size() > 0)
+ {
+ node->mNumChildren = (unsigned int)pNode->mChildren.size();
+ node->mChildren = new aiNode* [node->mNumChildren];
+
+ for ( unsigned int a = 0; a < pNode->mChildren.size(); a++)
+ node->mChildren[a] = CreateNodes( pScene, node, pNode->mChildren[a]);
+ }
+
+ return node;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Creates the meshes for the given node.
+void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector<XFile::Mesh*>& pMeshes)
+{
+ if ( pMeshes.size() == 0)
+ return;
+
+ // create a mesh for each mesh-material combination in the source node
+ std::vector<aiMesh*> meshes;
+ for ( unsigned int a = 0; a < pMeshes.size(); a++)
+ {
+ const XFile::Mesh* sourceMesh = pMeshes[a];
+ // first convert its materials so that we can find them when searching by name afterwards
+ ConvertMaterials( pScene, sourceMesh->mMaterials);
+
+ unsigned int numMaterials = std::max( (unsigned int)sourceMesh->mMaterials.size(), 1u);
+ for ( unsigned int b = 0; b < numMaterials; b++)
+ {
+ // collect the faces belonging to this material
+ std::vector<unsigned int> faces;
+ unsigned int numVertices = 0;
+ if ( sourceMesh->mFaceMaterials.size() > 0)
+ {
+ // if there is a per-face material defined, select the faces with the corresponding material
+ for ( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); c++)
+ {
+ if ( sourceMesh->mFaceMaterials[c] == b)
+ {
+ faces.push_back( c);
+ numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size();
+ }
+ }
+ } else
+ {
+ // if there is no per-face material, place everything into one mesh
+ for ( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); c++)
+ {
+ faces.push_back( c);
+ numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size();
+ }
+ }
+
+ // no faces/vertices using this material? strange...
+ if ( numVertices == 0)
+ continue;
+
+ // create a submesh using this material
+ aiMesh* mesh = new aiMesh;
+ meshes.push_back( mesh);
+
+ // find the material by name in the scene's material list. Either own material
+ // or referenced material, it should already be found there
+ if ( sourceMesh->mFaceMaterials.size() > 0)
+ {
+ std::map<std::string, unsigned int>::const_iterator matIt = mImportedMats.find( sourceMesh->mMaterials[b].mName);
+ if ( matIt == mImportedMats.end())
+ mesh->mMaterialIndex = 0;
+ else
+ mesh->mMaterialIndex = matIt->second;
+ } else
+ {
+ mesh->mMaterialIndex = 0;
+ }
+
+ // Create properly sized data arrays in the mesh. We store unique vertices per face,
+ // as specified
+ mesh->mNumVertices = numVertices;
+ mesh->mVertices = new aiVector3D[numVertices];
+ mesh->mNumFaces = (unsigned int)faces.size();
+ mesh->mFaces = new aiFace[mesh->mNumFaces];
+
+ // normals?
+ if ( sourceMesh->mNormals.size() > 0)
+ mesh->mNormals = new aiVector3D[numVertices];
+ // texture coords
+ for ( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; c++)
+ {
+ if ( sourceMesh->mTexCoords[c].size() > 0)
+ mesh->mTextureCoords[c] = new aiVector3D[numVertices];
+ }
+ // vertex colors
+ for ( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; c++)
+ {
+ if ( sourceMesh->mColors[c].size() > 0)
+ mesh->mColors[c] = new aiColor4D[numVertices];
+ }
+
+ // now collect the vertex data of all data streams present in the imported mesh
+ unsigned int newIndex = 0;
+ std::vector<unsigned int> orgPoints; // from which original point each new vertex stems
+ orgPoints.resize( numVertices, 0);
+
+ for ( unsigned int c = 0; c < faces.size(); c++)
+ {
+ unsigned int f = faces[c]; // index of the source face
+ const XFile::Face& pf = sourceMesh->mPosFaces[f]; // position source face
+
+ // create face. either triangle or triangle fan depending on the index count
+ aiFace& df = mesh->mFaces[c]; // destination face
+ df.mNumIndices = (unsigned int)pf.mIndices.size();
+ df.mIndices = new unsigned int[ df.mNumIndices];
+
+ // collect vertex data for indices of this face
+ for ( unsigned int d = 0; d < df.mNumIndices; d++)
+ {
+ df.mIndices[d] = newIndex;
+ orgPoints[newIndex] = pf.mIndices[d];
+
+ // Position
+ mesh->mVertices[newIndex] = sourceMesh->mPositions[pf.mIndices[d]];
+ // Normal, if present
+ if ( mesh->HasNormals())
+ mesh->mNormals[newIndex] = sourceMesh->mNormals[sourceMesh->mNormFaces[f].mIndices[d]];
+
+ // texture coord sets
+ for ( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; e++)
+ {
+ if ( mesh->HasTextureCoords( e))
+ {
+ aiVector2D tex = sourceMesh->mTexCoords[e][pf.mIndices[d]];
+ mesh->mTextureCoords[e][newIndex] = aiVector3D( tex.x, 1.0f - tex.y, 0.0f);
+ }
+ }
+ // vertex color sets
+ for ( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; e++)
+ if ( mesh->HasVertexColors( e))
+ mesh->mColors[e][newIndex] = sourceMesh->mColors[e][pf.mIndices[d]];
+
+ newIndex++;
+ }
+ }
+
+ // there should be as much new vertices as we calculated before
+ assert( newIndex == numVertices);
+
+ // convert all bones of the source mesh which influence vertices in this newly created mesh
+ const std::vector<XFile::Bone>& bones = sourceMesh->mBones;
+ std::vector<aiBone*> newBones;
+ for ( unsigned int c = 0; c < bones.size(); c++)
+ {
+ const XFile::Bone& obone = bones[c];
+ // set up a vertex-linear array of the weights for quick searching if a bone influences a vertex
+ std::vector<float> oldWeights( sourceMesh->mPositions.size(), 0.0f);
+ for ( unsigned int d = 0; d < obone.mWeights.size(); d++)
+ oldWeights[obone.mWeights[d].mVertex] = obone.mWeights[d].mWeight;
+
+ // collect all vertex weights that influence a vertex in the new mesh
+ std::vector<aiVertexWeight> newWeights;
+ newWeights.reserve( numVertices);
+ for ( unsigned int d = 0; d < orgPoints.size(); d++)
+ {
+ // does the new vertex stem from an old vertex which was influenced by this bone?
+ float w = oldWeights[orgPoints[d]];
+ if ( w > 0.0f)
+ newWeights.push_back( aiVertexWeight( d, w));
+ }
+
+ // if the bone has no weights in the newly created mesh, ignore it
+ if ( newWeights.size() == 0)
+ continue;
+
+ // create
+ aiBone* nbone = new aiBone;
+ newBones.push_back( nbone);
+ // copy name and matrix
+ nbone->mName.Set( obone.mName);
+ nbone->mOffsetMatrix = obone.mOffsetMatrix;
+ nbone->mNumWeights = (unsigned int)newWeights.size();
+ nbone->mWeights = new aiVertexWeight[nbone->mNumWeights];
+ for ( unsigned int d = 0; d < newWeights.size(); d++)
+ nbone->mWeights[d] = newWeights[d];
+ }
+
+ // store the bones in the mesh
+ mesh->mNumBones = (unsigned int)newBones.size();
+ if ( newBones.size() > 0)
+ {
+ mesh->mBones = new aiBone*[mesh->mNumBones];
+ std::copy( newBones.begin(), newBones.end(), mesh->mBones);
+ }
+ }
+ }
+
+ // reallocate scene mesh array to be large enough
+ aiMesh** prevArray = pScene->mMeshes;
+ pScene->mMeshes = new aiMesh*[pScene->mNumMeshes + meshes.size()];
+ if ( prevArray)
+ {
+ memcpy( pScene->mMeshes, prevArray, pScene->mNumMeshes * sizeof( aiMesh*));
+ delete [] prevArray;
+ }
+
+ // allocate mesh index array in the node
+ pNode->mNumMeshes = (unsigned int)meshes.size();
+ pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
+
+ // store all meshes in the mesh library of the scene and store their indices in the node
+ for ( unsigned int a = 0; a < meshes.size(); a++)
+ {
+ pScene->mMeshes[pScene->mNumMeshes] = meshes[a];
+ pNode->mMeshes[a] = pScene->mNumMeshes;
+ pScene->mNumMeshes++;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts the animations from the given imported data and creates them in the scene.
+void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData)
+{
+ std::vector<aiAnimation*> newAnims;
+
+ for ( unsigned int a = 0; a < pData->mAnims.size(); a++)
+ {
+ const XFile::Animation* anim = pData->mAnims[a];
+ // some exporters mock me with empty animation tags.
+ if ( anim->mAnims.size() == 0)
+ continue;
+
+ // create a new animation to hold the data
+ aiAnimation* nanim = new aiAnimation;
+ newAnims.push_back( nanim);
+ nanim->mName.Set( anim->mName);
+ // duration will be determined by the maximum length
+ nanim->mDuration = 0;
+ nanim->mTicksPerSecond = pData->mAnimTicksPerSecond;
+ nanim->mNumChannels = (unsigned int)anim->mAnims.size();
+ nanim->mChannels = new aiNodeAnim*[nanim->mNumChannels];
+
+ for ( unsigned int b = 0; b < anim->mAnims.size(); b++)
+ {
+ const XFile::AnimBone* bone = anim->mAnims[b];
+ aiNodeAnim* nbone = new aiNodeAnim;
+ nbone->mNodeName.Set( bone->mBoneName);
+ nanim->mChannels[b] = nbone;
+
+ // keyframes are given as combined transformation matrix keys
+ if ( bone->mTrafoKeys.size() > 0)
+ {
+ nbone->mNumPositionKeys = (unsigned int)bone->mTrafoKeys.size();
+ nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys];
+ nbone->mNumRotationKeys = (unsigned int)bone->mTrafoKeys.size();
+ nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys];
+ nbone->mNumScalingKeys = (unsigned int)bone->mTrafoKeys.size();
+ nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys];
+
+ for ( unsigned int c = 0; c < bone->mTrafoKeys.size(); c++)
+ {
+ // deconstruct each matrix into separate position, rotation and scaling
+ double time = bone->mTrafoKeys[c].mTime;
+ aiMatrix4x4 trafo = bone->mTrafoKeys[c].mMatrix;
+
+ // extract position
+ aiVector3D pos( trafo.a4, trafo.b4, trafo.c4);
+
+ nbone->mPositionKeys[c].mTime = time;
+ nbone->mPositionKeys[c].mValue = pos;
+
+ // extract scaling
+ aiVector3D scale;
+ scale.x = aiVector3D( trafo.a1, trafo.b1, trafo.c1).Length();
+ scale.y = aiVector3D( trafo.a2, trafo.b2, trafo.c2).Length();
+ scale.z = aiVector3D( trafo.a3, trafo.b3, trafo.c3).Length();
+ nbone->mScalingKeys[c].mTime = time;
+ nbone->mScalingKeys[c].mValue = scale;
+
+ // reconstruct rotation matrix without scaling
+ aiMatrix3x3 rotmat(
+ trafo.a1 / scale.x, trafo.a2 / scale.y, trafo.a3 / scale.z,
+ trafo.b1 / scale.x, trafo.b2 / scale.y, trafo.b3 / scale.z,
+ trafo.c1 / scale.x, trafo.c2 / scale.y, trafo.c3 / scale.z);
+
+ // and convert it into a quaternion
+ nbone->mRotationKeys[c].mTime = time;
+ nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat);
+ }
+
+ // longest lasting key sequence determines duration
+ nanim->mDuration = std::max( nanim->mDuration, bone->mTrafoKeys.back().mTime);
+ } else
+ {
+ // separate key sequences for position, rotation, scaling
+ nbone->mNumPositionKeys = (unsigned int)bone->mPosKeys.size();
+ nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys];
+ for ( unsigned int c = 0; c < nbone->mNumPositionKeys; c++)
+ {
+ aiVector3D pos = bone->mPosKeys[c].mValue;
+
+ nbone->mPositionKeys[c].mTime = bone->mPosKeys[c].mTime;
+ nbone->mPositionKeys[c].mValue = pos;
+ }
+
+ // rotation
+ nbone->mNumRotationKeys = (unsigned int)bone->mRotKeys.size();
+ nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys];
+ for ( unsigned int c = 0; c < nbone->mNumRotationKeys; c++)
+ {
+ aiMatrix3x3 rotmat = bone->mRotKeys[c].mValue.GetMatrix();
+
+ nbone->mRotationKeys[c].mTime = bone->mRotKeys[c].mTime;
+ nbone->mRotationKeys[c].mValue = aiQuaternion( rotmat);
+ nbone->mRotationKeys[c].mValue.w *= -1.0f; // needs quat inversion
+ }
+
+ // scaling
+ nbone->mNumScalingKeys = (unsigned int)bone->mScaleKeys.size();
+ nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys];
+ for ( unsigned int c = 0; c < nbone->mNumScalingKeys; c++)
+ nbone->mScalingKeys[c] = bone->mScaleKeys[c];
+
+ // longest lasting key sequence determines duration
+ if ( bone->mPosKeys.size() > 0)
+ nanim->mDuration = std::max( nanim->mDuration, bone->mPosKeys.back().mTime);
+ if ( bone->mRotKeys.size() > 0)
+ nanim->mDuration = std::max( nanim->mDuration, bone->mRotKeys.back().mTime);
+ if ( bone->mScaleKeys.size() > 0)
+ nanim->mDuration = std::max( nanim->mDuration, bone->mScaleKeys.back().mTime);
+ }
+ }
+ }
+
+ // store all converted animations in the scene
+ if ( newAnims.size() > 0)
+ {
+ pScene->mNumAnimations = (unsigned int)newAnims.size();
+ pScene->mAnimations = new aiAnimation* [pScene->mNumAnimations];
+ for ( unsigned int a = 0; a < newAnims.size(); a++)
+ pScene->mAnimations[a] = newAnims[a];
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Converts all materials in the given array and stores them in the scene's material list.
+void XFileImporter::ConvertMaterials( aiScene* pScene, const std::vector<XFile::Material>& pMaterials)
+{
+ // count the non-referrer materials in the array
+ unsigned int numMaterials = 0;
+ for ( unsigned int a = 0; a < pMaterials.size(); a++)
+ if ( !pMaterials[a].mIsReference)
+ numMaterials++;
+
+ if ( numMaterials == 0)
+ return;
+
+ // resize the scene's material list to offer enough space for the new materials
+ aiMaterial** prevMats = pScene->mMaterials;
+ pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numMaterials];
+ if ( prevMats)
+ {
+ memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*));
+ delete [] prevMats;
+ }
+
+ // convert all the materials given in the array
+ for ( unsigned int a = 0; a < pMaterials.size(); a++)
+ {
+ const XFile::Material& oldMat = pMaterials[a];
+ if ( oldMat.mIsReference)
+ continue;
+
+ Assimp::MaterialHelper* mat = new Assimp::MaterialHelper;
+ aiString name;
+ name.Set( oldMat.mName);
+ mat->AddProperty( &name, AI_MATKEY_NAME);
+
+ // Shading model: hardcoded to PHONG, there is no such information in an XFile
+ // FIX (aramis): If the specular exponent is 0, use gouraud shading. This is a bugfix
+ // for some models in the SDK (e.g. good old tiny.x)
+ int shadeMode = (int)oldMat.mSpecularExponent == 0.0f
+ ? aiShadingMode_Gouraud : aiShadingMode_Phong;
+
+ mat->AddProperty<int>( &shadeMode, 1, AI_MATKEY_SHADING_MODEL);
+ // material colours
+ // FIX: Setup this as ambient not as emissive color
+ mat->AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_AMBIENT);
+ mat->AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE);
+ mat->AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR);
+ mat->AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS);
+
+
+ // texture, if there is one
+ if (1 == oldMat.mTextures.size())
+ {
+ const XFile::TexEntry& otex = oldMat.mTextures.back();
+ if (otex.mName.length())
+ {
+ // if there is only one texture assume it contains the diffuse color
+ aiString tex( otex.mName);
+ if ( otex.mIsNormalMap)
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(0));
+ else
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0));
+ }
+ }
+ else
+ {
+ // Otherwise ... try to search for typical strings in the
+ // texture's file name like 'bump' or 'diffuse'
+ unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0;
+ for ( unsigned int b = 0; b < oldMat.mTextures.size(); b++)
+ {
+ const XFile::TexEntry& otex = oldMat.mTextures[b];
+ std::string sz = otex.mName;
+ if (!sz.length())continue;
+
+
+ // find the file name
+ //const size_t iLen = sz.length();
+ std::string::size_type s = sz.find_last_of("\\/");
+ if (std::string::npos == s)
+ s = 0;
+
+ // cut off the file extension
+ std::string::size_type sExt = sz.find_last_of('.');
+ if (std::string::npos != sExt){
+ sz[sExt] = '\0';
+ }
+
+ // convert to lower case for easier comparision
+ for ( unsigned int c = 0; c < sz.length(); c++)
+ if ( isalpha( sz[c]))
+ sz[c] = tolower( sz[c]);
+
+
+ // Place texture filename property under the corresponding name
+ aiString tex( oldMat.mTextures[b].mName);
+
+ // bump map
+ if (std::string::npos != sz.find("bump", s) || std::string::npos != sz.find("height", s))
+ {
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_HEIGHT(iHM++));
+ } else
+ if (otex.mIsNormalMap || std::string::npos != sz.find( "normal", s) || std::string::npos != sz.find("nm", s))
+ {
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(iNM++));
+ } else
+ if (std::string::npos != sz.find( "spec", s) || std::string::npos != sz.find( "glanz", s))
+ {
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR(iSM++));
+ } else
+ if (std::string::npos != sz.find( "ambi", s) || std::string::npos != sz.find( "env", s))
+ {
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_AMBIENT(iAM++));
+ } else
+ if (std::string::npos != sz.find( "emissive", s) || std::string::npos != sz.find( "self", s))
+ {
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE(iEM++));
+ } else
+ {
+ // Assume it is a diffuse texture
+ mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(iDM++));
+ }
+ }
+ }
+
+ pScene->mMaterials[pScene->mNumMaterials] = mat;
+ mImportedMats[oldMat.mName] = pScene->mNumMaterials;
+ pScene->mNumMaterials++;
+ }
+}
+
+#endif // !! ASSIMP_BUILD_NO_X_IMPORTER
+
diff --git a/3rdparty/assimp/code/XFileImporter.h b/3rdparty/assimp/code/XFileImporter.h
new file mode 100644
index 000000000..8e4b1fbef
--- /dev/null
+++ b/3rdparty/assimp/code/XFileImporter.h
@@ -0,0 +1,159 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 XFileImporter.h
+ * @brief Definition of the XFile importer class.
+ */
+#ifndef AI_XFILEIMPORTER_H_INC
+#define AI_XFILEIMPORTER_H_INC
+
+#include <map>
+
+#include "XFileHelper.h"
+#include "BaseImporter.h"
+
+#include "../include/aiTypes.h"
+
+struct aiNode;
+
+namespace Assimp {
+
+namespace XFile {
+struct Scene;
+struct Node;
+}
+
+// ---------------------------------------------------------------------------
+/** The XFileImporter is a worker class capable of importing a scene from a
+ * DirectX file .x
+ */
+class XFileImporter : public BaseImporter
+{
+ friend class Importer;
+
+protected:
+ /** Constructor to be privately used by Importer */
+ XFileImporter();
+
+ /** Destructor, private as well */
+ ~XFileImporter();
+
+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;
+
+protected:
+
+ // -------------------------------------------------------------------
+ /** Called by Importer::GetExtensionList() for each loaded importer.
+ * See BaseImporter::GetExtensionList() for details
+ */
+ void GetExtensionList(std::set<std::string>& extensions);
+
+ // -------------------------------------------------------------------
+ /** Imports the given file into the given scene structure.
+ * See BaseImporter::InternReadFile() for details
+ */
+ void InternReadFile( const std::string& pFile, aiScene* pScene,
+ IOSystem* pIOHandler);
+
+ // -------------------------------------------------------------------
+ /** Constructs the return data structure out of the imported data.
+ * @param pScene The scene to construct the return data in.
+ * @param pData The imported data in the internal temporary
+ * representation.
+ */
+ void CreateDataRepresentationFromImport( aiScene* pScene,
+ const XFile::Scene* pData);
+
+ // -------------------------------------------------------------------
+ /** Recursively creates scene nodes from the imported hierarchy.
+ * The meshes and materials of the nodes will be extracted on the way.
+ * @param pScene The scene to construct the return data in.
+ * @param pParent The parent node where to create new child nodes
+ * @param pNode The temporary node to copy.
+ * @return The created node
+ */
+ aiNode* CreateNodes( aiScene* pScene, aiNode* pParent,
+ const XFile::Node* pNode);
+
+ // -------------------------------------------------------------------
+ /** Converts all meshes in the given mesh array. Each mesh is splitted
+ * up per material, the indices of the generated meshes are stored in
+ * the node structure.
+ * @param pScene The scene to construct the return data in.
+ * @param pNode The target node structure that references the
+ * constructed meshes.
+ * @param pMeshes The array of meshes to convert
+ */
+ void CreateMeshes( aiScene* pScene, aiNode* pNode,
+ const std::vector<XFile::Mesh*>& pMeshes);
+
+ // -------------------------------------------------------------------
+ /** Converts the animations from the given imported data and creates
+ * them in the scene.
+ * @param pScene The scene to hold to converted animations
+ * @param pData The data to read the animations from
+ */
+ void CreateAnimations( aiScene* pScene, const XFile::Scene* pData);
+
+ // -------------------------------------------------------------------
+ /** Converts all materials in the given array and stores them in the
+ * scene's material list.
+ * @param pScene The scene to hold the converted materials.
+ * @param pMaterials The material array to convert.
+ */
+ void ConvertMaterials( aiScene* pScene,
+ const std::vector<XFile::Material>& pMaterials);
+
+protected:
+ /** Buffer to hold the loaded file */
+ std::vector<char> mBuffer;
+
+ /** Imported materials: index in the scene's material list by name */
+ std::map<std::string, unsigned int> mImportedMats;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_BASEIMPORTER_H_INC
diff --git a/3rdparty/assimp/code/XFileParser.cpp b/3rdparty/assimp/code/XFileParser.cpp
new file mode 100644
index 000000000..93ada7317
--- /dev/null
+++ b/3rdparty/assimp/code/XFileParser.cpp
@@ -0,0 +1,1434 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 Implementation of the XFile parser helper class */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_X_IMPORTER
+
+#include "XFileParser.h"
+#include "XFileHelper.h"
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace Assimp::XFile;
+
+#ifndef ASSIMP_BUILD_NO_COMPRESSED_X
+
+# ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+# include <zlib.h>
+# else
+# include "../contrib/zlib/zlib.h"
+# endif
+
+// Magic identifier for MSZIP compressed data
+#define MSZIP_MAGIC 0x4B43
+#define MSZIP_BLOCK 32786
+
+// ------------------------------------------------------------------------------------------------
+// Dummy memory wrappers for use with zlib
+static void* dummy_alloc (void* /*opaque*/, unsigned int items, unsigned int size) {
+ return ::operator new(items*size);
+}
+
+static void dummy_free (void* /*opaque*/, void* address) {
+ return ::operator delete(address);
+}
+
+#endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
+
+// ------------------------------------------------------------------------------------------------
+// Constructor. Creates a data structure out of the XFile given in the memory block.
+XFileParser::XFileParser( const std::vector<char>& pBuffer)
+{
+ mMajorVersion = mMinorVersion = 0;
+ mIsBinaryFormat = false;
+ mBinaryNumCount = 0;
+ P = End = NULL;
+ mLineNumber = 0;
+ mScene = NULL;
+
+ // vector to store uncompressed file for INFLATE'd X files
+ std::vector<char> uncompressed;
+
+ // set up memory pointers
+ P = &pBuffer.front();
+ End = P + pBuffer.size();
+
+ // check header
+ if ( strncmp( P, "xof ", 4) != 0)
+ throw DeadlyImportError( "Header mismatch, file is not an XFile.");
+
+ // read version. It comes in a four byte format such as "0302"
+ mMajorVersion = (unsigned int)(P[4] - 48) * 10 + (unsigned int)(P[5] - 48);
+ mMinorVersion = (unsigned int)(P[6] - 48) * 10 + (unsigned int)(P[7] - 48);
+
+ bool compressed = false;
+
+ // txt - pure ASCII text format
+ if ( strncmp( P + 8, "txt ", 4) == 0)
+ mIsBinaryFormat = false;
+
+ // bin - Binary format
+ else if ( strncmp( P + 8, "bin ", 4) == 0)
+ mIsBinaryFormat = true;
+
+ // tzip - Inflate compressed text format
+ else if ( strncmp( P + 8, "tzip", 4) == 0)
+ {
+ mIsBinaryFormat = false;
+ compressed = true;
+ }
+ // bzip - Inflate compressed binary format
+ else if ( strncmp( P + 8, "bzip", 4) == 0)
+ {
+ mIsBinaryFormat = true;
+ compressed = true;
+ }
+ else ThrowException( boost::str(boost::format("Unsupported xfile format '%c%c%c%c'")
+ % P[8] % P[9] % P[10] % P[11]));
+
+ // float size
+ mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000
+ + (unsigned int)(P[13] - 48) * 100
+ + (unsigned int)(P[14] - 48) * 10
+ + (unsigned int)(P[15] - 48);
+
+ if ( mBinaryFloatSize != 32 && mBinaryFloatSize != 64)
+ ThrowException( boost::str( boost::format( "Unknown float size %1% specified in xfile header.")
+ % mBinaryFloatSize));
+
+ P += 16;
+
+ // If this is a compressed X file, apply the inflate algorithm to it
+ if (compressed)
+ {
+#ifdef ASSIMP_BUILD_NO_COMPRESSED_X
+ throw DeadlyImportError("Assimp was built without compressed X support");
+#else
+ /* ///////////////////////////////////////////////////////////////////////
+ * COMPRESSED X FILE FORMAT
+ * ///////////////////////////////////////////////////////////////////////
+ * [xhead]
+ * 2 major
+ * 2 minor
+ * 4 type // bzip,tzip
+ * [mszip_master_head]
+ * 4 unkn // checksum?
+ * 2 unkn // flags? (seems to be constant)
+ * [mszip_head]
+ * 2 ofs // offset to next section
+ * 2 magic // 'CK'
+ * ... ofs bytes of data
+ * ... next mszip_head
+ *
+ * http://www.kdedevelopers.org/node/3181 has been very helpful.
+ * ///////////////////////////////////////////////////////////////////////
+ */
+
+ // build a zlib stream
+ z_stream stream;
+ stream.opaque = NULL;
+ stream.zalloc = &dummy_alloc;
+ stream.zfree = &dummy_free;
+ stream.data_type = (mIsBinaryFormat ? Z_BINARY : Z_ASCII);
+
+ // initialize the inflation algorithm
+ ::inflateInit2(&stream, -MAX_WBITS);
+
+ // skip unknown data (checksum, flags?)
+ P += 6;
+
+ // First find out how much storage we'll need. Count sections.
+ const char* P1 = P;
+ unsigned int est_out = 0;
+ while (P1 < End)
+ {
+ // read next offset
+ uint16_t ofs = *((uint16_t*)P1);
+ AI_SWAP2(ofs); P1 += 2;
+
+ if (ofs >= MSZIP_BLOCK)
+ throw DeadlyImportError("X: Invalid offset to next MSZIP compressed block");
+
+ // check magic word
+ uint16_t magic = *((uint16_t*)P1);
+ AI_SWAP2(magic); P1 += 2;
+
+ if (magic != MSZIP_MAGIC)
+ throw DeadlyImportError("X: Unsupported compressed format, expected MSZIP header");
+
+ // and advance to the next offset
+ P1 += ofs;
+ est_out += MSZIP_BLOCK; // one decompressed block is 32786 in size
+ }
+
+ // Allocate storage and do the actual uncompressing
+ uncompressed.resize(est_out);
+ char* out = &uncompressed.front();
+ while (P < End)
+ {
+ uint16_t ofs = *((uint16_t*)P);
+ AI_SWAP2(ofs);
+ P += 4;
+
+ // push data to the stream
+ stream.next_in = (Bytef*)P;
+ stream.avail_in = ofs;
+ stream.next_out = (Bytef*)out;
+ stream.avail_out = MSZIP_BLOCK;
+
+ // and decompress the data ....
+ int ret = ::inflate( &stream, Z_SYNC_FLUSH );
+ if (ret != Z_OK && ret != Z_STREAM_END)
+ throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data");
+
+ ::inflateReset( &stream );
+ ::inflateSetDictionary( &stream, (const Bytef*)out , MSZIP_BLOCK - stream.avail_out );
+
+ // and advance to the next offset
+ out += MSZIP_BLOCK - stream.avail_out;
+ P += ofs;
+ }
+
+ // terminate zlib
+ ::inflateEnd(&stream);
+
+ // ok, update pointers to point to the uncompressed file data
+ P = &uncompressed[0];
+ End = out;
+
+ // FIXME: we don't need the compressed data anymore, could release
+ // it already for better memory usage. Consider breaking const-co.
+ DefaultLogger::get()->info("Successfully decompressed MSZIP-compressed file");
+#endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
+ }
+ else
+ {
+ // start reading here
+ ReadUntilEndOfLine();
+ }
+
+ mScene = new Scene;
+ ParseFile();
+
+ // filter the imported hierarchy for some degenerated cases
+ if ( mScene->mRootNode) {
+ FilterHierarchy( mScene->mRootNode);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor. Destroys all imported data along with it
+XFileParser::~XFileParser()
+{
+ // kill everything we created
+ delete mScene;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseFile()
+{
+ bool running = true;
+ while ( running )
+ {
+ // read name of next object
+ std::string objectName = GetNextToken();
+ if (objectName.length() == 0)
+ break;
+
+ // parse specific object
+ if ( objectName == "template")
+ ParseDataObjectTemplate();
+ else
+ if ( objectName == "Frame")
+ ParseDataObjectFrame( NULL);
+ else
+ if ( objectName == "Mesh")
+ {
+ // some meshes have no frames at all
+ Mesh* mesh = new Mesh;
+ ParseDataObjectMesh( mesh);
+ mScene->mGlobalMeshes.push_back( mesh);
+ } else
+ if ( objectName == "AnimTicksPerSecond")
+ ParseDataObjectAnimTicksPerSecond();
+ else
+ if ( objectName == "AnimationSet")
+ ParseDataObjectAnimationSet();
+ else
+ if ( objectName == "Material")
+ {
+ // Material outside of a mesh or node
+ Material material;
+ ParseDataObjectMaterial( &material);
+ mScene->mGlobalMaterials.push_back( material);
+ } else
+ if ( objectName == "}")
+ {
+ // whatever?
+ DefaultLogger::get()->warn("} found in dataObject");
+ } else
+ {
+ // unknown format
+ DefaultLogger::get()->warn("Unknown data object in animation of .x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectTemplate()
+{
+ // parse a template data object. Currently not stored.
+ std::string name;
+ readHeadOfDataObject( &name);
+
+ // read GUID
+ std::string guid = GetNextToken();
+
+ // read and ignore data members
+ bool running = true;
+ while ( running )
+ {
+ std::string s = GetNextToken();
+
+ if ( s == "}")
+ break;
+
+ if ( s.length() == 0)
+ ThrowException( "Unexpected end of file reached while parsing template definition");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectFrame( Node* pParent)
+{
+ // A coordinate frame, or "frame of reference." The Frame template
+ // is open and can contain any object. The Direct3D extensions (D3DX)
+ // mesh-loading functions recognize Mesh, FrameTransformMatrix, and
+ // Frame template instances as child objects when loading a Frame
+ // instance.
+ std::string name;
+ readHeadOfDataObject(&name);
+
+ // create a named node and place it at its parent, if given
+ Node* node = new Node( pParent);
+ node->mName = name;
+ if ( pParent)
+ {
+ pParent->mChildren.push_back( node);
+ } else
+ {
+ // there might be multiple root nodes
+ if ( mScene->mRootNode != NULL)
+ {
+ // place a dummy root if not there
+ if ( mScene->mRootNode->mName != "$dummy_root")
+ {
+ Node* exroot = mScene->mRootNode;
+ mScene->mRootNode = new Node( NULL);
+ mScene->mRootNode->mName = "$dummy_root";
+ mScene->mRootNode->mChildren.push_back( exroot);
+ exroot->mParent = mScene->mRootNode;
+ }
+ // put the new node as its child instead
+ mScene->mRootNode->mChildren.push_back( node);
+ node->mParent = mScene->mRootNode;
+ } else
+ {
+ // it's the first node imported. place it as root
+ mScene->mRootNode = node;
+ }
+ }
+
+ // Now inside a frame.
+ // read tokens until closing brace is reached.
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+ if (objectName.size() == 0)
+ ThrowException( "Unexpected end of file reached while parsing frame");
+
+ if ( objectName == "}")
+ break; // frame finished
+ else
+ if ( objectName == "Frame")
+ ParseDataObjectFrame( node); // child frame
+ else
+ if ( objectName == "FrameTransformMatrix")
+ ParseDataObjectTransformationMatrix( node->mTrafoMatrix);
+ else
+ if ( objectName == "Mesh")
+ {
+ Mesh* mesh = new Mesh;
+ node->mMeshes.push_back( mesh);
+ ParseDataObjectMesh( mesh);
+ } else
+ {
+ DefaultLogger::get()->warn("Unknown data object in frame in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix)
+{
+ // read header, we're not interested if it has a name
+ readHeadOfDataObject();
+
+ // read its components
+ pMatrix.a1 = ReadFloat(); pMatrix.b1 = ReadFloat();
+ pMatrix.c1 = ReadFloat(); pMatrix.d1 = ReadFloat();
+ pMatrix.a2 = ReadFloat(); pMatrix.b2 = ReadFloat();
+ pMatrix.c2 = ReadFloat(); pMatrix.d2 = ReadFloat();
+ pMatrix.a3 = ReadFloat(); pMatrix.b3 = ReadFloat();
+ pMatrix.c3 = ReadFloat(); pMatrix.d3 = ReadFloat();
+ pMatrix.a4 = ReadFloat(); pMatrix.b4 = ReadFloat();
+ pMatrix.c4 = ReadFloat(); pMatrix.d4 = ReadFloat();
+
+ // trailing symbols
+ CheckForSemicolon();
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMesh( Mesh* pMesh)
+{
+ std::string name;
+ readHeadOfDataObject( &name);
+
+ // read vertex count
+ unsigned int numVertices = ReadInt();
+ pMesh->mPositions.resize( numVertices);
+
+ // read vertices
+ for ( unsigned int a = 0; a < numVertices; a++)
+ pMesh->mPositions[a] = ReadVector3();
+
+ // read position faces
+ unsigned int numPosFaces = ReadInt();
+ pMesh->mPosFaces.resize( numPosFaces);
+ for ( unsigned int a = 0; a < numPosFaces; a++)
+ {
+ unsigned int numIndices = ReadInt();
+ if ( numIndices < 3)
+ ThrowException( boost::str( boost::format( "Invalid index count %1% for face %2%.") % numIndices % a));
+
+ // read indices
+ Face& face = pMesh->mPosFaces[a];
+ for ( unsigned int b = 0; b < numIndices; b++)
+ face.mIndices.push_back( ReadInt());
+ CheckForSeparator();
+ }
+
+ // here, other data objects may follow
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+
+ if ( objectName.size() == 0)
+ ThrowException( "Unexpected end of file while parsing mesh structure");
+ else
+ if ( objectName == "}")
+ break; // mesh finished
+ else
+ if ( objectName == "MeshNormals")
+ ParseDataObjectMeshNormals( pMesh);
+ else
+ if ( objectName == "MeshTextureCoords")
+ ParseDataObjectMeshTextureCoords( pMesh);
+ else
+ if ( objectName == "MeshVertexColors")
+ ParseDataObjectMeshVertexColors( pMesh);
+ else
+ if ( objectName == "MeshMaterialList")
+ ParseDataObjectMeshMaterialList( pMesh);
+ else
+ if ( objectName == "VertexDuplicationIndices")
+ ParseUnknownDataObject(); // we'll ignore vertex duplication indices
+ else
+ if ( objectName == "XSkinMeshHeader")
+ ParseDataObjectSkinMeshHeader( pMesh);
+ else
+ if ( objectName == "SkinWeights")
+ ParseDataObjectSkinWeights( pMesh);
+ else
+ {
+ DefaultLogger::get()->warn("Unknown data object in mesh in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectSkinWeights( Mesh *pMesh)
+{
+ readHeadOfDataObject();
+
+ std::string transformNodeName;
+ GetNextTokenAsString( transformNodeName);
+
+ pMesh->mBones.push_back( Bone());
+ Bone& bone = pMesh->mBones.back();
+ bone.mName = transformNodeName;
+
+ // read vertex weights
+ unsigned int numWeights = ReadInt();
+ bone.mWeights.reserve( numWeights);
+
+ for ( unsigned int a = 0; a < numWeights; a++)
+ {
+ BoneWeight weight;
+ weight.mVertex = ReadInt();
+ bone.mWeights.push_back( weight);
+ }
+
+ // read vertex weights
+ for ( unsigned int a = 0; a < numWeights; a++)
+ bone.mWeights[a].mWeight = ReadFloat();
+
+ // read matrix offset
+ bone.mOffsetMatrix.a1 = ReadFloat(); bone.mOffsetMatrix.b1 = ReadFloat();
+ bone.mOffsetMatrix.c1 = ReadFloat(); bone.mOffsetMatrix.d1 = ReadFloat();
+ bone.mOffsetMatrix.a2 = ReadFloat(); bone.mOffsetMatrix.b2 = ReadFloat();
+ bone.mOffsetMatrix.c2 = ReadFloat(); bone.mOffsetMatrix.d2 = ReadFloat();
+ bone.mOffsetMatrix.a3 = ReadFloat(); bone.mOffsetMatrix.b3 = ReadFloat();
+ bone.mOffsetMatrix.c3 = ReadFloat(); bone.mOffsetMatrix.d3 = ReadFloat();
+ bone.mOffsetMatrix.a4 = ReadFloat(); bone.mOffsetMatrix.b4 = ReadFloat();
+ bone.mOffsetMatrix.c4 = ReadFloat(); bone.mOffsetMatrix.d4 = ReadFloat();
+
+ CheckForSemicolon();
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectSkinMeshHeader( Mesh* /*pMesh*/ )
+{
+ readHeadOfDataObject();
+
+ /*unsigned int maxSkinWeightsPerVertex =*/ ReadInt();
+ /*unsigned int maxSkinWeightsPerFace =*/ ReadInt();
+ /*unsigned int numBonesInMesh = */ReadInt();
+
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshNormals( Mesh* pMesh)
+{
+ readHeadOfDataObject();
+
+ // read count
+ unsigned int numNormals = ReadInt();
+ pMesh->mNormals.resize( numNormals);
+
+ // read normal vectors
+ for ( unsigned int a = 0; a < numNormals; a++)
+ pMesh->mNormals[a] = ReadVector3();
+
+ // read normal indices
+ unsigned int numFaces = ReadInt();
+ if ( numFaces != pMesh->mPosFaces.size())
+ ThrowException( "Normal face count does not match vertex face count.");
+
+ for ( unsigned int a = 0; a < numFaces; a++)
+ {
+ unsigned int numIndices = ReadInt();
+ pMesh->mNormFaces.push_back( Face());
+ Face& face = pMesh->mNormFaces.back();
+
+ for ( unsigned int b = 0; b < numIndices; b++)
+ face.mIndices.push_back( ReadInt());
+
+ CheckForSeparator();
+ }
+
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshTextureCoords( Mesh* pMesh)
+{
+ readHeadOfDataObject();
+ std::vector<aiVector2D>& coords = pMesh->mTexCoords[pMesh->mNumTextures++];
+
+ unsigned int numCoords = ReadInt();
+ if ( numCoords != pMesh->mPositions.size())
+ ThrowException( "Texture coord count does not match vertex count");
+
+ coords.resize( numCoords);
+ for ( unsigned int a = 0; a < numCoords; a++)
+ coords[a] = ReadVector2();
+
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshVertexColors( Mesh* pMesh)
+{
+ readHeadOfDataObject();
+ std::vector<aiColor4D>& colors = pMesh->mColors[pMesh->mNumColorSets++];
+
+ unsigned int numColors = ReadInt();
+ if ( numColors != pMesh->mPositions.size())
+ ThrowException( "Vertex color count does not match vertex count");
+
+ colors.resize( numColors, aiColor4D( 0, 0, 0, 1));
+ for ( unsigned int a = 0; a < numColors; a++)
+ {
+ unsigned int index = ReadInt();
+ if ( index >= pMesh->mPositions.size())
+ ThrowException( "Vertex color index out of bounds");
+
+ colors[index] = ReadRGBA();
+ // HACK: (thom) Maxon Cinema XPort plugin puts a third separator here, kwxPort puts a comma.
+ // Ignore gracefully.
+ if ( !mIsBinaryFormat)
+ {
+ FindNextNoneWhiteSpace();
+ if ( *P == ';' || *P == ',')
+ P++;
+ }
+ }
+
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMeshMaterialList( Mesh* pMesh)
+{
+ readHeadOfDataObject();
+
+ // read material count
+ /*unsigned int numMaterials =*/ ReadInt();
+ // read non triangulated face material index count
+ unsigned int numMatIndices = ReadInt();
+
+ // some models have a material index count of 1... to be able to read them we
+ // replicate this single material index on every face
+ if ( numMatIndices != pMesh->mPosFaces.size() && numMatIndices != 1)
+ ThrowException( "Per-Face material index count does not match face count.");
+
+ // read per-face material indices
+ for ( unsigned int a = 0; a < numMatIndices; a++)
+ pMesh->mFaceMaterials.push_back( ReadInt());
+
+ // in version 03.02, the face indices end with two semicolons.
+ // commented out version check, as version 03.03 exported from blender also has 2 semicolons
+ if ( !mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2)
+ {
+ if ( *P == ';')
+ ++P;
+ }
+
+ // if there was only a single material index, replicate it on all faces
+ while ( pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size())
+ pMesh->mFaceMaterials.push_back( pMesh->mFaceMaterials.front());
+
+ // read following data objects
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+ if ( objectName.size() == 0)
+ ThrowException( "Unexpected end of file while parsing mesh material list.");
+ else
+ if ( objectName == "}")
+ break; // material list finished
+ else
+ if ( objectName == "{")
+ {
+ // template materials
+ std::string matName = GetNextToken();
+ Material material;
+ material.mIsReference = true;
+ material.mName = matName;
+ pMesh->mMaterials.push_back( material);
+
+ CheckForClosingBrace(); // skip }
+ } else
+ if ( objectName == "Material")
+ {
+ pMesh->mMaterials.push_back( Material());
+ ParseDataObjectMaterial( &pMesh->mMaterials.back());
+ } else
+ if ( objectName == ";")
+ {
+ // ignore
+ } else
+ {
+ DefaultLogger::get()->warn("Unknown data object in material list in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectMaterial( Material* pMaterial)
+{
+ std::string matName;
+ readHeadOfDataObject( &matName);
+ if ( matName.empty())
+ matName = std::string( "material") + boost::lexical_cast<std::string>( mLineNumber);
+ pMaterial->mName = matName;
+ pMaterial->mIsReference = false;
+
+ // read material values
+ pMaterial->mDiffuse = ReadRGBA();
+ pMaterial->mSpecularExponent = ReadFloat();
+ pMaterial->mSpecular = ReadRGB();
+ pMaterial->mEmissive = ReadRGB();
+
+ // read other data objects
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+ if ( objectName.size() == 0)
+ ThrowException( "Unexpected end of file while parsing mesh material");
+ else
+ if ( objectName == "}")
+ break; // material finished
+ else
+ if ( objectName == "TextureFilename" || objectName == "TextureFileName")
+ {
+ // some exporters write "TextureFileName" instead.
+ std::string texname;
+ ParseDataObjectTextureFilename( texname);
+ pMaterial->mTextures.push_back( TexEntry( texname));
+ } else
+ if ( objectName == "NormalmapFilename" || objectName == "NormalmapFileName")
+ {
+ // one exporter writes out the normal map in a separate filename tag
+ std::string texname;
+ ParseDataObjectTextureFilename( texname);
+ pMaterial->mTextures.push_back( TexEntry( texname, true));
+ } else
+ {
+ DefaultLogger::get()->warn("Unknown data object in material in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimTicksPerSecond()
+{
+ readHeadOfDataObject();
+ mScene->mAnimTicksPerSecond = ReadInt();
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimationSet()
+{
+ std::string animName;
+ readHeadOfDataObject( &animName);
+
+ Animation* anim = new Animation;
+ mScene->mAnims.push_back( anim);
+ anim->mName = animName;
+
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+ if ( objectName.length() == 0)
+ ThrowException( "Unexpected end of file while parsing animation set.");
+ else
+ if ( objectName == "}")
+ break; // animation set finished
+ else
+ if ( objectName == "Animation")
+ ParseDataObjectAnimation( anim);
+ else
+ {
+ DefaultLogger::get()->warn("Unknown data object in animation set in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimation( Animation* pAnim)
+{
+ readHeadOfDataObject();
+ AnimBone* banim = new AnimBone;
+ pAnim->mAnims.push_back( banim);
+
+ bool running = true;
+ while ( running )
+ {
+ std::string objectName = GetNextToken();
+
+ if ( objectName.length() == 0)
+ ThrowException( "Unexpected end of file while parsing animation.");
+ else
+ if ( objectName == "}")
+ break; // animation finished
+ else
+ if ( objectName == "AnimationKey")
+ ParseDataObjectAnimationKey( banim);
+ else
+ if ( objectName == "AnimationOptions")
+ ParseUnknownDataObject(); // not interested
+ else
+ if ( objectName == "{")
+ {
+ // read frame name
+ banim->mBoneName = GetNextToken();
+ CheckForClosingBrace();
+ } else
+ {
+ DefaultLogger::get()->warn("Unknown data object in animation in x file");
+ ParseUnknownDataObject();
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectAnimationKey( AnimBone* pAnimBone)
+{
+ readHeadOfDataObject();
+
+ // read key type
+ unsigned int keyType = ReadInt();
+
+ // read number of keys
+ unsigned int numKeys = ReadInt();
+
+ for ( unsigned int a = 0; a < numKeys; a++)
+ {
+ // read time
+ unsigned int time = ReadInt();
+
+ // read keys
+ switch( keyType)
+ {
+ case 0: // rotation quaternion
+ {
+ // read count
+ if ( ReadInt() != 4)
+ ThrowException( "Invalid number of arguments for quaternion key in animation");
+
+ aiQuatKey key;
+ key.mTime = double( time);
+ key.mValue.w = ReadFloat();
+ key.mValue.x = ReadFloat();
+ key.mValue.y = ReadFloat();
+ key.mValue.z = ReadFloat();
+ pAnimBone->mRotKeys.push_back( key);
+
+ CheckForSemicolon();
+ break;
+ }
+
+ case 1: // scale vector
+ case 2: // position vector
+ {
+ // read count
+ if ( ReadInt() != 3)
+ ThrowException( "Invalid number of arguments for vector key in animation");
+
+ aiVectorKey key;
+ key.mTime = double( time);
+ key.mValue = ReadVector3();
+
+ if ( keyType == 2)
+ pAnimBone->mPosKeys.push_back( key);
+ else
+ pAnimBone->mScaleKeys.push_back( key);
+
+ break;
+ }
+
+ case 3: // combined transformation matrix
+ case 4: // denoted both as 3 or as 4
+ {
+ // read count
+ if ( ReadInt() != 16)
+ ThrowException( "Invalid number of arguments for matrix key in animation");
+
+ // read matrix
+ MatrixKey key;
+ key.mTime = double( time);
+ key.mMatrix.a1 = ReadFloat(); key.mMatrix.b1 = ReadFloat();
+ key.mMatrix.c1 = ReadFloat(); key.mMatrix.d1 = ReadFloat();
+ key.mMatrix.a2 = ReadFloat(); key.mMatrix.b2 = ReadFloat();
+ key.mMatrix.c2 = ReadFloat(); key.mMatrix.d2 = ReadFloat();
+ key.mMatrix.a3 = ReadFloat(); key.mMatrix.b3 = ReadFloat();
+ key.mMatrix.c3 = ReadFloat(); key.mMatrix.d3 = ReadFloat();
+ key.mMatrix.a4 = ReadFloat(); key.mMatrix.b4 = ReadFloat();
+ key.mMatrix.c4 = ReadFloat(); key.mMatrix.d4 = ReadFloat();
+ pAnimBone->mTrafoKeys.push_back( key);
+
+ CheckForSemicolon();
+ break;
+ }
+
+ default:
+ ThrowException( boost::str( boost::format( "Unknown key type %1% in animation.") % keyType));
+ break;
+ } // end switch
+
+ // key separator
+ CheckForSeparator();
+ }
+
+ CheckForClosingBrace();
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseDataObjectTextureFilename( std::string& pName)
+{
+ readHeadOfDataObject();
+ GetNextTokenAsString( pName);
+ CheckForClosingBrace();
+
+ // FIX: some files (e.g. AnimationTest.x) have "" as texture file name
+ if (!pName.length())
+ {
+ DefaultLogger::get()->warn("Length of texture file name is zero. Skipping this texture.");
+ }
+
+ // some exporters write double backslash paths out. We simply replace them if we find them
+ while ( pName.find( "\\\\") != std::string::npos)
+ pName.replace( pName.find( "\\\\"), 2, "\\");
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ParseUnknownDataObject()
+{
+ // find opening delimiter
+ bool running = true;
+ while ( running )
+ {
+ std::string t = GetNextToken();
+ if ( t.length() == 0)
+ ThrowException( "Unexpected end of file while parsing unknown segment.");
+
+ if ( t == "{")
+ break;
+ }
+
+ unsigned int counter = 1;
+
+ // parse until closing delimiter
+ while ( counter > 0)
+ {
+ std::string t = GetNextToken();
+
+ if ( t.length() == 0)
+ ThrowException( "Unexpected end of file while parsing unknown segment.");
+
+ if ( t == "{")
+ ++counter;
+ else
+ if ( t == "}")
+ --counter;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+//! checks for closing curly brace
+void XFileParser::CheckForClosingBrace()
+{
+ if ( GetNextToken() != "}")
+ ThrowException( "Closing brace expected.");
+}
+
+// ------------------------------------------------------------------------------------------------
+//! checks for one following semicolon
+void XFileParser::CheckForSemicolon()
+{
+ if ( mIsBinaryFormat)
+ return;
+
+ if ( GetNextToken() != ";")
+ ThrowException( "Semicolon expected.");
+}
+
+// ------------------------------------------------------------------------------------------------
+//! checks for a separator char, either a ',' or a ';'
+void XFileParser::CheckForSeparator()
+{
+ if ( mIsBinaryFormat)
+ return;
+
+ std::string token = GetNextToken();
+ if ( token != "," && token != ";")
+ ThrowException( "Separator character (';' or ',') expected.");
+}
+
+// ------------------------------------------------------------------------------------------------
+// tests and possibly consumes a separator char, but does nothing if there was no separator
+void XFileParser::TestForSeparator()
+{
+ if ( mIsBinaryFormat)
+ return;
+
+ FindNextNoneWhiteSpace();
+ if ( P >= End)
+ return;
+
+ // test and skip
+ if ( *P == ';' || *P == ',')
+ P++;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::readHeadOfDataObject( std::string* poName)
+{
+ std::string nameOrBrace = GetNextToken();
+ if ( nameOrBrace != "{")
+ {
+ if ( poName)
+ *poName = nameOrBrace;
+
+ if ( GetNextToken() != "{")
+ ThrowException( "Opening brace expected.");
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string XFileParser::GetNextToken()
+{
+ std::string s;
+
+ // process binary-formatted file
+ if ( mIsBinaryFormat)
+ {
+ // in binary mode it will only return NAME and STRING token
+ // and (correctly) skip over other tokens.
+
+ unsigned int tok = ReadBinWord();
+ unsigned int len;
+
+ // standalone tokens
+ switch( tok)
+ {
+ case 1:
+ // name token
+ len = ReadBinDWord();
+ s = std::string(P, len);
+ P += len;
+ return s;
+ case 2:
+ // string token
+ len = ReadBinDWord();
+ s = std::string(P, len);
+ P += (len + 2);
+ return s;
+ case 3:
+ // integer token
+ P += 4;
+ return "<integer>";
+ case 5:
+ // GUID token
+ P += 16;
+ return "<guid>";
+ case 6:
+ len = ReadBinDWord();
+ P += (len * 4);
+ return "<int_list>";
+ case 7:
+ len = ReadBinDWord();
+ P += (len * mBinaryFloatSize);
+ return "<flt_list>";
+ case 0x0a:
+ return "{";
+ case 0x0b:
+ return "}";
+ case 0x0c:
+ return "(";
+ case 0x0d:
+ return ")";
+ case 0x0e:
+ return "[";
+ case 0x0f:
+ return "]";
+ case 0x10:
+ return "<";
+ case 0x11:
+ return ">";
+ case 0x12:
+ return ".";
+ case 0x13:
+ return ",";
+ case 0x14:
+ return ";";
+ case 0x1f:
+ return "template";
+ case 0x28:
+ return "WORD";
+ case 0x29:
+ return "DWORD";
+ case 0x2a:
+ return "FLOAT";
+ case 0x2b:
+ return "DOUBLE";
+ case 0x2c:
+ return "CHAR";
+ case 0x2d:
+ return "UCHAR";
+ case 0x2e:
+ return "SWORD";
+ case 0x2f:
+ return "SDWORD";
+ case 0x30:
+ return "void";
+ case 0x31:
+ return "string";
+ case 0x32:
+ return "unicode";
+ case 0x33:
+ return "cstring";
+ case 0x34:
+ return "array";
+ }
+ }
+ // process text-formatted file
+ else
+ {
+ FindNextNoneWhiteSpace();
+ if ( P >= End)
+ return s;
+
+ while ( (P < End) && !isspace( (unsigned char) *P))
+ {
+ // either keep token delimiters when already holding a token, or return if first valid char
+ if ( *P == ';' || *P == '}' || *P == '{' || *P == ',')
+ {
+ if ( !s.size())
+ s.append( P++, 1);
+ break; // stop for delimiter
+ }
+ s.append( P++, 1);
+ }
+ }
+ return s;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::FindNextNoneWhiteSpace()
+{
+ if ( mIsBinaryFormat)
+ return;
+
+ bool running = true;
+ while ( running )
+ {
+ while ( P < End && isspace( (unsigned char) *P))
+ {
+ if ( *P == '\n')
+ mLineNumber++;
+ ++P;
+ }
+
+ if ( P >= End)
+ return;
+
+ // check if this is a comment
+ if ( (P[0] == '/' && P[1] == '/') || P[0] == '#')
+ ReadUntilEndOfLine();
+ else
+ break;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::GetNextTokenAsString( std::string& poString)
+{
+ if ( mIsBinaryFormat)
+ {
+ poString = GetNextToken();
+ return;
+ }
+
+ FindNextNoneWhiteSpace();
+ if ( P >= End)
+ ThrowException( "Unexpected end of file while parsing string");
+
+ if ( *P != '"')
+ ThrowException( "Expected quotation mark.");
+ ++P;
+
+ while ( P < End && *P != '"')
+ poString.append( P++, 1);
+
+ if ( P >= End-1)
+ ThrowException( "Unexpected end of file while parsing string");
+
+ if ( P[1] != ';' || P[0] != '"')
+ ThrowException( "Expected quotation mark and semicolon at the end of a string.");
+ P+=2;
+}
+
+// ------------------------------------------------------------------------------------------------
+void XFileParser::ReadUntilEndOfLine()
+{
+ if ( mIsBinaryFormat)
+ return;
+
+ while ( P < End)
+ {
+ if ( *P == '\n' || *P == '\r')
+ {
+ ++P; mLineNumber++;
+ return;
+ }
+
+ ++P;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned short XFileParser::ReadBinWord()
+{
+ const unsigned char* q = (const unsigned char*) P;
+ unsigned short tmp = q[0] | (q[1] << 8);
+ P += 2;
+ return tmp;
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int XFileParser::ReadBinDWord()
+{
+ const unsigned char* q = (const unsigned char*) P;
+ unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24);
+ P += 4;
+ return tmp;
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int XFileParser::ReadInt()
+{
+ if ( mIsBinaryFormat)
+ {
+ if ( mBinaryNumCount == 0)
+ {
+ unsigned short tmp = ReadBinWord(); // 0x06 or 0x03
+ if ( tmp == 0x06) // array of ints follows
+ mBinaryNumCount = ReadBinDWord();
+ else // single int follows
+ mBinaryNumCount = 1;
+ }
+
+ --mBinaryNumCount;
+ return ReadBinDWord();
+ } else
+ {
+ FindNextNoneWhiteSpace();
+
+ // TODO: consider using strtol10s instead???
+
+ // check preceeding minus sign
+ bool isNegative = false;
+ if ( *P == '-')
+ {
+ isNegative = true;
+ P++;
+ }
+
+ // at least one digit expected
+ if ( !isdigit( *P))
+ ThrowException( "Number expected.");
+
+ // read digits
+ unsigned int number = 0;
+ while ( P < End)
+ {
+ if ( !isdigit( *P))
+ break;
+ number = number * 10 + (*P - 48);
+ P++;
+ }
+
+ CheckForSeparator();
+ return isNegative ? ((unsigned int) -int( number)) : number;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+float XFileParser::ReadFloat()
+{
+ if ( mIsBinaryFormat)
+ {
+ if ( mBinaryNumCount == 0)
+ {
+ unsigned short tmp = ReadBinWord(); // 0x07 or 0x42
+ if ( tmp == 0x07) // array of floats following
+ mBinaryNumCount = ReadBinDWord();
+ else // single float following
+ mBinaryNumCount = 1;
+ }
+
+ --mBinaryNumCount;
+ if ( mBinaryFloatSize == 8)
+ {
+ float result = (float) (*(double*) P);
+ P += 8;
+ return result;
+ } else
+ {
+ float result = *(float*) P;
+ P += 4;
+ return result;
+ }
+ }
+
+ // text version
+ FindNextNoneWhiteSpace();
+ // check for various special strings to allow reading files from faulty exporters
+ // I mean you, Blender!
+ if ( strncmp( P, "-1.#IND00", 9) == 0 || strncmp( P, "1.#IND00", 8) == 0)
+ {
+ P += 9;
+ CheckForSeparator();
+ return 0.0f;
+ } else
+ if ( strncmp( P, "1.#QNAN0", 8) == 0)
+ {
+ P += 8;
+ CheckForSeparator();
+ return 0.0f;
+ }
+
+ float result = 0.0f;
+ P = fast_atof_move( P, result);
+
+ CheckForSeparator();
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector2D XFileParser::ReadVector2()
+{
+ aiVector2D vector;
+ vector.x = ReadFloat();
+ vector.y = ReadFloat();
+ TestForSeparator();
+
+ return vector;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiVector3D XFileParser::ReadVector3()
+{
+ aiVector3D vector;
+ vector.x = ReadFloat();
+ vector.y = ReadFloat();
+ vector.z = ReadFloat();
+ TestForSeparator();
+
+ return vector;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiColor4D XFileParser::ReadRGBA()
+{
+ aiColor4D color;
+ color.r = ReadFloat();
+ color.g = ReadFloat();
+ color.b = ReadFloat();
+ color.a = ReadFloat();
+ TestForSeparator();
+
+ return color;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiColor3D XFileParser::ReadRGB()
+{
+ aiColor3D color;
+ color.r = ReadFloat();
+ color.g = ReadFloat();
+ color.b = ReadFloat();
+ TestForSeparator();
+
+ return color;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Throws an exception with a line number and the given text.
+void XFileParser::ThrowException( const std::string& pText)
+{
+ if ( mIsBinaryFormat)
+ throw DeadlyImportError( pText);
+ else
+ throw DeadlyImportError( boost::str( boost::format( "Line %d: %s") % mLineNumber % pText));
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Filters the imported hierarchy for some degenerated cases that some exporters produce.
+void XFileParser::FilterHierarchy( XFile::Node* pNode)
+{
+ // if the node has just a single unnamed child containing a mesh, remove
+ // the anonymous node inbetween. The 3DSMax kwXport plugin seems to produce this
+ // mess in some cases
+ if ( pNode->mChildren.size() == 1)
+ {
+ XFile::Node* child = pNode->mChildren.front();
+ if ( child->mName.length() == 0 && child->mMeshes.size() > 0)
+ {
+ // transfer its meshes to us
+ for ( unsigned int a = 0; a < child->mMeshes.size(); a++)
+ pNode->mMeshes.push_back( child->mMeshes[a]);
+ child->mMeshes.clear();
+
+ // then kill it
+ delete child;
+ pNode->mChildren.clear();
+ }
+ }
+
+ // recurse
+ for ( unsigned int a = 0; a < pNode->mChildren.size(); a++)
+ FilterHierarchy( pNode->mChildren[a]);
+}
+
+#endif // !! ASSIMP_BUILD_NO_X_IMPORTER
diff --git a/3rdparty/assimp/code/XFileParser.h b/3rdparty/assimp/code/XFileParser.h
new file mode 100644
index 000000000..8000c133b
--- /dev/null
+++ b/3rdparty/assimp/code/XFileParser.h
@@ -0,0 +1,162 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 to parse a XFile into a temporary structure */
+#ifndef AI_XFILEPARSER_H_INC
+#define AI_XFILEPARSER_H_INC
+
+#include <string>
+#include <vector>
+
+#include "../include/aiTypes.h"
+
+namespace Assimp
+{
+ namespace XFile
+ {
+ struct Node;
+ struct Mesh;
+ struct Scene;
+ struct Material;
+ struct Animation;
+ struct AnimBone;
+ }
+
+/** The XFileParser reads a XFile either in text or binary form and builds a temporary
+ * data structure out of it.
+ */
+class XFileParser
+{
+public:
+ /** Constructor. Creates a data structure out of the XFile given in the memory block.
+ * @param pBuffer Memory buffer containing the XFile
+ */
+ XFileParser( const std::vector<char>& pBuffer);
+
+ /** Destructor. Destroys all imported data along with it */
+ ~XFileParser();
+
+ /** Returns the temporary representation of the imported data */
+ const XFile::Scene* GetImportedData() const { return mScene; }
+
+protected:
+ void ParseFile();
+ void ParseDataObjectTemplate();
+ void ParseDataObjectFrame( XFile::Node *pParent);
+ void ParseDataObjectTransformationMatrix( aiMatrix4x4& pMatrix);
+ void ParseDataObjectMesh( XFile::Mesh* pMesh);
+ void ParseDataObjectSkinWeights( XFile::Mesh* pMesh);
+ void ParseDataObjectSkinMeshHeader( XFile::Mesh* pMesh);
+ void ParseDataObjectMeshNormals( XFile::Mesh* pMesh);
+ void ParseDataObjectMeshTextureCoords( XFile::Mesh* pMesh);
+ void ParseDataObjectMeshVertexColors( XFile::Mesh* pMesh);
+ void ParseDataObjectMeshMaterialList( XFile::Mesh* pMesh);
+ void ParseDataObjectMaterial( XFile::Material* pMaterial);
+ void ParseDataObjectAnimTicksPerSecond();
+ void ParseDataObjectAnimationSet();
+ void ParseDataObjectAnimation( XFile::Animation* pAnim);
+ void ParseDataObjectAnimationKey( XFile::AnimBone *pAnimBone);
+ void ParseDataObjectTextureFilename( std::string& pName);
+ void ParseUnknownDataObject();
+
+ //! places pointer to next begin of a token, and ignores comments
+ void FindNextNoneWhiteSpace();
+
+ //! returns next parseable token. Returns empty string if no token there
+ std::string GetNextToken();
+
+ //! reads header of dataobject including the opening brace.
+ //! returns false if error happened, and writes name of object
+ //! if there is one
+ void readHeadOfDataObject( std::string* poName = NULL);
+
+ //! checks for closing curly brace, throws exception if not there
+ void CheckForClosingBrace();
+
+ //! checks for one following semicolon, throws exception if not there
+ void CheckForSemicolon();
+
+ //! checks for a separator char, either a ',' or a ';'
+ void CheckForSeparator();
+
+ /// tests and possibly consumes a separator char, but does nothing if there was no separator
+ void TestForSeparator();
+
+ //! reads a x file style string
+ void GetNextTokenAsString( std::string& poString);
+
+ void ReadUntilEndOfLine();
+
+ unsigned short ReadBinWord();
+ unsigned int ReadBinDWord();
+ unsigned int ReadInt();
+ float ReadFloat();
+ aiVector2D ReadVector2();
+ aiVector3D ReadVector3();
+ aiColor3D ReadRGB();
+ aiColor4D ReadRGBA();
+
+ /** Throws an exception with a line number and the given text. */
+ void ThrowException( const std::string& pText);
+
+ /** Filters the imported hierarchy for some degenerated cases that some exporters produce.
+ * @param pData The sub-hierarchy to filter
+ */
+ void FilterHierarchy( XFile::Node* pNode);
+
+protected:
+ unsigned int mMajorVersion, mMinorVersion; ///< version numbers
+ bool mIsBinaryFormat; ///< true if the file is in binary, false if it's in text form
+ unsigned int mBinaryFloatSize; ///< float size, either 32 or 64 bits
+ // counter for number arrays in binary format
+ unsigned int mBinaryNumCount;
+
+ const char* P;
+ const char* End;
+
+ /// Line number when reading in text format
+ unsigned int mLineNumber;
+
+ /// Imported data
+ XFile::Scene* mScene;
+};
+
+}
+#endif // AI_XFILEPARSER_H_INC
diff --git a/3rdparty/assimp/code/aiAssert.cpp b/3rdparty/assimp/code/aiAssert.cpp
new file mode 100644
index 000000000..bb0781baf
--- /dev/null
+++ b/3rdparty/assimp/code/aiAssert.cpp
@@ -0,0 +1,68 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, ASSIMP Development 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 Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#include "AssimpPCH.h"
+#include "../include/aiAssert.h"
+#ifdef _WIN32
+#ifndef __GNUC__
+# include "crtdbg.h"
+#endif // ndef gcc
+#endif // _WIN32
+
+// Set a breakpoint using win32, else line, file and message will be returned and progam ends with
+// errrocode = 1
+AI_WONT_RETURN void Assimp::aiAssert (const std::string &message, unsigned int uiLine, const std::string &file)
+{
+ // moved expression testing out of the function and into the macro to avoid constructing
+ // two std::string on every single ai_assert test
+// if (!expression)
+ {
+ // FIX (Aramis): changed std::cerr to std::cout that the message appears in VS' output window ...
+ std::cout << "File :" << file << ", line " << uiLine << " : " << message << std::endl;
+
+#ifdef _WIN32
+#ifndef __GNUC__
+ // Set breakpoint
+ __debugbreak();
+#endif //ndef gcc
+#else
+ exit (1);
+#endif
+ }
+}
diff --git a/3rdparty/assimp/code/assbin_chunks.h b/3rdparty/assimp/code/assbin_chunks.h
new file mode 100644
index 000000000..5d0a4ed4a
--- /dev/null
+++ b/3rdparty/assimp/code/assbin_chunks.h
@@ -0,0 +1,196 @@
+
+#ifndef INCLUDED_ASSBIN_CHUNKS_H
+#define INCLUDED_ASSBIN_CHUNKS_H
+
+#define ASSBIN_VERSION_MAJOR 1
+#define ASSBIN_VERSION_MINOR 0
+
+/**
+@page assfile .ASS File formats
+
+@section over Overview
+Assimp provides its own interchange format, which is intended to applications which need
+to serialize 3D-models and to reload them quickly. Assimp's file formats are designed to
+be read by Assimp itself. They encode additional information needed by Assimp to optimize
+its postprocessing pipeline. If you once apply specific steps to a scene, then save it
+and reread it from an ASS format using the same post processing settings, they won't
+be executed again.
+
+The format comes in two flavours: XML and binary - both of them hold a complete dump of
+the 'aiScene' data structure returned by the APIs. The focus for the binary format
+(<tt>.assbin</tt>) is fast loading. Optional deflate compression helps reduce file size. The XML
+flavour, <tt>.assxml</tt> or simply .xml, is just a plain-to-xml conversion of aiScene.
+
+ASSBIN is Assimp's binary interchange format. assimp_cmd (<tt>&lt;root&gt;/tools/assimp_cmd</tt>) is able to
+write it and the core library provides a loader for it.
+
+@section assxml XML File format
+
+The format is pretty much self-explanatory due to its similarity to the in-memory aiScene structure.
+With few exceptions, C structures are wrapped in XML elements.
+
+The DTD for ASSXML can be found in <tt>&lt;root&gt;/doc/AssXML_Scheme.xml</tt>. Or have look
+at the output files generated by assimp_cmd.
+
+@section assbin Binary file format
+
+The ASSBIN file format is composed of chunks to represent the hierarchical aiScene data structure.
+This makes the format extensible and allows backward-compatibility with future data structure
+versions. The <tt>&lt;root&gt;/code/assbin_chunks.h</tt> header contains some magic constants
+for use by stand-alone ASSBIN loaders. Also, Assimp's own file writer can be found
+in <tt>&lt;root&gt;/tools/assimp_cmd/WriteDumb.cpp</tt> (yes, the 'b' is no typo ...).
+
+@verbatim
+
+-------------------------------------------------------------------------------
+1. File structure:
+-------------------------------------------------------------------------------
+
+----------------------
+| Header (500 bytes) |
+----------------------
+| Variable chunks |
+----------------------
+
+-------------------------------------------------------------------------------
+2. Definitions:
+-------------------------------------------------------------------------------
+
+integer is four bytes wide, stored in little-endian byte order.
+short is two bytes wide, stored in little-endian byte order.
+byte is a single byte.
+string is an integer n followed by n UTF-8 characters, not terminated by zero
+float is an IEEE 754 single-precision floating-point value
+double is an IEEE 754 double-precision floating-point value
+t[n] is an array of n elements of type t
+
+-------------------------------------------------------------------------------
+2. Header:
+-------------------------------------------------------------------------------
+
+byte[44] Magic identification string for ASSBIN files.
+ 'ASSIMP.binary'
+
+integer Major version of the Assimp library which wrote the file
+integer Minor version of the Assimp library which wrote the file
+ match these against ASSBIN_VERSION_MAJOR and ASSBIN_VERSION_MINOR
+
+integer SVN revision of the Assimp library (intended for our internal
+ debugging - if you write Ass files from your own APPs, set this value to 0.
+integer Assimp compile flags
+
+short 0 for normal files, 1 for shortened dumps for regression tests
+ these should have the file extension assbin.regress
+
+short 1 if the data after the header is compressed with the DEFLATE algorithm,
+ 0 for uncompressed files.
+ For compressed files, the first integer after the header is
+ always the uncompressed data size
+
+byte[256] Zero-terminated source file name, UTF-8
+byte[128] Zero-terminated command line parameters passed to assimp_cmd, UTF-8
+
+byte[64] Reserved for future use
+---> Total length: 512 bytes
+
+-------------------------------------------------------------------------------
+3. Chunks:
+-------------------------------------------------------------------------------
+
+integer Magic chunk ID (ASSBIN_CHUNK_XXX)
+integer Chunk data length, in bytes
+ (unknown chunks are possible, a good reader skips over them)
+
+byte[n] length-of-chunk bytes of data, depending on the chunk type
+
+Chunks can contain nested chunks. Nested chunks are ALWAYS at the end of the chunk,
+their size is included in length-of-chunk.
+
+The chunk layout for all ASSIMP data structures is derived from their C declarations.
+The general 'rule' to get from Assimp headers to the serialized layout is:
+
+ 1. POD members (i.e. aiMesh::mPrimitiveTypes, aiMesh::mNumVertices),
+ in order of declaration.
+
+ 2. Array-members (aiMesh::mFaces, aiMesh::mVertices, aiBone::mWeights),
+ in order of declaration.
+
+ 2. Object array members (i.e aiMesh::mBones, aiScene::mMeshes) are stored in
+ subchunks directly following the data written in 1.) and 2.)
+
+
+ Of course, there are some exceptions to this general order:
+
+[[aiScene]]
+
+ - The root node holding the scene structure is naturally stored in
+ a ASSBIN_CHUNK_AINODE subchunk following 1.) and 2.) (which is
+ empty for aiScene).
+
+[[aiMesh]]
+
+ - mTextureCoords and mNumUVComponents are serialized as follows:
+
+ [number of used uv channels times]
+ integer mNumUVComponents[n]
+ float mTextureCoords[n][mNumUVComponents[n]]
+
+ -> more than AI_MAX_TEXCOORD_CHANNELS can be stored. This allows Assimp
+ builds with different settings for AI_MAX_TEXCOORD_CHANNELS to exchange
+ data. Unlike the in-memory format, only the used components of the
+ UV coordinates are written to disk. If mNumUVComponents[0] is 1, the
+ corresponding mTextureCoords array consists of mNumTextureCoords*1
+ single floats.
+
+ - The array member block of aiMesh is prefixed with an integer that specifies
+ the kinds of vertex components actually present in the mesh. This is a
+ bitwise combination of the ASSBIN_MESH_HAS_xxx constants.
+
+[[aiFace]]
+
+ - mNumIndices is stored as short
+ - mIndices are written as short, if aiMesh::mNumVertices<65536
+
+[[aiNode]]
+
+ - mParent is ommitted
+
+[[aiLight]]
+
+ - mAttenuationXXX not written if aiLight::mType == aiLightSource_DIRECTIONAL
+ - mAngleXXX not written if aiLight::mType != aiLightSource_SPOT
+
+[[aiMaterial]]
+
+ - mNumAllocated is ommitted, for obvious reasons :-)
+
+
+ @endverbatim*/
+
+
+#define ASSBIN_HEADER_LENGTH 512
+
+// these are the magic chunk identifiers for the binary ASS file format
+#define ASSBIN_CHUNK_AICAMERA 0x1234
+#define ASSBIN_CHUNK_AILIGHT 0x1235
+#define ASSBIN_CHUNK_AITEXTURE 0x1236
+#define ASSBIN_CHUNK_AIMESH 0x1237
+#define ASSBIN_CHUNK_AINODEANIM 0x1238
+#define ASSBIN_CHUNK_AISCENE 0x1239
+#define ASSBIN_CHUNK_AIBONE 0x123a
+#define ASSBIN_CHUNK_AIANIMATION 0x123b
+#define ASSBIN_CHUNK_AINODE 0x123c
+#define ASSBIN_CHUNK_AIMATERIAL 0x123d
+#define ASSBIN_CHUNK_AIMATERIALPROPERTY 0x123e
+
+#define ASSBIN_MESH_HAS_POSITIONS 0x1
+#define ASSBIN_MESH_HAS_NORMALS 0x2
+#define ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS 0x4
+#define ASSBIN_MESH_HAS_TEXCOORD_BASE 0x100
+#define ASSBIN_MESH_HAS_COLOR_BASE 0x10000
+
+#define ASSBIN_MESH_HAS_TEXCOORD(n) (ASSBIN_MESH_HAS_TEXCOORD_BASE << n)
+#define ASSBIN_MESH_HAS_COLOR(n) (ASSBIN_MESH_HAS_COLOR_BASE << n)
+
+
+#endif // INCLUDED_ASSBIN_CHUNKS_H
diff --git a/3rdparty/assimp/code/fast_atof.h b/3rdparty/assimp/code/fast_atof.h
new file mode 100644
index 000000000..6b242afc5
--- /dev/null
+++ b/3rdparty/assimp/code/fast_atof.h
@@ -0,0 +1,306 @@
+// Copyright (C) 2002-2007 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine" and the "irrXML" project.
+// For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
+
+// ------------------------------------------------------------------------------------
+// Original description: (Schrompf)
+// Adapted to the ASSIMP library because the builtin atof indeed takes AGES to parse a
+// float inside a large string. Before parsing, it does a strlen on the given point.
+// Changes:
+// 22nd October 08 (Aramis_acg): Added temporary cast to double, added strtol10_64
+// to ensure long numbers are handled correctly
+// ------------------------------------------------------------------------------------
+
+
+#ifndef __FAST_A_TO_F_H_INCLUDED__
+#define __FAST_A_TO_F_H_INCLUDED__
+
+#include <math.h>
+
+namespace Assimp
+{
+
+const float fast_atof_table[16] = { // we write [16] here instead of [] to work around a swig bug
+ 0.f,
+ 0.1f,
+ 0.01f,
+ 0.001f,
+ 0.0001f,
+ 0.00001f,
+ 0.000001f,
+ 0.0000001f,
+ 0.00000001f,
+ 0.000000001f,
+ 0.0000000001f,
+ 0.00000000001f,
+ 0.000000000001f,
+ 0.0000000000001f,
+ 0.00000000000001f,
+ 0.000000000000001f
+};
+
+
+// ------------------------------------------------------------------------------------
+// Convert a string in decimal format to a number
+// ------------------------------------------------------------------------------------
+inline unsigned int strtol10( const char* in, const char** out=0)
+{
+ unsigned int value = 0;
+
+ bool running = true;
+ while ( running )
+ {
+ if ( *in < '0' || *in > '9' )
+ break;
+
+ value = ( value * 10 ) + ( *in - '0' );
+ ++in;
+ }
+ if (out)*out = in;
+ return value;
+}
+
+// ------------------------------------------------------------------------------------
+// Convert a string in octal format to a number
+// ------------------------------------------------------------------------------------
+inline unsigned int strtol8( const char* in, const char** out=0)
+{
+ unsigned int value = 0;
+
+ bool running = true;
+ while ( running )
+ {
+ if ( *in < '0' || *in > '7' )
+ break;
+
+ value = ( value << 3 ) + ( *in - '0' );
+ ++in;
+ }
+ if (out)*out = in;
+ return value;
+}
+
+// ------------------------------------------------------------------------------------
+// Convert a string in hex format to a number
+// ------------------------------------------------------------------------------------
+inline unsigned int strtol16( const char* in, const char** out=0)
+{
+ unsigned int value = 0;
+
+ bool running = true;
+ while ( running )
+ {
+ if ( *in >= '0' && *in <= '9' )
+ {
+ value = ( value << 4u ) + ( *in - '0' );
+ }
+ else if (*in >= 'A' && *in <= 'F')
+ {
+ value = ( value << 4u ) + ( *in - 'A' ) + 10;
+ }
+ else if (*in >= 'a' && *in <= 'f')
+ {
+ value = ( value << 4u ) + ( *in - 'a' ) + 10;
+ }
+ else break;
+ ++in;
+ }
+ if (out)*out = in;
+ return value;
+}
+
+// ------------------------------------------------------------------------------------
+// Convert just one hex digit
+// Return value is 0xffffffff if the input is not hex
+// ------------------------------------------------------------------------------------
+inline unsigned int HexDigitToDecimal(char in)
+{
+ unsigned int out = 0xffffffff;
+ if (in >= '0' && in <= '9')
+ out = in - '0';
+
+ else if (in >= 'a' && in <= 'f')
+ out = 10u + in - 'a';
+
+ else if (in >= 'A' && in <= 'F')
+ out = 10u + in - 'A';
+
+ // return value is 0xffffffff if the input is not a hex digit
+ return out;
+}
+
+// ------------------------------------------------------------------------------------
+// Convert a hex-encoded octet (2 characters processed)
+// Return value is 0xffffffff if the input is not hex
+// ------------------------------------------------------------------------------------
+inline uint8_t HexOctetToDecimal(const char* in)
+{
+ return ((uint8_t)HexDigitToDecimal(in[0])<<4)+(uint8_t)HexDigitToDecimal(in[1]);
+}
+
+
+// ------------------------------------------------------------------------------------
+// signed variant of strtol10
+// ------------------------------------------------------------------------------------
+inline int strtol10s( const char* in, const char** out=0)
+{
+ bool inv = (*in=='-');
+ if (inv || *in=='+')
+ ++in;
+
+ int value = strtol10(in,out);
+ if (inv) {
+ value = -value;
+ }
+ return value;
+}
+
+// ------------------------------------------------------------------------------------
+// Parse a C++-like integer literal - hex and oct prefixes.
+// 0xNNNN - hex
+// 0NNN - oct
+// NNN - dec
+// ------------------------------------------------------------------------------------
+inline unsigned int strtol_cppstyle( const char* in, const char** out=0)
+{
+ if ('0' == in[0])
+ {
+ return 'x' == in[1] ? strtol16(in+2,out) : strtol8(in+1,out);
+ }
+ return strtol10(in, out);
+}
+
+// ------------------------------------------------------------------------------------
+// Special version of the function, providing higher accuracy and safety
+// It is mainly used by fast_atof to prevent ugly and unwanted integer overflows.
+// ------------------------------------------------------------------------------------
+inline uint64_t strtol10_64( const char* in, const char** out=0, unsigned int* max_inout=0)
+{
+ unsigned int cur = 0;
+ uint64_t value = 0;
+
+ bool running = true;
+ while ( running )
+ {
+ if ( *in < '0' || *in > '9' )
+ break;
+
+ const uint64_t new_value = ( value * 10 ) + ( *in - '0' );
+
+ if (new_value < value) /* numeric overflow, we rely on you */
+ return value;
+
+ value = new_value;
+
+ ++in;
+ ++cur;
+
+ if (max_inout && *max_inout == cur) {
+
+ if (out) { /* skip to end */
+ while (*in >= '0' && *in <= '9')
+ ++in;
+ *out = in;
+ }
+
+ return value;
+ }
+ }
+ if (out)
+ *out = in;
+
+ if (max_inout)
+ *max_inout = cur;
+
+ return value;
+}
+
+// Number of relevant decimals for floating-point parsing.
+#define AI_FAST_ATOF_RELAVANT_DECIMALS 6
+
+// ------------------------------------------------------------------------------------
+//! Provides a fast function for converting a string into a float,
+//! about 6 times faster than atof in win32.
+// If you find any bugs, please send them to me, niko (at) irrlicht3d.org.
+// ------------------------------------------------------------------------------------
+inline const char* fast_atof_move( const char* c, float& out)
+{
+ float f;
+
+ bool inv = (*c=='-');
+ if (inv || *c=='+')
+ ++c;
+
+ f = (float) strtol10_64 ( c, &c);
+ if (*c == '.' || (c[0] == ',' && (c[1] >= '0' || c[1] <= '9'))) // allow for commas, too
+ {
+ ++c;
+
+ // NOTE: The original implementation is highly unaccurate here. The precision of a single
+ // IEEE 754 float is not high enough, everything behind the 6th digit tends to be more
+ // inaccurate than it would need to be. Casting to double seems to solve the problem.
+ // strtol_64 is used to prevent integer overflow.
+
+ // Another fix: this tends to become 0 for long numbers if we don't limit the maximum
+ // number of digits to be read. AI_FAST_ATOF_RELAVANT_DECIMALS can be a value between
+ // 1 and 15.
+ unsigned int diff = AI_FAST_ATOF_RELAVANT_DECIMALS;
+ double pl = (double) strtol10_64 ( c, &c, &diff );
+
+ pl *= fast_atof_table[diff];
+ f += (float)pl;
+ }
+
+ // A major 'E' must be allowed. Necessary for proper reading of some DXF files.
+ // Thanks to Zhao Lei to point out that this if () must be outside the if (*c == '.' ..)
+ if (*c == 'e' || *c == 'E')
+ {
+ ++c;
+ bool einv = (*c=='-');
+ if (einv || *c=='+')
+ ++c;
+
+ float exp = (float)strtol10_64(c, &c);
+ if (einv)
+ exp *= -1.0f;
+
+ f *= pow(10.0f, exp);
+ }
+
+ if (inv)
+ f *= -1.0f;
+
+ out = f;
+ return c;
+}
+
+// ------------------------------------------------------------------------------------
+// The same but more human.
+inline float fast_atof(const char* c)
+{
+ float ret;
+ fast_atof_move(c, ret);
+ return ret;
+}
+
+
+inline float fast_atof( const char* c, const char** cout)
+{
+ float ret;
+ *cout = fast_atof_move(c, ret);
+
+ return ret;
+}
+
+inline float fast_atof( const char** inout)
+{
+ float ret;
+ *inout = fast_atof_move(*inout, ret);
+
+ return ret;
+}
+
+} // end of namespace Assimp
+
+#endif
+
diff --git a/3rdparty/assimp/code/irrXMLWrapper.h b/3rdparty/assimp/code/irrXMLWrapper.h
new file mode 100644
index 000000000..d3d0bbf7f
--- /dev/null
+++ b/3rdparty/assimp/code/irrXMLWrapper.h
@@ -0,0 +1,131 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 INCLUDED_AI_IRRXML_WRAPPER
+#define INCLUDED_AI_IRRXML_WRAPPER
+
+// some long includes ....
+#include "./../contrib/irrXML/irrXML.h"
+#include "./../include/IOStream.h"
+namespace Assimp {
+
+// ---------------------------------------------------------------------------------
+/** @brief Utility class to make IrrXML work together with our custom IO system
+ * See the IrrXML docs for more details.
+ *
+ * Construct IrrXML-Reader in BaseImporter::InternReadFile():
+ * @code
+ * // open the file
+ * boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
+ * if ( file.get() == NULL) {
+ * throw DeadlyImportError( "Failed to open file " + pFile + ".");
+ * }
+ *
+ * // generate a XML reader for it
+ * boost::scoped_ptr<CIrrXML_IOStreamReader> mIOWrapper( new CIrrXML_IOStreamReader( file.get()));
+ * mReader = irr::io::createIrrXMLReader( mIOWrapper.get());
+ * if ( !mReader) {
+ * ThrowException( "xxxx: Unable to open file.");
+ * }
+ * @endcode
+ **/
+class CIrrXML_IOStreamReader
+ : public irr::io::IFileReadCallBack
+{
+public:
+
+ // ----------------------------------------------------------------------------------
+ //! Construction from an existing IOStream
+ CIrrXML_IOStreamReader(IOStream* _stream)
+ : stream (_stream)
+ , t (0)
+ {
+
+ // Map the buffer into memory and convert it to UTF8. IrrXML provides its
+ // own conversion, which is merely a cast from uintNN_t to uint8_t. Thus,
+ // it is not suitable for our purposes and we have to do it BEFORE IrrXML
+ // gets the buffer. Sadly, this forces as to map the whole file into
+ // memory.
+
+ data.resize(stream->FileSize());
+ stream->Read(&data[0],data.size(),1);
+
+ BaseImporter::ConvertToUTF8(data);
+ }
+
+ // ----------------------------------------------------------------------------------
+ //! Virtual destructor
+ virtual ~CIrrXML_IOStreamReader() {};
+
+ // ----------------------------------------------------------------------------------
+ //! Reads an amount of bytes from the file.
+ /** @param buffer: Pointer to output buffer.
+ * @param sizeToRead: Amount of bytes to read
+ * @return Returns how much bytes were read. */
+ virtual int read(void* buffer, int sizeToRead) {
+ if (sizeToRead<0) {
+ return 0;
+ }
+ if (t+sizeToRead>data.size()) {
+ sizeToRead = data.size()-t;
+ }
+
+ memcpy(buffer,&data.front()+t,sizeToRead);
+
+ t += sizeToRead;
+ return sizeToRead;
+ }
+
+ // ----------------------------------------------------------------------------------
+ //! Returns size of file in bytes
+ virtual int getSize() {
+ return (int)data.size();
+ }
+
+private:
+ IOStream* stream;
+ std::vector<char> data;
+ size_t t;
+
+}; // ! class CIrrXML_IOStreamReader
+
+} // ! Assimp
+
+#endif // !! INCLUDED_AI_IRRXML_WRAPPER
diff --git a/3rdparty/assimp/code/makefile b/3rdparty/assimp/code/makefile
new file mode 100644
index 000000000..5710158da
--- /dev/null
+++ b/3rdparty/assimp/code/makefile
@@ -0,0 +1,111 @@
+### USE OF THIS MAKEFILE IS NOT RECOMMENDED.
+### It is no longer maintained. Use CMAKE instead.
+
+# Makefile for Open Asset Import Library (GNU-make)
+# aramis_acg@users.sourceforge.net
+
+#
+# Usage: make <target> <macros>
+
+# TARGETS:
+# all Build a shared so from the whole library
+# clean Cleanup object files, prepare for rebuild
+# static Build a static library (*.a)
+# install SDK will be installed to /usr/bin/assimp
+
+# MACROS: (make clean before you change one)
+# NOBOOST=1 Build against boost workaround
+# SINGLETHREADED=1 Build single-threaded library
+# DEBUG=1 Build debug build of library
+
+# C++ object files
+OBJECTS := $(patsubst %.cpp,%.o, $(wildcard *.cpp))
+OBJECTS += $(patsubst %.cpp,%.o, $(wildcard extra/*.cpp))
+OBJECTS += $(patsubst %.cpp,%.o, $(wildcard ./../contrib/irrXML/*.cpp))
+
+# C object files
+OBJECTSC := $(patsubst %.c,%.oc, $(wildcard ./../contrib/zlib/*.c))
+OBJECTSC += $(patsubst %.c,%.oc, $(wildcard ./../contrib/ConvertUTF/*.c))
+OBJECTSC += $(patsubst %.c,%.oc, $(wildcard ./../contrib/unzip/*.c))
+
+# Directory for install
+INSTALLDIR = /usr/bin/assimp
+
+# Include flags for gcc
+INCLUDEFLAGS =
+
+# Preprocessor defines for gcc
+DEFINEFLAGS =
+
+# Suffix for the output binary, represents build type
+NAMESUFFIX =
+
+# Output path for binaries
+BINPATH = ../bin/gcc
+INCPATH = ../include
+
+# GCC compiler flags
+CPPFLAGS=-Wall
+
+# Setup environment for noboost build
+ifeq ($(NOBOOST),1)
+ SINGLETHREADED = 1
+ INCLUDEFLAGS += -IBoostWorkaround/
+ DEFINEFLAGS += -DASSIMP_BUILD_BOOST_WORKAROUND
+# NAMESUFFIX += -noboost
+# else
+# INCLUDEFLAGS += -I"C:/Program Files/boost/boost_1_35_0"
+endif
+
+# Setup environment for st build
+ifeq ($(SINGLETHREADED),1)
+ DEFINEFLAGS += -DASSIMP_BUILD_SINGLETHREADED
+# NAMESUFFIX += -st
+endif
+
+# Setup environment for debug build
+ifeq ($(DEBUG),1)
+ DEFINEFLAGS += -D_DEBUG -DDEBUG
+# NAMESUFFIX += -debug
+else
+ CPPFLAGS += -O3
+ DEFINEFLAGS += -DNDEBUG -D_NDEBUG
+endif
+
+OUTPUT_NAME = dummy
+
+# Output name of shared library
+SHARED_TARGET = $(BINPATH)/libassimp$(NAMESUFFIX).so
+
+# Output name of static library
+STATIC = $(BINPATH)/libassimp$(NAMESUFFIX).a
+
+# target: all
+# usage : build a shared library (*.so)
+all: $(SHARED_TARGET)
+
+$(SHARED_TARGET): $(OBJECTS) $(OBJECTSC)
+ gcc -o $@ $(OBJECTS) $(OBJECTSC) -shared -lstdc++
+%.o:%.cpp
+ $(CXX) -g -c $(CPPFLAGS) $? -o $@ $(INCLUDEFLAGS) $(DEFINEFLAGS) -fPIC
+%.oc:%.c
+ $(CXX) -x c -g -c -ansi $(CPPFLAGS) $? -o $@ -fPIC
+
+# target: clean
+# usage : cleanup all object files, prepare for a rebuild
+.PHONY: clean
+clean:
+ -rm -f $(OBJECTS) $(OBJECTSC) $(TARGET)
+
+# target: static
+# usage : build a static library (*.a)
+static: $(STATIC)
+$(STATIC): $(OBJECTS) $(OBJECTSC)
+ ar rcs $@ $(OBJECTS) $(OBJECTSC)
+
+install:
+ mkdir -p $(INSTALLDIR)
+ mkdir -p $(INSTALLDIR)/include
+ mkdir -p $(INSTALLDIR)/lib
+ cp $(BINPATH)/libassimp$(NAMESUFFIX).* $(INSTALLDIR)/lib
+ cp $(INCPATH)/* $(INSTALLDIR)/include
diff --git a/3rdparty/assimp/code/makefile.mingw b/3rdparty/assimp/code/makefile.mingw
new file mode 100644
index 000000000..c0b7bde05
--- /dev/null
+++ b/3rdparty/assimp/code/makefile.mingw
@@ -0,0 +1,105 @@
+### USE OF THIS MAKEFILE IS NOT RECOMMENDED.
+### It is no longer maintained. Use CMAKE instead.
+
+# ---------------------------------------------------------------------------
+# Makefile for Open Asset Import Library (MinGW32-make)
+# aramis_acg@users.sourceforge.net
+# - just a quick'n'dirty one, could be buggy ...
+#
+# Usage: mingw32-make -f makefile.mingw <target> <macros>
+
+# TARGETS:
+# all Build a shared so from the whole library
+# clean Cleanup object files, prepare for rebuild
+# static Build a static library (*.a)
+
+# MACROS: (make clean before you change one)
+# NOBOOST=1 Build against boost workaround
+# SINGLETHREADED=1 Build single-threaded library
+# DEBUG=1 Build debug build of library
+#
+# ---------------------------------------------------------------------------
+
+# C++ object files
+OBJECTS := $(patsubst %.cpp,%.o, $(wildcard *.cpp))
+OBJECTS += $(patsubst %.cpp,%.o, $(wildcard extra/*.cpp))
+OBJECTS += $(patsubst %.cpp,%.o, $(wildcard ./../contrib/irrXML/*.cpp))
+
+# C object files
+OBJECTSC := $(patsubst %.c,%.oc, $(wildcard ./../contrib/zlib/*.c))
+OBJECTSC += $(patsubst %.c,%.oc, $(wildcard ./../contrib/ConvertUTF/*.c))
+OBJECTSC += $(patsubst %.c,%.oc, $(wildcard ./../contrib/unzip/*.c))
+
+# Include flags for gcc
+INCLUDEFLAGS =
+
+# Preprocessor defines for gcc
+DEFINEFLAGS =
+
+# Suffix for the output binary, represents build type
+NAMESUFFIX =
+
+# Output path for binaries
+BINPATH = ../bin/mingw/
+
+# GCC compiler flags
+CPPFLAGS=-Wall
+
+# Setup environment for noboost build
+ifeq ($(NOBOOST),1)
+ SINGLETHREADED = 1
+ INCLUDEFLAGS += -I./BoostWorkaround/
+ DEFINEFLAGS += -DASSIMP_BUILD_BOOST_WORKAROUND
+# NAMESUFFIX += -noboost
+else
+ # adjust this manually if your boost is stored elsewhere
+ INCLUDEFLAGS += -I"C:/Program Files/boost/boost_1_38"
+ #INCLUDEFLAGS += -I"$(BOOST_DIR)"
+
+endif
+
+# Setup environment for st build
+ifeq ($(SINGLETHREADED),1)
+ DEFINEFLAGS += -DASSIMP_BUILD_SINGLETHREADED
+# NAMESUFFIX += -st
+endif
+
+# Setup environment for debug build
+ifeq ($(DEBUG),1)
+ DEFINEFLAGS += -D_DEBUG -DDEBUG
+ CPPFLAGS += -g
+# NAMESUFFIX += -debug
+else
+ CPPFLAGS += -O2 -s
+ DEFINEFLAGS += -DNDEBUG -D_NDEBUG
+endif
+
+# Output name of shared library
+SHARED_TARGET = $(BINPATH)/libassimp$(NAMESUFFIX).so
+
+# Output name of static library
+STATIC = $(BINPATH)/libassimp$(NAMESUFFIX).a
+
+# target: all
+# usage : build a shared library (*.so)
+all: $(SHARED_TARGET)
+
+$(SHARED_TARGET): $(OBJECTS) $(OBJECTSC)
+ gcc -o $@ $(OBJECTS) $(OBJECTSC) -shared -lstdc++
+%.o:%.cpp
+ $(CXX) -c $(CPPFLAGS) $? -o $@ $(INCLUDEFLAGS) $(DEFINEFLAGS)
+%.oc:%.c
+ $(CXX) -x c -c -ansi $(CPPFLAGS) $? -o $@
+
+# target: clean
+# usage : cleanup all object files, prepare for a rebuild
+.PHONY: clean
+clean:
+ -del *.o .\..\contrib\irrXML\*.o .\..\contrib\zlib\*.oc .\..\contrib\unzip\*.oc .\..\contrib\ConvertUTF\*.oc
+
+# target: static
+# usage : build a static library (*.a)
+static: $(STATIC)
+$(STATIC): $(OBJECTS) $(OBJECTSC)
+ ar rcs $@ $(OBJECTS) $(OBJECTSC)
+
diff --git a/3rdparty/assimp/code/pstdint.h b/3rdparty/assimp/code/pstdint.h
new file mode 100644
index 000000000..0c1cec84e
--- /dev/null
+++ b/3rdparty/assimp/code/pstdint.h
@@ -0,0 +1,729 @@
+/* A portable stdint.h
+ ****************************************************************************
+ * BSD License:
+ ****************************************************************************
+ *
+ * Copyright (c) 2005-2007 Paul Hsieh
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ ****************************************************************************
+ *
+ * Version 0.1.10
+ *
+ * The ANSI C standard committee, for the C99 standard, specified the
+ * inclusion of a new standard include file called stdint.h. This is
+ * a very useful and long desired include file which contains several
+ * very precise definitions for integer scalar types that is
+ * critically important for making portable several classes of
+ * applications including cryptography, hashing, variable length
+ * integer libraries and so on. But for most developers its likely
+ * useful just for programming sanity.
+ *
+ * The problem is that most compiler vendors have decided not to
+ * implement the C99 standard, and the next C++ language standard
+ * (which has a lot more mindshare these days) will be a long time in
+ * coming and its unknown whether or not it will include stdint.h or
+ * how much adoption it will have. Either way, it will be a long time
+ * before all compilers come with a stdint.h and it also does nothing
+ * for the extremely large number of compilers available today which
+ * do not include this file, or anything comparable to it.
+ *
+ * So that's what this file is all about. Its an attempt to build a
+ * single universal include file that works on as many platforms as
+ * possible to deliver what stdint.h is supposed to. A few things
+ * that should be noted about this file:
+ *
+ * 1) It is not guaranteed to be portable and/or present an identical
+ * interface on all platforms. The extreme variability of the
+ * ANSI C standard makes this an impossibility right from the
+ * very get go. Its really only meant to be useful for the vast
+ * majority of platforms that possess the capability of
+ * implementing usefully and precisely defined, standard sized
+ * integer scalars. Systems which are not intrinsically 2s
+ * complement may produce invalid constants.
+ *
+ * 2) There is an unavoidable use of non-reserved symbols.
+ *
+ * 3) Other standard include files are invoked.
+ *
+ * 4) This file may come in conflict with future platforms that do
+ * include stdint.h. The hope is that one or the other can be
+ * used with no real difference.
+ *
+ * 5) In the current verison, if your platform can't represent
+ * int32_t, int16_t and int8_t, it just dumps out with a compiler
+ * error.
+ *
+ * 6) 64 bit integers may or may not be defined. Test for their
+ * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX.
+ * Note that this is different from the C99 specification which
+ * requires the existence of 64 bit support in the compiler. If
+ * this is not defined for your platform, yet it is capable of
+ * dealing with 64 bits then it is because this file has not yet
+ * been extended to cover all of your system's capabilities.
+ *
+ * 7) (u)intptr_t may or may not be defined. Test for its presence
+ * with the test: #ifdef PTRDIFF_MAX. If this is not defined
+ * for your platform, then it is because this file has not yet
+ * been extended to cover all of your system's capabilities, not
+ * because its optional.
+ *
+ * 8) The following might not been defined even if your platform is
+ * capable of defining it:
+ *
+ * WCHAR_MIN
+ * WCHAR_MAX
+ * (u)int64_t
+ * PTRDIFF_MIN
+ * PTRDIFF_MAX
+ * (u)intptr_t
+ *
+ * 9) The following have not been defined:
+ *
+ * WINT_MIN
+ * WINT_MAX
+ *
+ * 10) The criteria for defining (u)int_least(*)_t isn't clear,
+ * except for systems which don't have a type that precisely
+ * defined 8, 16, or 32 bit types (which this include file does
+ * not support anyways). Default definitions have been given.
+ *
+ * 11) The criteria for defining (u)int_fast(*)_t isn't something I
+ * would trust to any particular compiler vendor or the ANSI C
+ * committee. It is well known that "compatible systems" are
+ * commonly created that have very different performance
+ * characteristics from the systems they are compatible with,
+ * especially those whose vendors make both the compiler and the
+ * system. Default definitions have been given, but its strongly
+ * recommended that users never use these definitions for any
+ * reason (they do *NOT* deliver any serious guarantee of
+ * improved performance -- not in this file, nor any vendor's
+ * stdint.h).
+ *
+ * 12) The following macros:
+ *
+ * PRINTF_INTMAX_MODIFIER
+ * PRINTF_INT64_MODIFIER
+ * PRINTF_INT32_MODIFIER
+ * PRINTF_INT16_MODIFIER
+ * PRINTF_LEAST64_MODIFIER
+ * PRINTF_LEAST32_MODIFIER
+ * PRINTF_LEAST16_MODIFIER
+ * PRINTF_INTPTR_MODIFIER
+ *
+ * are strings which have been defined as the modifiers required
+ * for the "d", "u" and "x" printf formats to correctly output
+ * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t,
+ * (u)least32_t, (u)least16_t and (u)intptr_t types respectively.
+ * PRINTF_INTPTR_MODIFIER is not defined for some systems which
+ * provide their own stdint.h. PRINTF_INT64_MODIFIER is not
+ * defined if INT64_MAX is not defined. These are an extension
+ * beyond what C99 specifies must be in stdint.h.
+ *
+ * In addition, the following macros are defined:
+ *
+ * PRINTF_INTMAX_HEX_WIDTH
+ * PRINTF_INT64_HEX_WIDTH
+ * PRINTF_INT32_HEX_WIDTH
+ * PRINTF_INT16_HEX_WIDTH
+ * PRINTF_INT8_HEX_WIDTH
+ * PRINTF_INTMAX_DEC_WIDTH
+ * PRINTF_INT64_DEC_WIDTH
+ * PRINTF_INT32_DEC_WIDTH
+ * PRINTF_INT16_DEC_WIDTH
+ * PRINTF_INT8_DEC_WIDTH
+ *
+ * Which specifies the maximum number of characters required to
+ * print the number of that type in either hexadecimal or decimal.
+ * These are an extension beyond what C99 specifies must be in
+ * stdint.h.
+ *
+ * Compilers tested (all with 0 warnings at their highest respective
+ * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32
+ * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio
+ * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3
+ *
+ * This file should be considered a work in progress. Suggestions for
+ * improvements, especially those which increase coverage are strongly
+ * encouraged.
+ *
+ * Acknowledgements
+ *
+ * The following people have made significant contributions to the
+ * development and testing of this file:
+ *
+ * Chris Howie
+ * John Steele Scott
+ * Dave Thorup
+ *
+ */
+
+#include <stddef.h>
+#include <limits.h>
+#include <signal.h>
+
+/*
+ * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and
+ * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_.
+ */
+
+#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) )) && !defined (_PSTDINT_H_INCLUDED)
+#include <stdint.h>
+#define _PSTDINT_H_INCLUDED
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER "l"
+# endif
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER "h"
+# endif
+# ifndef PRINTF_INTMAX_MODIFIER
+# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
+# endif
+# ifndef PRINTF_INT64_HEX_WIDTH
+# define PRINTF_INT64_HEX_WIDTH "16"
+# endif
+# ifndef PRINTF_INT32_HEX_WIDTH
+# define PRINTF_INT32_HEX_WIDTH "8"
+# endif
+# ifndef PRINTF_INT16_HEX_WIDTH
+# define PRINTF_INT16_HEX_WIDTH "4"
+# endif
+# ifndef PRINTF_INT8_HEX_WIDTH
+# define PRINTF_INT8_HEX_WIDTH "2"
+# endif
+# ifndef PRINTF_INT64_DEC_WIDTH
+# define PRINTF_INT64_DEC_WIDTH "20"
+# endif
+# ifndef PRINTF_INT32_DEC_WIDTH
+# define PRINTF_INT32_DEC_WIDTH "10"
+# endif
+# ifndef PRINTF_INT16_DEC_WIDTH
+# define PRINTF_INT16_DEC_WIDTH "5"
+# endif
+# ifndef PRINTF_INT8_DEC_WIDTH
+# define PRINTF_INT8_DEC_WIDTH "3"
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH
+# endif
+
+/*
+ * Something really weird is going on with Open Watcom. Just pull some of
+ * these duplicated definitions from Open Watcom's stdint.h file for now.
+ */
+
+# if defined (__WATCOMC__) && __WATCOMC__ >= 1250
+# if !defined (INT64_C)
+# define INT64_C(x) (x + (INT64_MAX - INT64_MAX))
+# endif
+# if !defined (UINT64_C)
+# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX))
+# endif
+# if !defined (INT32_C)
+# define INT32_C(x) (x + (INT32_MAX - INT32_MAX))
+# endif
+# if !defined (UINT32_C)
+# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX))
+# endif
+# if !defined (INT16_C)
+# define INT16_C(x) (x)
+# endif
+# if !defined (UINT16_C)
+# define UINT16_C(x) (x)
+# endif
+# if !defined (INT8_C)
+# define INT8_C(x) (x)
+# endif
+# if !defined (UINT8_C)
+# define UINT8_C(x) (x)
+# endif
+# if !defined (UINT64_MAX)
+# define UINT64_MAX 18446744073709551615ULL
+# endif
+# if !defined (INT64_MAX)
+# define INT64_MAX 9223372036854775807LL
+# endif
+# if !defined (UINT32_MAX)
+# define UINT32_MAX 4294967295UL
+# endif
+# if !defined (INT32_MAX)
+# define INT32_MAX 2147483647L
+# endif
+# if !defined (INTMAX_MAX)
+# define INTMAX_MAX INT64_MAX
+# endif
+# if !defined (INTMAX_MIN)
+# define INTMAX_MIN INT64_MIN
+# endif
+# endif
+#endif
+
+#ifndef _PSTDINT_H_INCLUDED
+#define _PSTDINT_H_INCLUDED
+
+#ifndef SIZE_MAX
+# define SIZE_MAX (~(size_t)0)
+#endif
+
+/*
+ * Deduce the type assignments from limits.h under the assumption that
+ * integer sizes in bits are powers of 2, and follow the ANSI
+ * definitions.
+ */
+
+#ifndef UINT8_MAX
+# define UINT8_MAX 0xff
+#endif
+#ifndef uint8_t
+# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S)
+ typedef unsigned char uint8_t;
+# define UINT8_C(v) ((uint8_t) v)
+# else
+# error "Platform not supported"
+# endif
+#endif
+
+#ifndef INT8_MAX
+# define INT8_MAX 0x7f
+#endif
+#ifndef INT8_MIN
+# define INT8_MIN INT8_C(0x80)
+#endif
+#ifndef int8_t
+# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S)
+ typedef signed char int8_t;
+# define INT8_C(v) ((int8_t) v)
+# else
+# error "Platform not supported"
+# endif
+#endif
+
+#ifndef UINT16_MAX
+# define UINT16_MAX 0xffff
+#endif
+#ifndef uint16_t
+#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S)
+ typedef unsigned int uint16_t;
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER ""
+# endif
+# define UINT16_C(v) ((uint16_t) (v))
+#elif (USHRT_MAX == UINT16_MAX)
+ typedef unsigned short uint16_t;
+# define UINT16_C(v) ((uint16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER "h"
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef INT16_MAX
+# define INT16_MAX 0x7fff
+#endif
+#ifndef INT16_MIN
+# define INT16_MIN INT16_C(0x8000)
+#endif
+#ifndef int16_t
+#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S)
+ typedef signed int int16_t;
+# define INT16_C(v) ((int16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER ""
+# endif
+#elif (SHRT_MAX == INT16_MAX)
+ typedef signed short int16_t;
+# define INT16_C(v) ((int16_t) (v))
+# ifndef PRINTF_INT16_MODIFIER
+# define PRINTF_INT16_MODIFIER "h"
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef UINT32_MAX
+# define UINT32_MAX (0xffffffffUL)
+#endif
+#ifndef uint32_t
+#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S)
+ typedef unsigned long uint32_t;
+# define UINT32_C(v) v ## UL
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER "l"
+# endif
+#elif (UINT_MAX == UINT32_MAX)
+ typedef unsigned int uint32_t;
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+# define UINT32_C(v) v ## U
+#elif (USHRT_MAX == UINT32_MAX)
+ typedef unsigned short uint32_t;
+# define UINT32_C(v) ((unsigned short) (v))
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+#ifndef INT32_MAX
+# define INT32_MAX (0x7fffffffL)
+#endif
+#ifndef INT32_MIN
+# define INT32_MIN INT32_C(0x80000000)
+#endif
+#ifndef int32_t
+#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S)
+ typedef signed long int32_t;
+# define INT32_C(v) v ## L
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER "l"
+# endif
+#elif (INT_MAX == INT32_MAX)
+ typedef signed int int32_t;
+# define INT32_C(v) v
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+#elif (SHRT_MAX == INT32_MAX)
+ typedef signed short int32_t;
+# define INT32_C(v) ((short) (v))
+# ifndef PRINTF_INT32_MODIFIER
+# define PRINTF_INT32_MODIFIER ""
+# endif
+#else
+#error "Platform not supported"
+#endif
+#endif
+
+/*
+ * The macro stdint_int64_defined is temporarily used to record
+ * whether or not 64 integer support is available. It must be
+ * defined for any 64 integer extensions for new platforms that are
+ * added.
+ */
+
+#undef stdint_int64_defined
+#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S)
+# if (__STDC__ && __STDC_VERSION >= 199901L) || defined (S_SPLINT_S)
+# define stdint_int64_defined
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+# define UINT64_C(v) v ## ULL
+# define INT64_C(v) v ## LL
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# endif
+#endif
+
+#if !defined (stdint_int64_defined)
+# if defined(__GNUC__)
+# define stdint_int64_defined
+ __extension__ typedef long long int64_t;
+ __extension__ typedef unsigned long long uint64_t;
+# define UINT64_C(v) v ## ULL
+# define INT64_C(v) v ## LL
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S)
+# define stdint_int64_defined
+ typedef long long int64_t;
+ typedef unsigned long long uint64_t;
+# define UINT64_C(v) v ## ULL
+# define INT64_C(v) v ## LL
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "ll"
+# endif
+# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC)
+# define stdint_int64_defined
+ typedef __int64 int64_t;
+ typedef unsigned __int64 uint64_t;
+# define UINT64_C(v) v ## UI64
+# define INT64_C(v) v ## I64
+# ifndef PRINTF_INT64_MODIFIER
+# define PRINTF_INT64_MODIFIER "I64"
+# endif
+# endif
+#endif
+
+#if !defined (LONG_LONG_MAX) && defined (INT64_C)
+# define LONG_LONG_MAX INT64_C (9223372036854775807)
+#endif
+#ifndef ULONG_LONG_MAX
+# define ULONG_LONG_MAX UINT64_C (18446744073709551615)
+#endif
+
+#if !defined (INT64_MAX) && defined (INT64_C)
+# define INT64_MAX INT64_C (9223372036854775807)
+#endif
+#if !defined (INT64_MIN) && defined (INT64_C)
+# define INT64_MIN INT64_C (-9223372036854775808)
+#endif
+#if !defined (UINT64_MAX) && defined (INT64_C)
+# define UINT64_MAX UINT64_C (18446744073709551615)
+#endif
+
+/*
+ * Width of hexadecimal for number field.
+ */
+
+#ifndef PRINTF_INT64_HEX_WIDTH
+# define PRINTF_INT64_HEX_WIDTH "16"
+#endif
+#ifndef PRINTF_INT32_HEX_WIDTH
+# define PRINTF_INT32_HEX_WIDTH "8"
+#endif
+#ifndef PRINTF_INT16_HEX_WIDTH
+# define PRINTF_INT16_HEX_WIDTH "4"
+#endif
+#ifndef PRINTF_INT8_HEX_WIDTH
+# define PRINTF_INT8_HEX_WIDTH "2"
+#endif
+
+#ifndef PRINTF_INT64_DEC_WIDTH
+# define PRINTF_INT64_DEC_WIDTH "20"
+#endif
+#ifndef PRINTF_INT32_DEC_WIDTH
+# define PRINTF_INT32_DEC_WIDTH "10"
+#endif
+#ifndef PRINTF_INT16_DEC_WIDTH
+# define PRINTF_INT16_DEC_WIDTH "5"
+#endif
+#ifndef PRINTF_INT8_DEC_WIDTH
+# define PRINTF_INT8_DEC_WIDTH "3"
+#endif
+
+/*
+ * Ok, lets not worry about 128 bit integers for now. Moore's law says
+ * we don't need to worry about that until about 2040 at which point
+ * we'll have bigger things to worry about.
+ */
+
+#ifdef stdint_int64_defined
+ typedef int64_t intmax_t;
+ typedef uint64_t uintmax_t;
+# define INTMAX_MAX INT64_MAX
+# define INTMAX_MIN INT64_MIN
+# define UINTMAX_MAX UINT64_MAX
+# define UINTMAX_C(v) UINT64_C(v)
+# define INTMAX_C(v) INT64_C(v)
+# ifndef PRINTF_INTMAX_MODIFIER
+# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH
+# endif
+#else
+ typedef int32_t intmax_t;
+ typedef uint32_t uintmax_t;
+# define INTMAX_MAX INT32_MAX
+# define UINTMAX_MAX UINT32_MAX
+# define UINTMAX_C(v) UINT32_C(v)
+# define INTMAX_C(v) INT32_C(v)
+# ifndef PRINTF_INTMAX_MODIFIER
+# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER
+# endif
+# ifndef PRINTF_INTMAX_HEX_WIDTH
+# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH
+# endif
+# ifndef PRINTF_INTMAX_DEC_WIDTH
+# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH
+# endif
+#endif
+
+/*
+ * Because this file currently only supports platforms which have
+ * precise powers of 2 as bit sizes for the default integers, the
+ * least definitions are all trivial. Its possible that a future
+ * version of this file could have different definitions.
+ */
+
+#ifndef stdint_least_defined
+ typedef int8_t int_least8_t;
+ typedef uint8_t uint_least8_t;
+ typedef int16_t int_least16_t;
+ typedef uint16_t uint_least16_t;
+ typedef int32_t int_least32_t;
+ typedef uint32_t uint_least32_t;
+# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER
+# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER
+# define UINT_LEAST8_MAX UINT8_MAX
+# define INT_LEAST8_MAX INT8_MAX
+# define UINT_LEAST16_MAX UINT16_MAX
+# define INT_LEAST16_MAX INT16_MAX
+# define UINT_LEAST32_MAX UINT32_MAX
+# define INT_LEAST32_MAX INT32_MAX
+# define INT_LEAST8_MIN INT8_MIN
+# define INT_LEAST16_MIN INT16_MIN
+# define INT_LEAST32_MIN INT32_MIN
+# ifdef stdint_int64_defined
+ typedef int64_t int_least64_t;
+ typedef uint64_t uint_least64_t;
+# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER
+# define UINT_LEAST64_MAX UINT64_MAX
+# define INT_LEAST64_MAX INT64_MAX
+# define INT_LEAST64_MIN INT64_MIN
+# endif
+#endif
+#undef stdint_least_defined
+
+/*
+ * The ANSI C committee pretending to know or specify anything about
+ * performance is the epitome of misguided arrogance. The mandate of
+ * this file is to *ONLY* ever support that absolute minimum
+ * definition of the fast integer types, for compatibility purposes.
+ * No extensions, and no attempt to suggest what may or may not be a
+ * faster integer type will ever be made in this file. Developers are
+ * warned to stay away from these types when using this or any other
+ * stdint.h.
+ */
+
+typedef int_least8_t int_fast8_t;
+typedef uint_least8_t uint_fast8_t;
+typedef int_least16_t int_fast16_t;
+typedef uint_least16_t uint_fast16_t;
+typedef int_least32_t int_fast32_t;
+typedef uint_least32_t uint_fast32_t;
+#define UINT_FAST8_MAX UINT_LEAST8_MAX
+#define INT_FAST8_MAX INT_LEAST8_MAX
+#define UINT_FAST16_MAX UINT_LEAST16_MAX
+#define INT_FAST16_MAX INT_LEAST16_MAX
+#define UINT_FAST32_MAX UINT_LEAST32_MAX
+#define INT_FAST32_MAX INT_LEAST32_MAX
+#define INT_FAST8_MIN INT_LEAST8_MIN
+#define INT_FAST16_MIN INT_LEAST16_MIN
+#define INT_FAST32_MIN INT_LEAST32_MIN
+#ifdef stdint_int64_defined
+ typedef int_least64_t int_fast64_t;
+ typedef uint_least64_t uint_fast64_t;
+# define UINT_FAST64_MAX UINT_LEAST64_MAX
+# define INT_FAST64_MAX INT_LEAST64_MAX
+# define INT_FAST64_MIN INT_LEAST64_MIN
+#endif
+
+#undef stdint_int64_defined
+
+/*
+ * Whatever piecemeal, per compiler thing we can do about the wchar_t
+ * type limits.
+ */
+
+#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__)
+# include <wchar.h>
+# ifndef WCHAR_MIN
+# define WCHAR_MIN 0
+# endif
+# ifndef WCHAR_MAX
+# define WCHAR_MAX ((wchar_t)-1)
+# endif
+#endif
+
+/*
+ * Whatever piecemeal, per compiler/platform thing we can do about the
+ * (u)intptr_t types and limits.
+ */
+
+#if defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED)
+# define STDINT_H_UINTPTR_T_DEFINED
+#endif
+
+#ifndef STDINT_H_UINTPTR_T_DEFINED
+# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64)
+# define stdint_intptr_bits 64
+# elif defined (__WATCOMC__) || defined (__TURBOC__)
+# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__)
+# define stdint_intptr_bits 16
+# else
+# define stdint_intptr_bits 32
+# endif
+# elif defined (__i386__) || defined (_WIN32) || defined (WIN32)
+# define stdint_intptr_bits 32
+# elif defined (__INTEL_COMPILER)
+/* TODO -- what will Intel do about x86-64? */
+# endif
+
+# ifdef stdint_intptr_bits
+# define stdint_intptr_glue3_i(a,b,c) a##b##c
+# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c)
+# ifndef PRINTF_INTPTR_MODIFIER
+# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER)
+# endif
+# ifndef PTRDIFF_MAX
+# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
+# endif
+# ifndef PTRDIFF_MIN
+# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
+# endif
+# ifndef UINTPTR_MAX
+# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX)
+# endif
+# ifndef INTPTR_MAX
+# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX)
+# endif
+# ifndef INTPTR_MIN
+# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN)
+# endif
+# ifndef INTPTR_C
+# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x)
+# endif
+# ifndef UINTPTR_C
+# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x)
+# endif
+ typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t;
+ typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t;
+# else
+/* TODO -- This following is likely wrong for some platforms, and does
+ nothing for the definition of uintptr_t. */
+ typedef ptrdiff_t intptr_t;
+# endif
+# define STDINT_H_UINTPTR_T_DEFINED
+#endif
+
+/*
+ * Assumes sig_atomic_t is signed and we have a 2s complement machine.
+ */
+
+#ifndef SIG_ATOMIC_MAX
+# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1)
+#endif
+
+#endif
+
diff --git a/3rdparty/assimp/code/qnan.h b/3rdparty/assimp/code/qnan.h
new file mode 100644
index 000000000..c21dc86a1
--- /dev/null
+++ b/3rdparty/assimp/code/qnan.h
@@ -0,0 +1,110 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (ASSIMP)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development 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 Development 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 qnan.h
+ * @brief Some utilities for our dealings with qnans.
+ *
+ * @note Some loaders use qnans to mark invalid values tempoarily, also
+ * Assimp explicitly enforces undefined normals to be set to qnan.
+ * qnan utilities are available in standard libraries (C99 for example)
+ * but last time I checked compiler coverage was so bad that I decided
+ * to reinvent the wheel.
+ */
+
+#ifndef AI_QNAN_H_INCLUDED
+#define AI_QNAN_H_INCLUDED
+
+// ---------------------------------------------------------------------------
+/** Data structure to represent the bit pattern of a 32 Bit
+ * IEEE 754 floating-point number. */
+union _IEEESingle
+{
+ float Float;
+ struct
+ {
+ uint32_t Frac : 23;
+ uint32_t Exp : 8;
+ uint32_t Sign : 1;
+ } IEEE;
+} ;
+
+// ---------------------------------------------------------------------------
+/** Check whether a given float is qNaN.
+ * @param in Input value */
+AI_FORCE_INLINE bool is_qnan(float in)
+{
+ // the straightforward solution does not work:
+ // return (in != in);
+ // compiler generates code like this
+ // load <in> to <register-with-different-width>
+ // compare <register-with-different-width> against <in>
+
+ // FIXME: Use <float> stuff instead? I think fpclassify needs C99
+ return (reinterpret_cast<_IEEESingle*>(&in)->IEEE.Exp == (1u << 8)-1 &&
+ reinterpret_cast<_IEEESingle*>(&in)->IEEE.Frac);
+}
+
+// ---------------------------------------------------------------------------
+/** Check whether a float is NOT qNaN.
+ * @param in Input value */
+AI_FORCE_INLINE bool is_not_qnan(float in)
+{
+ return !is_qnan(in);
+}
+
+// ---------------------------------------------------------------------------
+/** @brief check whether a float is either NaN or (+/-) INF.
+ *
+ * Denorms return false, they're treated like normal values.
+ * @param in Input value */
+AI_FORCE_INLINE bool is_special_float(float in)
+{
+ return (reinterpret_cast<_IEEESingle*>(&in)->IEEE.Exp == (1u << 8)-1);
+}
+
+// ---------------------------------------------------------------------------
+/** @brief Get a fresh qnan. */
+AI_FORCE_INLINE float get_qnan()
+{
+ return std::numeric_limits<float>::quiet_NaN();
+}
+
+#endif // !! AI_QNAN_H_INCLUDED
diff --git a/3rdparty/assimp/code/res/assimp.rc b/3rdparty/assimp/code/res/assimp.rc
new file mode 100644
index 000000000..5b3b5c06b
--- /dev/null
+++ b/3rdparty/assimp/code/res/assimp.rc
@@ -0,0 +1,80 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+#include "..\..\revision.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Deutsch (Deutschland) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU)
+#ifdef _WIN32
+LANGUAGE LANG_GERMAN, SUBLANG_GERMAN
+#pragma code_page(1252)
+#endif //_WIN32
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,1,SVNRevision, 0
+ PRODUCTVERSION 1,1,SVNRevision,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x7L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040704b0"
+ BEGIN
+ VALUE "Comments", "Licensed under a 3-clause BSD license"
+ VALUE "CompanyName", "ASSIMP Development Team"
+ VALUE "FileDescription", "Open Asset Import Library"
+ VALUE "FileVersion", 1,1,SVNRevision,0
+ VALUE "InternalName", "assimp "
+ VALUE "LegalCopyright", "Copyright (C) 2006-2010"
+ VALUE "OriginalFilename", "assimpNN.dll"
+ VALUE "ProductName", "Open Asset Import Library"
+ VALUE "ProductVersion", 1,1,SVNRevision,0
+ ,0
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x407, 1200
+ END
+END
+
+#endif // Deutsch (Deutschland) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/3rdparty/assimp/code/res/resource.h b/3rdparty/assimp/code/res/resource.h
new file mode 100644
index 000000000..a2542fea1
--- /dev/null
+++ b/3rdparty/assimp/code/res/resource.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by assimp.rc
+
+// Nchste Standardwerte fr neue Objekte
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif