Browse Source

Added Open Asset Import Library.
Added skeleton of AssetImporter tool.

Lasse Öörni 15 years ago
parent
commit
51629b18c6
100 changed files with 35079 additions and 6 deletions
  1. 8 6
      CMakeLists.txt
  2. 1 0
      Readme.txt
  3. 88 0
      ThirdParty/Assimp/CHANGES
  4. 745 0
      ThirdParty/Assimp/CMakeLists.txt
  5. 99 0
      ThirdParty/Assimp/CREDITS
  6. 57 0
      ThirdParty/Assimp/INSTALL
  7. 47 0
      ThirdParty/Assimp/LICENSE
  8. 79 0
      ThirdParty/Assimp/README
  9. 839 0
      ThirdParty/Assimp/code/3DSConverter.cpp
  10. 577 0
      ThirdParty/Assimp/code/3DSHelper.h
  11. 1378 0
      ThirdParty/Assimp/code/3DSLoader.cpp
  12. 280 0
      ThirdParty/Assimp/code/3DSLoader.h
  13. 856 0
      ThirdParty/Assimp/code/ACLoader.cpp
  14. 271 0
      ThirdParty/Assimp/code/ACLoader.h
  15. 1302 0
      ThirdParty/Assimp/code/ASELoader.cpp
  16. 208 0
      ThirdParty/Assimp/code/ASELoader.h
  17. 2150 0
      ThirdParty/Assimp/code/ASEParser.cpp
  18. 669 0
      ThirdParty/Assimp/code/ASEParser.h
  19. 731 0
      ThirdParty/Assimp/code/Assimp.cpp
  20. 70 0
      ThirdParty/Assimp/code/AssimpPCH.cpp
  21. 149 0
      ThirdParty/Assimp/code/AssimpPCH.h
  22. 672 0
      ThirdParty/Assimp/code/B3DImporter.cpp
  23. 126 0
      ThirdParty/Assimp/code/B3DImporter.h
  24. 505 0
      ThirdParty/Assimp/code/BVHLoader.cpp
  25. 173 0
      ThirdParty/Assimp/code/BVHLoader.h
  26. 527 0
      ThirdParty/Assimp/code/BaseImporter.cpp
  27. 499 0
      ThirdParty/Assimp/code/BaseImporter.h
  28. 96 0
      ThirdParty/Assimp/code/BaseProcess.cpp
  29. 289 0
      ThirdParty/Assimp/code/BaseProcess.h
  30. 372 0
      ThirdParty/Assimp/code/BlenderDNA.cpp
  31. 798 0
      ThirdParty/Assimp/code/BlenderDNA.h
  32. 706 0
      ThirdParty/Assimp/code/BlenderDNA.inl
  33. 183 0
      ThirdParty/Assimp/code/BlenderIntermediate.h
  34. 1005 0
      ThirdParty/Assimp/code/BlenderLoader.cpp
  35. 261 0
      ThirdParty/Assimp/code/BlenderLoader.h
  36. 311 0
      ThirdParty/Assimp/code/BlenderModifier.cpp
  37. 155 0
      ThirdParty/Assimp/code/BlenderModifier.h
  38. 596 0
      ThirdParty/Assimp/code/BlenderScene.cpp
  39. 683 0
      ThirdParty/Assimp/code/BlenderScene.h
  40. 223 0
      ThirdParty/Assimp/code/BlenderSceneGen.h
  41. 23 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/LICENSE_1_0.txt
  42. 93 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/foreach.hpp
  43. 81 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/format.hpp
  44. 23 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/lexical_cast.hpp
  45. 37 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/math/common_factor_rt.hpp
  46. 45 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/pointer_cast.hpp
  47. 79 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/scoped_array.hpp
  48. 79 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/scoped_ptr.hpp
  49. 228 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/shared_array.hpp
  50. 257 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/shared_ptr.hpp
  51. 20 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/static_assert.hpp
  52. 72 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/timer.hpp
  53. 285 0
      ThirdParty/Assimp/code/BoostWorkaround/boost/tuple/tuple.hpp
  54. 245 0
      ThirdParty/Assimp/code/ByteSwap.h
  55. 1278 0
      ThirdParty/Assimp/code/COBLoader.cpp
  56. 175 0
      ThirdParty/Assimp/code/COBLoader.h
  57. 271 0
      ThirdParty/Assimp/code/COBScene.h
  58. 281 0
      ThirdParty/Assimp/code/CSMLoader.cpp
  59. 88 0
      ThirdParty/Assimp/code/CSMLoader.h
  60. 283 0
      ThirdParty/Assimp/code/CalcTangentsProcess.cpp
  61. 118 0
      ThirdParty/Assimp/code/CalcTangentsProcess.h
  62. 601 0
      ThirdParty/Assimp/code/ColladaHelper.h
  63. 1492 0
      ThirdParty/Assimp/code/ColladaLoader.cpp
  64. 241 0
      ThirdParty/Assimp/code/ColladaLoader.h
  65. 2801 0
      ThirdParty/Assimp/code/ColladaParser.cpp
  66. 341 0
      ThirdParty/Assimp/code/ColladaParser.h
  67. 504 0
      ThirdParty/Assimp/code/ComputeUVMappingProcess.cpp
  68. 149 0
      ThirdParty/Assimp/code/ComputeUVMappingProcess.h
  69. 318 0
      ThirdParty/Assimp/code/ConvertToLHProcess.cpp
  70. 169 0
      ThirdParty/Assimp/code/ConvertToLHProcess.h
  71. 609 0
      ThirdParty/Assimp/code/DXFLoader.cpp
  72. 184 0
      ThirdParty/Assimp/code/DXFLoader.h
  73. 139 0
      ThirdParty/Assimp/code/DefaultIOStream.cpp
  74. 133 0
      ThirdParty/Assimp/code/DefaultIOStream.h
  75. 167 0
      ThirdParty/Assimp/code/DefaultIOSystem.cpp
  76. 83 0
      ThirdParty/Assimp/code/DefaultIOSystem.h
  77. 419 0
      ThirdParty/Assimp/code/DefaultLogger.cpp
  78. 64 0
      ThirdParty/Assimp/code/DefaultProgressHandler.h
  79. 122 0
      ThirdParty/Assimp/code/Exceptional.h
  80. 64 0
      ThirdParty/Assimp/code/FileLogStream.h
  81. 244 0
      ThirdParty/Assimp/code/FileSystemFilter.h
  82. 216 0
      ThirdParty/Assimp/code/FindDegenerates.cpp
  83. 110 0
      ThirdParty/Assimp/code/FindDegenerates.h
  84. 288 0
      ThirdParty/Assimp/code/FindInstancesProcess.cpp
  85. 140 0
      ThirdParty/Assimp/code/FindInstancesProcess.h
  86. 419 0
      ThirdParty/Assimp/code/FindInvalidDataProcess.cpp
  87. 111 0
      ThirdParty/Assimp/code/FindInvalidDataProcess.h
  88. 176 0
      ThirdParty/Assimp/code/FixNormalsStep.cpp
  89. 96 0
      ThirdParty/Assimp/code/FixNormalsStep.h
  90. 138 0
      ThirdParty/Assimp/code/GenFaceNormalsProcess.cpp
  91. 88 0
      ThirdParty/Assimp/code/GenFaceNormalsProcess.h
  92. 228 0
      ThirdParty/Assimp/code/GenVertexNormalsProcess.cpp
  93. 118 0
      ThirdParty/Assimp/code/GenVertexNormalsProcess.h
  94. 112 0
      ThirdParty/Assimp/code/GenericProperty.h
  95. 134 0
      ThirdParty/Assimp/code/HMPFileData.h
  96. 497 0
      ThirdParty/Assimp/code/HMPLoader.cpp
  97. 159 0
      ThirdParty/Assimp/code/HMPLoader.h
  98. 150 0
      ThirdParty/Assimp/code/HalfLifeFileData.h
  99. 108 0
      ThirdParty/Assimp/code/Hash.h
  100. 102 0
      ThirdParty/Assimp/code/IFF.h

+ 8 - 6
CMakeLists.txt

@@ -90,17 +90,17 @@ endmacro ()
 macro (add_shader NAME)
     add_custom_command (
         OUTPUT ../../Bin/Data/Shaders/SM2/${NAME}.xml
-	    COMMAND ../../Bin/ShaderCompiler ${NAME}.xml ../../Bin/Data/Shaders/SM2 SM2
-	    DEPENDS ShaderCompiler Common.hlsl ${NAME}.hlsl ${NAME}.xml
+        COMMAND ../../Bin/ShaderCompiler ${NAME}.xml ../../Bin/Data/Shaders/SM2 SM2
+        DEPENDS ShaderCompiler Common.hlsl ${NAME}.hlsl ${NAME}.xml
     )
 
     add_custom_command (
         OUTPUT ../../Bin/Data/Shaders/SM3/${NAME}.xml
-	    COMMAND ../../Bin/ShaderCompiler ${NAME}.xml ../../Bin/Data/Shaders/SM3 SM3
-	    DEPENDS ShaderCompiler Common.hlsl ${NAME}.hlsl ${NAME}.xml
-	)
+        COMMAND ../../Bin/ShaderCompiler ${NAME}.xml ../../Bin/Data/Shaders/SM3 SM3
+        DEPENDS ShaderCompiler Common.hlsl ${NAME}.hlsl ${NAME}.xml
+    )
 
-	set (ALL_SHADERS ${ALL_SHADERS} ../../Bin/Data/Shaders/SM2/${NAME}.xml ../../Bin/Data/Shaders/SM3/${NAME}.xml)
+    set (ALL_SHADERS ${ALL_SHADERS} ../../Bin/Data/Shaders/SM2/${NAME}.xml ../../Bin/Data/Shaders/SM3/${NAME}.xml)
 endmacro ()
 
 # Recurse subdirectories
@@ -123,11 +123,13 @@ add_subdirectory (Examples/Test)
 add_subdirectory (SourceAssets/Models)
 add_subdirectory (SourceAssets/Shaders)
 add_subdirectory (ThirdParty/AngelScript)
+add_subdirectory (ThirdParty/Assimp)
 add_subdirectory (ThirdParty/ENet)
 add_subdirectory (ThirdParty/ODE)
 add_subdirectory (ThirdParty/StanHull)
 add_subdirectory (ThirdParty/STB)
 add_subdirectory (ThirdParty/TinyXML)
+add_subdirectory (Tools/AssetImporter)
 add_subdirectory (Tools/ModelConverter)
 add_subdirectory (Tools/NormalMapTool)
 add_subdirectory (Tools/PackageTool)

+ 1 - 0
Readme.txt

@@ -31,6 +31,7 @@ Urho3D is greatly inspired by OGRE (http://www.ogre3d.org) and Horde3D
 Urho3D uses the following third-party libraries:
 - AngelScript (2.20.0)
 - ENet (1.3.0)
+- Open Asset Import Library (2.0.863)
 - Open Dynamics Engine (svn rev 1770)
 - StanHull
 - stb_image (1.29)

+ 88 - 0
ThirdParty/Assimp/CHANGES

@@ -0,0 +1,88 @@
+----------------------------------------------------------------------
+CHANGELOG
+----------------------------------------------------------------------
+
+
+
+2.0 (2010-11-21)
+
+FEATURES:
+   - Add support for static Blender (*.blend) scenes
+   - Add support for Q3BSP scenes
+   - Add a windows-based OpenGL sample featuring texturing & basic materials
+   - Add an experimental progress feedback interface.
+   - Vastly improved performance (up to 500%, depending on mesh size and
+     spatial structure) in some expensive postprocessing steps
+   - AssimpView now uses a reworked layout which leaves more space
+     to the scene hierarchy window
+     
+   - Add C# bindings ('Assimp.NET')
+   - Keep BSD-licensed and otherwise free test files in separate 
+     folders (./test/models and ./test/models-nonbsd).
+
+FIXES:
+   - Many Collada bugfixes, improve fault tolerance
+   - Fix possible crashes in the Obj loader
+   - Improve the Ogre XML loader
+   - OpenGL-sample now works with MinGW
+   - Fix Importer::FindLoader failing on uppercase file extensions
+   - Fix flawed path handling when locating external files
+   - Limit the maximum number of vertices, faces, face indices and 
+     weights that Assimp is able to handle. This is to avoid
+     crashes due to overflowing counters.
+   
+   - Updated XCode project files
+   - Further CMAKE build improvements
+   
+
+API CHANGES:
+   - Add data structures for vertex-based animations (These are not
+     currently used, however ...)
+   - Some Assimp::Importer methods are const now.
+ 
+ 
+
+
+
+
+
+1.1 (2010-04-17)
+This is the list of relevant changes from the 1.0 (r412) release to 1.1 (r700).
+
+FEATURES:
+  - Vastly improved Collada support
+  - Add MS3D (Milkshape 3D) support
+  - Add support for Ogre XML static meshes
+  - Add experimental COB (TrueSpace) support
+  - Automatic test suite to quickly locate regressions
+  - D bindings (`dAssimp`)
+  - Python 2.n bindings (`PyAssimp`)
+  - Add basic support for Unicode input files (utf8, utf16 and utf32)
+  - Add further utilities to the `assimp` tool (xml/binary dumps, quick file stats)
+  - Switch to a CMAKE-based build system including an install target for unix'es
+  - Automatic evaluation of subdivision surfaces for some formats.
+  - Add `Importer::ReadFileFromMemory` and the corresponding C-API `aiReadFileFromMemory`
+  - Expose further math utilities via the C-API (i.e. `aiMultiplyMatrix4`)
+
+  - Move noboost files away from the public include directory
+  - Many, many bugfixes and improvements in existing loaders and postprocessing steps
+  - Documentation improved and clarified in many places.
+  - Add a sample on using Assimp in conjunction with OpenGL
+
+  - Distribution/packaging: comfortable SDK installer for Windows
+  - Distribution/packaging: improved release packages for other architectures
+
+CRITICAL FIXES:
+  - Resolve problems with clashing heap managers, STL ABIs and runtime libraries (win32)
+  - Fix automatic detection of file type if no file extension is given
+  - Improved exception safety and robustness, prevent leaking of exceptions through the C interface
+  - Fix possible heap corruption due to material properties pulled in incorrectly
+  - Avoid leaking in certain error scenarios
+  - Fix 64 bit compatibility problems in some loaders (i.e. MDL)
+
+BREAKING API CHANGES:
+  - None -
+
+MINOR API BEHAVIOUR CHANGES:
+ - Change quaternion orientation to suit to the more common convention (-w).
+ - aiString is utf8 now. Not yet consistent, however.

+ 745 - 0
ThirdParty/Assimp/CMakeLists.txt

@@ -0,0 +1,745 @@
+INCLUDE_DIRECTORIES( code/BoostWorkaround )
+ADD_DEFINITIONS( -DASSIMP_BUILD_BOOST_WORKAROUND )
+
+#
+# Listing and grouping of all the source files for use with IDE project
+# generators.
+#
+SET( HEADER_PATH include )
+
+SET( COMPILER_HEADERS
+	${HEADER_PATH}/Compiler/pushpack1.h
+	${HEADER_PATH}/Compiler/poppack1.h
+	code/pstdint.h
+)
+
+SET( PUBLIC_HEADERS
+	${HEADER_PATH}/aiAnim.h
+	${HEADER_PATH}/aiAssert.h
+	${HEADER_PATH}/aiCamera.h
+	${HEADER_PATH}/aiColor4D.h
+	${HEADER_PATH}/aiColor4D.inl
+	${HEADER_PATH}/aiConfig.h
+	${HEADER_PATH}/aiDefines.h
+	${HEADER_PATH}/aiFileIO.h
+	${HEADER_PATH}/aiLight.h
+	${HEADER_PATH}/aiMaterial.h
+	${HEADER_PATH}/aiMaterial.inl
+	${HEADER_PATH}/aiMatrix3x3.h
+	${HEADER_PATH}/aiMatrix3x3.inl
+	${HEADER_PATH}/aiMatrix4x4.h
+	${HEADER_PATH}/aiMatrix4x4.inl
+	${HEADER_PATH}/aiMesh.h
+	${HEADER_PATH}/aiPostProcess.h
+	${HEADER_PATH}/aiQuaternion.h
+	${HEADER_PATH}/aiScene.h
+	${HEADER_PATH}/aiTexture.h
+	${HEADER_PATH}/aiTypes.h
+	${HEADER_PATH}/aiVector2D.h
+	${HEADER_PATH}/aiVector3D.h
+	${HEADER_PATH}/aiVector3D.inl
+	${HEADER_PATH}/aiVersion.h
+	${HEADER_PATH}/assimp.h
+	${HEADER_PATH}/assimp.hpp
+	${HEADER_PATH}/DefaultLogger.h
+	${HEADER_PATH}/ProgressHandler.h
+	${HEADER_PATH}/IOStream.h
+	${HEADER_PATH}/IOSystem.h
+	${HEADER_PATH}/Logger.h
+	${HEADER_PATH}/LogStream.h
+	${HEADER_PATH}/NullLogger.h
+)
+
+SOURCE_GROUP( Compiler FILES	${HEADER_PATH}/Compiler/pushpack1.h
+	${HEADER_PATH}/Compiler/poppack1.h
+)
+
+SOURCE_GROUP( Boost FILES
+	code/BoostWorkaround/boost/math/common_factor_rt.hpp
+	code/BoostWorkaround/boost/foreach.hpp
+	code/BoostWorkaround/boost/format.hpp
+	code/BoostWorkaround/boost/scoped_array.hpp
+	code/BoostWorkaround/boost/scoped_ptr.hpp
+	code/BoostWorkaround/boost/shared_array.hpp
+	code/BoostWorkaround/boost/shared_ptr.hpp
+	code/BoostWorkaround/boost/static_assert.hpp
+	code/BoostWorkaround/boost/tuple/tuple.hpp
+)
+
+SOURCE_GROUP( Logging FILES
+	${HEADER_PATH}/DefaultLogger.h
+	${HEADER_PATH}/IOStream.h
+	${HEADER_PATH}/LogStream.h
+	${HEADER_PATH}/Logger.h
+	${HEADER_PATH}/NullLogger.h
+	code/Win32DebugLogStream.h
+	code/DefaultLogger.cpp
+	code/FileLogStream.h
+)
+
+SOURCE_GROUP( Common FILES
+	code/aiAssert.cpp
+	code/fast_atof.h
+	code/qnan.h
+	code/BaseImporter.cpp
+	code/BaseImporter.h
+	code/BaseProcess.cpp
+	code/BaseProcess.h
+	code/ByteSwap.h
+	code/ProcessHelper.h
+	code/DefaultProgressHandler.h
+	code/DefaultIOStream.cpp
+	code/DefaultIOStream.h
+	code/DefaultIOSystem.cpp
+	code/DefaultIOSystem.h
+	code/Hash.h
+	code/Importer.cpp
+	code/IFF.h
+	code/ParsingUtils.h
+	code/StdOStreamLogStream.h
+	code/StreamReader.h
+	code/StringComparison.h
+	code/SGSpatialSort.cpp
+	code/SGSpatialSort.h
+	code/VertexTriangleAdjacency.cpp
+	code/VertexTriangleAdjacency.h
+	code/GenericProperty.h
+	code/SpatialSort.cpp
+	code/SpatialSort.h
+	code/SceneCombiner.cpp
+	code/SceneCombiner.h
+	code/ScenePreprocessor.cpp
+	code/ScenePreprocessor.h
+	code/SkeletonMeshBuilder.cpp
+	code/SkeletonMeshBuilder.h
+	code/SmoothingGroups.h
+	code/StandardShapes.cpp
+	code/StandardShapes.h
+	code/TargetAnimation.cpp
+	code/TargetAnimation.h
+	code/RemoveComments.cpp
+	code/RemoveComments.h
+	code/Subdivision.cpp
+	code/Subdivision.h
+	code/Vertex.h
+	code/LineSplitter.h
+	code/TinyFormatter.h
+	code/Profiler.h
+)
+
+SOURCE_GROUP( 3DS FILES
+	code/3DSConverter.cpp
+	code/3DSHelper.h
+	code/3DSLoader.cpp
+	code/3DSLoader.h
+)
+
+SOURCE_GROUP( AC FILES
+	code/ACLoader.cpp
+	code/ACLoader.h
+)
+
+SOURCE_GROUP( ASE FILES
+	code/ASELoader.cpp
+	code/ASELoader.h
+	code/ASEParser.cpp
+	code/ASEParser.h
+)
+SOURCE_GROUP( B3D FILES
+	code/B3DImporter.cpp
+	code/B3DImporter.h
+)
+
+SOURCE_GROUP( BVH FILES
+	code/BVHLoader.cpp
+	code/BVHLoader.h
+)
+
+SOURCE_GROUP( Collada FILES
+	code/ColladaHelper.h
+	code/ColladaLoader.cpp
+	code/ColladaLoader.h
+	code/ColladaParser.cpp
+	code/ColladaParser.h
+)
+
+SOURCE_GROUP( DXF FILES
+	code/DXFLoader.cpp
+	code/DXFLoader.h
+)
+
+SOURCE_GROUP( CSM FILES
+	code/CSMLoader.cpp
+	code/CSMLoader.h
+)
+
+SOURCE_GROUP( HMP FILES
+	code/HMPFileData.h
+	code/HMPLoader.cpp
+	code/HMPLoader.h
+	code/HalfLifeFileData.h
+)
+
+SOURCE_GROUP( Irr FILES
+	code/IRRLoader.cpp
+	code/IRRLoader.h
+	code/IRRMeshLoader.cpp
+	code/IRRMeshLoader.h
+	code/IRRShared.cpp
+	code/IRRShared.h
+)
+
+SOURCE_GROUP( LWO FILES
+	code/LWOAnimation.cpp
+	code/LWOAnimation.h
+	code/LWOBLoader.cpp
+	code/LWOFileData.h
+	code/LWOLoader.cpp
+	code/LWOLoader.h
+	code/LWOMaterial.cpp
+)
+
+SOURCE_GROUP( LWS FILES
+	code/LWSLoader.cpp
+	code/LWSLoader.h
+)
+
+SOURCE_GROUP( MD2 FILES
+	code/MD2FileData.h
+	code/MD2Loader.cpp
+	code/MD2Loader.h
+	code/MD2NormalTable.h
+)
+
+SOURCE_GROUP( MD3 FILES
+	code/MD3FileData.h
+	code/MD3Loader.cpp
+	code/MD3Loader.h
+)
+
+SOURCE_GROUP( MD5 FILES
+	code/MD5Loader.cpp
+	code/MD5Loader.h
+	code/MD5Parser.cpp
+	code/MD5Parser.h
+)
+
+SOURCE_GROUP( MDC FILES
+	code/MDCFileData.h
+	code/MDCLoader.cpp
+	code/MDCLoader.h
+	code/MDCNormalTable.h
+)
+
+SOURCE_GROUP( MDL FILES
+	code/MDLDefaultColorMap.h
+	code/MDLFileData.h
+	code/MDLLoader.cpp
+	code/MDLLoader.h
+	code/MDLMaterialLoader.cpp
+)
+
+SOURCE_GROUP( MaterialSystem FILES
+	code/MaterialSystem.cpp
+	code/MaterialSystem.h
+)
+
+SOURCE_GROUP( NFF FILES
+	code/NFFLoader.cpp
+	code/NFFLoader.h
+)
+
+SOURCE_GROUP( NDO FILES
+	code/NDOLoader.cpp
+	code/NDOLoader.h
+)
+
+SOURCE_GROUP( OFFFormat FILES
+	code/OFFLoader.cpp
+	code/OFFLoader.h
+)
+
+SOURCE_GROUP( Obj FILES
+	code/ObjFileData.h
+	code/ObjFileImporter.cpp
+	code/ObjFileImporter.h
+	code/ObjFileMtlImporter.cpp
+	code/ObjFileMtlImporter.h
+	code/ObjFileParser.cpp
+	code/ObjFileParser.h
+	code/ObjTools.h
+)
+
+SOURCE_GROUP( Ogre FILES
+	code/OgreImporter.h
+	code/OgreImporter.cpp
+	code/OgreImporterMaterial.cpp
+)
+
+SOURCE_GROUP( Ply FILES
+	code/PlyLoader.cpp
+	code/PlyLoader.h
+	code/PlyParser.cpp
+	code/PlyParser.h
+)
+
+SOURCE_GROUP(MS3D FILES
+	code/MS3DLoader.cpp
+	code/MS3DLoader.h
+)
+
+SOURCE_GROUP(COB FILES
+	code/COBLoader.cpp
+	code/COBLoader.h
+	code/COBScene.h
+)
+
+SOURCE_GROUP(BLENDER FILES
+	code/BlenderLoader.cpp
+	code/BlenderLoader.h
+	code/BlenderDNA.cpp
+	code/BlenderDNA.h
+	code/BlenderDNA.inl
+	code/BlenderScene.cpp
+	code/BlenderScene.h
+	code/BlenderSceneGen.h
+	code/BlenderIntermediate.h
+	code/BlenderModifier.h
+	code/BlenderModifier.cpp
+)
+
+SOURCE_GROUP( PostProcessing FILES
+	code/CalcTangentsProcess.cpp
+	code/CalcTangentsProcess.h
+	code/ComputeUVMappingProcess.cpp
+	code/ComputeUVMappingProcess.h
+	code/ConvertToLHProcess.cpp
+	code/ConvertToLHProcess.h
+	code/FindDegenerates.cpp
+	code/FindDegenerates.h
+	code/FindInstancesProcess.cpp
+	code/FindInstancesProcess.h
+	code/FindInvalidDataProcess.cpp
+	code/FindInvalidDataProcess.h
+	code/FixNormalsStep.cpp
+	code/FixNormalsStep.h
+	code/GenFaceNormalsProcess.cpp
+	code/GenFaceNormalsProcess.h
+	code/GenVertexNormalsProcess.cpp
+	code/GenVertexNormalsProcess.h
+	code/PretransformVertices.cpp
+	code/PretransformVertices.h
+	code/ImproveCacheLocality.cpp
+	code/ImproveCacheLocality.h
+	code/JoinVerticesProcess.cpp
+	code/JoinVerticesProcess.h
+	code/LimitBoneWeightsProcess.cpp
+	code/LimitBoneWeightsProcess.h
+	code/RemoveRedundantMaterials.cpp
+	code/RemoveRedundantMaterials.h
+	code/RemoveVCProcess.cpp
+	code/RemoveVCProcess.h
+	code/SortByPTypeProcess.cpp
+	code/SortByPTypeProcess.h
+	code/SplitLargeMeshes.cpp
+	code/SplitLargeMeshes.h
+	code/TerragenLoader.cpp
+	code/TerragenLoader.h
+	code/TextureTransform.cpp
+	code/TextureTransform.h
+	code/TriangulateProcess.cpp
+	code/TriangulateProcess.h
+	code/ValidateDataStructure.cpp
+	code/ValidateDataStructure.h
+	code/OptimizeGraph.cpp
+	code/OptimizeGraph.h
+	code/OptimizeMeshes.cpp
+	code/OptimizeMeshes.h
+)
+
+SOURCE_GROUP( Q3D FILES
+	code/Q3DLoader.cpp
+	code/Q3DLoader.h
+)
+
+SOURCE_GROUP( Q3BSP FILES
+	code/Q3BSPFileData.h
+	code/Q3BSPFileParser.h
+	code/Q3BSPFileParser.cpp
+	code/Q3BSPFileImporter.h
+	code/Q3BSPFileImporter.cpp
+	code/Q3BSPZipArchive.h
+	code/Q3BSPZipArchive.cpp
+)
+
+SOURCE_GROUP( Raw FILES
+	code/RawLoader.cpp
+	code/RawLoader.h
+)
+
+SOURCE_GROUP( SMD FILES
+	code/SMDLoader.cpp
+	code/SMDLoader.h
+)
+
+SOURCE_GROUP( STL FILES
+	code/STLLoader.cpp
+	code/STLLoader.h
+)
+
+SOURCE_GROUP( Unreal FILES
+	code/UnrealLoader.cpp
+	code/UnrealLoader.h
+)
+
+SOURCE_GROUP( XFile FILES
+	code/XFileHelper.h
+	code/XFileImporter.cpp
+	code/XFileImporter.h
+	code/XFileParser.cpp
+	code/XFileParser.h
+)
+
+SOURCE_GROUP( Extra FILES
+	code/MakeVerboseFormat.cpp
+	code/MakeVerboseFormat.h
+	code/MD4FileData.h
+)
+
+SOURCE_GROUP( IrrXML FILES
+	irrXMLWrapper.h
+	contrib/irrXML/CXMLReaderImpl.h
+	contrib/irrXML/heapsort.h
+	contrib/irrXML/irrArray.h
+	contrib/irrXML/irrString.h
+	contrib/irrXML/irrTypes.h
+	contrib/irrXML/irrXML.cpp
+	contrib/irrXML/irrXML.h
+)
+
+SOURCE_GROUP( ConvertUTF FILES
+	contrib/ConvertUTF/ConvertUTF.h
+	contrib/ConvertUTF/ConvertUTF.c
+)
+
+SOURCE_GROUP( zlib FILES
+	contrib/zlib/adler32.c
+	contrib/zlib/compress.c
+	contrib/zlib/crc32.c
+	contrib/zlib/crc32.h
+	contrib/zlib/deflate.c
+	contrib/zlib/deflate.h
+	contrib/zlib/inffast.c
+	contrib/zlib/inffast.h
+	contrib/zlib/inffixed.h
+	contrib/zlib/inflate.c
+	contrib/zlib/inflate.h
+	contrib/zlib/inftrees.c
+	contrib/zlib/inftrees.h
+	contrib/zlib/trees.c
+	contrib/zlib/trees.h
+	contrib/zlib/zconf.h
+	contrib/zlib/zconf.in.h
+	contrib/zlib/zlib.h
+	contrib/zlib/zutil.c
+	contrib/zlib/zutil.h
+)
+
+SOURCE_GROUP( unzip FILES
+	contrib/unzip/crypt.h
+	contrib/unzip/ioapi.c
+	contrib/unzip/ioapi.h
+	contrib/unzip/unzip.c
+	contrib/unzip/unzip.h
+)
+
+SET (HEADER_FILES
+	code/3DSHelper.h
+	code/3DSLoader.h
+	code/ACLoader.h
+	code/ASELoader.h
+	code/ASEParser.h
+	code/AssimpPCH.h
+	code/B3DImporter.h
+	code/BVHLoader.h
+	code/BaseImporter.h
+	code/BaseProcess.h
+	code/BlenderScene.h
+	code/BlenderSceneGen.h
+	code/BlenderIntermediate.h
+	code/BlenderLoader.h
+	code/BlenderModifier.h
+	code/BlenderDNA.h
+	code/BlenderDNA.inl
+	code/ByteSwap.h
+	code/CalcTangentsProcess.h
+	code/COBLoader.h
+	code/COBScene.h
+	code/ColladaHelper.h
+	code/ColladaLoader.h
+	code/ColladaParser.h
+	code/ComputeUVMappingProcess.h
+	code/ConvertToLHProcess.h
+	code/DXFLoader.h
+	code/CSMLoader.h
+	code/DefaultIOStream.h
+	code/DefaultIOSystem.h
+	code/DefaultProgressHandler.h
+	code/FileLogStream.h
+    code/FindDegenerates.h
+	code/FindInstancesProcess.h
+	code/FindInvalidDataProcess.h
+	code/FixNormalsStep.h
+	code/GenFaceNormalsProcess.h
+	code/GenVertexNormalsProcess.h
+	code/GenericProperty.h
+	code/HMPFileData.h
+	code/HMPLoader.h
+	code/HalfLifeFileData.h
+	code/Hash.h
+	code/IFF.h
+	code/IRRLoader.h
+	code/IRRMeshLoader.h
+	code/IRRShared.h
+	code/ImproveCacheLocality.h
+	code/JoinVerticesProcess.h
+	code/LimitBoneWeightsProcess.h
+	code/LineSplitter.h
+    code/LWOAnimation.h
+	code/LWOFileData.h
+ 	code/LWOLoader.h
+	code/LWSLoader.h
+	code/MakeVerboseFormat.h
+	code/MD2FileData.h
+	code/MD2Loader.h
+	code/MD2NormalTable.h
+	code/MD3FileData.h
+    code/MD3Loader.h
+	code/MD4FileData.h
+	code/MDCLoader.h
+	code/MDCNormalTable.h
+	code/MDLDefaultColorMap.h
+	code/MDLFileData.h
+	code/MaterialSystem.h
+	code/MD5Loader.h
+	code/MD5Parser.h
+	code/MDCFileData.h
+	code/MDLLoader.h
+	code/MS3DLoader.h
+	code/NDOLoader.h
+	code/NFFLoader.h
+	code/OFFLoader.h
+	code/ObjFileData.h
+	code/ObjFileImporter.h
+	code/ObjFileMtlImporter.h
+	code/ObjFileParser.h
+	code/ObjTools.h
+	code/OgreImporter.h
+	code/OptimizeGraph.h
+	code/OptimizeMeshes.h
+	code/ParsingUtils.h
+	code/PlyLoader.h
+	code/PlyParser.h
+	code/PretransformVertices.h
+	code/ProcessHelper.h
+	code/Profiler.h
+	code/RawLoader.h
+	code/RemoveComments.h
+	code/RemoveRedundantMaterials.h
+	code/Q3DLoader.h
+	code/Q3BSPFileData.h
+	code/Q3BSPFileParser.h
+	code/Q3BSPFileImporter.h
+	code/Q3BSPZipArchive.h
+	code/RemoveVCProcess.h
+	code/SGSpatialSort.h
+	code/SMDLoader.h
+	code/STLLoader.h
+	code/SceneCombiner.h
+	code/ScenePreprocessor.h
+	code/SkeletonMeshBuilder.h
+	code/SmoothingGroups.h
+	code/SortByPTypeProcess.h
+	code/SpatialSort.h
+	code/SplitLargeMeshes.h
+	code/StandardShapes.h
+	code/StdOStreamLogStream.h
+	code/StreamReader.h
+	code/StringComparison.h
+	code/Subdivision.h
+	code/TargetAnimation.h
+	code/TerragenLoader.h
+	code/TextureTransform.h
+	code/TinyFormatter.h
+	code/TriangulateProcess.h
+	code/UnrealLoader.h
+	code/ValidateDataStructure.h
+	code/Vertex.h
+	code/VertexTriangleAdjacency.h
+	code/Win32DebugLogStream.h
+	code/XFileHelper.h
+	code/XFileImporter.h
+	code/XFileParser.h
+	code/fast_atof.h
+	code/irrXMLWrapper.h
+	code/qnan.h
+	code/BoostWorkaround/boost/math/common_factor_rt.hpp
+	code/BoostWorkaround/boost/foreach.hpp
+	code/BoostWorkaround/boost/format.hpp
+	code/BoostWorkaround/boost/scoped_array.hpp
+	code/BoostWorkaround/boost/scoped_ptr.hpp
+	code/BoostWorkaround/boost/shared_array.hpp
+	code/BoostWorkaround/boost/shared_ptr.hpp
+	code/BoostWorkaround/boost/static_assert.hpp
+	code/BoostWorkaround/boost/tuple/tuple.hpp
+	${PUBLIC_HEADERS}
+	${COMPILER_HEADERS}
+)
+
+SET (SOURCE_FILES
+	code/3DSConverter.cpp
+	code/3DSLoader.cpp
+	code/ACLoader.cpp
+	code/ASELoader.cpp
+	code/ASEParser.cpp
+	code/Assimp.cpp
+	code/AssimpPCH.cpp
+	code/B3DImporter.cpp
+	code/BVHLoader.cpp
+	code/BaseImporter.cpp
+	code/BaseProcess.cpp
+	code/BlenderLoader.cpp
+	code/BlenderDNA.cpp
+	code/BlenderScene.cpp
+	code/BlenderModifier.cpp
+	code/CalcTangentsProcess.cpp
+	code/COBLoader.cpp
+	code/ColladaLoader.cpp
+	code/ColladaParser.cpp
+	code/ComputeUVMappingProcess.cpp
+	code/ConvertToLHProcess.cpp
+	code/DXFLoader.cpp
+	code/CSMLoader.cpp
+	code/DefaultIOStream.cpp
+	code/DefaultIOSystem.cpp
+	code/DefaultLogger.cpp
+	code/FindDegenerates.cpp
+	code/FindInstancesProcess.cpp
+	code/FindInvalidDataProcess.cpp
+	code/FixNormalsStep.cpp
+	code/GenFaceNormalsProcess.cpp
+	code/GenVertexNormalsProcess.cpp
+	code/HMPLoader.cpp
+	code/IRRLoader.cpp
+	code/IRRMeshLoader.cpp
+	code/IRRShared.cpp
+	code/Importer.cpp
+	code/ImproveCacheLocality.cpp
+	code/JoinVerticesProcess.cpp
+	code/LWOAnimation.cpp
+	code/LWOBLoader.cpp
+	code/LWOLoader.cpp
+	code/LWOMaterial.cpp
+	code/LWSLoader.cpp
+	code/LimitBoneWeightsProcess.cpp
+	code/MakeVerboseFormat.cpp
+	code/MD2Loader.cpp
+	code/MD3Loader.cpp
+	code/MD5Loader.cpp
+	code/MD5Parser.cpp
+	code/MDCLoader.cpp
+	code/MDLLoader.cpp
+	code/MDLMaterialLoader.cpp
+	code/MS3DLoader.cpp
+	code/MaterialSystem.cpp
+	code/NDOLoader.cpp
+	code/NFFLoader.cpp
+	code/OFFLoader.cpp
+	code/ObjFileImporter.cpp
+	code/ObjFileMtlImporter.cpp
+	code/ObjFileParser.cpp
+	code/OgreImporter.cpp
+	code/OgreImporterMaterial.cpp
+	code/OptimizeGraph.cpp
+	code/OptimizeMeshes.cpp
+	code/PlyLoader.cpp
+	code/PlyParser.cpp
+	code/PretransformVertices.cpp
+	code/Q3DLoader.cpp
+	code/Q3BSPFileParser.cpp
+	code/Q3BSPFileImporter.cpp
+	code/Q3BSPZipArchive.cpp
+	code/RawLoader.cpp
+	code/RemoveComments.cpp
+	code/RemoveRedundantMaterials.cpp
+	code/RemoveVCProcess.cpp
+	code/SGSpatialSort.cpp
+	code/SMDLoader.cpp
+	code/STLLoader.cpp
+	code/SceneCombiner.cpp
+	code/ScenePreprocessor.cpp
+	code/SkeletonMeshBuilder.cpp
+	code/SortByPTypeProcess.cpp
+	code/SpatialSort.cpp
+	code/SplitLargeMeshes.cpp
+	code/StandardShapes.cpp
+	code/Subdivision.cpp
+	code/TargetAnimation.cpp
+	code/TerragenLoader.cpp
+	code/TextureTransform.cpp
+	code/TriangulateProcess.cpp
+	code/UnrealLoader.cpp
+	code/ValidateDataStructure.cpp
+	code/VertexTriangleAdjacency.cpp
+	code/XFileImporter.cpp
+	code/XFileParser.cpp
+	code/aiAssert.cpp
+)
+
+SET (CONTRIB_FILES
+	contrib/irrXML/CXMLReaderImpl.h
+	contrib/irrXML/heapsort.h
+	contrib/irrXML/irrArray.h
+	contrib/irrXML/irrString.h
+	contrib/irrXML/irrTypes.h
+	contrib/irrXML/irrXML.cpp
+	contrib/irrXML/irrXML.h
+	contrib/zlib/adler32.c
+	contrib/zlib/compress.c
+	contrib/zlib/crc32.c
+	contrib/zlib/crc32.h
+	contrib/zlib/deflate.c
+	contrib/zlib/deflate.h
+	contrib/zlib/inffast.c
+	contrib/zlib/inffast.h
+	contrib/zlib/inffixed.h
+	contrib/zlib/inflate.c
+	contrib/zlib/inflate.h
+	contrib/zlib/inftrees.c
+	contrib/zlib/inftrees.h
+	contrib/zlib/trees.c
+	contrib/zlib/trees.h
+	contrib/zlib/zconf.h
+	contrib/zlib/zconf.in.h
+	contrib/zlib/zlib.h
+	contrib/zlib/zutil.c
+	contrib/zlib/zutil.h
+	contrib/ConvertUTF/ConvertUTF.c
+	contrib/unzip/crypt.h
+	contrib/unzip/ioapi.c
+	contrib/unzip/ioapi.h
+	contrib/unzip/unzip.c
+	contrib/unzip/unzip.h
+)
+
+ADD_LIBRARY ( Assimp STATIC ${HEADER_FILES} ${SOURCE_FILES} ${CONTRIB_FILES} )
+
+if (MSVC)
+    foreach(FILE ${SOURCE_FILES})
+        if (${FILE} MATCHES "AssimpPCH.cpp$")
+            set_source_files_properties(${FILE} PROPERTIES COMPILE_FLAGS "/YcAssimpPCH.h")
+        else()
+            set_source_files_properties(${FILE} PROPERTIES COMPILE_FLAGS "/YuAssimpPCH.h")
+        endif()
+    endforeach ()
+endif ()

+ 99 - 0
ThirdParty/Assimp/CREDITS

@@ -0,0 +1,99 @@
+===============================================================
+Open Asset Import Library (Assimp)
+Developers and Contributors
+===============================================================
+
+The following is the list of all constributors.  
+Thanks for your help!
+
+- Alexander Gessler,
+3DS-, BLEND-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL-, RAW-, OFF-, MS3D-, Q3D- and LWO-Loader, Assimp-Viewer, assimp-cmd, -noboost, Website (Admin and Design).
+
+- Thomas Schulze,
+X-, Collada-, BVH-Loader, Postprocessing framework. Data structure & Interface design, documentation.
+
+- Kim Kulling,
+Obj-Loader, Logging system, Scons-build environment, CMake build environment, Linux build.
+
+- R.Schmidt,
+Linux build, eclipse support.
+
+- Matthias Gubisch,
+Assimp.net
+Visual Studio 9 support, bugfixes.
+
+- Mark Sibly
+B3D-Loader, Assimp testing
+
+- Jonathan Klein
+Ogre Loader
+
+- Sebastian Hempel,
+PyAssimp (first version)
+Compile-Bugfixes for mingw, add enviroment for static library support in make.
+
+- Jonathan Pokrass
+Supplied a bugfix concerning the scaling in the md3 loader.
+
+- Andrew Galante,
+Submitted patches to make Assimp compile with GCC-4, a makefile and the xcode3 workspace.
+
+- Andreas Nagel
+First Assimp testing & verification under Windows Vista 64 Bit.
+
+- Marius Schröder
+Allowed us to use many of his models for screenshots and testing.
+
+- Christian Schubert
+Supplied various XFiles for testing purposes.
+
+- Tizian Wieland
+Searched the web for hundreds of test models for internal use
+
+- John Connors
+Supplied patches for linux and SCons.
+
+- T. R.
+The GUY who performed some of the CSM mocaps.
+
+- Andy Maloney
+Contributed fixes for the documentation and the doxygen markup
+
+- Zhao Lei
+Contributed several bugfixes fixing memory leaks and improving float parsing 
+
+- sueastside
+Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date.
+
+- Tobias Rittig
+Collada testing with Cinema 4D
+
+- Brad Grantham
+Improvements in OpenGL-Sample.
+
+- Robert Ramirez
+Add group loading feature to Obj-Loader.
+
+- Chris Maiwald
+Many bugreports, improving Assimp's portability, regular testing & feedback.
+
+- Stepan Hrbek
+Bugreport and fix for a obj-materialloader crash.
+
+- David Nadlinger
+D bindings, CMake install support. 
+
+- Dario Accornero
+Contributed several patches regarding Mac OS/XCode targets, bug reports.
+
+- Martin Walser (Samhayne)
+Contributed the 'SimpleTexturedOpenGl' sample.
+
+- Matthias Fauconneau
+Contributed a fix for the Q3-BSP loader.
+
+- Jørgen P. Tjernø
+Contributed updated and improved xcode workspaces
+
+- drparallax
+Contributed the /samples/SimpleAssimpViewX sample

+ 57 - 0
ThirdParty/Assimp/INSTALL

@@ -0,0 +1,57 @@
+	
+
+Open Asset Import Library (Assimp) Install
+------------------------------------------------
+
+
+=======================================================================
+Please refer to the doxygen documentation for full install instructions
+=======================================================================
+
+A regularly-updated copy is available at 
+http://assimp.sourceforge.net/lib_html/index.html
+
+A CHM file is included in the SVN repos: ./doc/AssimpDoc_Html/AssimpDoc.chm.
+To build the doxygen documentation on your own, follow these steps:
+
+a) download & install latest doxygen 
+b) make sure doxygen is in the executable search path
+c) navigate to ./doc
+d) and run 'doxygen'
+
+Open the generated HTML (AssimpDoc_Html/index.html) in the browser of your choice.
+Windows only: To generate the CHM doc install the 'Microsoft HTML Workshop'
+and configure the path to it in the DOXYFILE. Run doxygen again.
+
+=======================================================================
+For the inpatient:
+
+Windows: go to ./workspaces/vc8 or ./workspaces/vc9, open the
+VS Solution and build for your preferred build target ...
+release-dll is usually the best choice. Adjust the runtime library to
+your needs, multithreaded-dll is preconfigured by default.
+
+These solutions are currently hand-maintained, they copy all
+the resulting binaries to ./bin/config-name at the moment. 
+
+Unix & Family:
+
+CMake is now our preferred build tool. Run cmake with your
+favourite build script generator from *here* and have fun.
+If you use cmake only to get a makefile, run 
+'make' and 'make install' afterwards. 
+
+NOTE: in order to use the assimp command line tools, you may
+need to run ldconfig as root to make the so loader find
+the assimp shared library --
+
+Note that running make from ./code is deprecated, although there
+is still a suitable makefile in it.
+
+
+
+
+
+
+
+

+ 47 - 0
ThirdParty/Assimp/LICENSE

@@ -0,0 +1,47 @@
+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.
+
+
+
+AN EXCEPTION applies to all files in the ./test/models-nonbsd subfolder.
+These are 3d models for testing purposes, from various free sources
+on the internet. They are - unless otherwise stated - copyright of
+their respective creators, which may impose additional requirements
+on the use of their work. For any of these models, see 
+<model-name>.source.txt for more legal information. Contact us if you
+are a copyright holder and believe that we credited you inproperly or 
+if you don't want your files to appear in the repository.
+

+ 79 - 0
ThirdParty/Assimp/README

@@ -0,0 +1,79 @@
+	Open Asset Import Library (Assimp) Readme
+	-----------------------------------------
+
+Here you can find information regarding Open Asset Import Library:
+
+	1.		Overview
+	1.1		Supported file formats
+	1.2		File structure
+	2.		Build the Asset Import Library
+	3. 		Help
+	4.		License
+
+
+
+1.	Overview
+1.1	Supported file formats
+
+The Asset Import Library provides a lot of model formats:
+	- 3DS
+	- ASE
+	- DXF
+	- HMP
+	- MD2
+ 	- MD3 
+	- MD5
+	- MDC
+	- MDL
+	- NFF
+ 	- PLY
+	- STL
+	- X
+	- LWO
+	- OBJ
+	- SMD
+	
+	- Collada
+	
+	- LWO
+	
+	- Ogre XML
+	
+	- partly LWS
+	
+	- .. + many more, see http://assimp.sourceforge.net/main_features_formats.html for a full list.
+
+
+1.2 Repository structure
+
+Open Asset Import Library is implemented in C++ and provides a C-interface. The directory structure is:
+
+	/bin			Binaries of the Asset Import Library.
+	/code			Code of the Asset Import Library.
+	/contrib		Third-party-libraries used by the Asset Import Library.
+	/doc			Documentation (doxygen generated and data structure as a xml file)
+	/include		Public headers.
+	/lib			Static library location for Windows.
+	/obj			Object file location for Windows.
+	/port			Ports to other languages. 
+	
+	/test			Unit- and regression tests, model test suite.
+	/tools			Tools (viewer, command line `assimp`).
+	/workspaces		Build enviroments for vc,xcode,...
+
+
+
+2. Build the Asset Import Library
+
+Take a look into the INSTALL file.
+
+
+
+3. Help
+
+For more help go to http://assimp.sourceforge.net/ (or, for germans, http://www.zfx.info). Or take a look into the doc-folder, which contains the doxygen-generated documentation in HTMl format (CHMs for Windows are as well contained in some distributions and located right here in the root folder).
+
+
+4. License
+
+The license of the Asset Import Library is based on the BSD-License. It is contained in the LICENSE file.

+ 839 - 0
ThirdParty/Assimp/code/3DSConverter.cpp

@@ -0,0 +1,839 @@
+/*
+---------------------------------------------------------------------------
+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();
+			meshOut->mName = name;
+			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);	
+		}
+
+		// 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

+ 577 - 0
ThirdParty/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

+ 1378 - 0
ThirdParty/Assimp/code/3DSLoader.cpp

@@ -0,0 +1,1378 @@
+/*
+---------------------------------------------------------------------------
+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();
+}
+
+// ------------------------------------------------------------------------------------------------
+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

+ 280 - 0
ThirdParty/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

+ 856 - 0
ThirdParty/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

+ 271 - 0
ThirdParty/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

+ 1302 - 0
ThirdParty/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

+ 208 - 0
ThirdParty/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

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

+ 669 - 0
ThirdParty/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

+ 731 - 0
ThirdParty/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();
+}
+
+

+ 70 - 0
ThirdParty/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) 2008-2010, ASSIMP Development Team\n"
+"License under the terms and conditions of the 3-clause BSD license\n"
+"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 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get Assimp major version
+ASSIMP_API unsigned int aiGetVersionMajor ()	{
+	return 2;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+}
+

+ 149 - 0
ThirdParty/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

+ 672 - 0
ThirdParty/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

+ 126 - 0
ThirdParty/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

+ 505 - 0
ThirdParty/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

+ 173 - 0
ThirdParty/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

+ 527 - 0
ThirdParty/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 %%%");
+	}
+}
+
+
+
+

+ 499 - 0
ThirdParty/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

+ 96 - 0
ThirdParty/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
+}

+ 289 - 0
ThirdParty/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

+ 372 - 0
ThirdParty/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

+ 798 - 0
ThirdParty/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()
+		: next_cache_idx()
+		, _cacheArrays(*this)
+		, _cache(*this)
+	{} 
+
+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

+ 706 - 0
ThirdParty/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

+ 183 - 0
ThirdParty/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

+ 1005 - 0
ThirdParty/Assimp/code/BlenderLoader.cpp

@@ -0,0 +1,1005 @@
+/*
+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",
+	"Assimp Team",
+	"",
+	"",
+	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

+ 261 - 0
ThirdParty/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

+ 311 - 0
ThirdParty/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

+ 155 - 0
ThirdParty/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

+ 596 - 0
ThirdParty/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

+ 683 - 0
ThirdParty/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

+ 223 - 0
ThirdParty/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

+ 23 - 0
ThirdParty/Assimp/code/BoostWorkaround/boost/LICENSE_1_0.txt

@@ -0,0 +1,23 @@
+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.

+ 93 - 0
ThirdParty/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

+ 81 - 0
ThirdParty/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
+

+ 23 - 0
ThirdParty/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

+ 37 - 0
ThirdParty/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

+ 45 - 0
ThirdParty/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

+ 79 - 0
ThirdParty/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
+

+ 79 - 0
ThirdParty/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
+

+ 228 - 0
ThirdParty/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

+ 257 - 0
ThirdParty/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

+ 20 - 0
ThirdParty/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;assert_dummy;}
+
+#endif
+#endif // !! AI_BOOST_STATIC_ASSERT_INCLUDED

+ 72 - 0
ThirdParty/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

+ 285 - 0
ThirdParty/Assimp/code/BoostWorkaround/boost/tuple/tuple.hpp

@@ -0,0 +1,285 @@
+// A very small replacement for boost::tuple
+// (c) Alexander Gessler, 2008 [[email protected]]
+
+#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

+ 245 - 0
ThirdParty/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

+ 1278 - 0
ThirdParty/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

+ 175 - 0
ThirdParty/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

+ 271 - 0
ThirdParty/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

+ 281 - 0
ThirdParty/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

+ 88 - 0
ThirdParty/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
+

+ 283 - 0
ThirdParty/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;
+}

+ 118 - 0
ThirdParty/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

+ 601 - 0
ThirdParty/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

+ 1492 - 0
ThirdParty/Assimp/code/ColladaLoader.cpp

@@ -0,0 +1,1492 @@
+/*
+---------------------------------------------------------------------------
+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::string meshMaterial;
+			std::map<std::string, Collada::SemanticMappingTable >::const_iterator meshMatIt = mid.mMaterials.find( submesh.mMaterial);
+
+			const Collada::SemanticMappingTable* table = NULL;
+			if( meshMatIt != mid.mMaterials.end())
+			{
+				table = &meshMatIt->second;
+				meshMaterial = table->mMatName;
+			}
+			else 
+			{
+				DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup \"%s\" in geometry \"%s\".") % submesh.mMaterial % mid.mMeshOrController));
+				if( !mid.mMaterials.empty() )
+					meshMaterial = mid.mMaterials.begin()->second.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

+ 241 - 0
ThirdParty/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

+ 2801 - 0
ThirdParty/Assimp/code/ColladaParser.cpp

@@ -0,0 +1,2801 @@
+/*
+---------------------------------------------------------------------------
+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)
+{
+	// check if we're already on the closing tag and return right away
+	if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp( mReader->getNodeName(), pName) == 0)
+		return;
+
+	// if not, read some more
+	if( !mReader->read())
+		ThrowException( 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));
+
+	// but this has the be the closing tag, or we're lost
+	if( mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp( mReader->getNodeName(), pName) != 0)
+		ThrowException( 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

+ 341 - 0
ThirdParty/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

+ 504 - 0
ThirdParty/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");
+}

+ 149 - 0
ThirdParty/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

+ 318 - 0
ThirdParty/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

+ 169 - 0
ThirdParty/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

+ 609 - 0
ThirdParty/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
+

+ 184 - 0
ThirdParty/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

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

+ 133 - 0
ThirdParty/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
+

+ 167 - 0
ThirdParty/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

+ 83 - 0
ThirdParty/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

+ 419 - 0
ThirdParty/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

+ 64 - 0
ThirdParty/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

+ 122 - 0
ThirdParty/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

+ 64 - 0
ThirdParty/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

+ 244 - 0
ThirdParty/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

+ 216 - 0
ThirdParty/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");
+	}
+}

+ 110 - 0
ThirdParty/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

+ 288 - 0
ThirdParty/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"); 
+	}
+}

+ 140 - 0
ThirdParty/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

+ 419 - 0
ThirdParty/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

+ 111 - 0
ThirdParty/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

+ 176 - 0
ThirdParty/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;
+}

+ 96 - 0
ThirdParty/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

+ 138 - 0
ThirdParty/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;
+}

+ 88 - 0
ThirdParty/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

+ 228 - 0
ThirdParty/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;
+}

+ 118 - 0
ThirdParty/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
+

+ 112 - 0
ThirdParty/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

+ 134 - 0
ThirdParty/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

+ 497 - 0
ThirdParty/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

+ 159 - 0
ThirdParty/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
+

+ 150 - 0
ThirdParty/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

+ 108 - 0
ThirdParty/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

+ 102 - 0
ThirdParty/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

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