Browse Source

Added Assimp 3.1.1

markcwm 8 years ago
parent
commit
441a39b55d
100 changed files with 41345 additions and 0 deletions
  1. 116 0
      assimplib.mod/assimp/CHANGES
  2. 150 0
      assimplib.mod/assimp/CREDITS
  3. 84 0
      assimplib.mod/assimp/LICENSE
  4. 120 0
      assimplib.mod/assimp/Readme.md
  5. 861 0
      assimplib.mod/assimp/code/3DSConverter.cpp
  6. 584 0
      assimplib.mod/assimp/code/3DSHelper.h
  7. 1402 0
      assimplib.mod/assimp/code/3DSLoader.cpp
  8. 280 0
      assimplib.mod/assimp/code/3DSLoader.h
  9. 866 0
      assimplib.mod/assimp/code/ACLoader.cpp
  10. 267 0
      assimplib.mod/assimp/code/ACLoader.h
  11. 1316 0
      assimplib.mod/assimp/code/ASELoader.cpp
  12. 205 0
      assimplib.mod/assimp/code/ASELoader.h
  13. 2153 0
      assimplib.mod/assimp/code/ASEParser.cpp
  14. 669 0
      assimplib.mod/assimp/code/ASEParser.h
  15. 609 0
      assimplib.mod/assimp/code/Assimp.cpp
  16. 127 0
      assimplib.mod/assimp/code/AssimpCExport.cpp
  17. 135 0
      assimplib.mod/assimp/code/AssimpPCH.cpp
  18. 162 0
      assimplib.mod/assimp/code/AssimpPCH.h
  19. 687 0
      assimplib.mod/assimp/code/B3DImporter.cpp
  20. 126 0
      assimplib.mod/assimp/code/B3DImporter.h
  21. 534 0
      assimplib.mod/assimp/code/BVHLoader.cpp
  22. 169 0
      assimplib.mod/assimp/code/BVHLoader.h
  23. 598 0
      assimplib.mod/assimp/code/BaseImporter.cpp
  24. 368 0
      assimplib.mod/assimp/code/BaseImporter.h
  25. 105 0
      assimplib.mod/assimp/code/BaseProcess.cpp
  26. 294 0
      assimplib.mod/assimp/code/BaseProcess.h
  27. 145 0
      assimplib.mod/assimp/code/Bitmap.cpp
  28. 139 0
      assimplib.mod/assimp/code/Bitmap.h
  29. 176 0
      assimplib.mod/assimp/code/BlenderBMesh.cpp
  30. 93 0
      assimplib.mod/assimp/code/BlenderBMesh.h
  31. 372 0
      assimplib.mod/assimp/code/BlenderDNA.cpp
  32. 804 0
      assimplib.mod/assimp/code/BlenderDNA.h
  33. 732 0
      assimplib.mod/assimp/code/BlenderDNA.inl
  34. 183 0
      assimplib.mod/assimp/code/BlenderIntermediate.h
  35. 1149 0
      assimplib.mod/assimp/code/BlenderLoader.cpp
  36. 223 0
      assimplib.mod/assimp/code/BlenderLoader.h
  37. 324 0
      assimplib.mod/assimp/code/BlenderModifier.cpp
  38. 155 0
      assimplib.mod/assimp/code/BlenderModifier.h
  39. 716 0
      assimplib.mod/assimp/code/BlenderScene.cpp
  40. 757 0
      assimplib.mod/assimp/code/BlenderScene.h
  41. 253 0
      assimplib.mod/assimp/code/BlenderSceneGen.h
  42. 520 0
      assimplib.mod/assimp/code/BlenderTessellator.cpp
  43. 208 0
      assimplib.mod/assimp/code/BlenderTessellator.h
  44. 326 0
      assimplib.mod/assimp/code/BlobIOSystem.h
  45. 23 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/LICENSE_1_0.txt
  46. 99 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/foreach.hpp
  47. 81 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/format.hpp
  48. 26 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/lexical_cast.hpp
  49. 57 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/make_shared.hpp
  50. 37 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/math/common_factor_rt.hpp
  51. 36 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/noncopyable.hpp
  52. 45 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/pointer_cast.hpp
  53. 79 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/scoped_array.hpp
  54. 79 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/scoped_ptr.hpp
  55. 228 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/shared_array.hpp
  56. 257 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/shared_ptr.hpp
  57. 20 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/static_assert.hpp
  58. 72 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/timer.hpp
  59. 283 0
      assimplib.mod/assimp/code/BoostWorkaround/boost/tuple/tuple.hpp
  60. 285 0
      assimplib.mod/assimp/code/ByteSwap.h
  61. 158 0
      assimplib.mod/assimp/code/CInterfaceIOWrapper.h
  62. 742 0
      assimplib.mod/assimp/code/CMakeLists.txt
  63. 1291 0
      assimplib.mod/assimp/code/COBLoader.cpp
  64. 170 0
      assimplib.mod/assimp/code/COBLoader.h
  65. 271 0
      assimplib.mod/assimp/code/COBScene.h
  66. 299 0
      assimplib.mod/assimp/code/CSMLoader.cpp
  67. 88 0
      assimplib.mod/assimp/code/CSMLoader.h
  68. 318 0
      assimplib.mod/assimp/code/CalcTangentsProcess.cpp
  69. 115 0
      assimplib.mod/assimp/code/CalcTangentsProcess.h
  70. 821 0
      assimplib.mod/assimp/code/ColladaExporter.cpp
  71. 176 0
      assimplib.mod/assimp/code/ColladaExporter.h
  72. 604 0
      assimplib.mod/assimp/code/ColladaHelper.h
  73. 1568 0
      assimplib.mod/assimp/code/ColladaLoader.cpp
  74. 242 0
      assimplib.mod/assimp/code/ColladaLoader.h
  75. 2829 0
      assimplib.mod/assimp/code/ColladaParser.cpp
  76. 341 0
      assimplib.mod/assimp/code/ColladaParser.h
  77. 504 0
      assimplib.mod/assimp/code/ComputeUVMappingProcess.cpp
  78. 144 0
      assimplib.mod/assimp/code/ComputeUVMappingProcess.h
  79. 318 0
      assimplib.mod/assimp/code/ConvertToLHProcess.cpp
  80. 166 0
      assimplib.mod/assimp/code/ConvertToLHProcess.h
  81. 230 0
      assimplib.mod/assimp/code/DXFHelper.h
  82. 913 0
      assimplib.mod/assimp/code/DXFLoader.cpp
  83. 152 0
      assimplib.mod/assimp/code/DXFLoader.h
  84. 464 0
      assimplib.mod/assimp/code/DeboneProcess.cpp
  85. 132 0
      assimplib.mod/assimp/code/DeboneProcess.h
  86. 146 0
      assimplib.mod/assimp/code/DefaultIOStream.cpp
  87. 133 0
      assimplib.mod/assimp/code/DefaultIOStream.h
  88. 167 0
      assimplib.mod/assimp/code/DefaultIOSystem.cpp
  89. 83 0
      assimplib.mod/assimp/code/DefaultIOSystem.h
  90. 423 0
      assimplib.mod/assimp/code/DefaultLogger.cpp
  91. 64 0
      assimplib.mod/assimp/code/DefaultProgressHandler.h
  92. 124 0
      assimplib.mod/assimp/code/Exceptional.h
  93. 468 0
      assimplib.mod/assimp/code/Exporter.cpp
  94. 313 0
      assimplib.mod/assimp/code/FBXAnimation.cpp
  95. 398 0
      assimplib.mod/assimp/code/FBXBinaryTokenizer.cpp
  96. 66 0
      assimplib.mod/assimp/code/FBXCompileConfig.h
  97. 2982 0
      assimplib.mod/assimp/code/FBXConverter.cpp
  98. 63 0
      assimplib.mod/assimp/code/FBXConverter.h
  99. 169 0
      assimplib.mod/assimp/code/FBXDeformer.cpp
  100. 721 0
      assimplib.mod/assimp/code/FBXDocument.cpp

+ 116 - 0
assimplib.mod/assimp/CHANGES

@@ -0,0 +1,116 @@
+----------------------------------------------------------------------
+CHANGELOG
+----------------------------------------------------------------------
+
+
+3.0 (2012-07-07)
+
+FEATURES:
+   - new export interface similar to the import API. 
+   - Supported export formats: Collada, OBJ, PLY and STL
+   - added new import formats: XGL/ZGL, M3 (experimental)
+   - new postprocessing steps: Debone
+   - vastly improved IFC (Industry Foundation Classes) support
+   - introduced API to query importer meta information (such as supported
+       format versions, full name, maintainer info).
+   - reworked Ogre XML import
+   - C-API now supports per-import properties
+
+FIXES/HOUSEKEEPING:
+
+   - hundreds of bugfixes in all parts of the library
+   - unified naming and cleanup of public headers
+   - improved CMake build system
+   - templatized math library
+   - reduce dependency on boost.thread, only remaining spot
+     is synchronization for the C logging API 
+
+API COMPATIBILITY:
+   - renamed headers, export interface, C API properties and meta data
+     prevent compatibility with code written for 2.0, but in 
+     most cases these can be easily resolved
+   - Note: 3.0 is not binary compatible with 2.0
+
+
+
+
+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.

+ 150 - 0
assimplib.mod/assimp/CREDITS

@@ -0,0 +1,150 @@
+===============================================================
+Open Asset Import Library (Assimp)
+Developers and Contributors
+===============================================================
+
+The following is a non-exhaustive list of all constributors over the years.
+If you think your name should be listed here, drop us a line and we'll add you.
+
+- 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, VC2010 fixes and CMake fixes.
+
+- 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
+
+- Carsten Fuchs
+Contributed a fix for the Normalize method in aiQuaternion.
+
+- dbburgess
+Contributes a Android-specific build issue: log the hardware architecture for ARM.
+
+- alfiereinre7
+Contributes a obj-fileparser fix: missing tokens in the obj-token list.
+
+- Roman Kharitonov
+Contributes a fix for the configure script environment.
+
+- Ed Diana
+Contributed AssimpDelphi (/port/AssimpDelphi).
+
+- rdb 
+Contributes a bundle of fixes and improvments for the bsp-importer.
+
+- Mick P
+For contributing the De-bone postprocessing step and filing various bug reports.
+
+- Rosen Diankov
+Contributed patches to build assimp debian packages using cmake.
+
+- Mark Page
+Contributed a patch to fix the VertexTriangleAdjacency postprocessing step.
+
+- IOhannes
+Contributed the Debian build fixes ( architecture macro ).
+
+- gellule
+Several LWO and LWS fixes (pivoting). 
+
+- Marcel Metz
+GCC/Linux fixes for the SimpleOpenGL sample.
+
+- Brian Miller
+Bugfix for a compiler fix for iOS on arm.
+
+- S�verin Lemaignan
+Rewrite of PyAssimp, distutils and Python3 support
+
+- albert-wang
+Bugfixes for the collada parser
+
+- Ya ping Jin
+Bugfixes for uv-tanget calculation.
+
+- Jonne Nauha
+Ogre Binary format support

+ 84 - 0
assimplib.mod/assimp/LICENSE

@@ -0,0 +1,84 @@
+Open Asset Import Library (assimp)
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+******************************************************************************
+
+AN EXCEPTION applies to all files in the ./test/models-nonbsd folder.
+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.
+
+
+******************************************************************************
+
+Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+http://code.google.com/p/poly2tri/
+
+All rights reserved.
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+* Neither the name of Poly2Tri nor the names of its contributors may be
+  used to endorse or promote products derived from this software without specific
+  prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+
+
+

+ 120 - 0
assimplib.mod/assimp/Readme.md

@@ -0,0 +1,120 @@
+Open Asset Import Library (assimp) 
+========
+
+Open Asset Import Library is a Open Source library designed to load various __3d file formats and convert them into a shared, in-memory format__. It supports more than __30 file formats__ for import and a growing selection of file formats for export. Additionally, assimp features various __post processing tools__ to refine the imported data: _normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials_ and many more.
+
+This is the development trunk of assimp containing the latest features and bugfixes. For productive use though, we recommend one of the stable releases available from [assimp.sf.net](http://assimp.sf.net) or from *nix package repositories. According to [Travis-CI] (https://travis-ci.org/), the current build status of the trunk is [![Build Status](https://travis-ci.org/assimp/assimp.png)](https://travis-ci.org/assimp/assimp)
+
+#### Supported file formats ####
+
+The library provides importers for a lot of file formats, including:
+
+- 3DS
+- BLEND (Blender 3D)
+- DAE/Collada
+- FBX
+- IFC-STEP 
+- ASE
+- DXF
+- HMP
+- MD2
+- MD3 
+- MD5
+- MDC
+- MDL
+- NFF
+- PLY
+- STL
+- X 
+- OBJ 
+- SMD
+- LWO 
+- LXO 
+- LWS  
+- TER 
+- AC3D 
+- MS3D 
+- COB
+- Q3BSP
+- XGL
+- CSM
+- BVH
+- B3D
+- NDO
+- Ogre Binary
+- Ogre XML
+- Q3D
+ 
+Additionally, the following formats are also supported, but not part of the core library as they depend on proprietary libraries.
+
+- C4D (https://github.com/acgessler/assimp-cinema4d)
+
+Exporters include:
+
+- DAE (Collada)
+- STL
+- OBJ
+- PLY
+- JSON (for WebGl, via https://github.com/acgessler/assimp2json)
+	
+See [the full list here](http://assimp.sourceforge.net/main_features_formats.html).
+
+
+
+#### Repository structure ####
+
+
+Open Asset Import Library is implemented in C++ (but provides both a C and a 
+C++ish interface). The directory structure is:
+
+	/bin		Folder for binaries, only used on Windows
+	/code		Source code
+	/contrib	Third-party libraries
+	/doc		Documentation (doxysource and pre-compiled docs)
+	/include	Public header C and C++ header files
+	/lib		Static library location for Windows
+	/obj		Object file location for Windows
+	/scripts 	Scripts used to generate the loading code for some formats
+	/port		Ports to other languages and scripts to maintain those.
+	/test		Unit- and regression tests, test suite of models
+	/tools		Tools (viewer, command line `assimp`)
+	/samples	A small number of samples to illustrate possible 
+                        use cases for Assimp
+	/workspaces	Build enviroments for vc,xcode,... (deprecated,
+			CMake has superseeded all legacy build options!)
+
+
+
+### Building ###
+
+
+Take a look into the `INSTALL` file. Our build system is CMake, if you already used CMake before there is a good chance you know what to do.
+
+
+### Where to get help ###
+
+
+For more information, visit [our website](http://assimp.sourceforge.net/). Or check out the `./doc`- folder, which contains the official documentation in HTML format.
+(CHMs for Windows are included in some release packages and should be located right here in the root folder).
+
+If the documentation doesn't solve your problems, 
+[try our forums at SF.net](http://sourceforge.net/p/assimp/discussion/817654) or ask on
+[StackOverflow](http://stackoverflow.com/questions/tagged/assimp?sort=newest).
+
+For development discussions, there is also a mailing list, _assimp-discussions_
+  [(subscribe here)]( https://lists.sourceforge.net/lists/listinfo/assimp-discussions) 
+
+### Contributing ###
+
+Contributions to assimp are highly appreciated. The easiest way to get involved is to submit 
+a pull request with your changes against the main repository's `master` branch.
+
+
+### License ###
+
+Our license is based on the modified, __3-clause BSD__-License, which is very liberal. 
+
+An _informal_ summary is: do whatever you want, but include Assimp's license text with your product - 
+and don't sue us if our code doesn't work. Note that, unlike LGPLed code, you may link statically to Assimp.
+For the legal details, see the `LICENSE` file. 
+

+ 861 - 0
assimplib.mod/assimp/code/3DSConverter.cpp

@@ -0,0 +1,861 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file 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(aiMaterial& 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,
+	aiMaterial& 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];
+
+			if (mesh->mColors[1] == NULL)
+			{
+				// 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;
+					}
+				}
+
+				mesh->mColors[1] = (aiColor4D*)1;
+			}
+			else
+				mesh->mColors[1] = (aiColor4D*)1;
+
+			// Setup the mesh index
+			pcOut->mMeshes[i] = iIndex;
+		}
+	}
+
+	// Setup the name of the node
+	// First instance keeps its name otherwise something might break, all others will be postfixed with their instance number
+	if (pcIn->mInstanceNumber > 1)
+	{
+		char tmp[12];
+		ASSIMP_itoa10(tmp, pcIn->mInstanceNumber);
+		std::string tempStr = pcIn->mName + "_inst_";
+		tempStr += tmp;
+		pcOut->mName.Set(tempStr);
+	}
+	else
+		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 messed 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 and second vertex color set to store some temporary values so we need to cleanup here
+	for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a)
+	{
+		pcOut->mMeshes[a]->mColors[0] = NULL;
+		pcOut->mMeshes[a]->mColors[1] = NULL;
+	}
+
+	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)
+	{
+		aiMaterial* pcNew = new aiMaterial();
+		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

+ 584 - 0
assimplib.mod/assimp/code/3DSHelper.h

@@ -0,0 +1,584 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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/assimp/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/assimp/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)
+		,	mInstanceCount		(1)
+
+	{
+		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;
+
+	//! InstanceNumber of the node
+	int32_t mInstanceNumber;
+
+	//! 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;
+
+	//instance count, will be kept only for the first node
+	int32_t mInstanceCount;
+
+	//! 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

+ 1402 - 0
assimplib.mod/assimp/code/3DSLoader.cpp

@@ -0,0 +1,1402 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  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;
+
+static const aiImporterDesc desc = {
+	"Discreet 3DS Importer",
+	"",
+	"",
+	"Limited animation support",
+	aiImporterFlags_SupportBinaryFlavour,
+	0,
+	0,
+	0,
+	0,
+	"3ds prj" 
+};
+
+		
+// ------------------------------------------------------------------------------------------------
+// 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);	             \
+    if(chunkSize <= 0)                                                   \
+        continue;                                                        \
+	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;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader registry entry
+const aiImporterDesc* Discreet3DSImporter::GetInfo () const
+{
+	return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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);
+		int instanceNumber = 1;
+
+		if ( pcNode)
+		{
+			// if the source is not a CHUNK_TRACKINFO block it wont be an object instance
+			if (parent != Discreet3DS::CHUNK_TRACKINFO)
+			{
+				mCurrentNode = pcNode;
+				break;
+			}
+			pcNode->mInstanceCount++;
+			instanceNumber = pcNode->mInstanceCount;
+		}
+		pcNode = new D3DS::Node();
+		pcNode->mName = name;
+		pcNode->mInstanceNumber = instanceNumber;
+
+		// 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);
+	};
+	(void)bGamma;
+}
+
+#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER

+ 280 - 0
assimplib.mod/assimp/code/3DSLoader.h

@@ -0,0 +1,280 @@
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  3DSLoader.h
+ *  @brief 3DS File format loader
+ */
+#ifndef AI_3DSIMPORTER_H_INC
+#define AI_3DSIMPORTER_H_INC
+
+#include "BaseImporter.h"
+#include "../include/assimp/types.h"
+
+#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
+
+struct aiNode;
+#include "3DSHelper.h"
+
+namespace Assimp	{
+
+
+using namespace D3DS;
+
+// ---------------------------------------------------------------------------------
+/** Importer class for 3D Studio r3 and r4 3DS files
+ */
+class Discreet3DSImporter : public BaseImporter
+{
+public:
+
+	Discreet3DSImporter();
+	~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:
+
+	// -------------------------------------------------------------------
+	/** Return importer meta information.
+	 * See #BaseImporter::GetInfo for the details
+	 */
+	const aiImporterDesc* GetInfo () const;
+
+	// -------------------------------------------------------------------
+	/** 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,
+		aiMaterial& 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;
+};
+
+#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC

+ 866 - 0
assimplib.mod/assimp/code/ACLoader.cpp

@@ -0,0 +1,866 @@
+
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file 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;
+
+static const aiImporterDesc desc = {
+	"AC3D Importer",
+	"",
+	"",
+	"",
+	aiImporterFlags_SupportTextFlavour,
+	0,
+	0,
+	0,
+	0,
+	"ac acc ac3d"
+};
+
+// ------------------------------------------------------------------------------------------------
+// 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_atoreal_move<float>(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;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader meta information
+const aiImporterDesc* AC3DImporter::GetInfo () const
+{
+	return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 = strtoul10(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 = strtoul10(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 = strtoul10(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 = strtoul10(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 = strtoul_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 = strtoul10(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 = strtoul10(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 = strtoul10(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,
+	aiMaterial& 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<aiMaterial*>& 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 aiMaterial());
+			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 aiMaterial());
+				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<aiMaterial*> 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

+ 267 - 0
assimplib.mod/assimp/code/ACLoader.h

@@ -0,0 +1,267 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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/assimp/types.h"
+
+namespace Assimp	{
+
+// ---------------------------------------------------------------------------
+/** AC3D (*.ac) importer class
+*/
+class AC3DImporter : public BaseImporter
+{
+public:
+	AC3DImporter();
+	~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:
+
+	// -------------------------------------------------------------------
+	/** Return importer meta information.
+	 * See #BaseImporter::GetInfo for the details */
+	const aiImporterDesc* GetInfo () const;
+
+	// -------------------------------------------------------------------
+	/** 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<aiMaterial*>& 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,
+		aiMaterial& 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

+ 1316 - 0
assimplib.mod/assimp/code/ASELoader.cpp

@@ -0,0 +1,1316 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  ASELoader.cpp
+ *  @brief Implementation of the ASE importer class
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
+
+// internal headers
+#include "ASELoader.h"
+#include "StringComparison.h"
+#include "SkeletonMeshBuilder.h"
+#include "TargetAnimation.h"
+
+// utilities
+#include "fast_atof.h"
+
+using namespace Assimp;
+using namespace Assimp::ASE;
+
+static const aiImporterDesc desc = {
+	"ASE Importer",
+	"",
+	"",
+	"Similar to 3DS but text-encoded",
+	aiImporterFlags_SupportTextFlavour,
+	0,
+	0,
+	0,
+	0,
+	"ase ask" 
+};
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ASEImporter::ASEImporter()
+: noSkeletonMesh()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader meta information
+const aiImporterDesc* ASEImporter::GetInfo () const
+{
+	return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration options
+void ASEImporter::SetupProperties(const Importer* pImp)
+{
+	configRecomputeNormals = (pImp->GetPropertyInteger(
+		AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false);
+
+	noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+		if (!noSkeletonMesh) {
+			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(aiMaterial& 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 aiMaterial();
+
+	// 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<std::vector<aiVertexWeight> > avBonesOut( 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;
+				}
+			}
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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

+ 205 - 0
assimplib.mod/assimp/code/ASELoader.h

@@ -0,0 +1,205 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  ASELoader.h
+ *  @brief Definition of the .ASE importer class.
+ */
+#ifndef AI_ASELOADER_H_INCLUDED
+#define AI_ASELOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+#include "../include/assimp/types.h"
+
+struct aiNode;
+#include "ASEParser.h"
+
+namespace Assimp {
+
+
+// --------------------------------------------------------------------------------
+/** Importer class for the 3DS ASE ASCII format.
+ *
+ */
+class ASEImporter : public BaseImporter	{
+public:
+	ASEImporter();
+	~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:
+
+	// -------------------------------------------------------------------
+	/** Return importer meta information.
+	 * See #BaseImporter::GetInfo for the details
+	 */
+	const aiImporterDesc* GetInfo () const;
+
+
+	// -------------------------------------------------------------------
+	/** 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 aiMaterial 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;
+	bool noSkeletonMesh;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC

+ 2153 - 0
assimplib.mod/assimp/code/ASEParser.cpp

@@ -0,0 +1,2153 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  ASEParser.cpp
+ *  @brief Implementation of the ASE parser class 
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
+
+// 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" && temp != "Normal Bump")
+				{
+					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 = strtoul10(filePtr,&filePtr);
+					if (iIndex >= iNumBones)
+					{
+						LogWarning("Bone index is out of bounds");
+						continue;
+					}
+					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 = strtoul10(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 = strtoul10(filePtr,&filePtr);
+
+					// then parse the vertex weight
+					if (!SkipSpaces(&filePtr))break;
+					filePtr = fast_atoreal_move<float>(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 = UINT_MAX;
+
+	// 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 != UINT_MAX && 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 = strtoul10(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] = strtoul10(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 << strtoul10(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 = strtoul10(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_atoreal_move<float>(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 = strtoul10(filePtr,&filePtr);
+}
+
+#endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER

+ 669 - 0
assimplib.mod/assimp/code/ASEParser.h

@@ -0,0 +1,669 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file 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/assimp/types.h"
+#include "../include/assimp/mesh.h"
+#include "../include/assimp/anim.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;
+
+	//! aiMaterial object
+	aiMaterial* 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

+ 609 - 0
assimplib.mod/assimp/code/Assimp.cpp

@@ -0,0 +1,609 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file  Assimp.cpp
+ *  @brief Implementation of the Plain-C API
+ */
+
+#include "AssimpPCH.h"
+#include "../include/assimp/cimport.h"
+
+#include "GenericProperty.h"
+#include "CInterfaceIOWrapper.h"
+#include "Importer.h"
+
+// ------------------------------------------------------------------------------------------------
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+#	include <boost/thread/thread.hpp>
+#	include <boost/thread/mutex.hpp>
+#endif
+// ------------------------------------------------------------------------------------------------
+using namespace Assimp;
+
+namespace Assimp
+{
+	// underlying structure for aiPropertyStore
+	typedef BatchLoader::PropertyMap PropertyMap;
+
+	/** 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 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;
+}
+
+
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+/** Global mutex to manage the access to the logstream map */
+static boost::mutex gLogStreamMutex;
+#endif
+
+
+// ------------------------------------------------------------------------------------------------
+// Custom LogStream implementation for the C-API
+class LogToCallbackRedirector : public LogStream
+{
+public:
+	LogToCallbackRedirector(const aiLogStream& s) 
+		: stream (s)	{
+			ai_assert(NULL != s.callback);
+	}
+
+	~LogToCallbackRedirector()	{
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+		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. "
+		"The C-API does not accept scenes produced by the C++ API and vice versa");
+
+	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)
+{
+	return aiImportFileExWithProperties(pFile, pFlags, pFS, NULL);
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* aiImportFileExWithProperties( const char* pFile, unsigned int pFlags, 
+	aiFileIO* pFS,
+	const aiPropertyStore* props)
+{
+	ai_assert(NULL != pFile);
+
+	const aiScene* scene = NULL;
+	ASSIMP_BEGIN_EXCEPTION_REGION();
+
+	// create an Importer for this file
+	Assimp::Importer* imp = new Assimp::Importer();
+
+	// copy properties
+	if(props) {
+		const PropertyMap* pp = reinterpret_cast<const PropertyMap*>(props);
+		ImporterPimpl* pimpl = imp->Pimpl();
+		pimpl->mIntProperties = pp->ints;
+		pimpl->mFloatProperties = pp->floats;
+		pimpl->mStringProperties = pp->strings;
+		pimpl->mMatrixProperties = pp->matrices;
+	}
+	// 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, store the importer in the scene and keep it alive
+	if( scene)	{
+		ScenePrivateData* priv = const_cast<ScenePrivateData*>( ScenePriv(scene) );
+		priv->mOrigImporter = 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)
+{
+	return aiImportFileFromMemoryWithProperties(pBuffer, pLength, pFlags, pHint, NULL);
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiScene* aiImportFileFromMemoryWithProperties( 
+	const char* pBuffer,
+	unsigned int pLength,
+	unsigned int pFlags,
+	const char* pHint,
+	const aiPropertyStore* props)
+{
+	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();
+
+	// copy properties
+	if(props) {
+		const PropertyMap* pp = reinterpret_cast<const PropertyMap*>(props);
+		ImporterPimpl* pimpl = imp->Pimpl();
+		pimpl->mIntProperties = pp->ints;
+		pimpl->mFloatProperties = pp->floats;
+		pimpl->mStringProperties = pp->strings;
+		pimpl->mMatrixProperties = pp->matrices;
+	}
+
+	// and have it read the file from the memory buffer
+	scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint);
+
+	// if succeeded, store the importer in the scene and keep it alive
+	if( scene)	{
+		 ScenePrivateData* priv = const_cast<ScenePrivateData*>( ScenePriv(scene) );
+		 priv->mOrigImporter = 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();
+	
+	// find the importer associated with this data
+	const ScenePrivateData* priv = ScenePriv(pScene);
+	if( !priv || !priv->mOrigImporter)	{
+		delete pScene;
+	}
+	else {
+		// deleting the Importer also deletes the scene
+		// Note: the reason that this is not written as 'delete priv->mOrigImporter'
+		// is a suspected bug in gcc 4.4+ (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52339)
+		Importer* importer = priv->mOrigImporter;
+		delete importer;
+	}
+	
+	ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API const aiScene* aiApplyPostProcessing(const aiScene* pScene,
+	unsigned int pFlags)
+{
+	const aiScene* sc = NULL;
+	
+
+	ASSIMP_BEGIN_EXCEPTION_REGION();
+
+	// find the importer associated with this data
+	const ScenePrivateData* priv = ScenePriv(pScene);
+	if( !priv || !priv->mOrigImporter)	{
+		ReportSceneNotFoundError();
+		return NULL;
+	}
+
+	sc = priv->mOrigImporter->ApplyPostProcessing(pFlags);
+
+	if (!sc) {
+		aiReleaseImport(pScene);
+		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();
+
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+	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();
+
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+	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();
+#ifndef ASSIMP_BUILD_SINGLETHREADED
+	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();
+
+	// 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();
+
+	// 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();
+
+	// find the importer associated with this data
+	const ScenePrivateData* priv = ScenePriv(pIn);
+	if( !priv || !priv->mOrigImporter)	{
+		ReportSceneNotFoundError();
+		return;
+	}
+
+	return priv->mOrigImporter->GetMemoryRequirements(*in);
+	ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiPropertyStore* aiCreatePropertyStore(void)
+{
+	return reinterpret_cast<aiPropertyStore*>( new PropertyMap() );
+}
+
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiReleasePropertyStore(aiPropertyStore* p)
+{
+	delete reinterpret_cast<PropertyMap*>(p);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyInteger
+ASSIMP_API void aiSetImportPropertyInteger(aiPropertyStore* p, const char* szName, int value)
+{
+	ASSIMP_BEGIN_EXCEPTION_REGION();
+	PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
+	SetGenericProperty<int>(pp->ints,szName,value,NULL);
+	ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyFloat
+ASSIMP_API void aiSetImportPropertyFloat(aiPropertyStore* p, const char* szName, float value)
+{
+	ASSIMP_BEGIN_EXCEPTION_REGION();
+	PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
+	SetGenericProperty<float>(pp->floats,szName,value,NULL);
+	ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyString
+ASSIMP_API void aiSetImportPropertyString(aiPropertyStore* p, const char* szName,
+	const C_STRUCT aiString* st)
+{
+	if (!st) {
+		return;
+	}
+	ASSIMP_BEGIN_EXCEPTION_REGION();
+	PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
+	SetGenericProperty<std::string>(pp->strings,szName,std::string(st->C_Str()),NULL);
+	ASSIMP_END_EXCEPTION_REGION(void);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Importer::SetPropertyMatrix
+ASSIMP_API void aiSetImportPropertyMatrix(aiPropertyStore* p, const char* szName,
+	const C_STRUCT aiMatrix4x4* mat)
+{
+	if (!mat) {
+		return;
+	}
+	ASSIMP_BEGIN_EXCEPTION_REGION();
+	PropertyMap* pp = reinterpret_cast<PropertyMap*>(p);
+	SetGenericProperty<aiMatrix4x4>(pp->matrices,szName,*mat,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(aiVector3D* vec, 
+	const aiMatrix3x3* mat)
+{
+	ai_assert(NULL != mat && NULL != vec);
+	*vec *= (*mat);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiTransformVecByMatrix4(aiVector3D* vec, 
+	const aiMatrix4x4* mat)
+{
+	ai_assert(NULL != mat && NULL != vec);
+	*vec *= (*mat);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix multiplication
+ASSIMP_API void aiMultiplyMatrix4(
+	aiMatrix4x4* dst, 
+	const aiMatrix4x4* src)
+{
+	ai_assert(NULL != dst && NULL != src);
+	*dst = (*dst) * (*src);
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiMultiplyMatrix3(
+	aiMatrix3x3* dst, 
+	const aiMatrix3x3* src)
+{
+	ai_assert(NULL != dst && NULL != src);
+	*dst = (*dst) * (*src);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Matrix identity
+ASSIMP_API void aiIdentityMatrix3(
+	aiMatrix3x3* mat)
+{
+	ai_assert(NULL != mat);
+	*mat = aiMatrix3x3();
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiIdentityMatrix4(
+	aiMatrix4x4* mat)
+{
+	ai_assert(NULL != mat);
+	*mat = aiMatrix4x4();
+}
+
+

+ 127 - 0
assimplib.mod/assimp/code/AssimpCExport.cpp

@@ -0,0 +1,127 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file AssimpCExport.cpp
+Assimp C export interface. See Exporter.cpp for some notes.
+*/
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#include "CInterfaceIOWrapper.h" 
+#include "SceneCombiner.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API size_t aiGetExportFormatCount(void)
+{
+	return Exporter().GetExportFormatCount();
+}
+
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex)
+{
+	return Exporter().GetExportFormatDescription(pIndex);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiCopyScene(const aiScene* pIn, aiScene** pOut)
+{
+	if (!pOut || !pIn) {
+		return;
+	}
+
+	SceneCombiner::CopyScene(pOut,pIn,true);
+	ScenePriv(*pOut)->mIsCopy = true;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn)
+{
+	// note: aiReleaseImport() is also able to delete scene copies, but in addition
+	// it also handles scenes with import metadata.
+	delete pIn;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiReturn aiExportScene( const aiScene* pScene, const char* pFormatId, const char* pFileName, unsigned int pPreprocessing )
+{
+	return ::aiExportSceneEx(pScene,pFormatId,pFileName,NULL,pPreprocessing);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiReturn aiExportSceneEx( const aiScene* pScene, const char* pFormatId, const char* pFileName, aiFileIO* pIO, unsigned int pPreprocessing )
+{
+	Exporter exp;
+
+	if (pIO) {
+		exp.SetIOHandler(new CIOSystemWrapper(pIO));
+	}
+	return exp.Export(pScene,pFormatId,pFileName,pPreprocessing);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing  )
+{
+	Exporter exp;
+	if (!exp.ExportToBlob(pScene,pFormatId,pPreprocessing)) {
+		return NULL;
+	}
+	const aiExportDataBlob* blob = exp.GetOrphanedBlob();
+	ai_assert(blob);
+
+	return blob;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API C_STRUCT void aiReleaseExportBlob( const aiExportDataBlob* pData )
+{
+	delete pData;
+}
+
+#endif // !ASSIMP_BUILD_NO_EXPORT

+ 135 - 0
assimplib.mod/assimp/code/AssimpPCH.cpp

@@ -0,0 +1,135 @@
+
+// Actually just a dummy, used by the compiler to build the precompiled header.
+
+#include "AssimpPCH.h"
+#include "./../include/assimp/version.h"
+
+static const unsigned int MajorVersion = 3;
+static const unsigned int MinorVersion = 1;
+
+// --------------------------------------------------------------------------------
+// Legal information string - dont't remove this.
+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 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 MinorVersion;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Get Assimp major version
+ASSIMP_API unsigned int aiGetVersionMajor ()	{
+    return MajorVersion;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 GitVersion;
+}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiScene::aiScene()
+	: mFlags(0)
+	, mRootNode(NULL)
+	, mNumMeshes(0)
+	, mMeshes(NULL)
+	, mNumMaterials(0)
+	, mMaterials(NULL)
+	, mNumAnimations(0)
+	, mAnimations(NULL)
+	, mNumTextures(0)
+	, mTextures(NULL)
+	, mNumLights(0)
+	, mLights(NULL)
+	, mNumCameras(0)
+	, mCameras(NULL)
+	, mPrivate(new Assimp::ScenePrivateData())
+	{
+	}
+
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API aiScene::~aiScene()
+{
+	// delete all sub-objects recursively
+	delete mRootNode;
+
+	// To make sure we won't crash if the data is invalid it's
+	// much better to check whether both mNumXXX and mXXX are
+	// valid instead of relying on just one of them.
+	if (mNumMeshes && mMeshes) 
+		for( unsigned int a = 0; a < mNumMeshes; a++)
+			delete mMeshes[a];
+	delete [] mMeshes;
+
+	if (mNumMaterials && mMaterials) 
+		for( unsigned int a = 0; a < mNumMaterials; a++)
+			delete mMaterials[a];
+	delete [] mMaterials;
+
+	if (mNumAnimations && mAnimations) 
+		for( unsigned int a = 0; a < mNumAnimations; a++)
+			delete mAnimations[a];
+	delete [] mAnimations;
+
+	if (mNumTextures && mTextures) 
+		for( unsigned int a = 0; a < mNumTextures; a++)
+			delete mTextures[a];
+	delete [] mTextures;
+
+	if (mNumLights && mLights) 
+		for( unsigned int a = 0; a < mNumLights; a++)
+			delete mLights[a];
+	delete [] mLights;
+
+	if (mNumCameras && mCameras) 
+		for( unsigned int a = 0; a < mNumCameras; a++)
+			delete mCameras[a];
+	delete [] mCameras;
+
+	delete static_cast<Assimp::ScenePrivateData*>( mPrivate );
+}
+

+ 162 - 0
assimplib.mod/assimp/code/AssimpPCH.h

@@ -0,0 +1,162 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file 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 defs.h. It is important that the user compiles
+ * using exactly the same settings in defs.h. Settings in AssimpPCH.h may differ,
+ * they won't affect the public API.
+ */
+#include "../include/assimp/defs.h"
+
+// Include our stdint.h replacement header for MSVC, take the global header for gcc/mingw
+#if defined( _MSC_VER) && (_MSC_VER < 1600)
+#	include "../include/assimp/Compiler/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 ASSIMP_BUILD_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>
+#include <limits.h>
+#include <memory>
+
+// 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/assimp/DefaultLogger.hpp"
+#include "../include/assimp/IOStream.hpp"
+#include "../include/assimp/IOSystem.hpp"
+#include "../include/assimp/scene.h"
+#include "../include/assimp/importerdesc.h"
+#include "../include/assimp/postprocess.h"
+#include "../include/assimp/Importer.hpp"
+#include "../include/assimp/Exporter.hpp"
+
+// Internal utility headers
+#include "BaseImporter.h"
+#include "StringComparison.h"
+#include "StreamReader.h"
+#include "qnan.h"
+#include "ScenePrivate.h" 
+
+
+// We need those constants, workaround for any platforms where nobody defined them yet
+#if (!defined SIZE_MAX)
+#	define SIZE_MAX (~((size_t)0))
+#endif
+
+#if (!defined UINT_MAX)
+#	define UINT_MAX (~((unsigned int)0))
+#endif
+
+
+#endif // !! ASSIMP_PCH_INCLUDED

+ 687 - 0
assimplib.mod/assimp/code/B3DImporter.cpp

@@ -0,0 +1,687 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  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;
+
+static const aiImporterDesc desc = {
+	"BlitzBasic 3D Importer",
+	"",
+	"",
+	"http://www.blitzbasic.com/",
+	aiImporterFlags_SupportBinaryFlavour,
+	0,
+	0,
+	0,
+	0,
+	"b3d" 
+};
+
+// (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');
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader meta information
+const aiImporterDesc* B3DImporter::GetInfo () const
+{
+	return &desc;
+}
+
+#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();
+
+		aiMaterial *mat=new aiMaterial;
+		_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 aiMaterial );
+	}
+	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
assimplib.mod/assimp/code/B3DImporter.h

@@ -0,0 +1,126 @@
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Definition of the .b3d importer class. */
+
+#ifndef AI_B3DIMPORTER_H_INC
+#define AI_B3DIMPORTER_H_INC
+
+#include "../include/assimp/types.h"
+#include "../include/assimp/mesh.h"
+#include "../include/assimp/material.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 const aiImporterDesc* GetInfo () const;
+	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

+ 534 - 0
assimplib.mod/assimp/code/BVHLoader.cpp

@@ -0,0 +1,534 @@
+/** Implementation of the BVH loader */
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
+
+#include "BVHLoader.h"
+#include "fast_atof.h"
+#include "SkeletonMeshBuilder.h"
+
+using namespace Assimp;
+
+static const aiImporterDesc desc = {
+	"BVH Importer (MoCap)",
+	"",
+	"",
+	"",
+	aiImporterFlags_SupportTextFlavour,
+	0,
+	0,
+	0,
+	0,
+	"bvh"
+};
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+BVHLoader::BVHLoader()
+: noSkeletonMesh()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BVHLoader::SetupProperties(const Importer* pImp)
+{
+	noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader meta information
+const aiImporterDesc* BVHLoader::GetInfo () const
+{
+	return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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);
+
+	if (!noSkeletonMesh) {
+		// 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_atoreal_move<float>( 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

+ 169 - 0
assimplib.mod/assimp/code/BVHLoader.h

@@ -0,0 +1,169 @@
+/** Defines the BHV motion capturing loader class */
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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
+{
+
+	/** 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) { }
+	};
+
+public:
+
+	BVHLoader();
+	~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;
+
+	void SetupProperties(const Importer* pImp);
+	const aiImporterDesc* GetInfo () const;
+
+protected:
+
+
+	/** 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;
+
+	bool noSkeletonMesh;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_BVHLOADER_H_INC

+ 598 - 0
assimplib.mod/assimp/code/BaseImporter.cpp

@@ -0,0 +1,598 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  BaseImporter.cpp
+ *  @brief Implementation of BaseImporter 
+ */
+
+#include "AssimpPCH.h"
+#include "BaseImporter.h"
+#include "FileSystemFilter.h"
+
+#include "Importer.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
+}
+
+// ------------------------------------------------------------------------------------------------
+void BaseImporter::GetExtensionList(std::set<std::string>& extensions)
+{
+	const aiImporterDesc* desc = GetInfo();
+	ai_assert(desc != NULL);
+
+	const char* ext = desc->mFileExtensions;
+	ai_assert(ext != NULL);
+
+	const char* last = ext;
+	do {
+		if (!*ext || *ext == ' ') {
+			extensions.insert(std::string(last,ext-last));
+			ai_assert(ext-last > 0);
+			last = ext;
+			while(*last == ' ') {
+				++last;
+			}
+		}
+	}
+	while(*ext++);
+}
+
+// ------------------------------------------------------------------------------------------------
+/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler,
+	const std::string&	pFile,
+	const char**		tokens, 
+	unsigned int		numTokens,
+	unsigned int		searchBytes /* = 200 */,
+	bool				tokensSol /* false */)
+{
+	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]);
+
+
+			const char* r = strstr(buffer,tokens[i]);
+			if (!r) 
+				continue;
+			// We got a match, either we don't care where it is, or it happens to
+			// be in the beginning of the file / line
+			if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') {
+				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;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert to UTF8 data to ISO-8859-1
+void BaseImporter::ConvertUTF8toISO8859_1(std::string& data)
+{
+	unsigned int size = data.size();
+	unsigned int i = 0, j = 0;
+
+	while(i < size) {
+		if((unsigned char) data[i] < 0x80) {
+			data[j] = data[i];
+		} else if(i < size - 1) {
+			if((unsigned char) data[i] == 0xC2) {
+				data[j] = data[++i];
+			} else if((unsigned char) data[i] == 0xC3) {
+				data[j] = ((unsigned char) data[++i] + 0x40);
+			} else {
+				std::stringstream stream;
+
+				stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1.";
+
+				DefaultLogger::get()->error(stream.str());
+
+				data[j++] = data[i++];
+				data[j] = data[i];
+			}
+		} else {
+			DefaultLogger::get()->error("UTF8 code but only one character remaining");
+
+			data[j] = data[i];
+		}
+
+		i++; j++;
+	}
+
+	data.resize(j);
+}
+
+// ------------------------------------------------------------------------------------------------
+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 ASSIMP_BUILD_DEBUG
+		pp |= aiProcess_ValidateDataStructure;
+#endif
+		// setup config properties if necessary
+		ImporterPimpl* pimpl = data->pImporter->Pimpl();
+		pimpl->mFloatProperties  = (*it).map.floats;
+		pimpl->mIntProperties    = (*it).map.ints;
+		pimpl->mStringProperties = (*it).map.strings;
+		pimpl->mMatrixProperties = (*it).map.matrices;
+
+		if (!DefaultLogger::isNullLogger())
+		{
+			DefaultLogger::get()->info("%%% BEGIN EXTERNAL FILE %%%");
+			DefaultLogger::get()->info("File: " + (*it).file);
+		}
+		data->pImporter->ReadFile((*it).file,pp);
+		(*it).scene = data->pImporter->GetOrphanedScene();
+		(*it).loaded = true;
+
+		DefaultLogger::get()->info("%%% END EXTERNAL FILE %%%");
+	}
+}
+
+
+
+

+ 368 - 0
assimplib.mod/assimp/code/BaseImporter.h

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

+ 105 - 0
assimplib.mod/assimp/code/BaseProcess.cpp

@@ -0,0 +1,105 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of BaseProcess */
+
+#include "AssimpPCH.h"
+#include "BaseImporter.h"
+#include "BaseProcess.h"
+
+#include "Importer.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
+}
+
+// ------------------------------------------------------------------------------------------------
+bool BaseProcess::RequireVerboseFormat() const
+{
+	return true;
+}
+

+ 294 - 0
assimplib.mod/assimp/code/BaseProcess.h

@@ -0,0 +1,294 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Base class of all import post processing steps */
+#ifndef INCLUDED_AI_BASEPROCESS_H
+#define INCLUDED_AI_BASEPROCESS_H
+
+#include <map>
+
+#include "../include/assimp/types.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 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_WINONLY 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;
+
+	// -------------------------------------------------------------------
+	/** Check whether this step expects its input vertex data to be 
+	 *  in verbose format. */
+	virtual bool RequireVerboseFormat() const;
+
+	// -------------------------------------------------------------------
+	/** 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

+ 145 - 0
assimplib.mod/assimp/code/Bitmap.cpp

@@ -0,0 +1,145 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Bitmap.cpp
+ *  @brief Defines bitmap format helper for textures
+ *
+ * Used for file formats which embed their textures into the model file.
+ */
+
+#include "AssimpPCH.h"
+
+#include "Bitmap.h"
+
+namespace Assimp {
+
+	void Bitmap::Save(aiTexture* texture, IOStream* file) {
+		if(file != NULL) {
+			Header header;
+			DIB dib;
+
+			dib.size = DIB::dib_size;
+			dib.width = texture->mWidth;
+			dib.height = texture->mHeight;
+			dib.planes = 1;
+			dib.bits_per_pixel = 8 * mBytesPerPixel;
+			dib.compression = 0;
+			dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height;
+			dib.x_resolution = 0;
+			dib.y_resolution = 0;
+			dib.nb_colors = 0;
+			dib.nb_important_colors = 0;
+
+			header.type = 0x4D42; // 'BM'
+			header.offset = Header::header_size + DIB::dib_size;
+			header.size = header.offset + dib.image_size;
+			header.reserved1 = 0;
+			header.reserved2 = 0;
+
+			WriteHeader(header, file);
+			WriteDIB(dib, file);
+			WriteData(texture, file);
+		}
+	}
+
+	template<typename T>
+	inline std::size_t Copy(uint8_t* data, T& field) {
+		std::memcpy(data, &AI_BE(field), sizeof(field)); return sizeof(field);
+	}
+
+	void Bitmap::WriteHeader(Header& header, IOStream* file) {
+		uint8_t data[Header::header_size];
+
+		std::size_t offset = 0;
+
+		offset += Copy(&data[offset], header.type);
+		offset += Copy(&data[offset], header.size);
+		offset += Copy(&data[offset], header.reserved1);
+		offset += Copy(&data[offset], header.reserved2);
+		offset += Copy(&data[offset], header.offset);
+
+		file->Write(data, Header::header_size, 1);
+	}
+
+	void Bitmap::WriteDIB(DIB& dib, IOStream* file) {
+		uint8_t data[DIB::dib_size];
+
+		std::size_t offset = 0;
+
+		offset += Copy(&data[offset], dib.size);
+		offset += Copy(&data[offset], dib.width);
+		offset += Copy(&data[offset], dib.height);
+		offset += Copy(&data[offset], dib.planes);
+		offset += Copy(&data[offset], dib.bits_per_pixel);
+		offset += Copy(&data[offset], dib.compression);
+		offset += Copy(&data[offset], dib.image_size);
+		offset += Copy(&data[offset], dib.x_resolution);
+		offset += Copy(&data[offset], dib.y_resolution);
+		offset += Copy(&data[offset], dib.nb_colors);
+		offset += Copy(&data[offset], dib.nb_important_colors);
+
+		file->Write(data, DIB::dib_size, 1);
+	}
+
+	void Bitmap::WriteData(aiTexture* texture, IOStream* file) {
+		static const std::size_t padding_offset = 4;
+		static const uint8_t padding_data[padding_offset] = {0x0, 0x0, 0x0, 0x0};
+
+		unsigned int padding = (padding_offset - ((mBytesPerPixel * texture->mWidth) % padding_offset)) % padding_offset;
+		uint8_t pixel[mBytesPerPixel];
+
+		for(std::size_t i = 0; i < texture->mHeight; ++i) {
+			for(std::size_t j = 0; j < texture->mWidth; ++j) {
+				const aiTexel& texel = texture->pcData[(texture->mHeight - i - 1) * texture->mWidth + j]; // Bitmap files are stored in bottom-up format
+
+				pixel[0] = texel.r;
+				pixel[1] = texel.g;
+				pixel[2] = texel.b;
+				pixel[3] = texel.a;
+
+				file->Write(pixel, mBytesPerPixel, 1);
+			}
+
+			file->Write(padding_data, padding, 1);
+		}
+	}
+
+}

+ 139 - 0
assimplib.mod/assimp/code/Bitmap.h

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

+ 176 - 0
assimplib.mod/assimp/code/BlenderBMesh.cpp

@@ -0,0 +1,176 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2013, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  BlenderBMesh.cpp
+ *  @brief Conversion of Blender's new BMesh stuff
+ */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include "BlenderBMesh.h"
+#include "BlenderTessellator.h"
+
+namespace Assimp
+{
+	template< > const std::string LogFunctions< BlenderBMeshConverter >::log_prefix = "BLEND_BMESH: ";
+}
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+using namespace Assimp::Formatter;
+
+// ------------------------------------------------------------------------------------------------
+BlenderBMeshConverter::BlenderBMeshConverter( const Mesh* mesh ):
+	BMesh( mesh ),
+	triMesh( NULL )
+{
+	AssertValidMesh( );
+}
+
+// ------------------------------------------------------------------------------------------------
+BlenderBMeshConverter::~BlenderBMeshConverter( )
+{
+	DestroyTriMesh( );
+}
+
+// ------------------------------------------------------------------------------------------------
+bool BlenderBMeshConverter::ContainsBMesh( ) const
+{
+	// TODO - Should probably do some additional verification here
+	return BMesh->totpoly && BMesh->totloop && BMesh->totvert;
+}
+
+// ------------------------------------------------------------------------------------------------
+const Mesh* BlenderBMeshConverter::TriangulateBMesh( )
+{
+	AssertValidMesh( );
+	AssertValidSizes( );
+	PrepareTriMesh( );
+
+	for ( int i = 0; i < BMesh->totpoly; ++i )
+	{
+		const MPoly& poly = BMesh->mpoly[ i ];
+		ConvertPolyToFaces( poly );
+	}
+
+	return triMesh;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::AssertValidMesh( )
+{
+	if ( !ContainsBMesh( ) )
+	{
+		ThrowException( "BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first" );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::AssertValidSizes( )
+{
+	if ( BMesh->totpoly != static_cast<int>( BMesh->mpoly.size( ) ) )
+	{
+		ThrowException( "BMesh poly array has incorrect size" );
+	}
+	if ( BMesh->totloop != static_cast<int>( BMesh->mloop.size( ) ) )
+	{
+		ThrowException( "BMesh loop array has incorrect size" );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::PrepareTriMesh( )
+{
+	if ( triMesh )
+	{
+		DestroyTriMesh( );
+	}
+
+	triMesh = new Mesh( *BMesh );
+	triMesh->totface = 0;
+	triMesh->mface.clear( );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::DestroyTriMesh( )
+{
+	delete triMesh;
+	triMesh = NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::ConvertPolyToFaces( const MPoly& poly )
+{
+	const MLoop* polyLoop = &BMesh->mloop[ poly.loopstart ];
+	if ( poly.totloop == 3 || poly.totloop == 4 )
+	{
+		AddFace( polyLoop[ 0 ].v, polyLoop[ 1 ].v, polyLoop[ 2 ].v, poly.totloop == 4 ? polyLoop[ 3 ].v : 0 );
+	}
+	else if ( poly.totloop > 4 )
+	{
+#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
+		BlenderTessellatorGL tessGL( *this );
+		tessGL.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
+#elif ASSIMP_BLEND_WITH_POLY_2_TRI
+		BlenderTessellatorP2T tessP2T( *this );
+		tessP2T.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
+#endif
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::AddFace( int v1, int v2, int v3, int v4 )
+{
+	MFace face;
+	face.v1 = v1;
+	face.v2 = v2;
+	face.v3 = v3;
+	face.v4 = v4;
+	// TODO - Work out how materials work
+	face.mat_nr = 0;
+	triMesh->mface.push_back( face );
+	triMesh->totface = triMesh->mface.size( );
+}
+
+#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 93 - 0
assimplib.mod/assimp/code/BlenderBMesh.h

@@ -0,0 +1,93 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2013, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  BlenderBMesh.h
+ *  @brief Conversion of Blender's new BMesh stuff
+ */
+#ifndef INCLUDED_AI_BLEND_BMESH_H
+#define INCLUDED_AI_BLEND_BMESH_H
+
+#include "LogAux.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;
+	}
+
+	// BlenderScene.h
+	namespace Blender
+	{
+		struct Mesh;
+		struct MPoly;
+		struct MLoop;
+	}
+
+	class BlenderBMeshConverter: public LogFunctions< BlenderBMeshConverter >
+	{
+	public:
+		BlenderBMeshConverter( const Blender::Mesh* mesh );
+		~BlenderBMeshConverter( );
+
+		bool ContainsBMesh( ) const;
+
+		const Blender::Mesh* TriangulateBMesh( );
+
+	private:
+		void AssertValidMesh( );
+		void AssertValidSizes( );
+		void PrepareTriMesh( );
+		void DestroyTriMesh( );
+		void ConvertPolyToFaces( const Blender::MPoly& poly );
+		void AddFace( int v1, int v2, int v3, int v4 = 0 );
+
+		const Blender::Mesh* BMesh;
+		Blender::Mesh* triMesh;
+
+		friend class BlenderTessellatorGL;
+		friend class BlenderTessellatorP2T;
+	};
+
+} // end of namespace Assimp
+
+#endif // INCLUDED_AI_BLEND_BMESH_H

+ 372 - 0
assimplib.mod/assimp/code/BlenderDNA.cpp

@@ -0,0 +1,372 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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] = strtoul10(&out[pos]);
+
+	pos = out.find('[',pos);
+	if (pos++ == std::string::npos) {
+		return;
+	}
+	array_sizes[1] = strtoul10(&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

+ 804 - 0
assimplib.mod/assimp/code/BlenderDNA.h

@@ -0,0 +1,804 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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 ASSIMP_BUILD_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)
+	// The return value indicates whether the data was already cached.
+	template <int error_policy, template <typename> class TOUT, typename T>
+	bool ReadFieldPtr(TOUT<T>& out, const char* name, 
+		const FileDatabase& db,
+		bool non_recursive = false) const;
+
+	// --------------------------------------------------------
+	// field parsing for static arrays of pointer or dynamic
+	// array types (boost::shared_ptr[] or boost::shared_array[])
+	// The return value indicates whether the data was already cached.
+	template <int error_policy, template <typename> class TOUT, typename T, size_t N>
+	bool ReadFieldPtr(TOUT<T> (&out)[N], const char* name, 
+		const FileDatabase& db) const;
+
+	// --------------------------------------------------------
+	// field parsing for `normal` values
+	// The return value indicates whether the data was already cached.
+	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>
+	bool ResolvePointer(TOUT<T>& out, const Pointer & ptrval, 
+		const FileDatabase& db, const Field& f,
+		bool non_recursive = false) const;
+
+	// --------------------------------------------------------
+	template <template <typename> class TOUT, typename T>
+	bool ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval, 
+		const FileDatabase& db, const Field& f, bool) const;
+
+	// --------------------------------------------------------
+	bool ResolvePointer( boost::shared_ptr< FileOffset >& out, const Pointer & ptrval, 
+		const FileDatabase& db, const Field& f, bool) 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 bool Structure :: ResolvePointer<boost::shared_ptr,ElemBase>(boost::shared_ptr<ElemBase>& out, 
+	const Pointer & ptrval, 
+	const FileDatabase& db, 
+	const Field& f,
+	bool
+	) 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>&, const Pointer&) {}
+	template <typename T> void set(const Structure&, const vector<T>&, const Pointer&) {}
+};
+
+#ifdef _MSC_VER
+#	pragma warning(disable:4355)
+#endif
+
+// -------------------------------------------------------------------------------
+/** Memory representation of a full BLEND file and all its dependencies. The
+ *  output aiScene is constructed from an instance of this data structure. */
+// -------------------------------------------------------------------------------
+class FileDatabase 
+{
+	template <template <typename> class TOUT> friend class ObjectCache;
+
+public:
+
+
+	FileDatabase()
+		: _cacheArrays(*this)
+		, _cache(*this)
+		, next_cache_idx()
+	{} 
+
+public:
+
+	// publicly accessible fields
+	bool i64bit;
+	bool little;
+
+	DNA dna;
+	boost::shared_ptr< StreamReaderAny > reader;
+	vector< FileBlockHead > entries;
+
+public:
+
+	Statistics& stats() const {
+		return _stats;
+	}
+
+	// For all our templates to work on both shared_ptr's and vector's
+	// using the same code, a dummy cache for arrays is provided. Actually,
+	// arrays of objects are never cached because we can't easily 
+	// ensure their proper destruction.
+	template <typename T>
+	ObjectCache<boost::shared_ptr>& cache(boost::shared_ptr<T>& /*in*/) const {
+		return _cache;
+	}
+
+	template <typename T>
+	ObjectCache<vector>& cache(vector<T>& /*in*/) const {
+		return _cacheArrays;
+	}
+
+private:
+
+	
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+	mutable Statistics _stats;
+#endif
+
+	mutable ObjectCache<vector> _cacheArrays;
+	mutable ObjectCache<boost::shared_ptr> _cache;
+
+	mutable size_t next_cache_idx;
+};
+
+#ifdef _MSC_VER
+#	pragma warning(default:4355)
+#endif
+
+// -------------------------------------------------------------------------------
+/** Factory to extract a #DNA from the DNA1 file block in a BLEND file. */
+// -------------------------------------------------------------------------------
+class DNAParser 
+{
+
+public:
+
+	/** Bind the parser to a empty DNA and an input stream */
+	DNAParser(FileDatabase& db)
+		: db(db)
+	{}
+
+public:
+
+	// --------------------------------------------------------
+	/** Locate the DNA in the file and parse it. The input
+	 *  stream is expected to point to the beginning of the DN1
+	 *  chunk at the time this method is called and is
+	 *  undefined afterwards.
+	 *  @throw DeadlyImportError if the DNA cannot be read.
+	 *  @note The position of the stream pointer is undefined
+	 *    afterwards.*/
+	void Parse ();
+
+public:
+
+	/** Obtain a reference to the extracted DNA information */
+	const Blender::DNA& GetDNA() const {
+		return db.dna;
+	}
+
+private:
+
+	FileDatabase& db;
+};
+
+	} // end Blend
+} // end Assimp
+
+#include "BlenderDNA.inl"
+
+#endif

+ 732 - 0
assimplib.mod/assimp/code/BlenderDNA.inl

@@ -0,0 +1,732 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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>
+bool Structure :: ReadFieldPtr(TOUT<T>& out, const char* name, const FileDatabase& db,
+	bool non_recursive /*= false*/) 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 false;
+	}
+
+	// resolve the pointer and load the corresponding structure
+	const bool res = ResolvePointer(out,ptrval,db,*f, non_recursive);
+
+	if(!non_recursive) {
+		// and recover the previous stream position
+		db.reader->SetCurrentPos(old);
+	}
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+	++db.stats().fields_read;
+#endif
+
+	return res;
+}
+
+//--------------------------------------------------------------------------------
+template <int error_policy, template <typename> class TOUT, typename T, size_t N>
+bool 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 false;
+	}
+
+	bool res = true;
+	for(size_t i = 0; i < N; ++i) {
+		// resolve the pointer and load the corresponding structure
+		res = ResolvePointer(out[i],ptrval[i],db,*f) && res;
+	}
+
+	// and recover the previous stream position
+	db.reader->SetCurrentPos(old);
+
+#ifndef ASSIMP_BUILD_BLENDER_NO_STATS
+	++db.stats().fields_read;
+#endif
+	return res;
+}
+
+//--------------------------------------------------------------------------------
+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>
+bool Structure :: ResolvePointer(TOUT<T>& out, const Pointer & ptrval, const FileDatabase& db, 
+	const Field& f, 
+	bool non_recursive /*= false*/) const 
+{
+	out.reset(); // ensure null pointers work
+	if (!ptrval.val) { 
+		return false;
+	}
+	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 true;
+	}
+
+	// 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); 
+
+	// if the non_recursive flag is set, we don't do anything but leave
+	// the cursor at the correct position to resolve the object.
+	if (!non_recursive) {
+		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
+	return false;
+}
+
+
+//--------------------------------------------------------------------------------
+inline bool Structure :: ResolvePointer( boost::shared_ptr< FileOffset >& out, const Pointer & ptrval, 
+	const FileDatabase& db, 
+	const Field&,
+	bool) const
+{
+	// Currently used exclusively by PackedFile::data to represent
+	// a simple offset into the mapped BLEND file. 
+	out.reset();
+	if (!ptrval.val) { 
+		return false;
+	}
+
+	// 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) );
+	return false;
+}
+
+//--------------------------------------------------------------------------------
+template <template <typename> class TOUT, typename T>
+bool Structure :: ResolvePointer(vector< TOUT<T> >& out, const Pointer & ptrval, 
+	const FileDatabase& db, 
+	const Field& f,
+	bool) 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 false;
+	}
+
+	// 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) ));
+
+	bool res = false;
+	// 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
+		res = ResolvePointer(out[i],val,db,f) && res; 
+	}
+
+	db.reader->SetCurrentPos(pold);
+	return res;
+}
+
+//--------------------------------------------------------------------------------
+template <> bool Structure :: ResolvePointer<boost::shared_ptr,ElemBase>(boost::shared_ptr<ElemBase>& out, 
+	const Pointer & ptrval, 
+	const FileDatabase& db, 
+	const Field&,
+	bool
+) 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 false;
+	}
+
+	// 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 true;
+	}
+
+	// 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 false;
+	}
+
+	// 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
+	return false;
+}
+
+//--------------------------------------------------------------------------------
+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
assimplib.mod/assimp/code/BlenderIntermediate.h

@@ -0,0 +1,183 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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

+ 1149 - 0
assimplib.mod/assimp/code/BlenderLoader.cpp

@@ -0,0 +1,1149 @@
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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 "BlenderBMesh.h"
+
+#include "StreamReader.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
+
+namespace Assimp {
+	template<> const std::string LogFunctions<BlenderImporter>::log_prefix = "BLEND: ";
+}
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+using namespace Assimp::Formatter;
+
+static const aiImporterDesc blenderDesc = {
+	"Blender 3D Importer \nhttp://www.blender3d.org",
+	"",
+	"",
+	"No animation support yet",
+	aiImporterFlags_SupportBinaryFlavour,
+	0,
+	0,
+	2,
+	50,
+	"blend"
+};
+
+
+// ------------------------------------------------------------------------------------------------
+// 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 aiImporterDesc* 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) {
+
+		// Fix: using the DNA index is more reliable to locate scenes
+		//if (bl.id == "SC") {
+
+		if (bl.dna_index == (*it).second) {
+			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, aiMatrix4x4());	
+		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(aiMaterial* out, const Material* mat, const MTex* tex, const Image* img, ConversionData& conv_data)
+{
+	(void)mat; (void)tex; (void)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' : ::tolower( s[1] );
+		tex->achFormatHint[1] = s+2>e ? '\0' : ::tolower( s[2] );
+		tex->achFormatHint[2] = s+3>e ? '\0' : ::tolower( 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 );
+	}
+
+	aiTextureType texture_type = aiTextureType_UNKNOWN;
+	MTex::MapType map_type = tex->mapto;
+
+	if (map_type & MTex::MapType_COL)
+	    texture_type = aiTextureType_DIFFUSE;
+	else if (map_type & MTex::MapType_NORM) {
+	    if (tex->tex->imaflag & Tex::ImageFlags_NORMALMAP) {
+	        texture_type = aiTextureType_NORMALS;
+	    }
+	    else {
+	        texture_type = aiTextureType_HEIGHT;
+	    }
+	    out->AddProperty(&tex->norfac,1,AI_MATKEY_BUMPSCALING);
+	}
+	else if (map_type & MTex::MapType_COLSPEC)
+		texture_type = aiTextureType_SPECULAR;
+	else if (map_type & MTex::MapType_COLMIR)
+		texture_type = aiTextureType_REFLECTION;
+	//else if (map_type & MTex::MapType_REF)
+	else if (map_type & MTex::MapType_SPEC)
+		texture_type = aiTextureType_SHININESS;
+	else if (map_type & MTex::MapType_EMIT)
+		texture_type = aiTextureType_EMISSIVE;
+	//else if (map_type & MTex::MapType_ALPHA)
+	//else if (map_type & MTex::MapType_HAR)
+	//else if (map_type & MTex::MapType_RAYMIRR)
+	//else if (map_type & MTex::MapType_TRANSLU)
+	else if (map_type & MTex::MapType_AMB)
+		texture_type = aiTextureType_AMBIENT;
+	else if (map_type & MTex::MapType_DISPLACE)
+		texture_type = aiTextureType_DISPLACEMENT;
+	//else if (map_type & MTex::MapType_WARP)
+
+	out->AddProperty(&name,AI_MATKEY_TEXTURE(texture_type,
+	    conv_data.next_texture[texture_type]++));
+
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderImporter::AddSentinelTexture(aiMaterial* out, const Material* mat, const MTex* tex, ConversionData& conv_data)
+{
+	(void)mat; (void)tex; (void)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(aiMaterial* 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 they'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->ambr = p->ambg = p->ambb = 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 ;
+		}
+	
+		aiMaterial* mout = new aiMaterial();
+		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.
+			// So we omit this member to express this intent.
+			mout->AddProperty(&col,1,AI_MATKEY_COLOR_DIFFUSE);
+
+			if (mat->emit) {
+				aiColor3D emit_col(mat->emit * mat->r, mat->emit * mat->g, mat->emit * mat->b) ;
+				mout->AddProperty(&emit_col, 1, AI_MATKEY_COLOR_EMISSIVE) ;
+			}
+		}
+
+		col = aiColor3D(mat->specr,mat->specg,mat->specb);
+		mout->AddProperty(&col,1,AI_MATKEY_COLOR_SPECULAR);
+
+		// is hardness/shininess set?
+		if( mat->har ) {
+			const float har = mat->har;
+			mout->AddProperty(&har,1,AI_MATKEY_SHININESS);
+		}
+
+		col = aiColor3D(mat->ambr,mat->ambg,mat->ambb);
+		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
+	) 
+{
+	BlenderBMeshConverter BMeshConverter( mesh );
+	if ( BMeshConverter.ContainsBMesh( ) )
+	{
+		mesh = BMeshConverter.TriangulateBMesh( );
+	}
+
+	typedef std::pair<const int,size_t> MyPair;
+	if ((!mesh->totface && !mesh->totloop) || !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");
+	}
+
+	if (static_cast<size_t> ( mesh->totloop ) > mesh->mloop.size()) {
+		ThrowException("Number of vertices is larger than the corresponding array");
+	}
+
+	// collect per-submesh numbers
+	std::map<int,size_t> per_mat;
+	std::map<int,size_t> per_mat_verts;
+	for (int i = 0; i < mesh->totface; ++i) {
+
+		const MFace& mf = mesh->mface[i];
+		per_mat[ mf.mat_nr ]++;
+		per_mat_verts[ mf.mat_nr ] += mf.v4?4:3;
+	}
+
+	for (int i = 0; i < mesh->totpoly; ++i) {
+		const MPoly& mp = mesh->mpoly[i];
+		per_mat[ mp.mat_nr ]++;
+		per_mat_verts[ mp.mat_nr ] += mp.totloop;
+	}
+
+	// ... 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[per_mat_verts[it.first]];
+		out->mNormals  = new aiVector3D[per_mat_verts[it.first]];
+
+		//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;
+
+		//	}
+		//	}
+		//	}
+	}
+
+	for (int i = 0; i < mesh->totpoly; ++i) {
+		
+		const MPoly& mf = mesh->mpoly[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.totloop ];
+		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.
+		for (int j = 0;j < mf.totloop; ++j)
+		{
+			const MLoop& loop = mesh->mloop[mf.loopstart + j];
+
+			if (loop.v >= mesh->totvert) {
+				ThrowException("Vertex index out of range");
+			}
+
+			const MVert& v = mesh->mvert[loop.v];
+			
+			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[j] = out->mNumVertices++;
+			
+			++vo;
+			++vn;
+			
+		}
+		if (mf.totloop == 3)
+		{
+			out->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+		}
+		else
+		{
+			out->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+		}
+	}
+	
+	// collect texture coordinates, they're stored in a separate per-face buffer
+	if (mesh->mtface || mesh->mloopuv) {
+		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];
+			}
+		}
+		
+		for (int i = 0; i < mesh->totpoly; ++i) {
+			const MPoly& v = mesh->mpoly[i];
+			aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ];
+			const aiFace& f = out->mFaces[out->mNumFaces++];
+			
+			aiVector3D* vo = &out->mTextureCoords[0][out->mNumVertices];
+			for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) {
+				const MLoopUV& uv = mesh->mloopuv[v.loopstart + j];
+				vo->x = uv.uv[0];
+				vo->y = uv.uv[1];
+			}
+			
+		}
+	}
+
+	// collect texture coordinates, old-style (marked as deprecated in current blender sources)
+	if (mesh->tface) {
+		if (mesh->totface > static_cast<int> ( mesh->tface.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 || mesh->mloopcol) {
+		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);
+		}
+		
+		for (int i = 0; i < mesh->totpoly; ++i) {
+			const MPoly& v = mesh->mpoly[i];
+			aiMesh* const out = temp[ mat_num_to_mesh_idx[ v.mat_nr ] ];
+			const aiFace& f = out->mFaces[out->mNumFaces++];
+			
+			aiColor4D* vo = &out->mColors[0][out->mNumVertices];
+			for (unsigned int j = 0; j < f.mNumIndices; ++j,++vo,++out->mNumVertices) {
+				const MLoopCol& col = mesh->mloopcol[v.loopstart + j];
+				vo->r = col.r;
+				vo->g = col.g;
+				vo->b = col.b;
+				vo->a = col.a;
+			}
+			
+		}
+
+	}
+
+	return;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiCamera* BlenderImporter::ConvertCamera(const Scene& /*in*/, const Object* obj, const Camera* camera, ConversionData& /*conv_data*/)
+{
+	ScopeGuard<aiCamera> out(new aiCamera());
+	out->mName = obj->id.name+2;
+	out->mPosition = aiVector3D(0.f, 0.f, 0.f);
+	out->mUp = aiVector3D(0.f, 1.f, 0.f);
+	out->mLookAt = aiVector3D(0.f, 0.f, -1.f);
+	return out.dismiss();
+}
+
+// ------------------------------------------------------------------------------------------------
+aiLight* BlenderImporter::ConvertLight(const Scene& in, const Object* obj, const Lamp* lamp, ConversionData& conv_data)
+{
+	ScopeGuard<aiLight> out(new aiLight());
+	out->mName = obj->id.name+2;
+
+	switch (lamp->type)
+	{
+	    case Lamp::Type_Local:
+	        out->mType = aiLightSource_POINT;
+	        break;
+	    case Lamp::Type_Sun:
+	        out->mType = aiLightSource_DIRECTIONAL;
+
+	        // blender orients directional lights as facing toward -z
+	        out->mDirection = aiVector3D(0.f, 0.f, -1.f);
+	        break;
+	    default:
+	        break;
+	}
+
+	out->mColorAmbient = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
+	out->mColorSpecular = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
+	out->mColorDiffuse = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
+	return out.dismiss();
+}
+
+// ------------------------------------------------------------------------------------------------
+aiNode* BlenderImporter::ConvertNode(const Scene& in, const Object* obj, ConversionData& conv_data, const aiMatrix4x4& parentTransform)
+{
+	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 == 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->obmat[x][y];
+		}
+	}
+
+	aiMatrix4x4 m = parentTransform;
+	m = m.Inverse();
+
+	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,node->mTransformation * parentTransform);
+			(*nd++)->mParent = node;
+		}
+	}
+
+	// apply modifiers
+	modifier_cache->ApplyModifiers(*node,conv_data,in,*obj);
+
+	return node.dismiss();
+}
+
+
+#endif

+ 223 - 0
assimplib.mod/assimp/code/BlenderLoader.h

@@ -0,0 +1,223 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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"
+#include "LogAux.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;
+	}
+
+
+
+// -------------------------------------------------------------------------------------------
+/** 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, public LogFunctions<BlenderImporter>
+{
+public:
+	BlenderImporter();
+	~BlenderImporter();
+
+
+public:
+
+	// --------------------
+	bool CanRead( const std::string& pFile, 
+		IOSystem* pIOHandler,
+		bool checkSig
+	) const;
+
+protected:
+
+	// --------------------
+	const aiImporterDesc* 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,
+		const aiMatrix4x4& parentTransform
+	); 
+
+	// --------------------
+	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(
+		aiMaterial* out, 
+		const Blender::Material* mat, 
+		const Blender::MTex* tex,
+		Blender::ConversionData& conv_data
+	);
+
+	// --------------------
+	void ResolveImage(
+		aiMaterial* out, 
+		const Blender::Material* mat, 
+		const Blender::MTex* tex, 
+		const Blender::Image* img,
+		Blender::ConversionData& conv_data
+	);
+
+	void AddSentinelTexture(
+		aiMaterial* 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
+	);
+
+
+private:
+
+	Blender::BlenderModifierShowcase* modifier_cache;
+
+}; // !class BlenderImporter
+
+} // end of namespace Assimp
+#endif // AI_UNREALIMPORTER_H_INC

+ 324 - 0
assimplib.mod/assimp/code/BlenderModifier.cpp

@@ -0,0 +1,324 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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"
+
+#include <functional>
+
+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);
+
+	conv_data.meshes->reserve(conv_data.meshes->size() + out.mNumMeshes);
+
+	// 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;
+			}
+		}
+
+		// Only reverse the winding order if an odd number of axes were mirrored.
+		if (xs * ys * zs < 0) {
+			for( unsigned int i = 0; i < mesh->mNumFaces; i++) {
+				aiFace& face = mesh->mFaces[i];
+				for( unsigned int fi = 0; fi < face.mNumIndices / 2; ++fi)
+					std::swap( face.mIndices[fi], face.mIndices[face.mNumIndices - 1 - fi]);
+			}
+		}
+
+		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
assimplib.mod/assimp/code/BlenderModifier.h

@@ -0,0 +1,155 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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

+ 716 - 0
assimplib.mod/assimp/code/BlenderScene.cpp

@@ -0,0 +1,716 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING 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 ASSIMP_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);
+    {
+        boost::shared_ptr<Object> parent;
+        ReadFieldPtr<ErrorPolicy_Warn>(parent,"*parent",db);
+        dest.parent = parent.get();
+    }
+    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>((short&)dest.mapto,"mapto",db);
+    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);
+    ReadField<ErrorPolicy_Igno>(dest.norfac,"norfac",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_Warn>(dest.subdivType,"subdivType",db);
+    ReadField<ErrorPolicy_Fail>(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
+{ 
+	// note: as per https://github.com/assimp/assimp/issues/128,
+	// reading the Object linked list recursively is prone to stack overflow.
+	// This structure converter is therefore an hand-written exception that
+	// does it iteratively.
+
+	const int initial_pos = db.reader->GetCurrentPos();
+
+	std::pair<Base*, int> todo = std::make_pair(&dest, initial_pos);
+	for ( ;; ) {
+	
+		Base& cur_dest = *todo.first;
+		db.reader->SetCurrentPos(todo.second);
+
+		// we know that this is a double-linked, circular list which we never
+		// traverse backwards, so don't bother resolving the back links.
+		cur_dest.prev = NULL;
+
+		ReadFieldPtr<ErrorPolicy_Warn>(cur_dest.object,"*object",db);
+
+		// the return value of ReadFieldPtr indicates whether the object 
+		// was already cached. In this case, we don't need to resolve
+		// it again.
+		if(!ReadFieldPtr<ErrorPolicy_Warn>(cur_dest.next,"*next",db, true) && cur_dest.next) {
+			todo = std::make_pair(&*cur_dest.next, db.reader->GetCurrentPos());
+			continue;
+		}
+		break;
+	}
+	
+	db.reader->SetCurrentPos(initial_pos + 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_Igno>(dest.har,"har",db);
+    ReadField<ErrorPolicy_Warn>(dest.ambr,"ambr",db);
+    ReadField<ErrorPolicy_Warn>(dest.ambg,"ambg",db);
+    ReadField<ErrorPolicy_Warn>(dest.ambb,"ambb",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<MTexPoly> (
+    MTexPoly& dest,
+    const FileDatabase& db
+    ) const
+{ 
+
+    {
+        boost::shared_ptr<Image> tpage;
+        ReadFieldPtr<ErrorPolicy_Igno>(tpage,"*tpage",db);
+        dest.tpage = tpage.get();
+    }
+    ReadField<ErrorPolicy_Igno>(dest.flag,"flag",db);
+    ReadField<ErrorPolicy_Igno>(dest.transp,"transp",db);
+    ReadField<ErrorPolicy_Igno>(dest.mode,"mode",db);
+    ReadField<ErrorPolicy_Igno>(dest.tile,"tile",db);
+    ReadField<ErrorPolicy_Igno>(dest.pad,"pad",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.totloop,"totloop",db);
+    ReadField<ErrorPolicy_Igno>(dest.totpoly,"totpoly",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.mloop,"*mloop",db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mloopuv,"*mloopuv",db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mloopcol,"*mloopcol",db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mpoly,"*mpoly",db);
+    ReadFieldPtr<ErrorPolicy_Igno>(dest.mtpoly,"*mtpoly",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<MLoopCol> (
+    MLoopCol& dest,
+    const FileDatabase& db
+    ) const
+{ 
+
+    ReadField<ErrorPolicy_Igno>(dest.r,"r",db);
+    ReadField<ErrorPolicy_Igno>(dest.g,"g",db);
+    ReadField<ErrorPolicy_Igno>(dest.b,"b",db);
+    ReadField<ErrorPolicy_Igno>(dest.a,"a",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<MLoopUV> (
+    MLoopUV& dest,
+    const FileDatabase& db
+    ) const
+{ 
+
+    ReadFieldArray<ErrorPolicy_Igno>(dest.uv,"uv",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<MLoop> (
+    MLoop& dest,
+    const FileDatabase& db
+    ) const
+{ 
+
+    ReadField<ErrorPolicy_Igno>(dest.v,"v",db);
+    ReadField<ErrorPolicy_Igno>(dest.e,"e",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<MPoly> (
+    MPoly& dest,
+    const FileDatabase& db
+    ) const
+{ 
+
+    ReadField<ErrorPolicy_Igno>(dest.loopstart,"loopstart",db);
+    ReadField<ErrorPolicy_Igno>(dest.totloop,"totloop",db);
+    ReadField<ErrorPolicy_Igno>(dest.mat_nr,"mat_nr",db);
+    ReadField<ErrorPolicy_Igno>(dest.flag,"flag",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_Igno>((short&)dest.imaflag,"imaflag",db);
+    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);
+}
+
+//--------------------------------------------------------------------------------
+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);
+}
+
+//--------------------------------------------------------------------------------
+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["MTexPoly"] = DNA::FactoryPair( &Structure::Allocate<MTexPoly>, &Structure::Convert<MTexPoly> );
+    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["MLoopCol"] = DNA::FactoryPair( &Structure::Allocate<MLoopCol>, &Structure::Convert<MLoopCol> );
+    converters["MVert"] = DNA::FactoryPair( &Structure::Allocate<MVert>, &Structure::Convert<MVert> );
+    converters["MEdge"] = DNA::FactoryPair( &Structure::Allocate<MEdge>, &Structure::Convert<MEdge> );
+    converters["MLoopUV"] = DNA::FactoryPair( &Structure::Allocate<MLoopUV>, &Structure::Convert<MLoopUV> );
+    converters["GroupObject"] = DNA::FactoryPair( &Structure::Allocate<GroupObject>, &Structure::Convert<GroupObject> );
+    converters["ListBase"] = DNA::FactoryPair( &Structure::Allocate<ListBase>, &Structure::Convert<ListBase> );
+    converters["MLoop"] = DNA::FactoryPair( &Structure::Allocate<MLoop>, &Structure::Convert<MLoop> );
+    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["MPoly"] = DNA::FactoryPair( &Structure::Allocate<MPoly>, &Structure::Convert<MPoly> );
+    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> );
+    converters["Image"] = DNA::FactoryPair( &Structure::Allocate<Image>, &Structure::Convert<Image> );
+}
+
+
+#endif

+ 757 - 0
assimplib.mod/assimp/code/BlenderScene.h

@@ -0,0 +1,757 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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 normally written as boost::shared_ptr, while a
+//   pointer to an array of elements is written as boost::
+//   shared_array. To avoid cyclic pointers, use raw pointers in
+//   one direction.
+//
+// * 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;
+struct Image;
+
+#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 MLoop : ElemBase {
+	int v, e;
+};
+
+// -------------------------------------------------------------------------------
+struct MLoopUV : ElemBase {
+	float uv[2];
+	int flag;
+};
+
+// -------------------------------------------------------------------------------
+// Note that red and blue are not swapped, as with MCol
+struct MLoopCol : ElemBase {
+	char r, g, b, a;
+};
+
+// -------------------------------------------------------------------------------
+struct MPoly : ElemBase {
+	int loopstart;
+	int totloop;
+	short mat_nr;
+	char flag;
+};
+
+// -------------------------------------------------------------------------------
+struct MTexPoly : ElemBase {
+	Image* tpage;
+	char flag, transp;
+	short mode, tile, pad;
+};
+
+// -------------------------------------------------------------------------------
+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;
+	short har;
+	float ambr,ambg,ambb 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;
+	int totloop;
+	int totpoly;
+
+	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<MLoop> mloop;
+	vector<MLoopUV> mloopuv;
+	vector<MLoopCol> mloopcol;
+	vector<MPoly> mpoly;
+	vector<MTexPoly> mtpoly;
+	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 omitted
+		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;
+	
+	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 {
+	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
+	};
+
+	enum ImageFlags {
+	     ImageFlags_INTERPOL    	 = 1
+	    ,ImageFlags_USEALPHA    	 = 2
+	    ,ImageFlags_MIPMAP      	 = 4
+	    ,ImageFlags_IMAROT      	 = 16
+	    ,ImageFlags_CALCALPHA   	 = 32
+	    ,ImageFlags_NORMALMAP   	 = 2048
+	    ,ImageFlags_GAUSS_MIP   	 = 4096
+	    ,ImageFlags_FILTER_MIN  	 = 8192
+	    ,ImageFlags_DERIVATIVEMAP   = 16384
+	};
+
+	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 flag;
+	ImageFlags imaflag;
+	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
+	};
+
+	enum MapType {
+	     MapType_COL         = 1
+	    ,MapType_NORM        = 2
+	    ,MapType_COLSPEC     = 4
+	    ,MapType_COLMIR      = 8
+	    ,MapType_REF         = 16
+	    ,MapType_SPEC        = 32
+	    ,MapType_EMIT        = 64
+	    ,MapType_ALPHA       = 128
+	    ,MapType_HAR         = 256
+	    ,MapType_RAYMIRR     = 512
+	    ,MapType_TRANSLU     = 1024
+	    ,MapType_AMB         = 2048
+	    ,MapType_DISPLACE    = 4096
+	    ,MapType_WARP        = 8192
+	};
+
+	// short texco, maptoneg;
+	MapType mapto;
+
+	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;
+	//float 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

+ 253 - 0
assimplib.mod/assimp/code/BlenderSceneGen.h

@@ -0,0 +1,253 @@
+/*
+Open Asset Import Library (ASSIMP)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2010, ASSIMP Development Team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the ASSIMP team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the ASSIMP Development Team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING 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<MTexPoly> (
+    MTexPoly& 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<MLoopCol> (
+    MLoopCol& 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<MLoopUV> (
+    MLoopUV& 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<MLoop> (
+    MLoop& 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<MPoly> (
+    MPoly& 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
+;
+
+template <> void Structure :: Convert<Image> (
+    Image& dest,
+    const FileDatabase& db
+    ) const
+;
+
+
+	}
+}
+
+#endif

+ 520 - 0
assimplib.mod/assimp/code/BlenderTessellator.cpp

@@ -0,0 +1,520 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2013, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  BlenderTessellator.cpp
+ *  @brief A simple tessellation wrapper
+ */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include "BlenderBMesh.h"
+#include "BlenderTessellator.h"
+
+static const unsigned int BLEND_TESS_MAGIC = 0x83ed9ac3;
+
+#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+namspace Assimp
+{
+	template< > const std::string LogFunctions< BlenderTessellatorGL >::log_prefix = "BLEND_TESS_GL: ";
+}
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+#ifndef CALLBACK
+#define CALLBACK
+#endif
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorGL::BlenderTessellatorGL( BlenderBMeshConverter& converter ):
+	converter( &converter )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorGL::~BlenderTessellatorGL( )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::Tessellate( const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices )
+{
+	AssertVertexCount( vertexCount );
+
+	std::vector< VertexGL > polyLoopGL;
+	GenerateLoopVerts( polyLoopGL, polyLoop, vertexCount, vertices );
+
+	TessDataGL tessData;
+	Tesssellate( polyLoopGL, tessData );
+
+	TriangulateDrawCalls( tessData );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::AssertVertexCount( int vertexCount )
+{
+	if ( vertexCount <= 4 )
+	{
+		ThrowException( "Expected more than 4 vertices for tessellation" );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::GenerateLoopVerts( std::vector< VertexGL >& polyLoopGL, const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices )
+{
+	for ( int i = 0; i < vertexCount; ++i )
+	{
+		const MLoop& loopItem = polyLoop[ i ];
+		const MVert& vertex = vertices[ loopItem.v ];
+		polyLoopGL.push_back( VertexGL( vertex.co[ 0 ], vertex.co[ 1 ], vertex.co[ 2 ], loopItem.v, BLEND_TESS_MAGIC ) );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::Tesssellate( std::vector< VertexGL >& polyLoopGL, TessDataGL& tessData )
+{
+	GLUtesselator* tessellator = gluNewTess( );
+	gluTessCallback( tessellator, GLU_TESS_BEGIN_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateBegin ) );
+	gluTessCallback( tessellator, GLU_TESS_END_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateEnd ) );
+	gluTessCallback( tessellator, GLU_TESS_VERTEX_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateVertex ) );
+	gluTessCallback( tessellator, GLU_TESS_COMBINE_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateCombine ) );
+	gluTessCallback( tessellator, GLU_TESS_EDGE_FLAG_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateEdgeFlag ) );
+	gluTessCallback( tessellator, GLU_TESS_ERROR_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateError ) );
+	gluTessProperty( tessellator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO );
+
+	gluTessBeginPolygon( tessellator, &tessData );
+	gluTessBeginContour( tessellator );
+
+	for ( unsigned int i = 0; i < polyLoopGL.size( ); ++i )
+	{
+		gluTessVertex( tessellator, reinterpret_cast< GLdouble* >( &polyLoopGL[ i ] ), &polyLoopGL[ i ] );
+	}
+
+	gluTessEndContour( tessellator );
+	gluTessEndPolygon( tessellator );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TriangulateDrawCalls( const TessDataGL& tessData )
+{
+	// NOTE - Because we are supplying a callback to GLU_TESS_EDGE_FLAG_DATA we don't technically
+	//        need support for GL_TRIANGLE_STRIP and GL_TRIANGLE_FAN but we'll keep it here in case
+	//        GLU tessellate changes or tristrips and fans are wanted.
+	//        See: http://www.opengl.org/sdk/docs/man2/xhtml/gluTessCallback.xml
+	for ( unsigned int i = 0; i < tessData.drawCalls.size( ); ++i )
+	{
+		const DrawCallGL& drawCallGL = tessData.drawCalls[ i ];
+		const VertexGL* vertices = &tessData.vertices[ drawCallGL.baseVertex ];
+		if ( drawCallGL.drawMode == GL_TRIANGLES )
+		{
+			MakeFacesFromTris( vertices, drawCallGL.vertexCount );
+		}
+		else if ( drawCallGL.drawMode == GL_TRIANGLE_STRIP )
+		{
+			MakeFacesFromTriStrip( vertices, drawCallGL.vertexCount );
+		}
+		else if ( drawCallGL.drawMode == GL_TRIANGLE_FAN )
+		{
+			MakeFacesFromTriFan( vertices, drawCallGL.vertexCount );
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::MakeFacesFromTris( const VertexGL* vertices, int vertexCount )
+{
+	int triangleCount = vertexCount / 3;
+	for ( int i = 0; i < triangleCount; ++i )
+	{
+		int vertexBase = i * 3;
+		converter->AddFace( vertices[ vertexBase + 0 ].index, vertices[ vertexBase + 1 ].index, vertices[ vertexBase + 2 ].index );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::MakeFacesFromTriStrip( const VertexGL* vertices, int vertexCount )
+{
+	int triangleCount = vertexCount - 2;
+	for ( int i = 0; i < triangleCount; ++i )
+	{
+		int vertexBase = i;
+		converter->AddFace( vertices[ vertexBase + 0 ].index, vertices[ vertexBase + 1 ].index, vertices[ vertexBase + 2 ].index );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::MakeFacesFromTriFan( const VertexGL* vertices, int vertexCount )
+{
+	int triangleCount = vertexCount - 2;
+	for ( int i = 0; i < triangleCount; ++i )
+	{
+		int vertexBase = i;
+		converter->AddFace( vertices[ 0 ].index, vertices[ vertexBase + 1 ].index, vertices[ vertexBase + 2 ].index );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateBegin( GLenum drawModeGL, void* userData )
+{
+	TessDataGL& tessData = *reinterpret_cast< TessDataGL* >( userData );
+	tessData.drawCalls.push_back( DrawCallGL( drawModeGL, tessData.vertices.size( ) ) );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateEnd( void* )
+{
+	// Do nothing
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateVertex( const void* vtxData, void* userData )
+{
+	TessDataGL& tessData = *reinterpret_cast< TessDataGL* >( userData );
+
+	const VertexGL& vertex = *reinterpret_cast< const VertexGL* >( vtxData );
+	if ( vertex.magic != BLEND_TESS_MAGIC )
+	{
+		ThrowException( "Point returned by GLU Tessellate was probably not one of ours. This indicates we need a new way to store vertex information" );
+	}
+	tessData.vertices.push_back( vertex );
+	if ( tessData.drawCalls.size( ) == 0 )
+	{
+		ThrowException( "\"Vertex\" callback received before \"Begin\"" );
+	}
+	++( tessData.drawCalls.back( ).vertexCount );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateCombine( const GLdouble intersection[ 3 ], const GLdouble* [ 4 ], const GLfloat [ 4 ], GLdouble** out, void* userData )
+{
+	ThrowException( "Intersected polygon loops are not yet supported" );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateEdgeFlag( GLboolean, void* )
+{
+	// Do nothing
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateError( GLenum errorCode, void* )
+{
+	ThrowException( reinterpret_cast< const char* >( gluErrorString( errorCode ) ) );
+}
+
+#endif // ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+#if ASSIMP_BLEND_WITH_POLY_2_TRI
+
+namespace Assimp
+{
+	template< > const std::string LogFunctions< BlenderTessellatorP2T >::log_prefix = "BLEND_TESS_P2T: ";
+}
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorP2T::BlenderTessellatorP2T( BlenderBMeshConverter& converter ):
+	converter( &converter )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorP2T::~BlenderTessellatorP2T( )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::Tessellate( const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices )
+{
+	AssertVertexCount( vertexCount );
+
+	// NOTE - We have to hope that points in a Blender polygon are roughly on the same plane.
+	//        There may be some triangulation artifacts if they are wildly different.
+
+	std::vector< PointP2T > points;
+	Copy3DVertices( polyLoop, vertexCount, vertices, points );
+
+	PlaneP2T plane = FindLLSQPlane( points );
+
+	aiMatrix4x4 transform = GeneratePointTransformMatrix( plane );
+
+	TransformAndFlattenVectices( transform, points );
+
+	std::vector< p2t::Point* > pointRefs;
+	ReferencePoints( points, pointRefs );
+
+	p2t::CDT cdt( pointRefs );
+
+	cdt.Triangulate( );
+	std::vector< p2t::Triangle* > triangles = cdt.GetTriangles( );
+
+	MakeFacesFromTriangles( triangles );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::AssertVertexCount( int vertexCount )
+{
+	if ( vertexCount <= 4 )
+	{
+		ThrowException( "Expected more than 4 vertices for tessellation" );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::Copy3DVertices( const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices, std::vector< PointP2T >& points ) const
+{
+	points.resize( vertexCount );
+	for ( int i = 0; i < vertexCount; ++i )
+	{
+		const MLoop& loop = polyLoop[ i ];
+		const MVert& vert = vertices[ loop.v ];
+
+		PointP2T& point = points[ i ];
+		point.point3D.Set( vert.co[ 0 ], vert.co[ 1 ], vert.co[ 2 ] );
+		point.index = loop.v;
+		point.magic = BLEND_TESS_MAGIC;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+aiMatrix4x4 BlenderTessellatorP2T::GeneratePointTransformMatrix( const Blender::PlaneP2T& plane ) const
+{
+	aiVector3D sideA( 1.0f, 0.0f, 0.0f );
+	if ( fabs( plane.normal * sideA ) > 0.999f )
+	{
+		sideA = aiVector3D( 0.0f, 1.0f, 0.0f );
+	}
+
+	aiVector3D sideB( plane.normal ^ sideA );
+	sideB.Normalize( );
+	sideA = sideB ^ plane.normal;
+
+	aiMatrix4x4 result;
+	result.a1 = sideA.x;
+	result.a2 = sideA.y;
+	result.a3 = sideA.z;
+	result.b1 = sideB.x;
+	result.b2 = sideB.y;
+	result.b3 = sideB.z;
+	result.c1 = plane.normal.x;
+	result.c2 = plane.normal.y;
+	result.c3 = plane.normal.z;
+	result.a4 = plane.centre.x;
+	result.b4 = plane.centre.y;
+	result.c4 = plane.centre.z;
+	result.Inverse( );
+
+	return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::TransformAndFlattenVectices( const aiMatrix4x4& transform, std::vector< Blender::PointP2T >& vertices ) const
+{
+	for ( unsigned int i = 0; i < vertices.size( ); ++i )
+	{
+		PointP2T& point = vertices[ i ];
+		point.point3D = transform * point.point3D;
+		point.point2D.set( point.point3D.y, point.point3D.z );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::ReferencePoints( std::vector< Blender::PointP2T >& points, std::vector< p2t::Point* >& pointRefs ) const
+{
+	pointRefs.resize( points.size( ) );
+	for ( unsigned int i = 0; i < points.size( ); ++i )
+	{
+		pointRefs[ i ] = &points[ i ].point2D;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Yes this is filthy... but we have no choice
+#define OffsetOf( Class, Member ) ( static_cast< unsigned int >( \
+	reinterpret_cast<uint8_t*>(&( reinterpret_cast< Class* >( NULL )->*( &Class::Member ) )) - \
+	static_cast<uint8_t*>(NULL) ) )
+
+inline PointP2T& BlenderTessellatorP2T::GetActualPointStructure( p2t::Point& point ) const
+{
+	unsigned int pointOffset = OffsetOf( PointP2T, point2D );
+	PointP2T& pointStruct = *reinterpret_cast< PointP2T* >( reinterpret_cast< char* >( &point ) - pointOffset );
+	if ( pointStruct.magic != static_cast<int>( BLEND_TESS_MAGIC ) )
+	{
+		ThrowException( "Point returned by poly2tri was probably not one of ours. This indicates we need a new way to store vertex information" );
+	}
+	return pointStruct;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::MakeFacesFromTriangles( std::vector< p2t::Triangle* >& triangles ) const
+{
+	for ( unsigned int i = 0; i < triangles.size( ); ++i )
+	{
+		p2t::Triangle& Triangle = *triangles[ i ];
+
+		PointP2T& pointA = GetActualPointStructure( *Triangle.GetPoint( 0 ) );
+		PointP2T& pointB = GetActualPointStructure( *Triangle.GetPoint( 1 ) );
+		PointP2T& pointC = GetActualPointStructure( *Triangle.GetPoint( 2 ) );
+
+		converter->AddFace( pointA.index, pointB.index, pointC.index );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+inline float p2tMax( float a, float b )
+{
+	return a > b ? a : b;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+float BlenderTessellatorP2T::FindLargestMatrixElem( const aiMatrix3x3& mtx ) const
+{
+	float result = 0.0f;
+
+	for ( int x = 0; x < 3; ++x )
+	{
+		for ( int y = 0; y < 3; ++y )
+		{
+			result = p2tMax( fabs( mtx[ x ][ y ] ), result );
+		}
+	}
+
+	return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Aparently Assimp doesn't have matrix scaling
+aiMatrix3x3 BlenderTessellatorP2T::ScaleMatrix( const aiMatrix3x3& mtx, float scale ) const
+{
+	aiMatrix3x3 result;
+
+	for ( int x = 0; x < 3; ++x )
+	{
+		for ( int y = 0; y < 3; ++y )
+		{
+			result[ x ][ y ] = mtx[ x ][ y ] * scale;
+		}
+	}
+
+	return result;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+aiVector3D BlenderTessellatorP2T::GetEigenVectorFromLargestEigenValue( const aiMatrix3x3& mtx ) const
+{
+	float scale = FindLargestMatrixElem( mtx );
+	aiMatrix3x3 mc = ScaleMatrix( mtx, 1.0f / scale );
+	mc = mc * mc * mc;
+
+	aiVector3D v( 1.0f );
+	aiVector3D lastV = v;
+	for ( int i = 0; i < 100; ++i )
+	{
+		v = mc * v;
+		v.Normalize( );
+		if ( ( v - lastV ).SquareLength( ) < 1e-16f )
+		{
+			break;
+		}
+		lastV = v;
+	}
+	return v;
+} 
+
+// ------------------------------------------------------------------------------------------------
+// Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+PlaneP2T BlenderTessellatorP2T::FindLLSQPlane( const std::vector< PointP2T >& points ) const
+{
+	PlaneP2T result;
+
+	aiVector3D sum( 0.0f );
+	for ( unsigned int i = 0; i < points.size( ); ++i )
+	{
+		sum += points[ i ].point3D;
+	}
+	result.centre = sum * ( 1.0f / points.size( ) );
+
+	float sumXX = 0.0f;
+	float sumXY = 0.0f;
+	float sumXZ = 0.0f;
+	float sumYY = 0.0f;
+	float sumYZ = 0.0f;
+	float sumZZ = 0.0f;
+	for ( unsigned int i = 0; i < points.size( ); ++i )
+	{
+		aiVector3D offset = points[ i ].point3D - result.centre;
+		sumXX += offset.x * offset.x;
+		sumXY += offset.x * offset.y;
+		sumXZ += offset.x * offset.z;
+		sumYY += offset.y * offset.y;
+		sumYZ += offset.y * offset.z;
+		sumZZ += offset.z * offset.z;
+	}
+
+	aiMatrix3x3 mtx( sumXX, sumXY, sumXZ, sumXY, sumYY, sumYZ, sumXZ, sumYZ, sumZZ );
+
+	float det = mtx.Determinant( );
+	if ( det == 0.0f )
+	{
+		result.normal = aiVector3D( 0.0f );
+	}
+	else
+	{
+		aiMatrix3x3 invMtx = mtx;
+		invMtx.Inverse( );
+		result.normal = GetEigenVectorFromLargestEigenValue( invMtx );
+	}
+
+	return result;
+}
+
+#endif // ASSIMP_BLEND_WITH_POLY_2_TRI
+
+#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 208 - 0
assimplib.mod/assimp/code/BlenderTessellator.h

@@ -0,0 +1,208 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2013, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  BlenderTessellator.h
+ *  @brief A simple tessellation wrapper
+ */
+#ifndef INCLUDED_AI_BLEND_TESSELLATOR_H
+#define INCLUDED_AI_BLEND_TESSELLATOR_H
+
+// Use these to toggle between GLU Tessellate or poly2tri
+// Note (acg) keep GLU Tesselate disabled by default - if it is turned on,
+// assimp needs to be linked against GLU, which is currently not yet 
+// made configurable in CMake and potentially not wanted by most users
+// as it requires a Gl environment.
+#ifndef ASSIMP_BLEND_WITH_GLU_TESSELLATE
+#	define ASSIMP_BLEND_WITH_GLU_TESSELLATE 0
+#endif
+
+#ifndef ASSIMP_BLEND_WITH_POLY_2_TRI
+#	define ASSIMP_BLEND_WITH_POLY_2_TRI 1
+#endif
+
+#include "LogAux.h"
+
+#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+#if defined( WIN32 ) || defined( _WIN32 ) || defined( _MSC_VER )
+#include <windows.h>
+#endif
+#include <GL/glu.h>
+
+namespace Assimp
+{
+	class BlenderBMeshConverter;
+
+	// 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;
+	}
+
+	// BlenderScene.h
+	namespace Blender
+	{
+		struct MLoop;
+		struct MVert;
+
+		struct VertexGL
+		{
+			GLdouble X;
+			GLdouble Y;
+			GLdouble Z;
+			int index;
+			int magic;
+
+			VertexGL( GLdouble X, GLdouble Y, GLdouble Z, int index, int magic ): X( X ), Y( Y ), Z( Z ), index( index ), magic( magic ) { }
+		};
+
+		struct DrawCallGL
+		{
+			GLenum drawMode;
+			int baseVertex;
+			int vertexCount;
+
+			DrawCallGL( GLenum drawMode, int baseVertex ): drawMode( drawMode ), baseVertex( baseVertex ), vertexCount( 0 ) { }
+		};
+
+		struct TessDataGL
+		{
+			std::vector< DrawCallGL > drawCalls;
+			std::vector< VertexGL > vertices;
+		};
+	}
+
+	class BlenderTessellatorGL: public LogFunctions< BlenderTessellatorGL >
+	{
+	public:
+		BlenderTessellatorGL( BlenderBMeshConverter& converter );
+		~BlenderTessellatorGL( );
+
+		void Tessellate( const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices );
+
+	private:
+		void AssertVertexCount( int vertexCount );
+		void GenerateLoopVerts( std::vector< Blender::VertexGL >& polyLoopGL, const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices );
+		void Tesssellate( std::vector< Blender::VertexGL >& polyLoopGL, Blender::TessDataGL& tessData );
+		void TriangulateDrawCalls( const Blender::TessDataGL& tessData );
+		void MakeFacesFromTris( const Blender::VertexGL* vertices, int vertexCount );
+		void MakeFacesFromTriStrip( const Blender::VertexGL* vertices, int vertexCount );
+		void MakeFacesFromTriFan( const Blender::VertexGL* vertices, int vertexCount );
+
+		static void TessellateBegin( GLenum drawModeGL, void* userData );
+		static void TessellateEnd( void* userData );
+		static void TessellateVertex( const void* vtxData, void* userData );
+		static void TessellateCombine( const GLdouble intersection[ 3 ], const GLdouble* [ 4 ], const GLfloat [ 4 ], GLdouble** out, void* userData );
+		static void TessellateEdgeFlag( GLboolean edgeFlag, void* userData );
+		static void TessellateError( GLenum errorCode, void* userData );
+
+		BlenderBMeshConverter* converter;
+	};
+} // end of namespace Assimp
+
+#endif // ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+#if ASSIMP_BLEND_WITH_POLY_2_TRI
+
+#include "../contrib/poly2tri/poly2tri/poly2tri.h"
+
+namespace Assimp
+{
+	class BlenderBMeshConverter;
+
+	// 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;
+	}
+
+	// BlenderScene.h
+	namespace Blender
+	{
+		struct MLoop;
+		struct MVert;
+
+		struct PointP2T
+		{
+			aiVector3D point3D;
+			p2t::Point point2D;
+			int magic;
+			int index;
+		};
+
+		struct PlaneP2T
+		{
+			aiVector3D centre;
+			aiVector3D normal;
+		};
+	}
+
+	class BlenderTessellatorP2T: public LogFunctions< BlenderTessellatorP2T >
+	{
+	public:
+		BlenderTessellatorP2T( BlenderBMeshConverter& converter );
+		~BlenderTessellatorP2T( );
+
+		void Tessellate( const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices );
+
+	private:
+		void AssertVertexCount( int vertexCount );
+		void Copy3DVertices( const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices, std::vector< Blender::PointP2T >& targetVertices ) const;
+		aiMatrix4x4 GeneratePointTransformMatrix( const Blender::PlaneP2T& plane ) const;
+		void TransformAndFlattenVectices( const aiMatrix4x4& transform, std::vector< Blender::PointP2T >& vertices ) const;
+		void ReferencePoints( std::vector< Blender::PointP2T >& points, std::vector< p2t::Point* >& pointRefs ) const;
+		inline Blender::PointP2T& GetActualPointStructure( p2t::Point& point ) const;
+		void MakeFacesFromTriangles( std::vector< p2t::Triangle* >& triangles ) const;
+
+		// Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+		float FindLargestMatrixElem( const aiMatrix3x3& mtx ) const;
+		aiMatrix3x3 ScaleMatrix( const aiMatrix3x3& mtx, float scale ) const;
+		aiVector3D GetEigenVectorFromLargestEigenValue( const aiMatrix3x3& mtx ) const;
+		Blender::PlaneP2T FindLLSQPlane( const std::vector< Blender::PointP2T >& points ) const;
+
+		BlenderBMeshConverter* converter;
+	};
+} // end of namespace Assimp
+
+#endif // ASSIMP_BLEND_WITH_POLY_2_TRI
+
+#endif // INCLUDED_AI_BLEND_TESSELLATOR_H

+ 326 - 0
assimplib.mod/assimp/code/BlobIOSystem.h

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

+ 23 - 0
assimplib.mod/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.

+ 99 - 0
assimplib.mod/assimp/code/BoostWorkaround/boost/foreach.hpp

@@ -0,0 +1,99 @@
+
+#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);
+}
+
+template<typename T>
+typename T::const_reference deref(auto_any_base const& cur, const 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& foreach_magic_b = boost::foreach_detail::begin(container)) {} else       \
+    if(boost::foreach_detail::auto_any_base const& foreach_magic_e = boost::foreach_detail::end(container))   {} else       \
+    for(;!boost::foreach_detail::done(foreach_magic_b,foreach_magic_e,container);  boost::foreach_detail::next(foreach_magic_b,container))   \
+        if (bool ugly_and_unique_break = false) {} else							\
+        for(item = boost::foreach_detail::deref(foreach_magic_b,container); !ugly_and_unique_break; ugly_and_unique_break = true)
+
+} // end boost
+
+#endif

+ 81 - 0
assimplib.mod/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::ostringstream 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
+

+ 26 - 0
assimplib.mod/assimp/code/BoostWorkaround/boost/lexical_cast.hpp

@@ -0,0 +1,26 @@
+/// 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
+
+#include <sstream>
+
+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
+

+ 57 - 0
assimplib.mod/assimp/code/BoostWorkaround/boost/make_shared.hpp

@@ -0,0 +1,57 @@
+
+// please note that this replacement implementation does not
+// provide the performance benefit of the original, which
+// makes only one allocation as opposed to two allocations
+// (smart pointer counter and payload) which are usually
+// required if object and smart pointer are constructed
+// independently.
+
+#ifndef INCLUDED_AI_BOOST_MAKE_SHARED
+#define INCLUDED_AI_BOOST_MAKE_SHARED
+
+
+namespace boost {
+
+	template <typename T>
+	shared_ptr<T> make_shared() {
+		return shared_ptr<T>(new T());
+	}
+
+	template <typename T, typename T0>
+	shared_ptr<T> make_shared(const T0& t0) {
+		return shared_ptr<T>(new T(t0));
+	}
+
+	template <typename T, typename T0,typename T1>
+	shared_ptr<T> make_shared(const T0& t0, const T1& t1) {
+		return shared_ptr<T>(new T(t0,t1));
+	}
+
+	template <typename T, typename T0,typename T1,typename T2>
+	shared_ptr<T> make_shared(const T0& t0, const T1& t1, const T2& t2) {
+		return shared_ptr<T>(new T(t0,t1,t2));
+	}
+
+	template <typename T, typename T0,typename T1,typename T2,typename T3>
+	shared_ptr<T> make_shared(const T0& t0, const T1& t1, const T2& t2, const T3& t3) {
+		return shared_ptr<T>(new T(t0,t1,t2,t3));
+	}
+
+	template <typename T, typename T0,typename T1,typename T2,typename T3, typename T4>
+	shared_ptr<T> make_shared(const T0& t0, const T1& t1, const T2& t2, const T3& t3, const T4& t4) {
+		return shared_ptr<T>(new T(t0,t1,t2,t3,t4));
+	}
+
+	template <typename T, typename T0,typename T1,typename T2,typename T3, typename T4, typename T5>
+	shared_ptr<T> make_shared(const T0& t0, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5) {
+		return shared_ptr<T>(new T(t0,t1,t2,t3,t4,t5));
+	}
+
+	template <typename T, typename T0,typename T1,typename T2,typename T3, typename T4, typename T5, typename T6>
+	shared_ptr<T> make_shared(const T0& t0, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6) {
+		return shared_ptr<T>(new T(t0,t1,t2,t3,t4,t5,t6));
+	}
+}
+
+
+#endif 

+ 37 - 0
assimplib.mod/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

+ 36 - 0
assimplib.mod/assimp/code/BoostWorkaround/boost/noncopyable.hpp

@@ -0,0 +1,36 @@
+//  Boost noncopyable.hpp header file  --------------------------------------//
+
+//  (C) Copyright Beman Dawes 1999-2003. 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/utility for documentation.
+
+#ifndef BOOST_NONCOPYABLE_HPP_INCLUDED
+#define BOOST_NONCOPYABLE_HPP_INCLUDED
+
+namespace boost {
+
+//  Private copy constructor and copy assignment ensure classes derived from
+//  class noncopyable cannot be copied.
+
+//  Contributed by Dave Abrahams
+
+namespace noncopyable_  // protection from unintended ADL
+{
+  class noncopyable
+  {
+   protected:
+      noncopyable() {}
+      ~noncopyable() {}
+   private:  // emphasize the following members are private
+      noncopyable( const noncopyable& );
+      const noncopyable& operator=( const noncopyable& );
+  };
+}
+
+typedef noncopyable_::noncopyable noncopyable;
+
+} // namespace boost
+
+#endif  // BOOST_NONCOPYABLE_HPP_INCLUDED

+ 45 - 0
assimplib.mod/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
assimplib.mod/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
assimplib.mod/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
assimplib.mod/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_SHARED_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
assimplib.mod/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_SHARED_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*() const {
+		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_SHARED_PTR

+ 20 - 0
assimplib.mod/assimp/code/BoostWorkaround/boost/static_assert.hpp

@@ -0,0 +1,20 @@
+
+#ifndef AI_BOOST_STATIC_ASSERT_INCLUDED
+#define AI_BOOST_STATIC_ASSERT_INCLUDED
+
+#ifndef BOOST_STATIC_ASSERT
+
+namespace boost {
+	namespace detail {
+
+		template <bool b>  class static_assertion_failure;
+		template <>        class static_assertion_failure<true> {};
+	}
+}
+
+
+#define BOOST_STATIC_ASSERT(eval) \
+{boost::detail::static_assertion_failure<(eval)> assert_dummy;(void)assert_dummy;}
+
+#endif
+#endif // !! AI_BOOST_STATIC_ASSERT_INCLUDED

+ 72 - 0
assimplib.mod/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

+ 283 - 0
assimplib.mod/assimp/code/BoostWorkaround/boost/tuple/tuple.hpp

@@ -0,0 +1,283 @@
+// 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.template get<N>();
+		}
+
+		// ... and the const version
+		template <unsigned N>
+		const typename detail::type_getter<T0,0,typename very_long::next_type, N>::type& get () const	{
+			return m.template 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 T0b, typename T1b,typename T2b,typename T3b, typename T4b>
+		operator tuple <T0b,T1b,T2b,T3b,T4b> () const {
+			tuple <T0b,T1b,T2b,T3b,T4b> s;
+			s.m = (typename tuple <T0b,T1b,T2b,T3b,T4b>::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::template type_getter<N>::type& get (
+			tuple<T0,T1,T2,T3,T4>& m)	{
+			return m.template 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::template type_getter<N>::type& get (
+			const tuple<T0,T1,T2,T3,T4>& m)	{
+			return m.template 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.template get<0>() = t0;
+		t.template get<1>() = t1;
+		t.template get<2>() = t2;
+		t.template get<3>() = t3;
+		t.template 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.template get<0>() = t0;
+		t.template get<1>() = t1;
+		t.template get<2>() = t2;
+		t.template 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.template get<0>() = t0;
+		t.template get<1>() = t1;
+		t.template get<2>() = t2;
+		return t;
+	}
+
+	// Constructs a tuple with 2 elements 
+	template <typename T0,typename T1>
+	inline tuple <T0,T1> make_tuple (const T0& t0,
+		const T1& t1) {
+		tuple <T0,T1> t;
+		t.template get<0>() = t0;
+		t.template get<1>() = t1;
+		return t;
+	}
+
+	// Constructs a tuple with 1 elements (well ...)
+	template <typename T0>
+	inline tuple <T0> make_tuple (const T0& t0) {
+		tuple <T0> t;
+		t.template get<0>() = t0;
+		return t;
+	}
+
+	// Constructs a tuple with 0 elements (well ...)
+	inline tuple <> make_tuple () {
+		tuple <> t;
+		return t;
+	}
+};
+
+#endif // !! BOOST_TUPLE_INCLUDED

+ 285 - 0
assimplib.mod/assimp/code/ByteSwap.h

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

+ 158 - 0
assimplib.mod/assimp/code/CInterfaceIOWrapper.h

@@ -0,0 +1,158 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file aiFileIO -> IOSystem wrapper*/
+
+#ifndef AI_CIOSYSTEM_H_INCLUDED
+#define AI_CIOSYSTEM_H_INCLUDED
+
+#include "../include/assimp/cfileio.h"
+
+namespace Assimp	{
+	
+// ------------------------------------------------------------------------------------------------
+// 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 {
+		aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile,"rb");		
+		if (p){
+			mFileSystem->CloseProc(mFileSystem,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;
+};
+
+}
+
+#endif
+

+ 742 - 0
assimplib.mod/assimp/code/CMakeLists.txt

@@ -0,0 +1,742 @@
+# Listing and grouping of all the source files.
+# 1) Set the file lists for each component
+# 2) Create a Source Group for each component, for IDE project orginization
+# 3) Add libassimp using the file lists (eliminates duplication of file names between
+#    source groups and library command)
+#
+cmake_minimum_required( VERSION 2.6 )
+SET( HEADER_PATH ../include/assimp )
+
+SET( COMPILER_HEADERS
+	${HEADER_PATH}/Compiler/pushpack1.h
+	${HEADER_PATH}/Compiler/poppack1.h
+	${HEADER_PATH}/Compiler/pstdint.h
+)
+SOURCE_GROUP( Compiler FILES ${COMPILER_HEADERS})
+
+SET( PUBLIC_HEADERS
+	${HEADER_PATH}/anim.h
+	${HEADER_PATH}/ai_assert.h
+	${HEADER_PATH}/camera.h
+	${HEADER_PATH}/color4.h
+	${HEADER_PATH}/color4.inl
+	${HEADER_PATH}/config.h
+	${HEADER_PATH}/defs.h
+	${HEADER_PATH}/cfileio.h
+	${HEADER_PATH}/light.h
+	${HEADER_PATH}/material.h
+	${HEADER_PATH}/material.inl
+	${HEADER_PATH}/matrix3x3.h
+	${HEADER_PATH}/matrix3x3.inl
+	${HEADER_PATH}/matrix4x4.h
+	${HEADER_PATH}/matrix4x4.inl
+	${HEADER_PATH}/mesh.h
+	${HEADER_PATH}/postprocess.h
+	${HEADER_PATH}/quaternion.h
+	${HEADER_PATH}/quaternion.inl
+	${HEADER_PATH}/scene.h
+	${HEADER_PATH}/metadata.h
+	${HEADER_PATH}/texture.h
+	${HEADER_PATH}/types.h
+	${HEADER_PATH}/vector2.h
+	${HEADER_PATH}/vector2.inl
+	${HEADER_PATH}/vector3.h
+	${HEADER_PATH}/vector3.inl
+	${HEADER_PATH}/version.h
+	${HEADER_PATH}/cimport.h
+	${HEADER_PATH}/importerdesc.h
+	${HEADER_PATH}/Importer.hpp
+	${HEADER_PATH}/DefaultLogger.hpp
+	${HEADER_PATH}/ProgressHandler.hpp
+	${HEADER_PATH}/IOStream.hpp
+	${HEADER_PATH}/IOSystem.hpp
+	${HEADER_PATH}/Logger.hpp
+	${HEADER_PATH}/LogStream.hpp
+	${HEADER_PATH}/NullLogger.hpp
+	${HEADER_PATH}/cexport.h
+	${HEADER_PATH}/Exporter.hpp
+)
+
+SET( Core_SRCS
+	Assimp.cpp
+)
+
+SET( Boost_SRCS
+	BoostWorkaround/boost/math/common_factor_rt.hpp
+	BoostWorkaround/boost/foreach.hpp
+	BoostWorkaround/boost/format.hpp
+	BoostWorkaround/boost/scoped_array.hpp
+	BoostWorkaround/boost/scoped_ptr.hpp
+	BoostWorkaround/boost/shared_array.hpp
+	BoostWorkaround/boost/shared_ptr.hpp
+	BoostWorkaround/boost/make_shared.hpp
+	BoostWorkaround/boost/static_assert.hpp
+	BoostWorkaround/boost/tuple/tuple.hpp
+)
+SOURCE_GROUP(Boost FILES ${Boost_SRCS})
+
+SET( Logging_SRCS
+	${HEADER_PATH}/DefaultLogger.hpp
+	${HEADER_PATH}/LogStream.hpp
+	${HEADER_PATH}/Logger.hpp
+	${HEADER_PATH}/NullLogger.hpp
+	Win32DebugLogStream.h
+	DefaultLogger.cpp
+	FileLogStream.h
+	StdOStreamLogStream.h
+)
+SOURCE_GROUP(Logging FILES ${Logging_SRCS})
+
+SET( Common_SRCS
+	fast_atof.h
+	qnan.h
+	BaseImporter.cpp
+	BaseImporter.h
+	BaseProcess.cpp
+	BaseProcess.h
+	Importer.h
+	ScenePrivate.h
+	PostStepRegistry.cpp
+	ImporterRegistry.cpp
+	ByteSwap.h
+	DefaultProgressHandler.h
+	DefaultIOStream.cpp
+	DefaultIOStream.h
+	DefaultIOSystem.cpp
+	DefaultIOSystem.h
+	CInterfaceIOWrapper.h
+	Hash.h
+	Importer.cpp
+	IFF.h
+	MemoryIOWrapper.h
+	ParsingUtils.h
+	StreamReader.h
+	StringComparison.h
+	SGSpatialSort.cpp
+	SGSpatialSort.h
+	VertexTriangleAdjacency.cpp
+	VertexTriangleAdjacency.h
+	GenericProperty.h
+	SpatialSort.cpp
+	SpatialSort.h
+	SceneCombiner.cpp
+	SceneCombiner.h
+	ScenePreprocessor.cpp
+	ScenePreprocessor.h
+	SkeletonMeshBuilder.cpp
+	SkeletonMeshBuilder.h
+	SplitByBoneCountProcess.cpp
+	SplitByBoneCountProcess.h
+	SmoothingGroups.h
+	StandardShapes.cpp
+	StandardShapes.h
+	TargetAnimation.cpp
+	TargetAnimation.h
+	RemoveComments.cpp
+	RemoveComments.h
+	Subdivision.cpp
+	Subdivision.h
+	Vertex.h
+	LineSplitter.h
+	TinyFormatter.h
+	Profiler.h
+	LogAux.h
+	Bitmap.cpp
+	Bitmap.h
+)
+SOURCE_GROUP(Common FILES ${Common_SRCS})
+
+SET( 3DS_SRCS
+	3DSConverter.cpp
+	3DSHelper.h
+	3DSLoader.cpp
+	3DSLoader.h
+)
+SOURCE_GROUP(3DS FILES ${3DS_SRCS})
+
+SET( AC_SRCS
+	ACLoader.cpp
+	ACLoader.h
+)
+SOURCE_GROUP( AC FILES ${AC_SRCS})
+
+SET( ASE_SRCS
+	ASELoader.cpp
+	ASELoader.h
+	ASEParser.cpp
+	ASEParser.h
+)
+SOURCE_GROUP( ASE FILES ${ASE_SRCS})
+
+SET( B3D_SRCS
+	B3DImporter.cpp
+	B3DImporter.h
+)
+SOURCE_GROUP( B3D FILES ${B3D_SRCS})
+
+SET( BVH_SRCS
+	BVHLoader.cpp
+	BVHLoader.h
+)
+SOURCE_GROUP( BVH FILES ${BVH_SRCS})
+
+SET( Collada_SRCS
+	ColladaHelper.h
+	ColladaLoader.cpp
+	ColladaLoader.h
+	ColladaParser.cpp
+	ColladaParser.h
+	ColladaExporter.h
+	ColladaExporter.cpp
+)
+SOURCE_GROUP( Collada FILES ${Collada_SRCS})
+
+SET( DXF_SRCS
+	DXFLoader.cpp
+	DXFLoader.h
+	DXFHelper.h
+)
+SOURCE_GROUP( DXF FILES ${DXF_SRCS})
+
+SET( CSM_SRCS
+	CSMLoader.cpp
+	CSMLoader.h
+)
+SOURCE_GROUP( CSM FILES ${CSM_SRCS})
+
+SET( HMP_SRCS
+	HMPFileData.h
+	HMPLoader.cpp
+	HMPLoader.h
+	HalfLifeFileData.h
+)
+SOURCE_GROUP( HMP FILES ${HMP_SRCS})
+
+SET( Irr_SRCS
+	IRRLoader.cpp
+	IRRLoader.h
+	IRRMeshLoader.cpp
+	IRRMeshLoader.h
+	IRRShared.cpp
+	IRRShared.h
+)
+SOURCE_GROUP( Irr FILES ${Irr_SRCS})
+
+SET( LWO_SRCS
+	LWOAnimation.cpp
+	LWOAnimation.h
+	LWOBLoader.cpp
+	LWOFileData.h
+	LWOLoader.cpp
+	LWOLoader.h
+	LWOMaterial.cpp
+)
+SOURCE_GROUP( LWO FILES ${LWO_SRCS})
+
+SET( LWS_SRCS
+	LWSLoader.cpp
+	LWSLoader.h
+)
+SOURCE_GROUP( LWS FILES ${LWS_SRCS})
+
+
+
+SET( MD2_SRCS
+	MD2FileData.h
+	MD2Loader.cpp
+	MD2Loader.h
+	MD2NormalTable.h
+)
+SOURCE_GROUP( MD2 FILES ${MD2_SRCS})
+
+SET( MD3_SRCS
+	MD3FileData.h
+	MD3Loader.cpp
+	MD3Loader.h
+)
+SOURCE_GROUP( MD3 FILES ${MD3_SRCS})
+
+SET( MD5_SRCS
+	MD5Loader.cpp
+	MD5Loader.h
+	MD5Parser.cpp
+	MD5Parser.h
+)
+SOURCE_GROUP( MD5 FILES ${MD5_SRCS})
+
+SET( MDC_SRCS
+	MDCFileData.h
+	MDCLoader.cpp
+	MDCLoader.h
+	MDCNormalTable.h
+)
+SOURCE_GROUP( MDC FILES ${MDC_SRCS})
+
+SET( MDL_SRCS
+	MDLDefaultColorMap.h
+	MDLFileData.h
+	MDLLoader.cpp
+	MDLLoader.h
+	MDLMaterialLoader.cpp
+)
+SOURCE_GROUP( MDL FILES ${MDL_SRCS})
+
+SET( MaterialSystem_SRCS
+	MaterialSystem.cpp
+	MaterialSystem.h
+)
+SOURCE_GROUP( MaterialSystem FILES ${MaterialSystem_SRCS})
+
+SET( NFF_SRCS
+	NFFLoader.cpp
+	NFFLoader.h
+)
+SOURCE_GROUP( NFF FILES ${NFF_SRCS})
+
+SET( NDO_SRCS
+	NDOLoader.cpp
+	NDOLoader.h
+)
+SOURCE_GROUP( NDO FILES ${NDO_SRCS})
+
+SET( OFFFormat_SRCS
+	OFFLoader.cpp
+	OFFLoader.h
+)
+SOURCE_GROUP( OFFFormat FILES ${OFFFormat_SRCS})
+
+SET( Obj_SRCS
+	ObjFileData.h
+	ObjFileImporter.cpp
+	ObjFileImporter.h
+	ObjFileMtlImporter.cpp
+	ObjFileMtlImporter.h
+	ObjFileParser.cpp
+	ObjFileParser.h
+	ObjTools.h
+	
+	ObjExporter.h
+	ObjExporter.cpp
+)
+SOURCE_GROUP( Obj FILES ${Obj_SRCS})
+
+SET( Ogre_SRCS
+	OgreImporter.h
+	OgreStructs.h
+	OgreParsingUtils.h
+	OgreBinarySerializer.h
+	OgreXmlSerializer.h
+	OgreImporter.cpp
+	OgreStructs.cpp
+	OgreBinarySerializer.cpp
+	OgreXmlSerializer.cpp
+	OgreMaterial.cpp
+)
+SOURCE_GROUP( Ogre FILES ${Ogre_SRCS})
+
+SET( Ply_SRCS
+	PlyLoader.cpp
+	PlyLoader.h
+	PlyParser.cpp
+	PlyParser.h
+	PlyExporter.cpp
+	PlyExporter.h
+)
+SOURCE_GROUP( Ply FILES ${Ply_SRCS})
+
+SET(MS3D_SRCS
+	MS3DLoader.cpp
+	MS3DLoader.h
+)
+SOURCE_GROUP( MS3D FILES ${MS3D_SRCS})
+
+SET(COB_SRCS
+	COBLoader.cpp
+	COBLoader.h
+	COBScene.h
+)
+SOURCE_GROUP( COB FILES ${COB_SRCS})
+
+SET(BLENDER_SRCS
+	BlenderLoader.cpp
+	BlenderLoader.h
+	BlenderDNA.cpp
+	BlenderDNA.h
+	BlenderDNA.inl
+	BlenderScene.cpp
+	BlenderScene.h
+	BlenderSceneGen.h
+	BlenderIntermediate.h
+	BlenderModifier.h
+	BlenderModifier.cpp
+	BlenderBMesh.h
+	BlenderBMesh.cpp
+	BlenderTessellator.h
+	BlenderTessellator.cpp
+)
+SOURCE_GROUP( BLENDER FILES ${BLENDER_SRCS})
+
+SET(IFC_SRCS
+	IFCLoader.cpp
+	IFCLoader.h
+	IFCReaderGen.cpp
+	IFCReaderGen.h
+	IFCUtil.h
+	IFCUtil.cpp
+	IFCGeometry.cpp
+	IFCMaterial.cpp
+	IFCProfile.cpp
+	IFCCurve.cpp
+	IFCBoolean.cpp
+	IFCOpenings.cpp
+	STEPFile.h
+	STEPFileReader.h
+	STEPFileReader.cpp
+	STEPFileEncoding.cpp
+	STEPFileEncoding.h
+)
+SOURCE_GROUP( IFC FILES ${IFC_SRCS})
+
+SET( XGL_SRCS
+	XGLLoader.cpp
+	XGLLoader.h
+)
+SOURCE_GROUP( XGL FILES ${XGL_SRCS})
+
+
+SET(FBX_SRCS
+	FBXImporter.cpp
+	FBXCompileConfig.h
+	FBXImporter.h
+	FBXParser.cpp
+	FBXParser.h
+	FBXTokenizer.cpp
+	FBXTokenizer.h
+	FBXImportSettings.h
+	FBXConverter.h
+	FBXConverter.cpp
+	FBXUtil.h
+	FBXUtil.cpp
+	FBXDocument.h
+	FBXDocument.cpp
+	FBXProperties.h
+	FBXProperties.cpp
+	FBXMeshGeometry.cpp
+	FBXMaterial.cpp
+	FBXModel.cpp
+	FBXAnimation.cpp
+	FBXNodeAttribute.cpp
+	FBXDeformer.cpp
+	FBXBinaryTokenizer.cpp
+	FBXDocumentUtil.cpp
+)
+SOURCE_GROUP( FBX FILES ${FBX_SRCS})
+
+
+SET( PostProcessing_SRCS
+	CalcTangentsProcess.cpp
+	CalcTangentsProcess.h
+	ComputeUVMappingProcess.cpp
+	ComputeUVMappingProcess.h
+	ConvertToLHProcess.cpp
+	ConvertToLHProcess.h
+	FindDegenerates.cpp
+	FindDegenerates.h
+	FindInstancesProcess.cpp
+	FindInstancesProcess.h
+	FindInvalidDataProcess.cpp
+	FindInvalidDataProcess.h
+	FixNormalsStep.cpp
+	FixNormalsStep.h
+	GenFaceNormalsProcess.cpp
+	GenFaceNormalsProcess.h
+	GenVertexNormalsProcess.cpp
+	GenVertexNormalsProcess.h
+	PretransformVertices.cpp
+	PretransformVertices.h
+	ImproveCacheLocality.cpp
+	ImproveCacheLocality.h
+	JoinVerticesProcess.cpp
+	JoinVerticesProcess.h
+	LimitBoneWeightsProcess.cpp
+	LimitBoneWeightsProcess.h
+	RemoveRedundantMaterials.cpp
+	RemoveRedundantMaterials.h
+	RemoveVCProcess.cpp
+	RemoveVCProcess.h
+	SortByPTypeProcess.cpp
+	SortByPTypeProcess.h
+	SplitLargeMeshes.cpp
+	SplitLargeMeshes.h
+	TextureTransform.cpp
+	TextureTransform.h
+	TriangulateProcess.cpp
+	TriangulateProcess.h
+	ValidateDataStructure.cpp
+	ValidateDataStructure.h
+	OptimizeGraph.cpp
+	OptimizeGraph.h
+	OptimizeMeshes.cpp
+	OptimizeMeshes.h
+	DeboneProcess.cpp
+	DeboneProcess.h
+	ProcessHelper.h
+	ProcessHelper.cpp
+	PolyTools.h
+	MakeVerboseFormat.cpp
+	MakeVerboseFormat.h
+)
+SOURCE_GROUP( PostProcessing FILES ${PostProcessing_SRCS})
+
+SET( Q3D_SRCS
+	Q3DLoader.cpp
+	Q3DLoader.h
+)
+SOURCE_GROUP( Q3D FILES ${Q3D_SRCS})
+
+SET( Q3BSP_SRCS
+	Q3BSPFileData.h
+	Q3BSPFileParser.h
+	Q3BSPFileParser.cpp
+	Q3BSPFileImporter.h
+	Q3BSPFileImporter.cpp
+	Q3BSPZipArchive.h
+	Q3BSPZipArchive.cpp
+)
+SOURCE_GROUP( Q3BSP FILES ${Q3BSP_SRCS})
+
+SET( Raw_SRCS
+	RawLoader.cpp
+	RawLoader.h
+)
+SOURCE_GROUP( Raw FILES ${Raw_SRCS})
+
+SET( SMD_SRCS
+	SMDLoader.cpp
+	SMDLoader.h
+)
+SOURCE_GROUP( SMD FILES ${SMD_SRCS})
+
+SET( STL_SRCS
+	STLLoader.cpp
+	STLLoader.h
+	STLExporter.h
+	STLExporter.cpp
+)
+SOURCE_GROUP( STL FILES ${STL_SRCS})
+
+SET( Terragen_SRCS
+	TerragenLoader.cpp
+	TerragenLoader.h
+)
+SOURCE_GROUP( Terragen FILES ${Terragen_SRCS})
+
+SET( Unreal_SRCS
+	UnrealLoader.cpp
+	UnrealLoader.h
+)
+SOURCE_GROUP( Unreal FILES ${Unreal_SRCS})
+
+SET( XFile_SRCS
+	XFileHelper.h
+	XFileImporter.cpp
+	XFileImporter.h
+	XFileParser.cpp
+	XFileParser.h
+)
+SOURCE_GROUP( XFile FILES ${XFile_SRCS})
+
+SET( Exporter_SRCS
+	Exporter.cpp
+	AssimpCExport.cpp
+	BlobIOSystem.h
+)
+SOURCE_GROUP( Exporter FILES ${Exporter_SRCS})
+
+SET( Extra_SRCS
+	MD4FileData.h
+)
+SOURCE_GROUP( Extra FILES ${Extra_SRCS})
+
+SET( IrrXML_SRCS
+	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( IrrXML FILES ${IrrXML_SRCS})
+
+SET( ConvertUTF_SRCS
+	../contrib/ConvertUTF/ConvertUTF.h
+	../contrib/ConvertUTF/ConvertUTF.c
+)
+SOURCE_GROUP( ConvertUTF FILES ${ConvertUTF_SRCS})
+
+SET( Clipper_SRCS 
+	../contrib/clipper/clipper.hpp
+	../contrib/clipper/clipper.cpp
+)
+SOURCE_GROUP( Clipper FILES ${Clipper_SRCS})
+
+
+SET( Poly2Tri_SRCS 
+	../contrib/poly2tri/poly2tri/common/shapes.cc
+	../contrib/poly2tri/poly2tri/common/shapes.h
+	../contrib/poly2tri/poly2tri/common/utils.h
+	../contrib/poly2tri/poly2tri/sweep/advancing_front.h
+	../contrib/poly2tri/poly2tri/sweep/advancing_front.cc
+	../contrib/poly2tri/poly2tri/sweep/cdt.cc
+	../contrib/poly2tri/poly2tri/sweep/cdt.h
+	../contrib/poly2tri/poly2tri/sweep/sweep.cc
+	../contrib/poly2tri/poly2tri/sweep/sweep.h
+	../contrib/poly2tri/poly2tri/sweep/sweep_context.cc
+	../contrib/poly2tri/poly2tri/sweep/sweep_context.h
+)
+SOURCE_GROUP( Poly2Tri FILES ${Poly2Tri_SRCS})
+
+SET( unzip_SRCS
+	../contrib/unzip/crypt.h
+	../contrib/unzip/ioapi.c
+	../contrib/unzip/ioapi.h
+	../contrib/unzip/unzip.c
+	../contrib/unzip/unzip.h
+)
+SOURCE_GROUP( unzip FILES ${unzip_SRCS})
+
+
+# VC2010 fixes
+if(MSVC10)
+	OPTION( VC10_STDINT_FIX "Fix for VC10 Compiler regarding pstdint.h redefinition errors" OFF )
+	if( VC10_STDINT_FIX )
+		ADD_DEFINITIONS( -D_STDINT )
+	endif( VC10_STDINT_FIX )
+endif(MSVC10)
+
+ADD_DEFINITIONS( -DASSIMP_BUILD_DLL_EXPORT )
+
+if ( MSVC )
+	ADD_DEFINITIONS( -D_SCL_SECURE_NO_WARNINGS )
+	ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS )
+endif ( MSVC )
+
+if (UNZIP_FOUND)
+	SET (unzip_compile_SRCS "")
+else (UNZIP_FOUND)
+	SET (unzip_compile_SRCS ${unzip_SRCS})
+endif (UNZIP_FOUND)
+
+SET( assimp_src
+	# Assimp Files
+	${Core_SRCS}
+	${Common_SRCS}
+	${Logging_SRCS}
+	${Exporter_SRCS}
+	${PostProcessing_SRCS}
+
+	# Model Support
+	${3DS_SRCS}
+	${AC_SRCS}
+	${ASE_SRCS}
+	${B3D_SRCS}
+	${BVH_SRCS}
+	${Collada_SRCS}
+	${DXF_SRCS}
+	${CSM_SRCS}
+	${HMP_SRCS}
+	${Irr_SRCS}
+	${LWO_SRCS}
+	${LWS_SRCS}
+	${MD2_SRCS}
+	${MD3_SRCS}
+	${MD5_SRCS}
+	${MDC_SRCS}
+	${MDL_SRCS}
+	${MaterialSystem_SRCS}
+	${NFF_SRCS}
+	${OFFFormat_SRCS}
+	${Obj_SRCS}
+	${Ogre_SRCS}
+	${Ply_SRCS}
+	${Q3D_SRCS}
+	${Q3BSP_SRCS}
+	${Raw_SRCS}
+	${SMD_SRCS}
+	${STL_SRCS}
+	${Terragen_SRCS}
+	${Unreal_SRCS}
+	${XFile_SRCS}
+	${Extra_SRCS}
+	${MS3D_SRCS}
+	${COB_SRCS}
+	${BLENDER_SRCS}
+	${NDO_SRCS}
+	${IFC_SRCS}
+	${XGL_SRCS}
+	${FBX_SRCS}
+	
+	# Third-party libraries
+	${IrrXML_SRCS}
+	${ConvertUTF_SRCS}
+	${unzip_compile_SRCS}
+	${Poly2Tri_SRCS}
+	${Clipper_SRCS}
+	# Necessary to show the headers in the project when using the VC++ generator:
+	${Boost_SRCS}
+
+	${PUBLIC_HEADERS}
+	${COMPILER_HEADERS}
+	
+	# Old precompiled header
+	# (removed because the precompiled header is not updated when visual studio switch configuration which leads to failed compilation.
+	# Moreover it's a drag to recompile assimp entirely each time a modification is made to one of the included header, which is definitely counter-productive.)
+	AssimpPCH.cpp
+)
+
+#ADD_MSVC_PRECOMPILED_HEADER("AssimpPCH.h" "AssimpPCH.cpp" assimp_src)
+
+ADD_LIBRARY( assimp ${assimp_src} )
+
+SET_PROPERTY(TARGET assimp PROPERTY DEBUG_POSTFIX ${ASSIMP_DEBUG_POSTFIX})
+
+TARGET_LINK_LIBRARIES(assimp ${ZLIB_LIBRARIES})
+SET_TARGET_PROPERTIES( assimp PROPERTIES
+	VERSION ${ASSIMP_VERSION}
+	SOVERSION ${ASSIMP_SOVERSION} # use full version 
+    OUTPUT_NAME assimp${ASSIMP_LIBRARY_SUFFIX}
+)
+
+if (APPLE)
+    SET_TARGET_PROPERTIES( assimp PROPERTIES INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}")
+endif()
+
+# Build against external unzip, or add ../contrib/unzip so
+# assimp can #include "unzip.h"
+if (UNZIP_FOUND)
+	INCLUDE_DIRECTORIES(${UNZIP_INCLUDE_DIRS})
+	TARGET_LINK_LIBRARIES(assimp ${UNZIP_LIBRARIES})
+else (UNZIP_FOUND)
+	INCLUDE_DIRECTORIES("../contrib/unzip")
+endif (UNZIP_FOUND)
+
+INSTALL( TARGETS assimp
+         LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+         ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+         RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR}
+         COMPONENT ${LIBASSIMP_COMPONENT})
+INSTALL( FILES ${PUBLIC_HEADERS} DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}/assimp COMPONENT assimp-dev)
+INSTALL( FILES ${COMPILER_HEADERS} DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}/assimp/Compiler COMPONENT assimp-dev)
+
+if(MSVC AND ASSIMP_INSTALL_PDB)
+	install(FILES ${Assimp_BINARY_DIR}/code/Debug/assimp${ASSIMP_DEBUG_POSTFIX}.pdb
+		DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+		CONFIGURATIONS Debug
+	)
+	install(FILES ${Assimp_BINARY_DIR}/code/RelWithDebInfo/assimp.pdb
+		DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+		CONFIGURATIONS RelWithDebInfo
+	)
+endif ()

+ 1291 - 0
assimplib.mod/assimp/code/COBLoader.cpp

@@ -0,0 +1,1291 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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
+};	
+
+static const aiImporterDesc desc = {
+	"TrueSpace Object Importer",
+	"",
+	"",
+	"little-endian files only",
+	aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
+	0,
+	0,
+	0,
+	0,
+	"cob scn"
+};
+
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Loader meta information
+const aiImporterDesc* COBImporter::GetInfo () const
+{
+	return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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, aiMaterial* 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());
+					}
+
+					aiMaterial* mat = new aiMaterial();
+					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 ../ omitted 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	= strtoul10(all_tokens[3]);
+	out.parent_id = strtoul10(all_tokens[5]);
+	out.size = strtol10(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 = strtoul10(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=strtoul10(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 = strtoul10(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 = strtoul10(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 = strtoul10(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(strtoul10(splitter[2]));
+				face.flags = strtoul10(splitter[4]);
+				face.material = strtoul10(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 = strtoul10(s,&s);
+					if (',' != *s++) {
+						ThrowException("Expected , token in Face entry");
+					}
+					face.indices[i].uv_idx = strtoul10(s,&s);
+					if ('>' != *s++) {
+						ThrowException("Expected < token in Face entry");
+					}
+				}
+			}
+			if (nfo.version <= 4) {
+				break;
+			}
+		}
+		else if (splitter.match_start("DrawFlags")) {
+			msh.draw_flags = strtoul10(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 = strtoul10((++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 numf = reader.GetI4();
+	msh.faces.reserve(numf);
+	for(size_t i = 0; i < numf; ++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

+ 170 - 0
assimplib.mod/assimp/code/COBLoader.h

@@ -0,0 +1,170 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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
+{
+public:
+	COBImporter();
+	~COBImporter();
+
+
+public:
+
+	// --------------------
+	bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
+		bool checkSig) const;
+
+protected:
+
+	// --------------------
+	const aiImporterDesc* GetInfo () const;
+
+	// --------------------
+	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
assimplib.mod/assimp/code/COBScene.h

@@ -0,0 +1,271 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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=UINT_MAX};
+
+	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(UINT_MAX),
+		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

+ 299 - 0
assimplib.mod/assimp/code/CSMLoader.cpp

@@ -0,0 +1,299 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  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;
+
+static const aiImporterDesc desc = {
+	"CharacterStudio Motion Importer (MoCap)",
+	"",
+	"",
+	"",
+	aiImporterFlags_SupportTextFlavour,
+	0,
+	0,
+	0,
+	0,
+	"csm" 
+};
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+CSMImporter::CSMImporter()
+: noSkeletonMesh()
+{}
+
+// ------------------------------------------------------------------------------------------------
+// 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
+const aiImporterDesc* CSMImporter::GetInfo () const
+{
+	return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Setup configuration properties for the loader
+void CSMImporter::SetupProperties(const Importer* pImp)
+{
+	noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 = strtol10(buffer,&buffer);
+			}
+			else if (TokenMatchI(buffer,"lastframe",9))		{
+				SkipSpaces(&buffer);
+				last = strtol10(buffer,&buffer);
+			}
+			else if (TokenMatchI(buffer,"rate",4))	{
+				SkipSpaces(&buffer);
+				float d;
+				buffer = fast_atoreal_move<float>(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 = ::strtoul10(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_atoreal_move<float>(buffer, (float&)sub->mValue.x);
+
+							if(!SkipSpacesAndLineEnd(&buffer))
+								throw DeadlyImportError("CSM: Unexpected EOF occured reading sample y coord");
+							buffer = fast_atoreal_move<float>(buffer, (float&)sub->mValue.y);
+
+							if(!SkipSpacesAndLineEnd(&buffer))
+								throw DeadlyImportError("CSM: Unexpected EOF occured reading sample z coord");
+							buffer = fast_atoreal_move<float>(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;
+	
+	if (!noSkeletonMesh) {
+		SkeletonMeshBuilder maker(pScene,pScene->mRootNode,true);
+	}
+}
+
+#endif // !! ASSIMP_BUILD_NO_CSM_IMPORTER

+ 88 - 0
assimplib.mod/assimp/code/CSMLoader.h

@@ -0,0 +1,88 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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
+{
+public:
+	CSMImporter();
+	~CSMImporter();
+
+
+public:
+	// -------------------------------------------------------------------
+	bool CanRead( const std::string& pFile, IOSystem* pIOHandler, 
+		bool checkSig) const;
+
+protected:
+
+	// -------------------------------------------------------------------
+	const aiImporterDesc* GetInfo () const;
+
+	// -------------------------------------------------------------------
+	void SetupProperties(const Importer* pImp);
+
+	// -------------------------------------------------------------------
+	void InternReadFile( const std::string& pFile, aiScene* pScene, 
+		IOSystem* pIOHandler);
+
+private:
+
+	bool noSkeletonMesh;
+
+}; // end of class CSMImporter
+} // end of namespace Assimp
+#endif // AI_AC3DIMPORTER_H_INC
+

+ 318 - 0
assimplib.mod/assimp/code/CalcTangentsProcess.cpp

@@ -0,0 +1,318 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file 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"
+#include "TinyFormatter.h"
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+CalcTangentsProcess::CalcTangentsProcess()
+: configMaxAngle( AI_DEG_TO_RAD(45.f) )
+, configSourceUV( 0 ) {
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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)
+{
+    ai_assert( NULL != pImp );
+
+	// get the current value of the property
+	configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f);
+	configMaxAngle = std::max(std::min(configMaxAngle,45.0f),0.0f);
+	configMaxAngle = AI_DEG_TO_RAD(configMaxAngle);
+
+	configSourceUV = pImp->GetPropertyInteger(AI_CONFIG_PP_CT_TEXTURE_CHANNEL_INDEX,0);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void CalcTangentsProcess::Execute( aiScene* pScene)
+{
+    ai_assert( NULL != 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()->info("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 coordinates. That's a requirement
+	if( pMesh->mNormals == NULL)
+	{
+		DefaultLogger::get()->error("Failed to compute tangents; need normals");
+		return false;
+	}
+	if( configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV] )
+	{
+		DefaultLogger::get()->error((Formatter::format("Failed to compute tangents; need UV data in channel"),configSourceUV));
+		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[configSourceUV];
+	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] = aiVector3D(qnan);
+				meshBitang  [idx] = aiVector3D(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;
+        // when t1, t2, t3 in same position in UV space, just use default UV direction.
+        if ( 0 == sx && 0 ==sy && 0 == tx && 0 == ty ) {
+            sx = 0.0; sy = 1.0;
+            tx = 1.0; ty = 0.0;
+        }
+
+		// tangent points in the direction where to positive X axis of the texture coord's would point in model space
+		// bitangent's points along the positive Y axis of the texture coord's, 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();
+
+            // reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN.
+            bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z);
+            bool invalid_bitangent = is_special_float(localBitangent.x) || is_special_float(localBitangent.y) || is_special_float(localBitangent.z);
+            if (invalid_tangent != invalid_bitangent) {
+                if (invalid_tangent) {
+                    localTangent = meshNorm[p] ^ localBitangent;
+                    localTangent.Normalize();
+                } else {
+                    localBitangent = localTangent ^ meshNorm[p]; 
+                    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(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;
+}

+ 115 - 0
assimplib.mod/assimp/code/CalcTangentsProcess.h

@@ -0,0 +1,115 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+
+/** @file 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_WINONLY CalcTangentsProcess : public BaseProcess
+{
+public:
+
+	CalcTangentsProcess();
+	~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;
+	unsigned int configSourceUV;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_CALCTANGENTSPROCESS_H_INC

+ 821 - 0
assimplib.mod/assimp/code/ColladaExporter.cpp

@@ -0,0 +1,821 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
+#include "ColladaExporter.h"
+
+#include "Bitmap.h"
+#include "fast_atof.h"
+#include "SceneCombiner.h" 
+
+#include <ctime>
+#include <set>
+
+using namespace Assimp;
+
+namespace Assimp
+{
+
+// ------------------------------------------------------------------------------------------------
+// Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
+void ExportSceneCollada(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene)
+{
+	std::string path = "";
+	std::string file = pFile;
+
+	// We need to test both types of folder separators because pIOSystem->getOsSeparator() is not reliable.
+	// Moreover, the path given by some applications is not even consistent with the OS specific type of separator.
+	const char* end_path = std::max(strrchr(pFile, '\\'), strrchr(pFile, '/'));
+
+	if(end_path != NULL) {
+		path = std::string(pFile, end_path + 1 - pFile);
+		file = file.substr(end_path + 1 - pFile, file.npos);
+
+		std::size_t pos = file.find_last_of('.');
+		if(pos != file.npos) {
+			file = file.substr(0, pos);
+		}
+	}
+
+	// invoke the exporter 
+	ColladaExporter iDoTheExportThing( pScene, pIOSystem, path, file);
+
+	// we're still here - export successfully completed. Write result to the given IOSYstem
+	boost::scoped_ptr<IOStream> outfile (pIOSystem->Open(pFile,"wt"));
+	if(outfile == NULL) {
+		throw DeadlyExportError("could not open output .dae file: " + std::string(pFile));
+	}
+
+	// XXX maybe use a small wrapper around IOStream that behaves like std::stringstream in order to avoid the extra copy.
+	outfile->Write( iDoTheExportThing.mOutput.str().c_str(), static_cast<size_t>(iDoTheExportThing.mOutput.tellp()),1);
+}
+
+} // end of namespace Assimp
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor for a specific scene to export
+ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) : mIOSystem(pIOSystem), mPath(path), mFile(file)
+{
+	// make sure that all formatting happens using the standard, C locale and not the user's current locale
+	mOutput.imbue( std::locale("C") );
+
+	mScene = pScene;
+	mSceneOwned = false;
+
+	// set up strings
+	endstr = "\n"; 
+
+	// start writing
+	WriteFile();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor
+ColladaExporter::~ColladaExporter()
+{
+	if(mSceneOwned) {
+		delete mScene;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Starts writing the contents
+void ColladaExporter::WriteFile()
+{
+	// write the DTD
+	mOutput << "<?xml version=\"1.0\"?>" << endstr;
+	// COLLADA element start
+	mOutput << "<COLLADA xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.4.1\">" << endstr;
+	PushTag();
+
+	WriteTextures();
+	WriteHeader();
+
+	WriteMaterials();
+	WriteGeometryLibrary();
+
+	WriteSceneLibrary();
+
+	// useless Collada fu at the end, just in case we haven't had enough indirections, yet. 
+	mOutput << startstr << "<scene>" << endstr;
+	PushTag();
+	mOutput << startstr << "<instance_visual_scene url=\"#" + std::string(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr;
+	PopTag();
+	mOutput << startstr << "</scene>" << endstr;
+	PopTag();
+	mOutput << "</COLLADA>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the asset header
+void ColladaExporter::WriteHeader()
+{
+	static const float epsilon = 0.000001f;
+	static const aiQuaternion x_rot(aiMatrix3x3( 
+		0, -1,  0,
+		1,  0,  0,
+		0,  0,  1));
+	static const aiQuaternion y_rot(aiMatrix3x3(
+		1,  0,  0,
+		0,  1,  0,
+		0,  0,  1));
+	static const aiQuaternion z_rot(aiMatrix3x3(
+		1,  0,  0,
+		0,  0,  1,
+		0, -1,  0));
+
+	static const unsigned int date_nb_chars = 20;
+	char date_str[date_nb_chars];
+	std::time_t date = std::time(NULL);
+	std::strftime(date_str, date_nb_chars, "%Y-%m-%dT%H:%M:%S", std::localtime(&date));
+
+	std::string scene_name = mScene->mRootNode->mName.C_Str();
+
+	aiVector3D scaling;
+	aiQuaternion rotation;
+	aiVector3D position;
+	mScene->mRootNode->mTransformation.Decompose(scaling, rotation, position);
+
+	bool add_root_node = false;
+
+	float scale = 1.0;
+	if(std::abs(scaling.x - scaling.y) <= epsilon && std::abs(scaling.x - scaling.z) <= epsilon && std::abs(scaling.y - scaling.z) <= epsilon) {
+		scale = (float) ((((double) scaling.x) + ((double) scaling.y) + ((double) scaling.z)) / 3.0);
+	} else {
+		add_root_node = true;
+	}
+
+	std::string up_axis = "Y_UP";
+	if(rotation.Equal(x_rot, epsilon)) {
+		up_axis = "X_UP";
+	} else if(rotation.Equal(y_rot, epsilon)) {
+		up_axis = "Y_UP";
+	} else if(rotation.Equal(z_rot, epsilon)) {
+		up_axis = "Z_UP";
+	} else {
+		add_root_node = true;
+	}
+
+	if(! position.Equal(aiVector3D(0, 0, 0))) {
+		add_root_node = true;
+	}
+
+	if(mScene->mRootNode->mNumChildren == 0) {
+		add_root_node = true;
+	}
+
+	if(add_root_node) {
+		aiScene* scene;
+		SceneCombiner::CopyScene(&scene, mScene);
+
+		aiNode* root = new aiNode("Scene");
+
+		root->mNumChildren = 1;
+		root->mChildren = new aiNode*[root->mNumChildren];
+
+		root->mChildren[0] = scene->mRootNode;
+		scene->mRootNode->mParent = root;
+		scene->mRootNode = root;
+
+		mScene = scene;
+		mSceneOwned = true;
+
+		up_axis = "Y_UP";
+		scale = 1.0;
+	}
+
+	mOutput << startstr << "<asset>" << endstr;
+	PushTag();
+	mOutput << startstr << "<contributor>" << endstr;
+	PushTag();
+	mOutput << startstr << "<author>Assimp</author>" << endstr;
+	mOutput << startstr << "<authoring_tool>Assimp Collada Exporter</authoring_tool>" << endstr;
+	PopTag();
+	mOutput << startstr << "</contributor>" << endstr;
+	mOutput << startstr << "<created>" << date_str << "</created>" << endstr;
+	mOutput << startstr << "<modified>" << date_str << "</modified>" << endstr;
+	mOutput << startstr << "<unit name=\"meter\" meter=\"" << scale << "\" />" << endstr;
+	mOutput << startstr << "<up_axis>" << up_axis << "</up_axis>" << endstr;
+	PopTag();
+	mOutput << startstr << "</asset>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Write the embedded textures
+void ColladaExporter::WriteTextures() {
+	static const unsigned int buffer_size = 1024;
+	char str[buffer_size];
+
+	if(mScene->HasTextures()) {
+		for(unsigned int i = 0; i < mScene->mNumTextures; i++) {
+			// It would be great to be able to create a directory in portable standard C++, but it's not the case,
+			// so we just write the textures in the current directory.
+
+			aiTexture* texture = mScene->mTextures[i];
+
+			ASSIMP_itoa10(str, buffer_size, i + 1);
+
+			std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char*) texture->achFormatHint);
+
+			boost::scoped_ptr<IOStream> outfile(mIOSystem->Open(mPath + name, "wb"));
+			if(outfile == NULL) {
+				throw DeadlyExportError("could not open output texture file: " + mPath + name);
+			}
+
+			if(texture->mHeight == 0) {
+				outfile->Write((void*) texture->pcData, texture->mWidth, 1);
+			} else {
+				Bitmap::Save(texture, outfile.get());
+			}
+
+			outfile->Flush();
+
+			textures.insert(std::make_pair(i, name));
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single surface entry from the given material keys
+void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex)
+{
+  if( pSrcMat->GetTextureCount( pTexture) > 0 )
+  {
+    aiString texfile;
+    unsigned int uvChannel = 0;
+    pSrcMat->GetTexture( pTexture, 0, &texfile, NULL, &uvChannel);
+
+    std::string index_str(texfile.C_Str());
+
+    if(index_str.size() != 0 && index_str[0] == '*')
+    {
+		unsigned int index;
+
+    	index_str = index_str.substr(1, std::string::npos);
+
+    	try {
+    		index = (unsigned int) strtoul10_64(index_str.c_str());
+    	} catch(std::exception& error) {
+    		throw DeadlyExportError(error.what());
+    	}
+
+    	std::map<unsigned int, std::string>::const_iterator name = textures.find(index);
+
+    	if(name != textures.end()) {
+    		poSurface.texture = name->second;
+    	} else {
+    		throw DeadlyExportError("could not find embedded texture at index " + index_str);
+    	}
+    } else
+    {
+		poSurface.texture = texfile.C_Str();
+    }
+
+    poSurface.channel = uvChannel;
+	poSurface.exist = true;
+  } else
+  {
+    if( pKey )
+      poSurface.exist = pSrcMat->Get( pKey, pType, pIndex, poSurface.color) == aiReturn_SUCCESS;
+  }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes an image entry for the given surface
+void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd)
+{
+  if( !pSurface.texture.empty() )
+  {
+    mOutput << startstr << "<image id=\"" << pNameAdd << "\">" << endstr;
+    PushTag(); 
+    mOutput << startstr << "<init_from>";
+    for( std::string::const_iterator it = pSurface.texture.begin(); it != pSurface.texture.end(); ++it )
+    {
+      if( isalnum( *it) || *it == '_' || *it == '.' || *it == '/' || *it == '\\' )
+        mOutput << *it;
+      else
+        mOutput << '%' << std::hex << size_t( (unsigned char) *it) << std::dec;
+    }
+    mOutput << "</init_from>" << endstr;
+    PopTag();
+    mOutput << startstr << "</image>" << endstr;
+  }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes a color-or-texture entry into an effect definition
+void ColladaExporter::WriteTextureColorEntry( const Surface& pSurface, const std::string& pTypeName, const std::string& pImageName)
+{
+  if(pSurface.exist) {
+    mOutput << startstr << "<" << pTypeName << ">" << endstr;
+    PushTag();
+    if( pSurface.texture.empty() )
+    {
+      mOutput << startstr << "<color sid=\"" << pTypeName << "\">" << pSurface.color.r << "   " << pSurface.color.g << "   " << pSurface.color.b << "   " << pSurface.color.a << "</color>" << endstr;
+    } else
+    {
+      mOutput << startstr << "<texture texture=\"" << pImageName << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
+    }
+    PopTag();
+    mOutput << startstr << "</" << pTypeName << ">" << endstr;
+  }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the two parameters necessary for referencing a texture in an effect entry
+void ColladaExporter::WriteTextureParamEntry( const Surface& pSurface, const std::string& pTypeName, const std::string& pMatName)
+{
+  // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture
+  if( !pSurface.texture.empty() )
+  {
+    mOutput << startstr << "<newparam sid=\"" << pMatName << "-" << pTypeName << "-surface\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<surface type=\"2D\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<init_from>" << pMatName << "-" << pTypeName << "-image</init_from>" << endstr;
+    PopTag();
+    mOutput << startstr << "</surface>" << endstr;
+    PopTag();
+    mOutput << startstr << "</newparam>" << endstr;
+
+    mOutput << startstr << "<newparam sid=\"" << pMatName << "-" << pTypeName << "-sampler\">" << endstr;
+    PushTag();
+    mOutput << startstr << "<sampler2D>" << endstr;
+    PushTag();
+    mOutput << startstr << "<source>" << pMatName << "-" << pTypeName << "-surface</source>" << endstr;
+    PopTag();
+    mOutput << startstr << "</sampler2D>" << endstr;
+    PopTag();
+    mOutput << startstr << "</newparam>" << endstr;
+  }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes a scalar property
+void ColladaExporter::WriteFloatEntry( const Property& pProperty, const std::string& pTypeName)
+{
+	if(pProperty.exist) {
+		mOutput << startstr << "<" << pTypeName << ">" << endstr;
+		PushTag();
+		mOutput << startstr << "<float sid=\"" << pTypeName << "\">" << pProperty.value << "</float>" << endstr;
+		PopTag();
+		mOutput << startstr << "</" << pTypeName << ">" << endstr;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the material setup
+void ColladaExporter::WriteMaterials()
+{
+  materials.resize( mScene->mNumMaterials);
+
+  std::set<std::string> material_names;
+
+  /// collect all materials from the scene
+  size_t numTextures = 0;
+  for( size_t a = 0; a < mScene->mNumMaterials; ++a )
+  {
+    const aiMaterial* mat = mScene->mMaterials[a];
+
+    aiString name;
+    if( mat->Get( AI_MATKEY_NAME, name) != aiReturn_SUCCESS )
+      name = "mat";
+    materials[a].name = std::string( "m") + boost::lexical_cast<std::string> (a) + name.C_Str();
+    for( std::string::iterator it = materials[a].name.begin(); it != materials[a].name.end(); ++it ) {
+		// isalnum on MSVC asserts for code points in [0,255]. Thus prevent unwanted promotion
+		// of char to signed int and take the unsigned char value.
+      if( !isalnum( static_cast<uint8_t>(*it) ) ) {
+        *it = '_';
+	  }
+	}
+
+	aiShadingMode shading;
+	materials[a].shading_model = "phong";
+	if(mat->Get( AI_MATKEY_SHADING_MODEL, shading) == aiReturn_SUCCESS) {
+		if(shading == aiShadingMode_Phong) {
+			materials[a].shading_model = "phong";
+		} else if(shading == aiShadingMode_Blinn) {
+			materials[a].shading_model = "blinn";
+		} else if(shading == aiShadingMode_NoShading) {
+			materials[a].shading_model = "constant";
+		} else if(shading == aiShadingMode_Gouraud) {
+			materials[a].shading_model = "lambert";
+		}
+	}
+
+    ReadMaterialSurface( materials[a].ambient, mat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT);
+    if( !materials[a].ambient.texture.empty() ) numTextures++;
+    ReadMaterialSurface( materials[a].diffuse, mat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE);
+    if( !materials[a].diffuse.texture.empty() ) numTextures++;
+    ReadMaterialSurface( materials[a].specular, mat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR);
+    if( !materials[a].specular.texture.empty() ) numTextures++;
+    ReadMaterialSurface( materials[a].emissive, mat, aiTextureType_EMISSIVE, AI_MATKEY_COLOR_EMISSIVE);
+    if( !materials[a].emissive.texture.empty() ) numTextures++;
+    ReadMaterialSurface( materials[a].reflective, mat, aiTextureType_REFLECTION, AI_MATKEY_COLOR_REFLECTIVE);
+    if( !materials[a].reflective.texture.empty() ) numTextures++;
+	ReadMaterialSurface( materials[a].transparent, mat, aiTextureType_OPACITY, AI_MATKEY_COLOR_TRANSPARENT);
+    if( !materials[a].transparent.texture.empty() ) numTextures++;
+    ReadMaterialSurface( materials[a].normal, mat, aiTextureType_NORMALS, NULL, 0, 0);
+    if( !materials[a].normal.texture.empty() ) numTextures++;
+
+	materials[a].shininess.exist = mat->Get( AI_MATKEY_SHININESS, materials[a].shininess.value) == aiReturn_SUCCESS;
+	materials[a].transparency.exist = mat->Get( AI_MATKEY_OPACITY, materials[a].transparency.value) == aiReturn_SUCCESS;
+	materials[a].transparency.value = 1 - materials[a].transparency.value;
+	materials[a].index_refraction.exist = mat->Get( AI_MATKEY_REFRACTI, materials[a].index_refraction.value) == aiReturn_SUCCESS;
+  }
+
+  // output textures if present
+  if( numTextures > 0 )
+  {
+    mOutput << startstr << "<library_images>" << endstr; 
+    PushTag();
+    for( std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it )
+    { 
+      const Material& mat = *it;
+      WriteImageEntry( mat.ambient, mat.name + "-ambient-image");
+      WriteImageEntry( mat.diffuse, mat.name + "-diffuse-image");
+      WriteImageEntry( mat.specular, mat.name + "-specular-image");
+      WriteImageEntry( mat.emissive, mat.name + "-emission-image");
+      WriteImageEntry( mat.reflective, mat.name + "-reflective-image");
+	  WriteImageEntry( mat.transparent, mat.name + "-transparent-image");
+      WriteImageEntry( mat.normal, mat.name + "-normal-image");
+    }
+    PopTag();
+    mOutput << startstr << "</library_images>" << endstr;
+  }
+
+  // output effects - those are the actual carriers of information
+  if( !materials.empty() )
+  {
+    mOutput << startstr << "<library_effects>" << endstr;
+    PushTag();
+    for( std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it )
+    {
+      const Material& mat = *it;
+      // this is so ridiculous it must be right
+      mOutput << startstr << "<effect id=\"" << mat.name << "-fx\" name=\"" << mat.name << "\">" << endstr;
+      PushTag();
+      mOutput << startstr << "<profile_COMMON>" << endstr;
+      PushTag();
+
+      // write sampler- and surface params for the texture entries
+      WriteTextureParamEntry( mat.emissive, "emission", mat.name);
+      WriteTextureParamEntry( mat.ambient, "ambient", mat.name);
+      WriteTextureParamEntry( mat.diffuse, "diffuse", mat.name);
+      WriteTextureParamEntry( mat.specular, "specular", mat.name);
+      WriteTextureParamEntry( mat.reflective, "reflective", mat.name);
+	  WriteTextureParamEntry( mat.transparent, "transparent", mat.name);
+	  WriteTextureParamEntry( mat.normal, "normal", mat.name);
+
+      mOutput << startstr << "<technique sid=\"standard\">" << endstr;
+      PushTag();
+	  mOutput << startstr << "<" << mat.shading_model << ">" << endstr;
+      PushTag();
+
+      WriteTextureColorEntry( mat.emissive, "emission", mat.name + "-emission-sampler");
+      WriteTextureColorEntry( mat.ambient, "ambient", mat.name + "-ambient-sampler");
+      WriteTextureColorEntry( mat.diffuse, "diffuse", mat.name + "-diffuse-sampler");
+      WriteTextureColorEntry( mat.specular, "specular", mat.name + "-specular-sampler");
+	  WriteFloatEntry(mat.shininess, "shininess");
+      WriteTextureColorEntry( mat.reflective, "reflective", mat.name + "-reflective-sampler");
+	  WriteTextureColorEntry( mat.transparent, "transparent", mat.name + "-transparent-sampler");
+	  WriteFloatEntry(mat.transparency, "transparency");
+	  WriteFloatEntry(mat.index_refraction, "index_of_refraction");
+
+	  if(! mat.normal.texture.empty()) {
+		WriteTextureColorEntry( mat.normal, "bump", mat.name + "-normal-sampler");
+	  }
+
+      PopTag();
+      mOutput << startstr << "</" << mat.shading_model << ">" << endstr;
+      PopTag();
+      mOutput << startstr << "</technique>" << endstr;
+      PopTag();
+      mOutput << startstr << "</profile_COMMON>" << endstr;
+      PopTag();
+      mOutput << startstr << "</effect>" << endstr;
+    }
+    PopTag();
+    mOutput << startstr << "</library_effects>" << endstr;
+
+    // write materials - they're just effect references
+    mOutput << startstr << "<library_materials>" << endstr;
+    PushTag();
+    for( std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it )
+    {
+      const Material& mat = *it;
+      mOutput << startstr << "<material id=\"" << mat.name << "\" name=\"" << mat.name << "\">" << endstr;
+      PushTag();
+      mOutput << startstr << "<instance_effect url=\"#" << mat.name << "-fx\"/>" << endstr;
+      PopTag();
+      mOutput << startstr << "</material>" << endstr;
+    }
+    PopTag();
+    mOutput << startstr << "</library_materials>" << endstr;
+  }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the geometry library
+void ColladaExporter::WriteGeometryLibrary()
+{
+	mOutput << startstr << "<library_geometries>" << endstr;
+	PushTag();
+
+	for( size_t a = 0; a < mScene->mNumMeshes; ++a)
+		WriteGeometry( a);
+
+	PopTag();
+	mOutput << startstr << "</library_geometries>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the given mesh
+void ColladaExporter::WriteGeometry( size_t pIndex)
+{
+	const aiMesh* mesh = mScene->mMeshes[pIndex];
+	std::string idstr = GetMeshId( pIndex);
+
+  if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
+    return;
+
+	// opening tag
+	mOutput << startstr << "<geometry id=\"" << idstr << "\" name=\"" << idstr << "_name\" >" << endstr;
+	PushTag();
+
+	mOutput << startstr << "<mesh>" << endstr;
+	PushTag();
+
+	// Positions
+	WriteFloatArray( idstr + "-positions", FloatType_Vector, (float*) mesh->mVertices, mesh->mNumVertices);
+	// Normals, if any
+	if( mesh->HasNormals() )
+		WriteFloatArray( idstr + "-normals", FloatType_Vector, (float*) mesh->mNormals, mesh->mNumVertices);
+
+	// texture coords
+	for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a)
+	{
+		if( mesh->HasTextureCoords( a) )
+		{
+			WriteFloatArray( idstr + "-tex" + boost::lexical_cast<std::string> (a), mesh->mNumUVComponents[a] == 3 ? FloatType_TexCoord3 : FloatType_TexCoord2,
+				(float*) mesh->mTextureCoords[a], mesh->mNumVertices);
+		}
+	}
+
+	// vertex colors
+	for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a)
+	{
+		if( mesh->HasVertexColors( a) )
+			WriteFloatArray( idstr + "-color" + boost::lexical_cast<std::string> (a), FloatType_Color, (float*) mesh->mColors[a], mesh->mNumVertices);
+	}
+
+	// assemble vertex structure
+	mOutput << startstr << "<vertices id=\"" << idstr << "-vertices" << "\">" << endstr;
+	PushTag();
+	mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << idstr << "-positions\" />" << endstr;
+	if( mesh->HasNormals() )
+		mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << idstr << "-normals\" />" << endstr;
+	for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
+	{
+		if( mesh->HasTextureCoords( a) )
+			mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << idstr << "-tex" << a << "\" " /*<< "set=\"" << a << "\"" */ << " />" << endstr;
+	}
+	for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
+	{
+		if( mesh->HasVertexColors( a) )
+			mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << idstr << "-color" << a << "\" " /*<< set=\"" << a << "\"" */ << " />" << endstr;
+	}
+	
+	PopTag();
+	mOutput << startstr << "</vertices>" << endstr;
+
+	// write face setup
+	mOutput << startstr << "<polylist count=\"" << mesh->mNumFaces << "\" material=\"theresonlyone\">" << endstr;
+	PushTag();
+	mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstr << "-vertices\" />" << endstr;
+	
+	mOutput << startstr << "<vcount>";
+	for( size_t a = 0; a < mesh->mNumFaces; ++a )
+		mOutput << mesh->mFaces[a].mNumIndices << " ";
+	mOutput << "</vcount>" << endstr;
+	
+	mOutput << startstr << "<p>";
+	for( size_t a = 0; a < mesh->mNumFaces; ++a )
+	{
+		const aiFace& face = mesh->mFaces[a];
+		for( size_t b = 0; b < face.mNumIndices; ++b )
+			mOutput << face.mIndices[b] << " ";
+	}
+	mOutput << "</p>" << endstr;
+	PopTag();
+	mOutput << startstr << "</polylist>" << endstr;
+
+	// closing tags
+	PopTag();
+	mOutput << startstr << "</mesh>" << endstr;
+	PopTag();
+	mOutput << startstr << "</geometry>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes a float array of the given type
+void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataType pType, const float* pData, size_t pElementCount)
+{
+	size_t floatsPerElement = 0;
+	switch( pType )
+	{
+		case FloatType_Vector: floatsPerElement = 3; break;
+		case FloatType_TexCoord2: floatsPerElement = 2; break;
+		case FloatType_TexCoord3: floatsPerElement = 3; break;
+		case FloatType_Color: floatsPerElement = 3; break;
+		default:
+			return;
+	}
+
+	std::string arrayId = pIdString + "-array";
+
+	mOutput << startstr << "<source id=\"" << pIdString << "\" name=\"" << pIdString << "\">" << endstr;
+	PushTag();
+
+	// source array
+	mOutput << startstr << "<float_array id=\"" << arrayId << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
+	PushTag();
+
+	if( pType == FloatType_TexCoord2 )
+	{
+		for( size_t a = 0; a < pElementCount; ++a )
+		{
+			mOutput << pData[a*3+0] << " ";
+			mOutput << pData[a*3+1] << " ";
+		}
+	} 
+	else if( pType == FloatType_Color )
+	{
+		for( size_t a = 0; a < pElementCount; ++a )
+		{
+			mOutput << pData[a*4+0] << " ";
+			mOutput << pData[a*4+1] << " ";
+			mOutput << pData[a*4+2] << " ";
+		}
+	}
+	else
+	{
+		for( size_t a = 0; a < pElementCount * floatsPerElement; ++a )
+			mOutput << pData[a] << " ";
+	}
+	mOutput << "</float_array>" << endstr; 
+	PopTag();
+
+	// the usual Collada fun. Let's bloat it even more!
+	mOutput << startstr << "<technique_common>" << endstr;
+	PushTag();
+	mOutput << startstr << "<accessor count=\"" << pElementCount << "\" offset=\"0\" source=\"#" << arrayId << "\" stride=\"" << floatsPerElement << "\">" << endstr;
+	PushTag();
+
+	switch( pType )
+	{
+		case FloatType_Vector:
+			mOutput << startstr << "<param name=\"X\" type=\"float\" />" << endstr;
+			mOutput << startstr << "<param name=\"Y\" type=\"float\" />" << endstr;
+			mOutput << startstr << "<param name=\"Z\" type=\"float\" />" << endstr;
+			break;
+
+		case FloatType_TexCoord2:
+			mOutput << startstr << "<param name=\"S\" type=\"float\" />" << endstr;
+			mOutput << startstr << "<param name=\"T\" type=\"float\" />" << endstr;
+			break;
+
+		case FloatType_TexCoord3:
+			mOutput << startstr << "<param name=\"S\" type=\"float\" />" << endstr;
+			mOutput << startstr << "<param name=\"T\" type=\"float\" />" << endstr;
+			mOutput << startstr << "<param name=\"P\" type=\"float\" />" << endstr;
+			break;
+
+		case FloatType_Color:
+			mOutput << startstr << "<param name=\"R\" type=\"float\" />" << endstr;
+			mOutput << startstr << "<param name=\"G\" type=\"float\" />" << endstr;
+			mOutput << startstr << "<param name=\"B\" type=\"float\" />" << endstr;
+			break;
+	}
+
+	PopTag();
+	mOutput << startstr << "</accessor>" << endstr;
+	PopTag();
+	mOutput << startstr << "</technique_common>" << endstr;
+	PopTag();
+	mOutput << startstr << "</source>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Writes the scene library
+void ColladaExporter::WriteSceneLibrary()
+{
+	std::string scene_name = mScene->mRootNode->mName.C_Str();
+
+	mOutput << startstr << "<library_visual_scenes>" << endstr;
+	PushTag();
+	mOutput << startstr << "<visual_scene id=\"" + scene_name + "\" name=\"" + scene_name + "\">" << endstr;
+	PushTag();
+
+	// start recursive write at the root node
+	for( size_t a = 0; a < mScene->mRootNode->mNumChildren; ++a )
+		WriteNode( mScene->mRootNode->mChildren[a]);
+
+	PopTag();
+	mOutput << startstr << "</visual_scene>" << endstr;
+	PopTag();
+	mOutput << startstr << "</library_visual_scenes>" << endstr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively writes the given node
+void ColladaExporter::WriteNode( const aiNode* pNode)
+{
+	mOutput << startstr << "<node id=\"" << pNode->mName.data << "\" name=\"" << pNode->mName.data << "\">" << endstr;
+	PushTag();
+
+	// write transformation - we can directly put the matrix there
+	// TODO: (thom) decompose into scale - rot - quad to allow adressing it by animations afterwards
+	const aiMatrix4x4& mat = pNode->mTransformation;
+	mOutput << startstr << "<matrix>";
+	mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " ";
+	mOutput << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << " ";
+	mOutput << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << " ";
+	mOutput << mat.d1 << " " << mat.d2 << " " << mat.d3 << " " << mat.d4;
+	mOutput << "</matrix>" << endstr;
+
+	// instance every geometry
+	for( size_t a = 0; a < pNode->mNumMeshes; ++a )
+	{
+		const aiMesh* mesh = mScene->mMeshes[pNode->mMeshes[a]];
+	// do not instanciate mesh if empty. I wonder how this could happen
+	if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
+		continue;
+
+		mOutput << startstr << "<instance_geometry url=\"#" << GetMeshId( pNode->mMeshes[a]) << "\">" << endstr;
+		PushTag();
+	mOutput << startstr << "<bind_material>" << endstr;
+	PushTag();
+	mOutput << startstr << "<technique_common>" << endstr;
+	PushTag();
+	mOutput << startstr << "<instance_material symbol=\"theresonlyone\" target=\"#" << materials[mesh->mMaterialIndex].name << "\" />" << endstr;
+		PopTag();
+	mOutput << startstr << "</technique_common>" << endstr;
+	PopTag();
+	mOutput << startstr << "</bind_material>" << endstr;
+	PopTag();
+		mOutput << startstr << "</instance_geometry>" << endstr;
+	}
+
+	// recurse into subnodes
+	for( size_t a = 0; a < pNode->mNumChildren; ++a )
+		WriteNode( pNode->mChildren[a]);
+
+	PopTag();
+	mOutput << startstr << "</node>" << endstr;
+}
+
+#endif
+#endif
+

+ 176 - 0
assimplib.mod/assimp/code/ColladaExporter.h

@@ -0,0 +1,176 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ColladaExporter.h
+ * Declares the exporter class to write a scene to a Collada file
+ */
+#ifndef AI_COLLADAEXPORTER_H_INC
+#define AI_COLLADAEXPORTER_H_INC
+
+#include "../include/assimp/ai_assert.h"
+#include <sstream>
+
+struct aiScene;
+struct aiNode;
+
+namespace Assimp	
+{
+
+/// Helper class to export a given scene to a Collada file. Just for my personal
+/// comfort when implementing it.
+class ColladaExporter
+{
+public:
+	/// Constructor for a specific scene to export
+	ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file);
+
+	/// Destructor
+	virtual ~ColladaExporter();
+
+protected:
+	/// Starts writing the contents
+	void WriteFile();
+
+	/// Writes the asset header
+	void WriteHeader();
+
+	/// Writes the embedded textures
+	void WriteTextures();
+
+	/// Writes the material setup
+	void WriteMaterials();
+
+	/// Writes the geometry library
+	void WriteGeometryLibrary();
+
+	/// Writes the given mesh
+	void WriteGeometry( size_t pIndex);
+
+	enum FloatDataType { FloatType_Vector, FloatType_TexCoord2, FloatType_TexCoord3, FloatType_Color };
+
+	/// Writes a float array of the given type
+	void WriteFloatArray( const std::string& pIdString, FloatDataType pType, const float* pData, size_t pElementCount);
+
+	/// Writes the scene library
+	void WriteSceneLibrary();
+
+	/// Recursively writes the given node
+	void WriteNode( const aiNode* pNode);
+
+	/// Enters a new xml element, which increases the indentation
+	void PushTag() { startstr.append( "  "); }
+	/// Leaves an element, decreasing the indentation
+	void PopTag() { ai_assert( startstr.length() > 1); startstr.erase( startstr.length() - 2); }
+
+	/// Creates a mesh ID for the given mesh
+	std::string GetMeshId( size_t pIndex) const { return std::string( "meshId" ) + boost::lexical_cast<std::string> (pIndex); }
+
+public:
+	/// Stringstream to write all output into
+	std::stringstream mOutput;
+
+protected:
+	/// The IOSystem for output
+	IOSystem* mIOSystem;
+
+	/// Path of the directory where the scene will be exported
+	const std::string mPath;
+
+	/// Name of the file (without extension) where the scene will be exported
+	const std::string mFile;
+
+	/// The scene to be written
+	const aiScene* mScene;
+	bool mSceneOwned;
+
+	/// current line start string, contains the current indentation for simple stream insertion
+	std::string startstr;
+	/// current line end string for simple stream insertion
+	std::string endstr;
+
+  // pair of color and texture - texture precedences color
+  struct Surface 
+  { 
+    bool exist;
+    aiColor4D color; 
+    std::string texture; 
+    size_t channel; 
+    Surface() { exist = false; channel = 0; }
+  };
+
+  struct Property
+  {
+    bool exist;
+	float value;
+	Property() { exist = false; }
+  };
+
+  // summarize a material in an convinient way. 
+  struct Material
+  {
+    std::string name;
+    std::string shading_model;
+    Surface ambient, diffuse, specular, emissive, reflective, transparent, normal;
+   	Property shininess, transparency, index_refraction;
+
+    Material() {}
+  };
+
+  std::vector<Material> materials;
+
+  std::map<unsigned int, std::string> textures;
+
+protected:
+  /// Dammit C++ - y u no compile two-pass? No I have to add all methods below the struct definitions
+  /// Reads a single surface entry from the given material keys
+  void ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex);
+  /// Writes an image entry for the given surface
+  void WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd);
+  /// Writes the two parameters necessary for referencing a texture in an effect entry
+  void WriteTextureParamEntry( const Surface& pSurface, const std::string& pTypeName, const std::string& pMatName);
+  /// Writes a color-or-texture entry into an effect definition
+  void WriteTextureColorEntry( const Surface& pSurface, const std::string& pTypeName, const std::string& pImageName);
+  /// Writes a scalar property
+  void WriteFloatEntry( const Property& pProperty, const std::string& pTypeName);
+};
+
+}
+
+#endif // !! AI_COLLADAEXPORTER_H_INC

+ 604 - 0
assimplib.mod/assimp/code/ColladaHelper.h

@@ -0,0 +1,604 @@
+/** Helper structures for the Collada loader */
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef 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
+#define ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET 1e9f
+
+/** A collada light source. */
+struct Light
+{	
+	Light()
+		:	mAttConstant     (1.f)
+		,	mAttLinear       (0.f)
+		,	mAttQuadratic    (0.f)
+		,	mFalloffAngle    (180.f)
+		,	mFalloffExponent (0.f)
+		,	mPenumbraAngle	 (ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET)
+		,	mOuterAngle		 (ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET)
+		,	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;
+	}
+    
+    std::string mName;
+
+	// 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		(UINT_MAX)
+		,	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 UINT_MAX 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

+ 1568 - 0
assimplib.mod/assimp/code/ColladaLoader.cpp

@@ -0,0 +1,1568 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Implementation of the Collada loader */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
+
+#include "../include/assimp/anim.h"
+#include "ColladaLoader.h"
+#include "ColladaParser.h"
+
+#include "fast_atof.h"
+#include "ParsingUtils.h"
+#include "SkeletonMeshBuilder.h"
+
+#include "time.h"
+
+using namespace Assimp;
+
+static const aiImporterDesc desc = {
+	"Collada Importer",
+	"",
+	"",
+	"http://collada.org",
+	aiImporterFlags_SupportTextFlavour,
+	1,
+	3,
+	1,
+	5,
+	"dae" 
+};
+
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+ColladaLoader::ColladaLoader()
+: noSkeletonMesh(), ignoreUpDirection(false)
+{}
+
+// ------------------------------------------------------------------------------------------------
+// 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;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ColladaLoader::SetupProperties(const Importer* pImp)
+{
+	noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
+	ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION,0) != 0;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Get file extension list
+const aiImporterDesc* ColladaLoader::GetInfo () const
+{
+	return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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();
+	mAnims.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);
+
+        // Apply unitsize scale calculation
+        pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0,  0,  0, 
+                                                          0,  parser.mUnitSize,  0,  0,
+                                                          0,  0,  parser.mUnitSize,  0,
+                                                          0,  0,  0,  1);
+        if( !ignoreUpDirection ) {
+        // Convert to Y_UP, if different orientation
+		if( parser.mUpDirection == ColladaParser::UP_X)
+			pScene->mRootNode->mTransformation *= aiMatrix4x4( 
+				 0, -1,  0,  0, 
+				 1,  0,  0,  0,
+				 0,  0,  1,  0,
+				 0,  0,  0,  1);
+		else if( parser.mUpDirection == ColladaParser::UP_Z)
+			pScene->mRootNode->mTransformation *= aiMatrix4x4( 
+				 1,  0,  0,  0, 
+				 0,  0,  1,  0,
+				 0, -1,  0,  0,
+				 0,  0,  0,  1);
+        }
+	// 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) {
+	
+		if (!noSkeletonMesh) {
+			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);
+		const 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 = 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. 
+			if (srcLight->mOuterAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f))
+			{
+				// ... some deprecation magic. 
+				if (srcLight->mPenumbraAngle >= ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET*(1-1e-6f))
+				{
+					// 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. 
+		 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;
+                if(dstMesh->mName.length == 0)
+                {
+                    dstMesh->mName = mid.mMeshOrController;
+                }
+      }
+		}
+	}
+
+	// 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;
+    
+    dstMesh->mName = pSrcMesh->mName;
+
+	// 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 glorious 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 addressing scheme. ");
+
+		// 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;
+		weightStartPerVertex.resize(pSrcController->mWeightCounts.size(),pSrcController->mWeights.end());
+
+		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);
+	}
+
+	mAnims.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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 expect 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 = SIZE_MAX;
+			for( size_t a = 0; a < srcNode->mTransforms.size(); ++a)
+				if( srcNode->mTransforms[a].mID == entry.mTransformId)
+					entry.mTransformIndex = a;
+
+			if( entry.mTransformIndex == SIZE_MAX) {
+				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));
+
+      if( e.mTimeAccessor->mCount > 0 )
+      {
+			  // 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));
+      }
+		}
+
+    std::vector<aiMatrix4x4> resultTrafos;
+    if( !entries.empty() && entries.front().mTimeAccessor->mCount > 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;
+		  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, but we aren't that fixated on valid input data
+//		ai_assert( resultTrafos.size() > 0);
+
+		// build an animation channel for the given node out of these trafo keys
+    if( !resultTrafos.empty() )
+    {
+		  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)
+		  {
+			  aiMatrix4x4 mat = resultTrafos[a];
+			  double time = double( mat.d4); // remember? time is stored in mat.d4
+        mat.d4 = 1.0f;
+
+			  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);
+    } else
+    {
+      DefaultLogger::get()->warn( "Collada loader: found empty animation channel, ignored. Please check your exporter.");
+    }
+	}
+
+	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 ( aiMaterial& 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 != UINT_MAX)
+		map = sampler.mUVId;
+	else {
+		map = -1;
+		for (std::string::const_iterator it = sampler.mUVChannel.begin();it != sampler.mUVChannel.end(); ++it){
+			if (IsNumeric(*it)) {
+				map = strtoul10(&(*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)
+	{
+		aiMaterial&  mat = (aiMaterial&)*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_NORMALS);
+
+		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( 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::iterator effIt = pParser.mEffectLibrary.find( material.mEffect);
+		if( effIt == pParser.mEffectLibrary.end())
+			continue;
+		Collada::Effect& effect = effIt->second;
+
+		// create material
+		aiMaterial* mat = new aiMaterial;
+		aiString name( matIt->first);
+		mat->AddProperty(&name,AI_MATKEY_NAME);
+
+		// store the material
+		mMaterialIndexByName[matIt->first] = newMats.size();
+		newMats.push_back( std::pair<Collada::Effect*, aiMaterial*>( &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)
+	{
+		aiMaterial* mat = new aiMaterial;
+		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';
+	}
+
+  // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... 
+  // I need to filter it without destroying linux paths starting with "/somewhere"
+  if( ss.data[0] == '/' && isalpha( ss.data[1]) && ss.data[2] == ':' )
+  {
+    ss.length--;
+    memmove( ss.data, ss.data+1, ss.length);
+    ss.data[ss.length] = 0;
+  }
+
+  // find and convert all %xy special chars
+  char* out = ss.data;
+  for( const char* it = ss.data; it != ss.data + ss.length; /**/ )
+  {
+    if( *it == '%' && (it + 3) < ss.data + ss.length )
+    {
+      // separate the number to avoid dragging in chars from behind into the parsing
+      char mychar[3] = { it[1], it[2], 0 };
+      size_t nbr = strtoul16( mychar);
+      it += 3;
+      *out++ = (char)(nbr & 0xFF);
+    } else
+    {
+      *out++ = *it++;
+    }
+  }
+
+  // adjust length and terminator of the shortened string
+  *out = 0;
+  ss.length = (ptrdiff_t) (out - ss.data);
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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

+ 242 - 0
assimplib.mod/assimp/code/ColladaLoader.h

@@ -0,0 +1,242 @@
+/** Defines the collada loader class */
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef 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
+{
+public:
+	ColladaLoader();
+	~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:
+	/** Return importer meta information.
+	 * See #BaseImporter::GetInfo for the details
+	 */
+	const aiImporterDesc* GetInfo () const;
+
+	void SetupProperties(const Importer* pImp);
+
+	/** Imports the given file into the given scene structure. 
+	 * See BaseImporter::InternReadFile() for details
+	 */
+	void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+	/** 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( 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 ( aiMaterial& 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;
+
+	bool noSkeletonMesh;
+	bool ignoreUpDirection;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_COLLADALOADER_H_INC

+ 2829 - 0
assimplib.mod/assimp/code/ColladaParser.cpp

@@ -0,0 +1,2829 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file ColladaParser.cpp
+ *  @brief Implementation of the Collada parser helper
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_COLLADA_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 single idea they've come up with
+				int sourceIndex = GetAttribute( "source");
+				pController.mMeshId = mReader->getAttributeValue( sourceIndex) + 1;
+			} 
+			else if( IsElement( "bind_shape_matrix"))
+			{
+				// content is 16 floats to define a matrix... it seems to be important for some models
+	      const char* content = GetTextContent();
+
+	      // read the 16 floats
+	      for( unsigned int a = 0; a < 16; a++)
+	      {
+		      // read a number
+          content = fast_atoreal_move<float>( 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\" in source attribute of <joints> data <input> element") % 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 <joints> data <input> element") % 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") && vertexCount > 0 )
+			{
+				InputChannel channel;
+
+				int indexSemantic = GetAttribute( "semantic");
+				const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
+				int indexSource = GetAttribute( "source");
+				const char* attrSource = mReader->getAttributeValue( indexSource);
+				int indexOffset = TestAttribute( "offset");
+				if( indexOffset >= 0)
+					channel.mOffset = mReader->getAttributeValueAsInt( indexOffset);
+
+				// local URLS always start with a '#'. We don't support global URLs
+				if( attrSource[0] != '#')
+					ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\" in source attribute of <vertex_weights> data <input> element") % 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_weights> data <input> element") % attrSemantic));
+
+				// skip inner data, if present
+				if( !mReader->isEmptyElement())
+					SkipElement();
+			}
+			else if( IsElement( "vcount") && vertexCount > 0 )
+			{
+				// read weight count per vertex
+				const char* text = GetTextContent();
+				size_t numWeights = 0;
+				for( std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it)
+				{
+					if( *text == 0)
+						ThrowException( "Out of data while reading <vcount>");
+
+					*it = strtoul10( text, &text);
+					numWeights += *it;
+					SkipSpacesAndLineEnd( &text);
+				}
+
+				TestClosing( "vcount");
+
+				// reserve weight count 
+				pController.mWeights.resize( numWeights);
+			}
+			else if( IsElement( "v") && vertexCount > 0 )
+			{
+				// read JointIndex - WeightIndex pairs
+				const char* text = GetTextContent();
+
+				for( std::vector< std::pair<size_t, size_t> >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it)
+				{
+					if( *text == 0)
+						ThrowException( "Out of data while reading <vertex_weights>");
+					it->first = strtoul10( text, &text);
+					SkipSpacesAndLineEnd( &text);
+					if( *text == 0)
+						ThrowException( "Out of data while reading <vertex_weights>");
+					it->second = strtoul10( text, &text);
+					SkipSpacesAndLineEnd( &text);
+				}
+
+				TestClosing( "v");
+			}
+			else
+			{
+				// ignore the rest
+				SkipElement();
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) 
+		{
+			if( strcmp( mReader->getNodeName(), "vertex_weights") != 0)
+				ThrowException( "Expected end of <vertex_weights> element.");
+
+			break;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the image library contents
+void ColladaParser::ReadImageLibrary()
+{
+	if( mReader->isEmptyElement())
+		return;
+
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+			if( IsElement( "image"))
+			{
+				// read ID. Another entry which is "optional" by design but obligatory in reality
+				int attrID = GetAttribute( "id");
+				std::string id = mReader->getAttributeValue( attrID);
+
+				// create an entry and store it in the library under its ID
+				mImageLibrary[id] = Image();
+
+				// read on from there
+				ReadImage( mImageLibrary[id]);
+			} else
+			{
+				// ignore the rest
+				SkipElement();
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+			if( strcmp( mReader->getNodeName(), "library_images") != 0)
+				ThrowException( "Expected end of <library_images> element.");
+
+			break;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an image entry into the given image
+void ColladaParser::ReadImage( Collada::Image& pImage)
+{
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT){
+			// Need to run different code paths here, depending on the Collada XSD version
+			if (IsElement("image")) {
+                SkipElement();
+            }
+			else if(  IsElement( "init_from"))
+			{
+				if (mFormat == FV_1_4_n) 
+				{
+					// FIX: C4D exporter writes empty <init_from/> tags
+					if (!mReader->isEmptyElement()) {
+						// element content is filename - hopefully
+						const char* sz = TestTextContent();
+						if (sz)pImage.mFileName = sz;
+						TestClosing( "init_from");
+					}
+					if (!pImage.mFileName.length()) {
+						pImage.mFileName = "unknown_texture";
+					}
+				}
+				else if (mFormat == FV_1_5_n) 
+				{
+					// make sure we skip over mip and array initializations, which
+					// we don't support, but which could confuse the loader if 
+					// they're not skipped.
+					int attrib = TestAttribute("array_index");
+					if (attrib != -1 && mReader->getAttributeValueAsInt(attrib) > 0) {
+						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_atoreal_move<float>( content, (float&)pLight.mColor.r);
+				SkipSpacesAndLineEnd( &content);
+				
+				content = fast_atoreal_move<float>( content, (float&)pLight.mColor.g);
+				SkipSpacesAndLineEnd( &content);
+
+				content = fast_atoreal_move<float>( 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 (IsElement("falloff")) {
+				pLight.mOuterAngle = ReadFloatFromTextContent();
+				TestClosing("falloff");
+			}
+			else if (IsElement("hotspot_beam")) {
+				pLight.mFalloffAngle = ReadFloatFromTextContent();
+				TestClosing("hotspot_beam");
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+			if( strcmp( mReader->getNodeName(), "light") == 0)
+				break;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a camera entry into the given light
+void ColladaParser::ReadCamera( Collada::Camera& pCamera)
+{
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+			if (IsElement("camera")) {
+                SkipElement();
+            }
+			else if (IsElement("orthographic")) {
+				pCamera.mOrtho = true;
+			}
+			else if (IsElement("xfov") || IsElement("xmag")) {
+				pCamera.mHorFov = ReadFloatFromTextContent();
+				TestClosing((pCamera.mOrtho ? "xmag" : "xfov"));
+			}
+			else if (IsElement("yfov") || IsElement("ymag")) {
+				pCamera.mVerFov = ReadFloatFromTextContent();
+				TestClosing((pCamera.mOrtho ? "ymag" : "yfov"));
+			}
+			else if (IsElement("aspect_ratio")) {
+				pCamera.mAspect = ReadFloatFromTextContent();
+				TestClosing("aspect_ratio");
+			}
+			else if (IsElement("znear")) {
+				pCamera.mZNear = ReadFloatFromTextContent();
+				TestClosing("znear");
+			}
+			else if (IsElement("zfar")) {
+				pCamera.mZFar = ReadFloatFromTextContent();
+				TestClosing("zfar");
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+			if( strcmp( mReader->getNodeName(), "camera") == 0)
+				break;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the effect library
+void ColladaParser::ReadEffectLibrary()
+{
+	if (mReader->isEmptyElement()) {
+		return;
+	}
+
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+			if( IsElement( "effect"))
+			{
+				// read ID. Do I have to repeat my ranting about "optional" attributes?
+				int attrID = GetAttribute( "id");
+				std::string id = mReader->getAttributeValue( attrID);
+
+				// create an entry and store it in the library under its ID
+				mEffectLibrary[id] = Effect();
+				// read on from there
+				ReadEffect( mEffectLibrary[id]);
+			} else
+			{
+				// ignore the rest
+				SkipElement();
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+			if( strcmp( mReader->getNodeName(), "library_effects") != 0)
+				ThrowException( "Expected end of <library_effects> element.");
+
+			break;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an effect entry into the given effect
+void ColladaParser::ReadEffect( Collada::Effect& pEffect)
+{
+	// for the moment we don't support any other type of effect.
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+		{
+			if( IsElement( "profile_COMMON"))
+				ReadEffectProfileCommon( pEffect);
+			else
+				SkipElement();
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) 
+		{
+			if( strcmp( mReader->getNodeName(), "effect") != 0)
+				ThrowException( "Expected end of <effect> element.");
+
+			break;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an COMMON effect profile
+void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect)
+{
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) 
+		{
+			if( IsElement( "newparam"))	{
+				// save ID
+				int attrSID = GetAttribute( "sid");
+				std::string sid = mReader->getAttributeValue( attrSID);
+				pEffect.mParams[sid] = EffectParam();
+				ReadEffectParam( pEffect.mParams[sid]);
+			} 
+			else if( IsElement( "technique") || IsElement( "extra"))
+			{
+				// just syntactic sugar
+			}
+
+			/* 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.mWrapV = ReadBoolFromTextContent();
+				TestClosing( "wrapV");
+			}
+			else if( IsElement( "mirrorU"))		{
+				out.mMirrorU = ReadBoolFromTextContent();
+				TestClosing( "mirrorU");
+			}
+			else if( IsElement( "mirrorV"))	{
+				out.mMirrorV = ReadBoolFromTextContent();
+				TestClosing( "mirrorV");
+			}
+			else if( IsElement( "repeatU"))	{
+				out.mTransform.mScaling.x = ReadFloatFromTextContent();
+				TestClosing( "repeatU");
+			}
+			else if( IsElement( "repeatV"))	{
+				out.mTransform.mScaling.y = ReadFloatFromTextContent();
+				TestClosing( "repeatV");
+			}
+			else if( IsElement( "offsetU"))	{
+				out.mTransform.mTranslation.x = ReadFloatFromTextContent();
+				TestClosing( "offsetU");
+			}
+			else if( IsElement( "offsetV"))	{
+				out.mTransform.mTranslation.y = ReadFloatFromTextContent();
+				TestClosing( "offsetV");
+			}
+			else if( IsElement( "rotateUV"))	{
+				out.mTransform.mRotation = ReadFloatFromTextContent();
+				TestClosing( "rotateUV");
+			}
+			else if( IsElement( "blend_mode"))	{
+				
+				const char* sz = GetTextContent();
+				// http://www.feelingsoftware.com/content/view/55/72/lang,en/
+				// NONE, OVER, IN, OUT, ADD, SUBTRACT, MULTIPLY, DIFFERENCE, LIGHTEN, DARKEN, SATURATE, DESATURATE and ILLUMINATE
+				if (0 == ASSIMP_strincmp(sz,"ADD",3)) 
+					out.mOp = aiTextureOp_Add;
+
+				else if (0 == ASSIMP_strincmp(sz,"SUBTRACT",8)) 
+					out.mOp = aiTextureOp_Subtract;
+
+				else if (0 == ASSIMP_strincmp(sz,"MULTIPLY",8)) 
+					out.mOp = aiTextureOp_Multiply;
+
+				else  {
+					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_atoreal_move<float>( content, (float&)pColor.r);
+				SkipSpacesAndLineEnd( &content);
+
+				content = fast_atoreal_move<float>( content, (float&)pColor.g);
+				SkipSpacesAndLineEnd( &content);
+
+				content = fast_atoreal_move<float>( content, (float&)pColor.b);
+				SkipSpacesAndLineEnd( &content);
+
+				content = fast_atoreal_move<float>( 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. Specification demands it to be there, but some exporters
+				// don't write it. It will be the default UV channel in case it's missing.
+				attrTex = TestAttribute( "texcoord");
+				if( attrTex >= 0 )
+	  				pSampler.mUVChannel = mReader->getAttributeValue( attrTex);
+				//SkipElement();
+			}
+			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_atoreal_move<float>( 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 the mesh name if it exists
+                const int nameIndex = TestAttribute("name");
+                if(nameIndex != -1)
+                {
+                    mesh->mName = mReader->getAttributeValue(nameIndex);
+                }
+
+				// read on from there
+				ReadGeometry( mesh);
+			} else
+			{
+				// ignore the rest
+				SkipElement();
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+		{
+			if( strcmp( mReader->getNodeName(), "library_geometries") != 0)
+				ThrowException( "Expected end of <library_geometries> element.");
+
+			break;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a geometry from the geometry library.
+void ColladaParser::ReadGeometry( Collada::Mesh* pMesh)
+{
+	if( mReader->isEmptyElement())
+		return;
+
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+		{
+			if( IsElement( "mesh"))
+			{
+				// read on from there
+				ReadMesh( pMesh);
+			} else
+			{
+				// ignore the rest
+				SkipElement();
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+		{
+			if( strcmp( mReader->getNodeName(), "geometry") != 0)
+				ThrowException( "Expected end of <geometry> element.");
+
+			break;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a mesh from the geometry library
+void ColladaParser::ReadMesh( Mesh* pMesh)
+{
+	if( mReader->isEmptyElement())
+		return;
+
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+		{
+			if( IsElement( "source"))
+			{
+				// we have professionals dealing with this
+				ReadSource();
+			}
+			else if( IsElement( "vertices"))
+			{
+				// read per-vertex mesh data
+				ReadVertexData( pMesh);
+			}
+			else if( IsElement( "triangles") || IsElement( "lines") || IsElement( "linestrips")
+				|| IsElement( "polygons") || IsElement( "polylist") || IsElement( "trifans") || IsElement( "tristrips")) 
+			{
+				// read per-index mesh data and faces setup
+				ReadIndexData( pMesh);
+			} else
+			{
+				// ignore the 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 care for your profiles 
+			}
+			else if( IsElement( "accessor"))
+			{
+				ReadAccessor( sourceID);
+			} else
+			{
+				// ignore the rest
+				SkipElement();
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+		{
+			if( strcmp( mReader->getNodeName(), "source") == 0)
+			{
+				// end of <source> - we're done
+				break;
+			}
+			else if( strcmp( mReader->getNodeName(), "technique_common") == 0)
+			{
+				// end of another meaningless element - read over it
+			} else
+			{
+				// everything else should be punished
+				ThrowException( "Expected end of <source> element.");
+			}
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a data array holding a number of floats, and stores it in the global library
+void ColladaParser::ReadDataArray()
+{
+	std::string elmName = mReader->getNodeName();
+	bool isStringArray = (elmName == "IDREF_array" || elmName == "Name_array");
+  bool isEmptyElement = mReader->isEmptyElement();
+
+	// read attributes
+	int indexID = GetAttribute( "id");
+	std::string id = mReader->getAttributeValue( indexID);
+	int indexCount = GetAttribute( "count");
+	unsigned int count = (unsigned int) mReader->getAttributeValueAsInt( indexCount);
+	const char* content = TestTextContent();
+
+  // read values and store inside an array in the data library
+  mDataLibrary[id] = Data();
+  Data& data = mDataLibrary[id];
+  data.mIsStringArray = isStringArray;
+
+  // some exporters write empty data arrays, but we need to conserve them anyways because others might reference them
+  if (content) 
+  { 
+		if( isStringArray)
+		{
+			data.mStrings.reserve( count);
+			std::string s;
+
+			for( unsigned int a = 0; a < count; a++)
+			{
+				if( *content == 0)
+					ThrowException( "Expected more values while reading IDREF_array contents.");
+
+				s.clear();
+				while( !IsSpaceOrNewLine( *content))
+					s += *content++;
+				data.mStrings.push_back( s);
+
+				SkipSpacesAndLineEnd( &content);
+			}
+		} else
+		{
+			data.mValues.reserve( count);
+
+			for( unsigned int a = 0; a < count; a++)
+			{
+				if( *content == 0)
+					ThrowException( "Expected more values while reading float_array contents.");
+
+				float value;
+				// read a number
+				content = fast_atoreal_move<float>( content, value);
+				data.mValues.push_back( value);
+				// skip whitespace after it
+				SkipSpacesAndLineEnd( &content);
+			}
+		}
+	}
+
+  // test for closing tag
+  if( !isEmptyElement )
+    TestClosing( elmName.c_str());
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads an accessor and stores it in the global library
+void ColladaParser::ReadAccessor( const std::string& pID)
+{
+	// read accessor attributes
+	int attrSource = GetAttribute( "source");
+	const char* source = mReader->getAttributeValue( attrSource);
+	if( source[0] != '#')
+		ThrowException( boost::str( boost::format( "Unknown reference format in url \"%s\" in source attribute of <accessor> element.") % 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( boost::str( boost::format( "Unexpected sub element <%s> in tag <accessor>") % mReader->getNodeName()));
+			}
+		} 
+		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( boost::str( boost::format( "Unexpected sub element <%s> in tag <vertices>") % mReader->getNodeName()));
+			}
+		} 
+		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())
+				{
+					if (numPrimitives)	// It is possible to define a mesh without any primitives
+					{
+						// case <polylist> - specifies the number of indices for each polygon
+						const char* content = GetTextContent();
+						vcount.reserve( numPrimitives);
+						for( unsigned int a = 0; a < numPrimitives; a++)
+						{
+							if( *content == 0)
+								ThrowException( "Expected more values while reading <vcount> contents.");
+							// read a number
+							vcount.push_back( (size_t) strtoul10( content, &content));
+							// skip whitespace after it
+							SkipSpacesAndLineEnd( &content);
+						}
+					}
+
+					TestClosing( "vcount");
+				}
+			}
+			else if( IsElement( "p"))
+			{
+				if( !mReader->isEmptyElement())
+				{
+					// now here the actual fun starts - these are the indices to construct the mesh data from
+					ReadPrimitives( pMesh, perIndexData, numPrimitives, vcount, primType);
+				}
+			} else
+			{
+				ThrowException( boost::str( boost::format( "Unexpected sub element <%s> in tag <%s>") % mReader->getNodeName() % elementName));
+			}
+		} 
+		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\" in source attribute of <input> element.") % 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\" in set attribute of <input> element") % (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 = SIZE_MAX; // 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);
+
+	if (pNumPrimitives > 0)	// It is possible to not contain any indicies
+	{
+		const char* content = GetTextContent();
+		while( *content != 0)
+		{
+			// read a value. 
+			// Hack: (thom) Some exporters put negative indices sometimes. We just try to carry on anyways.
+			int value = std::max( 0, strtol10( content, &content));
+			indices.push_back( size_t( value));
+			// skip whitespace after it
+			SkipSpacesAndLineEnd( &content);
+		}
+	}
+
+	// complain if the index count doesn't fit
+	if( expectedPointCount > 0 && indices.size() != expectedPointCount * numOffsets)
+		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.");
+			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
+			ai_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));
+
+				aiColor4D result(0, 0, 0, 1);
+				for (size_t i = 0; i < pInput.mResolved->mSize; ++i)
+				{
+					result[i] = obj[pInput.mResolved->mSubOffset[i]];
+				}
+				pMesh->mColors[pInput.mIndex].push_back(result); 
+			} 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
+				// ai_assert( TestAttribute( "sid") == -1);
+
+				if (pNode) 
+				{
+					pNode->mChildren.push_back( child);
+					child->mParent = pNode;
+				}
+				else 
+				{
+					// no parent node given, probably called from <library_nodes> element.
+					// create new node in node library
+					mNodeLibrary[child->mID] = child;
+				}
+
+				// read on recursively from there
+				ReadSceneNode( child);
+				continue;
+			}
+			// For any further stuff we need a valid node to work on
+			else if (!pNode)
+				continue;
+
+			if( IsElement( "lookat"))
+				ReadNodeTransformation( pNode, TF_LOOKAT);
+			else if( IsElement( "matrix"))
+				ReadNodeTransformation( pNode, TF_MATRIX);
+			else if( IsElement( "rotate"))
+				ReadNodeTransformation( pNode, TF_ROTATE);
+			else if( IsElement( "scale"))
+				ReadNodeTransformation( pNode, TF_SCALE);
+			else if( IsElement( "skew"))
+				ReadNodeTransformation( pNode, TF_SKEW);
+			else if( IsElement( "translate"))
+				ReadNodeTransformation( pNode, TF_TRANSLATE);
+			else if( IsElement( "render") && pNode->mParent == NULL && 0 == pNode->mPrimaryCamera.length())
+			{
+				// ... scene evaluation or, in other words, postprocessing pipeline,
+				// or, again in other words, a turing-complete description how to
+				// render a Collada scene. The only thing that is interesting for
+				// us is the primary camera.
+				int attrId = TestAttribute("camera_node");
+				if (-1 != attrId) 
+				{
+					const char* s = mReader->getAttributeValue(attrId);
+					if (s[0] != '#')
+						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_atoreal_move<float>( 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 in <instance_visual_scene> element");
+
+				// read the url of the scene to instance. Should be of format "#some_name"
+				int urlIndex = GetAttribute( "url");
+				const char* url = mReader->getAttributeValue( urlIndex);
+				if( url[0] != '#')
+					ThrowException( "Unknown reference format in <instance_visual_scene> element");
+
+				// find the referred scene, skip the leading # 
+				NodeLibrary::const_iterator sit = mNodeLibrary.find( url+1);
+				if( sit == mNodeLibrary.end())
+					ThrowException( "Unable to resolve visual_scene reference \"" + std::string(url) + "\" in <instance_visual_scene> element.");
+				mRootNode = sit->second;
+			} else	{
+				SkipElement();
+			}
+		} 
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END){
+			break;
+		} 
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Aborts the file reading with an exception
+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\" for 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())
+		return NULL;
+
+	// read contents of the element
+	if( !mReader->read() )
+		return NULL;
+	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: 
+				ai_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
assimplib.mod/assimp/code/ColladaParser.h

@@ -0,0 +1,341 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ColladaParser.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
assimplib.mod/assimp/code/ComputeUVMappingProcess.cpp

@@ -0,0 +1,504 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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 UINT_MAX;
+}
+
+// ------------------------------------------------------------------------------------------------
+// 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,0.f);
+		}
+	}
+	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,0.f);
+		}
+	}
+	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,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);
+		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,0.f);
+		}
+	}
+
+	// shouldn't be necessary to remove UV seams ...
+}
+
+// ------------------------------------------------------------------------------------------------
+void ComputeUVMappingProcess::ComputeBoxMapping( aiMesh*, aiVector3D* )
+{
+	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) ) == UINT_MAX ||
+								!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;
+					((aiMaterial*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex));
+				}
+			}
+		}
+	}
+	DefaultLogger::get()->debug("GenUVCoordsProcess finished");
+}

+ 144 - 0
assimplib.mod/assimp/code/ComputeUVMappingProcess.h

@@ -0,0 +1,144 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file 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/assimp/mesh.h"
+
+class ComputeUVMappingTest;
+namespace Assimp
+	{
+
+// ---------------------------------------------------------------------------
+/** ComputeUVMappingProcess - converts special mappings, such as spherical,
+ *  cylindrical or boxed to proper UV coordinates for rendering.
+*/
+class ComputeUVMappingProcess : public BaseProcess
+{
+public:
+	ComputeUVMappingProcess();
+	~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
assimplib.mod/assimp/code/ConvertToLHProcess.cpp

@@ -0,0 +1,318 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  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)
+{
+	aiMaterial* mat = (aiMaterial*)_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)
+{
+	aiMaterial* mat = (aiMaterial*)_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

+ 166 - 0
assimplib.mod/assimp/code/ConvertToLHProcess.h

@@ -0,0 +1,166 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  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/assimp/types.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 MakeLeftHandedProcess : public BaseProcess
+{
+	
+
+public:
+	MakeLeftHandedProcess();
+	~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 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 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

+ 230 - 0
assimplib.mod/assimp/code/DXFHelper.h

@@ -0,0 +1,230 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  DXFHelper.h 
+ *  @brief Internal utilities for the DXF loader.
+ */
+
+#ifndef INCLUDED_DXFHELPER_H
+#define INCLUDED_DXFHELPER_H
+
+#include "LineSplitter.h"
+#include "TinyFormatter.h"
+#include "StreamReader.h"
+
+namespace Assimp {
+	namespace DXF {
+
+
+// read pairs of lines, parse group code and value and provide utilities
+// to convert the data to the target data type.
+class LineReader
+{
+
+public:
+
+	LineReader(StreamReaderLE& reader)
+		 // do NOT skip empty lines. In DXF files, they count as valid data.
+		: splitter(reader,false,true)
+		, end()
+	{
+	}
+
+public:
+
+
+	// -----------------------------------------
+	bool Is(int gc, const char* what) const {
+		return groupcode == gc && !strcmp(what,value.c_str());
+	}
+
+	// -----------------------------------------
+	bool Is(int gc) const {
+		return groupcode == gc;
+	}
+
+	// -----------------------------------------
+	int GroupCode() const {
+		return groupcode;
+	}
+
+	// -----------------------------------------
+	const std::string& Value() const {
+		return value;
+	}
+
+	// -----------------------------------------
+	bool End() const {
+		return !((bool)*this);
+	}
+
+public:
+
+	// -----------------------------------------
+	unsigned int ValueAsUnsignedInt() const {
+		return strtoul10(value.c_str());
+	}
+
+	// -----------------------------------------
+	int ValueAsSignedInt() const {
+		return strtol10(value.c_str());
+	}
+
+	// -----------------------------------------
+	float ValueAsFloat() const {
+		return fast_atof(value.c_str());
+	}
+
+public:
+
+	// -----------------------------------------
+	/** pseudo-iterator increment to advance to the next (groupcode/value) pair */
+	LineReader& operator++() {
+		if (end) {
+			if (end == 1) {
+				++end;
+			}
+			return *this;
+		}
+
+		try {
+			groupcode = strtol10(splitter->c_str());
+			splitter++;
+
+			value = *splitter;
+			splitter++;
+
+			// automatically skip over {} meta blocks (these are for application use
+			// and currently not relevant for Assimp).
+			if (value.length() && value[0] == '{') {
+
+				size_t cnt = 0;
+				for(;splitter->length() && splitter->at(0) != '}'; splitter++, cnt++);
+
+				splitter++;
+				DefaultLogger::get()->debug((Formatter::format("DXF: skipped over control group ("),cnt," lines)"));
+			}
+		} catch(std::logic_error&) {
+			ai_assert(!splitter);
+		}
+		if (!splitter) {
+			end = 1;
+		}
+		return *this;
+	}
+
+	// -----------------------------------------
+	LineReader& operator++(int) {
+		return ++(*this);
+	}
+
+
+	// -----------------------------------------
+	operator bool() const {
+		return end <= 1;
+	}
+
+private:
+
+	LineSplitter splitter;
+	int groupcode;
+	std::string value;
+	int end;
+};
+
+
+
+// represents a POLYLINE or a LWPOLYLINE. or even a 3DFACE The data is converted as needed.
+struct PolyLine
+{
+	PolyLine()
+		: flags()
+	{}
+	
+	std::vector<aiVector3D> positions;
+	std::vector<aiColor4D>  colors;
+	std::vector<unsigned int> indices;
+	std::vector<unsigned int> counts;
+	unsigned int flags;
+
+	std::string layer;
+	std::string desc;
+};
+
+
+// reference to a BLOCK. Specifies its own coordinate system.
+struct InsertBlock
+{
+	InsertBlock()
+		: scale(1.f,1.f,1.f)
+		, angle()
+	{}
+
+	aiVector3D pos;
+	aiVector3D scale;
+	float angle;
+
+	std::string name;
+};
+
+
+// keeps track of all geometry in a single BLOCK.
+struct Block
+{
+	std::vector< boost::shared_ptr<PolyLine> > lines;
+	std::vector<InsertBlock> insertions;
+
+	std::string name;
+	aiVector3D base;
+};
+
+
+struct FileData
+{
+	// note: the LAST block always contains the stuff from ENTITIES.
+	std::vector<Block> blocks;
+};
+
+
+
+
+
+}}
+#endif

+ 913 - 0
assimplib.mod/assimp/code/DXFLoader.cpp

@@ -0,0 +1,913 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  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"
+
+#include "DXFHelper.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)
+
+// default vertex color that all uncolored vertices will receive
+#define AI_DXF_DEFAULT_COLOR aiColor4D(0.6f,0.6f,0.6f,0.6f)
+
+// color indices for DXF - 16 are supported, the table is 
+// taken directly from the DXF spec.
+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]))
+#define AI_DXF_ENTITIES_MAGIC_BLOCK "$ASSIMP_ENTITIES_MAGIC"
+
+
+static const aiImporterDesc desc = {
+	"Drawing Interchange Format (DXF) Importer",
+	"",
+	"",
+	"",
+	aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport,
+	0,
+	0,
+	0,
+	0,
+	"dxf" 
+};
+
+// ------------------------------------------------------------------------------------------------
+// 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
+const aiImporterDesc* DXFImporter::GetInfo () const
+{
+	return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Imports the given file into the given scene structure. 
+void DXFImporter::InternReadFile( const std::string& pFile, 
+	aiScene* pScene, 
+	IOSystem* pIOHandler)
+{
+	boost::shared_ptr<IOStream> file = boost::shared_ptr<IOStream>( pIOHandler->Open( pFile) );
+	
+	// Check whether we can read the file
+	if( file.get() == NULL) {
+		throw DeadlyImportError( "Failed to open DXF file " + pFile + "");
+	}
+
+	// check whether this is a binaray DXF file - we can't read binary DXF files :-(
+	char buff[AI_DXF_BINARY_IDENT_LEN+1] = {0};
+	file->Read(buff,AI_DXF_BINARY_IDENT_LEN,1);
+
+	if (!strncmp(AI_DXF_BINARY_IDENT,buff,AI_DXF_BINARY_IDENT_LEN)) {
+		throw DeadlyImportError("DXF: Binary files are not supported at the moment");
+	}
+
+	// DXF files can grow very large, so read them via the StreamReader,
+	// which will choose a suitable strategy.
+	file->Seek(0,aiOrigin_SET);
+	StreamReaderLE stream( file );
+	
+	DXF::LineReader reader (stream);
+	DXF::FileData output;
+
+	// now get all lines of the file and process top-level sections
+	bool eof = false;
+	while(!reader.End()) {
+
+		// blocks table - these 'build blocks' are later (in ENTITIES)
+		// referenced an included via INSERT statements.
+		if (reader.Is(2,"BLOCKS")) {
+			ParseBlocks(reader,output);
+			continue;
+		}
+	
+		// primary entity table
+		if (reader.Is(2,"ENTITIES")) {
+			ParseEntities(reader,output);
+			continue;
+		}
+
+		// skip unneeded sections entirely to avoid any problems with them 
+		// alltogether.
+		else if (reader.Is(2,"CLASSES") || reader.Is(2,"TABLES")) {
+			SkipSection(reader);
+			continue;
+		}
+
+		else if (reader.Is(2,"HEADER")) {
+			ParseHeader(reader,output);
+			continue;
+		}
+
+		// comments
+		else if (reader.Is(999)) {
+			DefaultLogger::get()->info("DXF Comment: " + reader.Value());
+		}
+
+		// don't read past the official EOF sign
+		else if (reader.Is(0,"EOF")) {
+			eof = true;
+			break;
+		}
+
+		++reader;
+	}
+	if (!eof) {
+		DefaultLogger::get()->warn("DXF: EOF reached, but did not encounter DXF EOF marker");
+	}
+
+	ConvertMeshes(pScene,output);
+
+	// Now rotate the whole scene by 90 degrees around the x axis to convert from AutoCAD's to Assimp's coordinate system
+	pScene->mRootNode->mTransformation = aiMatrix4x4(
+		1.f,0.f,0.f,0.f,
+		0.f,0.f,1.f,0.f,
+		0.f,-1.f,0.f,0.f,
+		0.f,0.f,0.f,1.f) * pScene->mRootNode->mTransformation;
+}
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::ConvertMeshes(aiScene* pScene, DXF::FileData& output)
+{
+	// the process of resolving all the INSERT statements can grow the
+	// polycount excessively, so log the original number.
+	// XXX Option to import blocks as separate nodes?
+	if (!DefaultLogger::isNullLogger()) {
+
+		unsigned int vcount = 0, icount = 0;
+		BOOST_FOREACH (const DXF::Block& bl, output.blocks) {
+			BOOST_FOREACH (boost::shared_ptr<const DXF::PolyLine> pl, bl.lines) {
+				vcount += pl->positions.size();
+				icount += pl->counts.size();
+			}
+		}
+
+		DefaultLogger::get()->debug((Formatter::format("DXF: Unexpanded polycount is "),
+			icount,", vertex count is ",vcount
+		));
+	}
+
+	if (! output.blocks.size()  ) {
+		throw DeadlyImportError("DXF: no data blocks loaded");
+	}
+
+	DXF::Block* entities = 0;
+	
+	// index blocks by name
+	DXF::BlockMap blocks_by_name;
+	BOOST_FOREACH (DXF::Block& bl, output.blocks) {
+		blocks_by_name[bl.name] = &bl;
+		if ( !entities && bl.name == AI_DXF_ENTITIES_MAGIC_BLOCK ) {
+			entities = &bl;
+		}
+	}
+
+	if (!entities) {
+		throw DeadlyImportError("DXF: no ENTITIES data block loaded");
+	}
+
+	typedef std::map<std::string, unsigned int> LayerMap;
+
+	LayerMap layers;
+	std::vector< std::vector< const DXF::PolyLine*> > corr;
+
+	// now expand all block references in the primary ENTITIES block
+	// XXX this involves heavy memory copying, consider a faster solution for future versions.
+	ExpandBlockReferences(*entities,blocks_by_name);
+
+	unsigned int cur = 0;
+	BOOST_FOREACH (boost::shared_ptr<const DXF::PolyLine> pl, entities->lines) {
+		if (pl->positions.size()) {
+
+			std::map<std::string, unsigned int>::iterator it = layers.find(pl->layer);
+			if (it == layers.end()) {
+				++pScene->mNumMeshes;
+
+				layers[pl->layer] = cur++;
+
+				std::vector< const DXF::PolyLine* > pv;
+				pv.push_back(&*pl);
+
+				corr.push_back(pv);
+			}
+			else {
+				corr[(*it).second].push_back(&*pl);
+			}
+		}
+	}
+
+	if (!pScene->mNumMeshes) {
+		throw DeadlyImportError("DXF: this file contains no 3d data");
+	}
+
+	pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ] ();
+
+	BOOST_FOREACH(const LayerMap::value_type& elem, layers){
+		aiMesh* const mesh =  pScene->mMeshes[elem.second] = new aiMesh();
+		mesh->mName.Set(elem.first);
+
+		unsigned int cvert = 0,cface = 0;
+		BOOST_FOREACH(const DXF::PolyLine* pl, corr[elem.second]){
+			// sum over all faces since we need to 'verbosify' them.
+			cvert += std::accumulate(pl->counts.begin(),pl->counts.end(),0); 
+			cface += pl->counts.size();
+		}
+
+		aiVector3D* verts = mesh->mVertices = new aiVector3D[cvert];
+		aiColor4D* colors = mesh->mColors[0] = new aiColor4D[cvert];
+		aiFace* faces = mesh->mFaces = new aiFace[cface];
+
+		mesh->mNumVertices = cvert;
+		mesh->mNumFaces = cface;
+
+		unsigned int prims = 0;
+		unsigned int overall_indices = 0;
+		BOOST_FOREACH(const DXF::PolyLine* pl, corr[elem.second]){
+
+			std::vector<unsigned int>::const_iterator it = pl->indices.begin();
+			BOOST_FOREACH(unsigned int facenumv,pl->counts) {
+				aiFace& face = *faces++;
+				face.mIndices = new unsigned int[face.mNumIndices = facenumv];
+
+				for (unsigned int i = 0; i < facenumv; ++i) {
+					face.mIndices[i] = overall_indices++;
+
+					ai_assert(pl->positions.size() == pl->colors.size());
+					if (*it >= pl->positions.size()) {
+						throw DeadlyImportError("DXF: vertex index out of bounds");
+					}
+
+					*verts++ = pl->positions[*it];
+					*colors++ = pl->colors[*it++];
+				}
+
+				// set primitive flags now, this saves the extra pass in ScenePreprocessor.
+				switch(face.mNumIndices) {
+					case 1:
+						prims |= aiPrimitiveType_POINT;
+						break;
+					case 2:
+						prims |= aiPrimitiveType_LINE;
+						break;
+					case 3:
+						prims |= aiPrimitiveType_TRIANGLE;
+						break;
+					default:
+						prims |= aiPrimitiveType_POLYGON;
+						break;
+				}
+			}
+		}
+
+		mesh->mPrimitiveTypes = prims;
+		mesh->mMaterialIndex = 0;
+	}
+
+	GenerateHierarchy(pScene,output);
+	GenerateMaterials(pScene,output);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::ExpandBlockReferences(DXF::Block& bl,const DXF::BlockMap& blocks_by_name)
+{
+	BOOST_FOREACH (const DXF::InsertBlock& insert, bl.insertions) {
+
+		// first check if the referenced blocks exists ...
+		const DXF::BlockMap::const_iterator it = blocks_by_name.find(insert.name);
+		if (it == blocks_by_name.end()) {
+			DefaultLogger::get()->error((Formatter::format("DXF: Failed to resolve block reference: "),
+				insert.name,"; skipping"
+			));
+			continue;
+		}
+
+		// XXX this would be the place to implement recursive expansion if needed.
+		const DXF::Block& bl_src = *(*it).second;
+		
+		BOOST_FOREACH (boost::shared_ptr<const DXF::PolyLine> pl_in, bl_src.lines) {
+			boost::shared_ptr<DXF::PolyLine> pl_out = boost::shared_ptr<DXF::PolyLine>(new DXF::PolyLine(*pl_in));
+
+			if (bl_src.base.Length() || insert.scale.x!=1.f || insert.scale.y!=1.f || insert.scale.z!=1.f || insert.angle || insert.pos.Length()) {
+				// manual coordinate system transformation
+				// XXX order
+				aiMatrix4x4 trafo, tmp;
+				aiMatrix4x4::Translation(-bl_src.base,trafo);
+				trafo *= aiMatrix4x4::Scaling(insert.scale,tmp);
+				trafo *= aiMatrix4x4::Translation(insert.pos,tmp);
+
+				// XXX rotation currently ignored - I didn't find an appropriate sample model.
+				if (insert.angle != 0.f) {
+					DefaultLogger::get()->warn("DXF: BLOCK rotation not currently implemented");
+				}
+
+				BOOST_FOREACH (aiVector3D& v, pl_out->positions) {
+					v *= trafo;
+				}
+			}
+
+			bl.lines.push_back(pl_out);
+		}
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::GenerateMaterials(aiScene* pScene, DXF::FileData& /*output*/)
+{
+	// generate an almost-white default material. Reason:
+	// the default vertex color is GREY, so we are
+	// already at Assimp's usual default color.
+	// generate a default material
+	aiMaterial* pcMat = new aiMaterial();
+	aiString s;
+	s.Set(AI_DEFAULT_MATERIAL_NAME);
+	pcMat->AddProperty(&s, AI_MATKEY_NAME);
+
+	aiColor4D clrDiffuse(0.9f,0.9f,0.9f,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;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::GenerateHierarchy(aiScene* pScene, DXF::FileData& /*output*/)
+{
+	// generate the output scene graph, which is just the root node with a single child for each layer.
+	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 (unsigned int m = 0; m < pScene->mRootNode->mNumChildren;++m)	{
+			aiNode* p = pScene->mRootNode->mChildren[m] = new aiNode();
+			p->mName = pScene->mMeshes[m]->mName;
+
+			p->mMeshes = new unsigned int[p->mNumMeshes = 1];
+			p->mMeshes[0] = m;
+			p->mParent = pScene->mRootNode;
+		}
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::SkipSection(DXF::LineReader& reader)
+{	
+	for( ;!reader.End() && !reader.Is(0,"ENDSEC"); reader++);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::ParseHeader(DXF::LineReader& reader, DXF::FileData& /*output*/)
+{	
+	for( ;!reader.End() && !reader.Is(0,"ENDSEC"); reader++);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::ParseBlocks(DXF::LineReader& reader, DXF::FileData& output)
+{	
+	while( !reader.End() && !reader.Is(0,"ENDSEC")) {
+		if (reader.Is(0,"BLOCK")) {
+			ParseBlock(++reader,output);
+			continue;
+		}
+		++reader;
+	}
+
+	DefaultLogger::get()->debug((Formatter::format("DXF: got "),
+		output.blocks.size()," entries in BLOCKS"
+	));
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::ParseBlock(DXF::LineReader& reader, DXF::FileData& output)
+{	
+	// push a new block onto the stack.
+	output.blocks.push_back( DXF::Block() );
+	DXF::Block& block = output.blocks.back();
+
+	while( !reader.End() && !reader.Is(0,"ENDBLK")) {
+
+		switch(reader.GroupCode()) {
+			case 2:
+				block.name = reader.Value();
+				break;
+
+			case 10:
+				block.base.x = reader.ValueAsFloat();
+				break;
+			case 20:
+				block.base.y = reader.ValueAsFloat();
+				break;
+			case 30:
+				block.base.z = reader.ValueAsFloat();
+				break;
+		}
+
+		if (reader.Is(0,"POLYLINE")) {
+			ParsePolyLine(++reader,output);
+			continue;
+		}
+
+		// XXX is this a valid case?
+		if (reader.Is(0,"INSERT")) {
+			DefaultLogger::get()->warn("DXF: INSERT within a BLOCK not currently supported; skipping");
+			for( ;!reader.End() && !reader.Is(0,"ENDBLK"); ++reader);
+			break;
+		}
+
+		else if (reader.Is(0,"3DFACE") || reader.Is(0,"LINE") || reader.Is(0,"3DLINE")) {
+			//http://sourceforge.net/tracker/index.php?func=detail&aid=2970566&group_id=226462&atid=1067632
+			Parse3DFace(++reader, output);
+			continue;
+		}
+		++reader;
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::ParseEntities(DXF::LineReader& reader, DXF::FileData& output)
+{	
+	// push a new block onto the stack.
+	output.blocks.push_back( DXF::Block() );
+	DXF::Block& block = output.blocks.back();
+
+	block.name = AI_DXF_ENTITIES_MAGIC_BLOCK;
+
+	while( !reader.End() && !reader.Is(0,"ENDSEC")) {
+		if (reader.Is(0,"POLYLINE")) {
+			ParsePolyLine(++reader,output);
+			continue;
+		}
+
+		else if (reader.Is(0,"INSERT")) {
+			ParseInsertion(++reader,output);
+			continue;
+		}
+
+		else if (reader.Is(0,"3DFACE") || reader.Is(0,"LINE") || reader.Is(0,"3DLINE")) {
+			//http://sourceforge.net/tracker/index.php?func=detail&aid=2970566&group_id=226462&atid=1067632
+			Parse3DFace(++reader, output);
+			continue;
+		}
+
+		++reader;
+	}
+
+	DefaultLogger::get()->debug((Formatter::format("DXF: got "),
+		block.lines.size()," polylines and ", block.insertions.size() ," inserted blocks in ENTITIES"
+	));
+}
+
+
+void DXFImporter::ParseInsertion(DXF::LineReader& reader, DXF::FileData& output)
+{	
+	output.blocks.back().insertions.push_back( DXF::InsertBlock() );
+	DXF::InsertBlock& bl = output.blocks.back().insertions.back();
+
+	while( !reader.End() && !reader.Is(0)) {
+
+		switch(reader.GroupCode()) 
+		{
+			// name of referenced block
+		case 2:
+			bl.name = reader.Value();
+			break;
+
+			// translation
+		case 10:
+			bl.pos.x = reader.ValueAsFloat();
+			break;
+		case 20:
+			bl.pos.y = reader.ValueAsFloat();
+			break;
+		case 30:
+			bl.pos.z = reader.ValueAsFloat();
+			break;
+
+			// scaling
+		case 41:
+			bl.scale.x = reader.ValueAsFloat();
+			break;
+		case 42:
+			bl.scale.y = reader.ValueAsFloat();
+			break;
+		case 43:
+			bl.scale.z = reader.ValueAsFloat();
+			break;
+
+			// rotation angle
+		case 50:
+			bl.angle = reader.ValueAsFloat();
+			break;
+		}
+		reader++;
+	}
+}
+
+#define DXF_POLYLINE_FLAG_CLOSED		0x1
+#define DXF_POLYLINE_FLAG_3D_POLYLINE	0x8
+#define DXF_POLYLINE_FLAG_3D_POLYMESH	0x10
+#define DXF_POLYLINE_FLAG_POLYFACEMESH	0x40
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output)
+{
+	output.blocks.back().lines.push_back( boost::shared_ptr<DXF::PolyLine>( new DXF::PolyLine() ) );
+	DXF::PolyLine& line = *output.blocks.back().lines.back();
+
+	unsigned int iguess = 0, vguess = 0;
+	while( !reader.End() && !reader.Is(0,"ENDSEC")) {
+	
+		if (reader.Is(0,"VERTEX")) {
+			ParsePolyLineVertex(++reader,line);
+			if (reader.Is(0,"SEQEND")) {
+				break;
+			}
+			continue;
+		}
+
+		switch(reader.GroupCode())	
+		{
+		// flags --- important that we know whether it is a 
+		// polyface mesh or 'just' a line.
+		case 70:
+			if (!line.flags)	{
+				line.flags = reader.ValueAsSignedInt();
+			}
+			break;
+
+		// optional number of vertices
+		case 71:
+			vguess = reader.ValueAsSignedInt();
+			line.positions.reserve(vguess);
+			break;
+
+		// optional number of faces
+		case 72:
+			iguess = reader.ValueAsSignedInt();
+			line.indices.reserve(iguess);
+			break;
+
+		// 8 specifies the layer on which this line is placed on
+		case 8:
+			line.layer = reader.Value();
+			break;
+		}
+
+		reader++;
+	}
+
+	//if (!(line.flags & DXF_POLYLINE_FLAG_POLYFACEMESH))	{
+	//	DefaultLogger::get()->warn((Formatter::format("DXF: polyline not currently supported: "),line.flags));
+	//	output.blocks.back().lines.pop_back();
+	//	return;
+	//}
+
+	if (vguess && line.positions.size() != vguess) {
+		DefaultLogger::get()->warn((Formatter::format("DXF: unexpected vertex count in polymesh: "),
+			line.positions.size(),", expected ", vguess
+		));
+	}
+
+	if (line.flags & DXF_POLYLINE_FLAG_POLYFACEMESH ) {
+		if (line.positions.size() < 3 || line.indices.size() < 3)	{
+				DefaultLogger::get()->warn("DXF: not enough vertices for polymesh; ignoring");
+				output.blocks.back().lines.pop_back();
+				return;
+		}
+
+		// if these numbers are wrong, parsing might have gone wild. 
+		// however, the docs state that applications are not required
+		// to set the 71 and 72 fields, respectively, to valid values.
+		// So just fire a warning.
+		if (iguess && line.counts.size() != iguess) {
+			DefaultLogger::get()->warn((Formatter::format("DXF: unexpected face count in polymesh: "),
+				line.counts.size(),", expected ", iguess
+			));
+		}
+	}
+	else if (!line.indices.size() && !line.counts.size()) {
+		// a polyline - so there are no indices yet.
+		size_t guess = line.positions.size() + (line.flags & DXF_POLYLINE_FLAG_CLOSED ? 1 : 0);
+		line.indices.reserve(guess);
+
+		line.counts.reserve(guess/2);
+		for (unsigned int i = 0; i < line.positions.size()/2; ++i) {
+			line.indices.push_back(i*2);
+			line.indices.push_back(i*2+1);
+			line.counts.push_back(2);
+		}
+
+		// closed polyline?
+		if (line.flags & DXF_POLYLINE_FLAG_CLOSED) {
+			line.indices.push_back(line.positions.size()-1);
+			line.indices.push_back(0);
+			line.counts.push_back(2);
+		}
+	}
+}
+
+#define DXF_VERTEX_FLAG_PART_OF_POLYFACE 0x80
+#define DXF_VERTEX_FLAG_HAS_POSITIONS 0x40
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::ParsePolyLineVertex(DXF::LineReader& reader, DXF::PolyLine& line)
+{
+	unsigned int cnti = 0, flags = 0;
+	unsigned int indices[4];
+
+	aiVector3D out;
+	aiColor4D clr = AI_DXF_DEFAULT_COLOR;
+
+	while( !reader.End() ) {
+
+		if (reader.Is(0)) { // SEQEND or another VERTEX
+			break;
+		}
+
+		switch (reader.GroupCode())
+		{
+		case 8:
+				// layer to which the vertex belongs to - assume that
+				// this is always the layer the top-level polyline
+				// entity resides on as well.
+				if(reader.Value() != line.layer) {
+					DefaultLogger::get()->warn("DXF: expected vertex to be part of a polyface but the 0x128 flag isn't set");
+				}
+				break;
+
+		case 70:
+				flags = reader.ValueAsUnsignedInt();
+				break;
+
+		// VERTEX COORDINATES
+		case 10: out.x = reader.ValueAsFloat();break;
+		case 20: out.y = reader.ValueAsFloat();break;
+		case 30: out.z = reader.ValueAsFloat();break;
+
+		// POLYFACE vertex indices
+		case 71: 
+		case 72:
+		case 73:
+		case 74: 
+			if (cnti == 4) {
+				DefaultLogger::get()->warn("DXF: more than 4 indices per face not supported; ignoring");
+				break;
+			}
+			indices[cnti++] = reader.ValueAsUnsignedInt();
+			break;
+
+		// color
+		case 62: 
+			clr = g_aclrDxfIndexColors[reader.ValueAsUnsignedInt() % AI_DXF_NUM_INDEX_COLORS]; 
+			break;
+		};
+	
+		reader++;
+	}
+	
+	if (line.flags & DXF_POLYLINE_FLAG_POLYFACEMESH && !(flags & DXF_VERTEX_FLAG_PART_OF_POLYFACE)) {
+		DefaultLogger::get()->warn("DXF: expected vertex to be part of a polyface but the 0x128 flag isn't set");
+	}
+
+	if (cnti) {
+		line.counts.push_back(cnti);
+		for (unsigned int i = 0; i < cnti; ++i) {
+			// IMPORTANT NOTE: POLYMESH indices are ONE-BASED
+			if (indices[i] == 0) {
+				DefaultLogger::get()->warn("DXF: invalid vertex index, indices are one-based.");
+				--line.counts.back();
+				continue;
+			}
+			line.indices.push_back(indices[i]-1);
+		}
+	}
+	else {
+		line.positions.push_back(out);
+		line.colors.push_back(clr);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void DXFImporter::Parse3DFace(DXF::LineReader& reader, DXF::FileData& output)
+{
+	// (note) this is also used for for parsing line entities, so we
+	// must handle the vertex_count == 2 case as well.
+
+	output.blocks.back().lines.push_back( boost::shared_ptr<DXF::PolyLine>( new DXF::PolyLine() )  );
+	DXF::PolyLine& line = *output.blocks.back().lines.back();
+
+	aiVector3D vip[4];
+	aiColor4D  clr = AI_DXF_DEFAULT_COLOR;
+	
+	bool b[4] = {false,false,false,false};
+	while( !reader.End() ) {
+
+		// next entity with a groupcode == 0 is probably already the next vertex or polymesh entity
+		if (reader.GroupCode() == 0) {
+			break;
+		}
+		switch (reader.GroupCode())	
+		{
+
+		// 8 specifies the layer
+		case 8:	
+			line.layer = reader.Value();
+			break;
+
+		// x position of the first corner
+		case 10: vip[0].x = reader.ValueAsFloat();
+			b[2] = true;
+			break;
+
+		// y position of the first corner
+		case 20: vip[0].y = reader.ValueAsFloat();
+			b[2] = true;
+			break;
+
+		// z position of the first corner
+		case 30: vip[0].z = reader.ValueAsFloat();
+			b[2] = true;
+			break;
+
+		// x position of the second corner
+		case 11: vip[1].x = reader.ValueAsFloat();
+			b[3] = true;
+			break;
+
+		// y position of the second corner
+		case 21: vip[1].y = reader.ValueAsFloat();
+			b[3] = true;
+			break;
+
+		// z position of the second corner
+		case 31: vip[1].z = reader.ValueAsFloat();
+			b[3] = true;
+			break;
+
+		// x position of the third corner
+		case 12: vip[2].x = reader.ValueAsFloat();
+			b[0] = true;
+			break;
+
+		// y position of the third corner
+		case 22: vip[2].y = reader.ValueAsFloat();
+			b[0] = true;
+			break;
+
+		// z position of the third corner
+		case 32: vip[2].z = reader.ValueAsFloat();
+			b[0] = true;
+			break;
+
+		// x position of the fourth corner
+		case 13: vip[3].x = reader.ValueAsFloat();
+			b[1] = true;
+			break;
+
+		// y position of the fourth corner
+		case 23: vip[3].y = reader.ValueAsFloat();
+			b[1] = true;
+			break;
+
+		// z position of the fourth corner
+		case 33: vip[3].z = reader.ValueAsFloat();
+			b[1] = true;
+			break;
+
+		// color
+		case 62: 
+			clr = g_aclrDxfIndexColors[reader.ValueAsUnsignedInt() % AI_DXF_NUM_INDEX_COLORS]; 
+			break;
+		};
+
+		++reader;
+	}
+
+	// the fourth corner may even be identical to the third,
+	// in this case we treat it as if it didn't exist.
+	if (vip[3] == vip[2]) {
+		b[1] = false;
+	}
+	
+	// sanity checks to see if we got something meaningful
+	if ((b[1] && !b[0]) || !b[2] || !b[3]) {
+		DefaultLogger::get()->warn("DXF: unexpected vertex setup in 3DFACE/LINE/FACE entity; ignoring");
+		output.blocks.back().lines.pop_back();
+		return;
+	}
+
+	const unsigned int cnt = (2+(b[0]?1:0)+(b[1]?1:0));
+	line.counts.push_back(cnt);
+
+	for (unsigned int i = 0; i < cnt; ++i) {
+		line.indices.push_back(line.positions.size());
+		line.positions.push_back(vip[i]);
+		line.colors.push_back(clr);
+	}
+}
+
+#endif // !! ASSIMP_BUILD_NO_DXF_IMPORTER
+

+ 152 - 0
assimplib.mod/assimp/code/DXFLoader.h

@@ -0,0 +1,152 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  DXFLoader.h 
+ *  @brief Declaration of the .dxf importer class.
+ */
+#ifndef AI_DXFLOADER_H_INCLUDED
+#define AI_DXFLOADER_H_INCLUDED
+
+#include "BaseImporter.h"
+
+namespace Assimp	{
+	namespace DXF {
+	
+		class LineReader;
+		struct FileData;
+		struct PolyLine;
+		struct Block;
+		struct InsertBlock;
+
+		typedef std::map<std::string, const DXF::Block*> BlockMap;
+	}
+
+
+// ---------------------------------------------------------------------------
+/** DXF importer implementation.
+ *
+*/
+class DXFImporter : public BaseImporter
+{
+public:
+	DXFImporter();
+	~DXFImporter();
+
+
+
+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:
+
+	// -------------------------------------------------------------------
+	/** Return importer meta information.
+	 * See #BaseImporter::GetInfo for the details*/
+	const aiImporterDesc* GetInfo () const;
+
+	// -------------------------------------------------------------------
+	/** Imports the given file into the given scene structure. 
+	 * See BaseImporter::InternReadFile() for details */
+	void InternReadFile( const std::string& pFile, 
+		aiScene* pScene, 
+		IOSystem* pIOHandler);
+
+private:
+
+	// -----------------------------------------------------
+	void SkipSection(DXF::LineReader& reader);
+
+	// -----------------------------------------------------
+	void ParseHeader(DXF::LineReader& reader,
+		DXF::FileData& output);
+
+	// -----------------------------------------------------
+	void ParseEntities(DXF::LineReader& reader,
+		DXF::FileData& output);
+
+	// -----------------------------------------------------
+	void ParseBlocks(DXF::LineReader& reader, 
+		DXF::FileData& output);
+
+	// -----------------------------------------------------
+	void ParseBlock(DXF::LineReader& reader, 
+		DXF::FileData& output);
+
+	// -----------------------------------------------------
+	void ParseInsertion(DXF::LineReader& reader, 
+		DXF::FileData& output);
+
+	// -----------------------------------------------------
+	void ParsePolyLine(DXF::LineReader& reader, 
+		DXF::FileData& output);
+
+	// -----------------------------------------------------
+	void ParsePolyLineVertex(DXF::LineReader& reader, 
+		DXF::PolyLine& line);
+
+	// -----------------------------------------------------
+	void Parse3DFace(DXF::LineReader& reader, 
+		DXF::FileData& output);
+
+	// -----------------------------------------------------
+	void ConvertMeshes(aiScene* pScene, 
+		DXF::FileData& output);
+
+	// -----------------------------------------------------
+	void GenerateHierarchy(aiScene* pScene, 
+		DXF::FileData& output);
+
+	// -----------------------------------------------------
+	void GenerateMaterials(aiScene* pScene, 
+		DXF::FileData& output);
+
+	// -----------------------------------------------------
+	void ExpandBlockReferences(DXF::Block& bl,
+		const DXF::BlockMap& blocks_by_name);
+};
+
+} // end of namespace Assimp
+
+#endif // AI_3DSIMPORTER_H_INC

+ 464 - 0
assimplib.mod/assimp/code/DeboneProcess.cpp

@@ -0,0 +1,464 @@
+									 /*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/// @file DeboneProcess.cpp
+/** Implementation of the DeboneProcess post processing step */
+
+#include "AssimpPCH.h"
+
+// internal headers of the post-processing framework
+#include "ProcessHelper.h"
+#include "DeboneProcess.h"
+
+
+using namespace Assimp;
+
+// ------------------------------------------------------------------------------------------------
+// Constructor to be privately used by Importer
+DeboneProcess::DeboneProcess()
+{
+	mNumBones = 0;
+	mNumBonesCanDoWithout = 0;
+
+	mThreshold = AI_DEBONE_THRESHOLD;
+	mAllOrNone = false;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Destructor, private as well
+DeboneProcess::~DeboneProcess()
+{
+	// nothing to do here
+}
+
+// ------------------------------------------------------------------------------------------------
+// Returns whether the processing step is present in the given flag field.
+bool DeboneProcess::IsActive( unsigned int pFlags) const
+{
+	return (pFlags & aiProcess_Debone) != 0;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void DeboneProcess::SetupProperties(const Importer* pImp)
+{	
+	// get the current value of the property
+	mAllOrNone = pImp->GetPropertyInteger(AI_CONFIG_PP_DB_ALL_OR_NONE,0)?true:false;
+	mThreshold = pImp->GetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD,AI_DEBONE_THRESHOLD);
+}
+
+// ------------------------------------------------------------------------------------------------
+// Executes the post processing step on the given imported data.
+void DeboneProcess::Execute( aiScene* pScene)
+{
+	DefaultLogger::get()->debug("DeboneProcess begin");
+
+	if(!pScene->mNumMeshes) {
+		return;
+	}
+
+	std::vector<bool> splitList(pScene->mNumMeshes); 
+	for( unsigned int a = 0; a < pScene->mNumMeshes; a++) {
+		splitList[a] = ConsiderMesh( pScene->mMeshes[a] );
+	}
+
+	int numSplits = 0; 
+
+	if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones))	{
+		for(unsigned int a = 0; a < pScene->mNumMeshes; a++)	{
+			if(splitList[a]) {
+				numSplits++;
+			}
+		}
+	}
+
+	if(numSplits)	{
+		// we need to do something. Let's go.
+		mSubMeshIndices.clear();
+		mSubMeshIndices.resize(pScene->mNumMeshes);
+
+		// build a new array of meshes for the scene
+		std::vector<aiMesh*> meshes;
+
+		for(unsigned int a=0;a<pScene->mNumMeshes;a++)
+		{
+			aiMesh* srcMesh = pScene->mMeshes[a];
+
+			std::vector<std::pair<aiMesh*,const aiBone*> > newMeshes;
+
+			if(splitList[a]) { 
+				SplitMesh(srcMesh,newMeshes);
+			}
+
+			// mesh was split
+			if(!newMeshes.empty())	{				
+				unsigned int out = 0, in = srcMesh->mNumBones;				
+
+				// store new meshes and indices of the new meshes
+				for(unsigned int b=0;b<newMeshes.size();b++)	{						
+					const aiString *find = newMeshes[b].second?&newMeshes[b].second->mName:0;
+
+					aiNode *theNode = find?pScene->mRootNode->FindNode(*find):0;
+					std::pair<unsigned int,aiNode*> push_pair(meshes.size(),theNode);
+
+					mSubMeshIndices[a].push_back(push_pair);
+					meshes.push_back(newMeshes[b].first);
+
+					out+=newMeshes[b].first->mNumBones;
+				}
+						   
+				if(!DefaultLogger::isNullLogger()) {
+					char buffer[1024];
+					::sprintf(buffer,"Removed %i bones. Input bones: %i. Output bones: %i",in-out,in,out);
+					DefaultLogger::get()->info(buffer);
+				}
+
+				// and destroy the source mesh. It should be completely contained inside the new submeshes
+				delete srcMesh;
+			}
+			else	{
+				// Mesh is kept unchanged - store it's new place in the mesh array
+				mSubMeshIndices[a].push_back(std::pair<unsigned int,aiNode*>(meshes.size(),(aiNode*)0));
+				meshes.push_back(srcMesh);
+			}
+		}	
+			
+		// rebuild the scene's mesh array
+		pScene->mNumMeshes = meshes.size();
+		delete [] pScene->mMeshes;
+		pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
+		std::copy( meshes.begin(), meshes.end(), pScene->mMeshes);
+
+		// recurse through all nodes and translate the node's mesh indices to fit the new mesh array
+		UpdateNode( pScene->mRootNode);
+	}
+
+	DefaultLogger::get()->debug("DeboneProcess end");
+}
+
+// ------------------------------------------------------------------------------------------------
+// Counts bones total/removable in a given mesh.
+bool DeboneProcess::ConsiderMesh(const aiMesh* pMesh)
+{
+	if(!pMesh->HasBones()) {
+		return false;
+	}
+
+	bool split = false;
+
+	//interstitial faces not permitted
+	bool isInterstitialRequired = false;
+
+	std::vector<bool> isBoneNecessary(pMesh->mNumBones,false);
+	std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX);
+
+	const unsigned int cUnowned = UINT_MAX;
+	const unsigned int cCoowned = UINT_MAX-1;
+
+	for(unsigned int i=0;i<pMesh->mNumBones;i++)	{
+		for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++)	{
+			float w = pMesh->mBones[i]->mWeights[j].mWeight;
+
+			if(w==0.0f) {
+				continue;
+			}
+
+			unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId;
+			if(w>=mThreshold)	{
+
+				if(vertexBones[vid]!=cUnowned)	{
+					if(vertexBones[vid]==i) //double entry
+					{
+						DefaultLogger::get()->warn("Encountered double entry in bone weights");					
+					}
+					else //TODO: track attraction in order to break tie
+					{
+						vertexBones[vid] = cCoowned;
+					}
+				}
+				else vertexBones[vid] = i;			
+			}
+
+			if(!isBoneNecessary[i]) {
+				isBoneNecessary[i] = w<mThreshold;
+			}
+		}
+
+		if(!isBoneNecessary[i])  {
+			isInterstitialRequired = true;
+		}
+	}
+
+	if(isInterstitialRequired) {
+		for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
+			unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]];
+
+			for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
+				unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]];
+
+				if(v!=w)	{
+					if(v<pMesh->mNumBones) isBoneNecessary[v] = true; 
+					if(w<pMesh->mNumBones) isBoneNecessary[w] = true; 
+				}
+			}
+		}
+	}
+
+	for(unsigned int i=0;i<pMesh->mNumBones;i++)	{
+		if(!isBoneNecessary[i])	{
+			mNumBonesCanDoWithout++; 
+			split = true;
+		}
+		
+		mNumBones++;
+	}
+	return split;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Splits the given mesh by bone count.
+void DeboneProcess::SplitMesh( const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const
+{
+	// same deal here as ConsiderMesh basically
+
+	std::vector<bool> isBoneNecessary(pMesh->mNumBones,false);
+	std::vector<unsigned int> vertexBones(pMesh->mNumVertices,UINT_MAX);
+
+	const unsigned int cUnowned = UINT_MAX;
+	const unsigned int cCoowned = UINT_MAX-1;
+
+	for(unsigned int i=0;i<pMesh->mNumBones;i++)	{
+		for(unsigned int j=0;j<pMesh->mBones[i]->mNumWeights;j++)	{
+			float w = pMesh->mBones[i]->mWeights[j].mWeight;
+
+			if(w==0.0f) {
+				continue;
+			}
+
+			unsigned int vid = pMesh->mBones[i]->mWeights[j].mVertexId;
+
+			if(w>=mThreshold) {
+				if(vertexBones[vid]!=cUnowned)  {
+					if(vertexBones[vid]==i) //double entry
+					{
+						//DefaultLogger::get()->warn("Encountered double entry in bone weights");					
+					}
+					else //TODO: track attraction in order to break tie
+					{
+						vertexBones[vid] = cCoowned;
+					}
+				}
+				else vertexBones[vid] = i;			
+			}
+
+			if(!isBoneNecessary[i]) {
+				isBoneNecessary[i] = w<mThreshold;
+			}
+		}
+	}
+
+	unsigned int nFacesUnowned = 0;
+
+	std::vector<unsigned int> faceBones(pMesh->mNumFaces,UINT_MAX);
+	std::vector<unsigned int> facesPerBone(pMesh->mNumBones,0);
+
+	for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
+		unsigned int nInterstitial = 1;
+
+		unsigned int v = vertexBones[pMesh->mFaces[i].mIndices[0]];
+
+		for(unsigned int j=1;j<pMesh->mFaces[i].mNumIndices;j++) {
+			unsigned int w = vertexBones[pMesh->mFaces[i].mIndices[j]];
+
+			if(v!=w)	{
+				if(v<pMesh->mNumBones) isBoneNecessary[v] = true; 
+				if(w<pMesh->mNumBones) isBoneNecessary[w] = true; 
+			}
+			else nInterstitial++;
+		}
+
+		if(v<pMesh->mNumBones &&nInterstitial==pMesh->mFaces[i].mNumIndices)	{				
+			faceBones[i] = v; //primitive belongs to bone #v
+			facesPerBone[v]++;
+		}
+		else nFacesUnowned++; 
+	}
+
+	// invalidate any "cojoined" faces
+	for(unsigned int i=0;i<pMesh->mNumFaces;i++) {
+		if(faceBones[i]<pMesh->mNumBones&&isBoneNecessary[faceBones[i]]) 
+		{
+			ai_assert(facesPerBone[faceBones[i]]>0);
+			facesPerBone[faceBones[i]]--; 
+			
+			nFacesUnowned++; 
+			faceBones[i] = cUnowned; 
+		}
+	}
+
+	if(nFacesUnowned) {	 	
+		std::vector<unsigned int> subFaces;
+
+		for(unsigned int i=0;i<pMesh->mNumFaces;i++)	{
+			if(faceBones[i]==cUnowned) {
+				subFaces.push_back(i);
+			}
+		}
+
+		aiMesh *baseMesh = MakeSubmesh(pMesh,subFaces,0);
+		std::pair<aiMesh*,const aiBone*> push_pair(baseMesh,(const aiBone*)0);
+
+		poNewMeshes.push_back(push_pair);
+	}
+
+	for(unsigned int i=0;i<pMesh->mNumBones;i++) {			
+
+		if(!isBoneNecessary[i]&&facesPerBone[i]>0)	{				
+			std::vector<unsigned int> subFaces;
+
+			for(unsigned int j=0;j<pMesh->mNumFaces;j++)	{
+				if(faceBones[j]==i) {
+					subFaces.push_back(j);
+				}
+			}
+
+			unsigned int f = AI_SUBMESH_FLAGS_SANS_BONES;
+			aiMesh *subMesh =MakeSubmesh(pMesh,subFaces,f);
+
+			//Lifted from PretransformVertices.cpp
+			ApplyTransform(subMesh,pMesh->mBones[i]->mOffsetMatrix);
+			std::pair<aiMesh*,const aiBone*> push_pair(subMesh,pMesh->mBones[i]);
+
+			poNewMeshes.push_back(push_pair);		
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Recursively updates the node's mesh list to account for the changed mesh list
+void DeboneProcess::UpdateNode(aiNode* pNode) const
+{
+	// rebuild the node's mesh index list
+	
+	std::vector<unsigned int> newMeshList;
+
+	// this will require two passes
+
+	unsigned int m = pNode->mNumMeshes, n = mSubMeshIndices.size();
+	
+	// first pass, look for meshes which have not moved
+
+	for(unsigned int a=0;a<m;a++)	{
+
+		unsigned int srcIndex = pNode->mMeshes[a];
+		const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[srcIndex];
+		unsigned int nSubmeshes = subMeshes.size();
+
+		for(unsigned int b=0;b<nSubmeshes;b++) {
+			if(!subMeshes[b].second) {
+				newMeshList.push_back(subMeshes[b].first);
+			}
+		}
+	}
+
+	// second pass, collect deboned meshes 
+
+	for(unsigned int a=0;a<n;a++)
+	{
+		const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[a];
+		unsigned int nSubmeshes = subMeshes.size();
+
+		for(unsigned int b=0;b<nSubmeshes;b++) {
+			if(subMeshes[b].second == pNode)	{
+				newMeshList.push_back(subMeshes[b].first);
+			}
+		}
+	}
+
+	if( pNode->mNumMeshes > 0 )	{
+		delete [] pNode->mMeshes; pNode->mMeshes = NULL;
+	}
+
+	pNode->mNumMeshes = newMeshList.size();
+
+	if(pNode->mNumMeshes)	{
+		pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
+		std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes);
+	}
+
+	// do that also recursively for all children
+	for( unsigned int a = 0; a < pNode->mNumChildren; ++a )	{
+		UpdateNode( pNode->mChildren[a]);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Apply the node transformation to a mesh
+void DeboneProcess::ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const
+{
+	// Check whether we need to transform the coordinates at all
+	if (!mat.IsIdentity()) {
+		
+		if (mesh->HasPositions()) {
+			for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+				mesh->mVertices[i] = mat * mesh->mVertices[i];
+			}
+		}
+		if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
+			aiMatrix4x4 mWorldIT = mat;
+			mWorldIT.Inverse().Transpose();
+
+			// TODO: implement Inverse() for aiMatrix3x3
+			aiMatrix3x3 m = aiMatrix3x3(mWorldIT);
+
+			if (mesh->HasNormals()) {
+				for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+					mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
+				}
+			}
+			if (mesh->HasTangentsAndBitangents()) {
+				for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+					mesh->mTangents[i]   = (m * mesh->mTangents[i]).Normalize();
+					mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
+				}
+			}
+		}
+	}
+}

+ 132 - 0
assimplib.mod/assimp/code/DeboneProcess.h

@@ -0,0 +1,132 @@
+				   /*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** Defines a post processing step to limit the number of bones affecting a single vertex. */
+#ifndef AI_DEBONEPROCESS_H_INC
+#define AI_DEBONEPROCESS_H_INC
+
+#include <vector>
+#include <utility>
+#include "BaseProcess.h"
+
+#include "../include/assimp/mesh.h"
+#include "../include/assimp/scene.h"
+
+class DeboneTest;
+
+namespace Assimp
+{
+
+#if (!defined AI_DEBONE_THRESHOLD)
+#	define AI_DEBONE_THRESHOLD	1.0f
+#endif // !! AI_DEBONE_THRESHOLD
+
+// ---------------------------------------------------------------------------
+/** This post processing step removes bones nearly losslessly or according to 
+* a configured threshold. In order to remove the bone, the primitives affected by
+* the bone are split from the mesh. The split off (new) mesh is boneless. At any 
+* point in time, bones without affect upon a given mesh are to be removed.
+*/
+class DeboneProcess : public BaseProcess
+{
+public:
+
+	DeboneProcess();
+	~DeboneProcess();
+
+public:
+	// -------------------------------------------------------------------
+	/** Returns whether the processing step is present in the given flag.
+	* @param pFlags The processing flags the importer was called with. 
+	*   A bitwise combination of #aiPostProcessSteps.
+	* @return true if the process is present in this flag fields, 
+	*   false if not.
+	*/
+	bool IsActive( unsigned int pFlags) const;
+
+	// -------------------------------------------------------------------
+	/** Called prior to ExecuteOnScene().
+	* The function is a request to the process to update its configuration
+	* basing on the Importer's configuration property list.
+	*/
+	void SetupProperties(const Importer* pImp);
+
+protected:
+		
+	// -------------------------------------------------------------------
+	/** 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);
+
+	// -------------------------------------------------------------------
+	/** Counts bones total/removable in a given mesh.
+	* @param pMesh The mesh to process.
+	*/
+	bool ConsiderMesh( const aiMesh* pMesh);
+
+	/// Splits the given mesh by bone count.
+	/// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split.
+	/// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary.
+	void SplitMesh(const aiMesh* pMesh, std::vector< std::pair< aiMesh*,const aiBone* > >& poNewMeshes) const;
+
+	/// Recursively updates the node's mesh list to account for the changed mesh list
+	void UpdateNode(aiNode* pNode) const;
+
+	// -------------------------------------------------------------------
+	// Apply transformation to a mesh 
+	void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat)const;
+
+public:
+	/** Number of bones present in the scene. */
+	unsigned int mNumBones;
+	unsigned int mNumBonesCanDoWithout;
+
+	float mThreshold;
+	bool mAllOrNone;
+
+	/// Per mesh index: Array of indices of the new submeshes.
+	std::vector< std::vector< std::pair< unsigned int,aiNode* > > > mSubMeshIndices;
+};
+
+} // end of namespace Assimp
+
+#endif // AI_DEBONEPROCESS_H_INC

+ 146 - 0
assimplib.mod/assimp/code/DefaultIOStream.cpp

@@ -0,0 +1,146 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file  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 (SIZE_MAX == cachedSize) {
+
+        // Although fseek/ftell would allow us to reuse the exising file handle here,
+        // it is generally unsafe because:
+        //  - For binary streams, it is not technically well-defined
+        //  - For text files the results are meaningless
+        // That's why we use the safer variant fstat here.
+        //
+        // See here for details:
+        // https://www.securecoding.cert.org/confluence/display/seccode/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+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
assimplib.mod/assimp/code/DefaultIOStream.h

@@ -0,0 +1,133 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Default file I/O using fXXX()-family of functions */
+#ifndef AI_DEFAULTIOSTREAM_H_INC
+#define AI_DEFAULTIOSTREAM_H_INC
+
+#include <stdio.h>
+#include "../include/assimp/IOStream.hpp"
+
+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	(SIZE_MAX)
+{
+	// empty
+}
+
+
+// ----------------------------------------------------------------------------------
+inline DefaultIOStream::DefaultIOStream (FILE* pFile, 
+		const std::string &strFilename) :
+	mFile(pFile), 
+	mFilename(strFilename),
+	cachedSize	(SIZE_MAX)
+{
+	// empty
+}
+// ----------------------------------------------------------------------------------
+
+} // ns assimp
+
+#endif //!!AI_DEFAULTIOSTREAM_H_INC
+

+ 167 - 0
assimplib.mod/assimp/code/DefaultIOSystem.cpp

@@ -0,0 +1,167 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+/** @file 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
assimplib.mod/assimp/code/DefaultIOSystem.h

@@ -0,0 +1,83 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Default implementation of IOSystem using the standard C file functions */
+#ifndef AI_DEFAULTIOSYSTEM_H_INC
+#define AI_DEFAULTIOSYSTEM_H_INC
+
+#include "../include/assimp/IOSystem.hpp"
+
+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

+ 423 - 0
assimplib.mod/assimp/code/DefaultLogger.cpp

@@ -0,0 +1,423 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file  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;
+
+static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging;
+
+// ----------------------------------------------------------------------------------
+// Represents a log-stream + 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 char* message)	{
+
+	// SECURITY FIX: otherwise it's easy to produce overruns since
+	// sometimes importers will include data from the input file
+	// (i.e. node names) in their messages.
+	if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
+		ai_assert(false);
+		return;
+	}
+	return OnDebug(message);
+}
+
+// ----------------------------------------------------------------------------------
+void Logger::info(const char* message)	{
+	
+	// SECURITY FIX: see above
+	if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
+		ai_assert(false);
+		return;
+	}
+	return OnInfo(message);
+}
+	
+// ----------------------------------------------------------------------------------
+void Logger::warn(const char* message)	{
+	
+	// SECURITY FIX: see above
+	if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
+		ai_assert(false);
+		return;
+	}
+	return OnWarn(message);
+}
+
+// ----------------------------------------------------------------------------------
+void Logger::error(const char* message)	{
+	
+	// SECURITY FIX: see above
+	if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
+		ai_assert(false);
+		return;
+	}
+	return OnError(message);
+}
+
+// ----------------------------------------------------------------------------------
+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 + 16];
+	::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 + 16];
+	::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 + 16];
+	::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 + 16];
+	::sprintf(msg,"Error, T%i: %s", GetThreadID(), message );
+
+	WriteToStreams( msg, Logger::Err );
+}
+
+// ----------------------------------------------------------------------------------
+//	Will attach 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 = SeverityAll;
+	}
+	
+	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
assimplib.mod/assimp/code/DefaultProgressHandler.h

@@ -0,0 +1,64 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file ProgressHandler.hpp
+ *  @brief Abstract base class 'ProgressHandler'.
+ */
+#ifndef INCLUDED_AI_DEFAULTPROGRESSHANDLER_H
+#define INCLUDED_AI_DEFAULTPROGRESSHANDLER_H
+
+#include "../include/assimp/ProgressHandler.hpp"
+namespace Assimp	{
+
+// ------------------------------------------------------------------------------------
+/** @brief Internal default implementation of the #ProgressHandler interface. */
+class DefaultProgressHandler 
+	: public ProgressHandler	{
+
+	
+	virtual bool Update(float /*percentage*/) {
+		return false;
+	}
+
+
+}; // !class DefaultProgressHandler 
+} // Namespace Assimp
+
+#endif

+ 124 - 0
assimplib.mod/assimp/code/Exceptional.h

@@ -0,0 +1,124 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2008, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef 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 DeadlyImportError
+	: public runtime_error
+{
+public:
+	/** Constructor with arguments */
+	explicit DeadlyImportError( const std::string& pErrorText)
+		: runtime_error(pErrorText)
+	{
+	}
+
+private:
+};
+
+typedef DeadlyImportError DeadlyExportError;
+
+#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

+ 468 - 0
assimplib.mod/assimp/code/Exporter.cpp

@@ -0,0 +1,468 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the following 
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+/** @file Exporter.cpp
+
+Assimp export interface. While it's public interface bears many similarities
+to the import interface (in fact, it is largely symmetric), the internal
+implementations differs a lot. Exporters are considered stateless and are
+simple callbacks which we maintain in a global list along with their
+description strings.
+
+Here we implement only the C++ interface (Assimp::Exporter).
+*/
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+
+#include "DefaultIOSystem.h"
+#include "BlobIOSystem.h" 
+#include "SceneCombiner.h" 
+#include "BaseProcess.h" 
+#include "Importer.h" // need this for GetPostProcessingStepInstanceList()
+
+#include "JoinVerticesProcess.h"
+#include "MakeVerboseFormat.h"
+#include "ConvertToLHProcess.h"
+
+namespace Assimp {
+
+// PostStepRegistry.cpp
+void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out);
+
+// ------------------------------------------------------------------------------------------------
+// Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype
+void ExportSceneCollada(const char*,IOSystem*, const aiScene*);
+void ExportSceneObj(const char*,IOSystem*, const aiScene*);
+void ExportSceneSTL(const char*,IOSystem*, const aiScene*);
+void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*);
+void ExportScenePly(const char*,IOSystem*, const aiScene*);
+void ExportScene3DS(const char*, IOSystem*, const aiScene*) {}
+
+// ------------------------------------------------------------------------------------------------
+// global array of all export formats which Assimp supports in its current build
+Exporter::ExportFormatEntry gExporters[] = 
+{
+#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
+	Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada),
+#endif
+
+#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
+	Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj, 
+		aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */),
+#endif
+
+#ifndef ASSIMP_BUILD_NO_STL_EXPORTER
+	Exporter::ExportFormatEntry( "stl", "Stereolithography", "stl" , &ExportSceneSTL, 
+		aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices
+	),
+	Exporter::ExportFormatEntry( "stlb", "Stereolithography (binary)", "stl" , &ExportSceneSTLBinary, 
+		aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices
+	),
+#endif
+
+#ifndef ASSIMP_BUILD_NO_PLY_EXPORTER
+	Exporter::ExportFormatEntry( "ply", "Stanford Polygon Library", "ply" , &ExportScenePly, 
+		aiProcess_PreTransformVertices
+	),
+#endif
+
+//#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
+//	ExportFormatEntry( "3ds", "Autodesk 3DS (legacy format)", "3ds" , &ExportScene3DS),
+//#endif
+};
+
+#define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0]))
+
+
+class ExporterPimpl {
+public:
+
+	ExporterPimpl()
+		: blob()
+		, mIOSystem(new Assimp::DefaultIOSystem())
+		, mIsDefaultIOHandler(true)
+	{
+		GetPostProcessingStepInstanceList(mPostProcessingSteps);
+
+		// grab all builtin exporters
+		mExporters.resize(ASSIMP_NUM_EXPORTERS);
+		std::copy(gExporters,gExporters+ASSIMP_NUM_EXPORTERS,mExporters.begin());
+	}
+
+	~ExporterPimpl() 
+	{
+		delete blob;
+
+		// Delete all post-processing plug-ins
+		for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) {
+			delete mPostProcessingSteps[a];
+		}
+	}
+
+public:
+		
+	aiExportDataBlob* blob;
+	boost::shared_ptr< Assimp::IOSystem > mIOSystem;
+	bool mIsDefaultIOHandler;
+
+	/** Post processing steps we can apply at the imported data. */
+	std::vector< BaseProcess* > mPostProcessingSteps;
+
+	/** Last fatal export error */
+	std::string mError;
+
+	/** Exporters, this includes those registered using #Assimp::Exporter::RegisterExporter */
+	std::vector<Exporter::ExportFormatEntry> mExporters;
+};
+
+
+} // end of namespace Assimp
+
+
+
+
+
+using namespace Assimp;
+
+
+// ------------------------------------------------------------------------------------------------
+Exporter :: Exporter() 
+: pimpl(new ExporterPimpl())
+{
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Exporter :: ~Exporter()
+{
+	FreeBlob();
+
+	delete pimpl;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void Exporter :: SetIOHandler( IOSystem* pIOHandler)
+{
+	pimpl->mIsDefaultIOHandler = !pIOHandler;
+	pimpl->mIOSystem.reset(pIOHandler);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+IOSystem* Exporter :: GetIOHandler() const
+{
+	return pimpl->mIOSystem.get();
+}
+
+
+// ------------------------------------------------------------------------------------------------
+bool Exporter :: IsDefaultIOHandler() const
+{
+	return pimpl->mIsDefaultIOHandler;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+const aiExportDataBlob* Exporter :: ExportToBlob(  const aiScene* pScene, const char* pFormatId, unsigned int )
+{
+	if (pimpl->blob) {
+		delete pimpl->blob;
+		pimpl->blob = NULL;
+	}
+
+
+	boost::shared_ptr<IOSystem> old = pimpl->mIOSystem;
+
+	BlobIOSystem* blobio = new BlobIOSystem();
+	pimpl->mIOSystem = boost::shared_ptr<IOSystem>( blobio );
+
+	if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName())) {
+		pimpl->mIOSystem = old;
+		return NULL;
+	}
+
+	pimpl->blob = blobio->GetBlobChain();
+	pimpl->mIOSystem = old;
+
+	return pimpl->blob;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+bool IsVerboseFormat(const aiMesh* mesh) 
+{
+	// avoid slow vector<bool> specialization
+	std::vector<unsigned int> seen(mesh->mNumVertices,0);
+	for(unsigned int i = 0; i < mesh->mNumFaces; ++i) {
+		const aiFace& f = mesh->mFaces[i];
+		for(unsigned int j = 0; j < f.mNumIndices; ++j) {
+			if(++seen[f.mIndices[j]] == 2) {
+				// found a duplicate index
+				return false;
+			}
+		}
+	}
+	return true;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+bool IsVerboseFormat(const aiScene* pScene) 
+{
+	for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+		if(!IsVerboseFormat(pScene->mMeshes[i])) {
+			return false;
+		}
+	}
+	return true;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+aiReturn Exporter :: Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing )
+{
+	ASSIMP_BEGIN_EXCEPTION_REGION();
+
+	// when they create scenes from scratch, users will likely create them not in verbose
+	// format. They will likely not be aware that there is a flag in the scene to indicate
+	// this, however. To avoid surprises and bug reports, we check for duplicates in
+	// meshes upfront.
+	const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || IsVerboseFormat(pScene);
+
+	pimpl->mError = "";
+	for (size_t i = 0; i < pimpl->mExporters.size(); ++i) {
+		const Exporter::ExportFormatEntry& exp = pimpl->mExporters[i];
+		if (!strcmp(exp.mDescription.id,pFormatId)) {
+
+			try {
+
+				// Always create a full copy of the scene. We might optimize this one day, 
+				// but for now it is the most pragmatic way.
+				aiScene* scenecopy_tmp;
+				SceneCombiner::CopyScene(&scenecopy_tmp,pScene);
+
+				std::auto_ptr<aiScene> scenecopy(scenecopy_tmp);
+				const ScenePrivateData* const priv = ScenePriv(pScene);
+
+				// steps that are not idempotent, i.e. we might need to run them again, usually to get back to the
+				// original state before the step was applied first. When checking which steps we don't need
+				// to run, those are excluded.
+				const unsigned int nonIdempotentSteps = aiProcess_FlipWindingOrder | aiProcess_FlipUVs | aiProcess_MakeLeftHanded;
+
+				// Erase all pp steps that were already applied to this scene
+				const unsigned int pp = (exp.mEnforcePP | pPreprocessing) & ~(priv && !priv->mIsCopy
+					? (priv->mPPStepsApplied & ~nonIdempotentSteps)
+					: 0u);
+
+				// If no extra postprocessing was specified, and we obtained this scene from an
+				// Assimp importer, apply the reverse steps automatically.
+				// TODO: either drop this, or document it. Otherwise it is just a bad surprise.
+				//if (!pPreprocessing && priv) {
+				//	pp |= (nonIdempotentSteps & priv->mPPStepsApplied);
+				//}
+
+				// If the input scene is not in verbose format, but there is at least postprocessing step that relies on it,
+				// we need to run the MakeVerboseFormat step first.
+				bool must_join_again = false;
+				if (!is_verbose_format) {
+					
+					bool verbosify = false;
+					for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
+						BaseProcess* const p = pimpl->mPostProcessingSteps[a];
+
+						if (p->IsActive(pp) && p->RequireVerboseFormat()) {
+							verbosify = true;
+							break;
+						}
+					}
+
+					if (verbosify || (exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) {
+						DefaultLogger::get()->debug("export: Scene data not in verbose format, applying MakeVerboseFormat step first");
+
+						MakeVerboseFormatProcess proc;
+						proc.Execute(scenecopy.get());
+
+						if(!(exp.mEnforcePP & aiProcess_JoinIdenticalVertices)) {
+							must_join_again = true;
+						}
+					}
+				}
+
+				if (pp) {
+					// the three 'conversion' steps need to be executed first because all other steps rely on the standard data layout
+					{
+						FlipWindingOrderProcess step;
+						if (step.IsActive(pp)) {
+							step.Execute(scenecopy.get());
+						}
+					}
+					
+					{
+						FlipUVsProcess step;
+						if (step.IsActive(pp)) {
+							step.Execute(scenecopy.get());
+						}
+					}
+
+					{
+						MakeLeftHandedProcess step;
+						if (step.IsActive(pp)) {
+							step.Execute(scenecopy.get());
+						}
+					}
+
+					// dispatch other processes
+					for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
+						BaseProcess* const p = pimpl->mPostProcessingSteps[a];
+
+						if (p->IsActive(pp) 
+							&& !dynamic_cast<FlipUVsProcess*>(p) 
+							&& !dynamic_cast<FlipWindingOrderProcess*>(p) 
+							&& !dynamic_cast<MakeLeftHandedProcess*>(p)) {
+
+							p->Execute(scenecopy.get());
+						}
+					}
+					ScenePrivateData* const privOut = ScenePriv(scenecopy.get());
+					ai_assert(privOut);
+
+					privOut->mPPStepsApplied |= pp;
+				}
+
+				if(must_join_again) {
+					JoinVerticesProcess proc;
+					proc.Execute(scenecopy.get());
+				}
+
+				exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get());
+			}
+			catch (DeadlyExportError& err) {
+				pimpl->mError = err.what();
+				return AI_FAILURE;
+			}
+			return AI_SUCCESS;
+		}
+	}
+
+	pimpl->mError = std::string("Found no exporter to handle this file format: ") + pFormatId;
+	ASSIMP_END_EXCEPTION_REGION(aiReturn);
+	return AI_FAILURE;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+const char* Exporter :: GetErrorString() const
+{
+	return pimpl->mError.c_str();
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void Exporter :: FreeBlob( )
+{
+	delete pimpl->blob;
+	pimpl->blob = NULL;
+
+	pimpl->mError = "";
+}
+
+
+// ------------------------------------------------------------------------------------------------
+const aiExportDataBlob* Exporter :: GetBlob() const 
+{
+	return pimpl->blob;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+const aiExportDataBlob* Exporter :: GetOrphanedBlob() const 
+{
+	const aiExportDataBlob* tmp = pimpl->blob;
+	pimpl->blob = NULL;
+	return tmp;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+size_t Exporter :: GetExportFormatCount() const 
+{
+	return pimpl->mExporters.size();
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiExportFormatDesc* Exporter :: GetExportFormatDescription( size_t pIndex ) const 
+{
+	if (pIndex >= GetExportFormatCount()) {
+		return NULL;
+	}
+
+	return &pimpl->mExporters[pIndex].mDescription;
+}
+
+// ------------------------------------------------------------------------------------------------
+aiReturn Exporter :: RegisterExporter(const ExportFormatEntry& desc)
+{
+	BOOST_FOREACH(const ExportFormatEntry& e, pimpl->mExporters) {
+		if (!strcmp(e.mDescription.id,desc.mDescription.id)) {
+			return aiReturn_FAILURE;
+		}
+	}
+
+	pimpl->mExporters.push_back(desc);
+	return aiReturn_SUCCESS;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void Exporter :: UnregisterExporter(const char* id)
+{
+	for(std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin(); it != pimpl->mExporters.end(); ++it) {
+		if (!strcmp((*it).mDescription.id,id)) {
+			pimpl->mExporters.erase(it);
+			break;
+		}
+	}
+}
+
+#endif // !ASSIMP_BUILD_NO_EXPORT

+ 313 - 0
assimplib.mod/assimp/code/FBXAnimation.cpp

@@ -0,0 +1,313 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  FBXAnimation.cpp
+ *  @brief Assimp::FBX::AnimationCurve, Assimp::FBX::AnimationCurveNode, 
+ *         Assimp::FBX::AnimationLayer, Assimp::FBX::AnimationStack 
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXParser.h"
+#include "FBXDocument.h"
+#include "FBXImporter.h"
+#include "FBXImportSettings.h"
+#include "FBXDocumentUtil.h"
+#include "FBXProperties.h"
+
+namespace Assimp {
+namespace FBX {
+
+	using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc)
+: Object(id, element, name)
+{
+	const Scope& sc = GetRequiredScope(element);
+	const Element& KeyTime = GetRequiredElement(sc,"KeyTime");
+	const Element& KeyValueFloat = GetRequiredElement(sc,"KeyValueFloat");
+
+	ParseVectorDataArray(keys, KeyTime);
+	ParseVectorDataArray(values, KeyValueFloat);
+
+	if(keys.size() != values.size()) {
+		DOMError("the number of key times does not match the number of keyframe values",&KeyTime);
+	}
+	
+	// check if the key times are well-ordered
+	if(!std::equal(keys.begin(), keys.end() - 1, keys.begin() + 1, std::less<KeyTimeList::value_type>())) {
+		DOMError("the keyframes are not in ascending order",&KeyTime);
+	}
+
+	const Element* KeyAttrDataFloat = sc["KeyAttrDataFloat"];
+	if(KeyAttrDataFloat) {
+		ParseVectorDataArray(attributes, *KeyAttrDataFloat);
+	}
+
+	const Element* KeyAttrFlags = sc["KeyAttrFlags"];
+	if(KeyAttrFlags) {
+		ParseVectorDataArray(flags, *KeyAttrFlags);
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurve::~AnimationCurve()
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNode::AnimationCurveNode(uint64_t id, const Element& element, const std::string& name, const Document& doc, 
+	const char* const * target_prop_whitelist /*= NULL*/, size_t whitelist_size /*= 0*/)
+: Object(id, element, name)
+, target()
+, doc(doc)
+{
+	const Scope& sc = GetRequiredScope(element);
+	
+	// find target node
+	const char* whitelist[] = {"Model","NodeAttribute"};
+	const std::vector<const Connection*>& conns = doc.GetConnectionsBySourceSequenced(ID(),whitelist,2);
+
+	BOOST_FOREACH(const Connection* con, conns) {
+
+		// link should go for a property
+		if (!con->PropertyName().length()) {
+			continue;
+		}
+
+		if(target_prop_whitelist) {
+			const char* const s = con->PropertyName().c_str();
+			bool ok = false;
+			for (size_t i = 0; i < whitelist_size; ++i) {
+				if (!strcmp(s, target_prop_whitelist[i])) {
+					ok = true;
+					break;
+				}
+			}
+
+			if (!ok) {
+				throw std::range_error("AnimationCurveNode target property is not in whitelist");
+			}
+		}
+
+		const Object* const ob = con->DestinationObject();
+		if(!ob) {
+			DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring",&element);
+			continue;
+		}
+
+		// XXX support constraints as DOM class
+		//ai_assert(dynamic_cast<const Model*>(ob) || dynamic_cast<const NodeAttribute*>(ob));
+		target = ob; 
+		if(!target) {
+			continue;
+		}
+
+		prop = con->PropertyName();
+		break;
+	}
+
+	if(!target) {
+		DOMWarning("failed to resolve target Model/NodeAttribute/Constraint for AnimationCurveNode",&element);
+	}
+
+	props = GetPropertyTable(doc,"AnimationCurveNode.FbxAnimCurveNode",element,sc,false);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNode::~AnimationCurveNode()
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+const AnimationCurveMap& AnimationCurveNode::Curves() const
+{
+	if(curves.empty()) {
+		// resolve attached animation curves
+		const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurve");
+
+		BOOST_FOREACH(const Connection* con, conns) {
+
+			// link should go for a property
+			if (!con->PropertyName().length()) {
+				continue;
+			}
+
+			const Object* const ob = con->SourceObject();
+			if(!ob) {
+				DOMWarning("failed to read source object for AnimationCurve->AnimationCurveNode link, ignoring",&element);
+				continue;
+			}
+
+			const AnimationCurve* const anim = dynamic_cast<const AnimationCurve*>(ob);
+			if(!anim) {
+				DOMWarning("source object for ->AnimationCurveNode link is not an AnimationCurve",&element);
+				continue;
+			}
+
+			curves[con->PropertyName()] = anim;
+		}
+	}
+
+	return curves;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+AnimationLayer::AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc)
+: Object(id, element, name)
+, doc(doc)
+{
+	const Scope& sc = GetRequiredScope(element);
+
+	// note: the props table here bears little importance and is usually absent
+	props = GetPropertyTable(doc,"AnimationLayer.FbxAnimLayer",element,sc, true);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+AnimationLayer::~AnimationLayer()
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNodeList AnimationLayer::Nodes(const char* const * target_prop_whitelist /*= NULL*/, 
+	size_t whitelist_size /*= 0*/) const
+{
+	AnimationCurveNodeList nodes;
+
+	// resolve attached animation nodes
+	const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationCurveNode");
+	nodes.reserve(conns.size());
+
+	BOOST_FOREACH(const Connection* con, conns) {
+
+		// link should not go to a property
+		if (con->PropertyName().length()) {
+			continue;
+		}
+
+		const Object* const ob = con->SourceObject();
+		if(!ob) {
+			DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring",&element);
+			continue;
+		}
+
+		const AnimationCurveNode* const anim = dynamic_cast<const AnimationCurveNode*>(ob);
+		if(!anim) {
+			DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode",&element);
+			continue;
+		}
+
+		if(target_prop_whitelist) {
+			const char* s = anim->TargetProperty().c_str();
+			bool ok = false;
+			for (size_t i = 0; i < whitelist_size; ++i) {
+				if (!strcmp(s, target_prop_whitelist[i])) {
+					ok = true;
+					break;
+				}
+			}
+			if(!ok) {
+				continue;
+			}
+		}
+		nodes.push_back(anim);
+	}
+
+	return nodes; // pray for NRVO
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationStack::AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc)
+: Object(id, element, name)
+{
+	const Scope& sc = GetRequiredScope(element);
+
+	// note: we don't currently use any of these properties so we shouldn't bother if it is missing
+	props = GetPropertyTable(doc,"AnimationStack.FbxAnimStack",element,sc, true);
+
+	// resolve attached animation layers
+	const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"AnimationLayer");
+	layers.reserve(conns.size());
+
+	BOOST_FOREACH(const Connection* con, conns) {
+
+		// link should not go to a property
+		if (con->PropertyName().length()) {
+			continue;
+		}
+
+		const Object* const ob = con->SourceObject();
+		if(!ob) {
+			DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring",&element);
+			continue;
+		}
+
+		const AnimationLayer* const anim = dynamic_cast<const AnimationLayer*>(ob);
+		if(!anim) {
+			DOMWarning("source object for ->AnimationStack link is not an AnimationLayer",&element);
+			continue;
+		}
+		layers.push_back(anim);
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+AnimationStack::~AnimationStack()
+{
+
+}
+
+} //!FBX
+} //!Assimp
+
+#endif

+ 398 - 0
assimplib.mod/assimp/code/FBXBinaryTokenizer.cpp

@@ -0,0 +1,398 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/** @file  FBXBinaryTokenizer.cpp
+ *  @brief Implementation of a fake lexer for binary fbx files -
+ *    we emit tokens so the parser needs almost no special handling
+ *    for binary files.
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXTokenizer.h"
+#include "FBXUtil.h"
+
+namespace Assimp {
+namespace FBX {
+
+
+// ------------------------------------------------------------------------------------------------
+Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int offset)
+	: sbegin(sbegin)
+	, send(send)
+	, type(type)
+	, line(offset)
+	, column(BINARY_MARKER)
+#ifdef DEBUG
+	, contents(sbegin, static_cast<size_t>(send-sbegin))
+#endif
+{
+	ai_assert(sbegin);
+	ai_assert(send);
+
+	// binary tokens may have zero length because they are sometimes dummies
+	// inserted by TokenizeBinary()
+	ai_assert(send >= sbegin);
+}
+
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// signal tokenization error, this is always unrecoverable. Throws DeadlyImportError.
+void TokenizeError(const std::string& message, unsigned int offset)
+{
+	throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset));
+}
+
+
+// ------------------------------------------------------------------------------------------------
+uint32_t Offset(const char* begin, const char* cursor)
+{
+	ai_assert(begin <= cursor);
+	return static_cast<unsigned int>(cursor - begin);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void TokenizeError(const std::string& message, const char* begin, const char* cursor)
+{
+	TokenizeError(message, Offset(begin, cursor));
+}
+
+
+// ------------------------------------------------------------------------------------------------
+uint32_t ReadWord(const char* input, const char*& cursor, const char* end)
+{
+	if(Offset(cursor, end) < 4) {
+		TokenizeError("cannot ReadWord, out of bounds",input, cursor);
+	} 
+
+	uint32_t word = *reinterpret_cast<const uint32_t*>(cursor);
+	AI_SWAP4(word);
+
+	cursor += 4;
+
+	return word;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+uint8_t ReadByte(const char* input, const char*& cursor, const char* end)
+{
+	if(Offset(cursor, end) < 1) {
+		TokenizeError("cannot ReadByte, out of bounds",input, cursor);
+	} 
+
+	uint8_t word = *reinterpret_cast<const uint8_t*>(cursor);
+	++cursor;
+
+	return word;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+unsigned int ReadString(const char*& sbegin_out, const char*& send_out, const char* input, const char*& cursor, const char* end, 
+	bool long_length = false,
+	bool allow_null = false)
+{
+	const uint32_t len_len = long_length ? 4 : 1;
+	if(Offset(cursor, end) < len_len) {
+		TokenizeError("cannot ReadString, out of bounds reading length",input, cursor);
+	} 
+
+	const uint32_t length = long_length ? ReadWord(input, cursor, end) : ReadByte(input, cursor, end);
+
+	if (Offset(cursor, end) < length) {
+		TokenizeError("cannot ReadString, length is out of bounds",input, cursor);
+	}
+
+	sbegin_out = cursor;
+	cursor += length;
+
+	send_out = cursor;
+
+	if(!allow_null) {
+		for (unsigned int i = 0; i < length; ++i) {
+			if(sbegin_out[i] == '\0') {
+				TokenizeError("failed ReadString, unexpected NUL character in string",input, cursor);
+			}
+		}
+	}
+
+	return length;
+}
+
+
+
+// ------------------------------------------------------------------------------------------------
+void ReadData(const char*& sbegin_out, const char*& send_out, const char* input, const char*& cursor, const char* end)
+{
+	if(Offset(cursor, end) < 1) {
+		TokenizeError("cannot ReadData, out of bounds reading length",input, cursor);
+	} 
+
+	const char type = *cursor;
+	sbegin_out = cursor++;
+
+	switch(type)
+	{
+		// 16 bit int
+	case 'Y':
+		cursor += 2;
+		break;
+
+		// 1 bit bool flag (yes/no)
+	case 'C':
+		cursor += 1;
+		break;
+
+		// 32 bit int
+	case 'I':
+		// <- fall thru
+
+		// float
+	case 'F':
+		cursor += 4;
+		break;
+
+		// double
+	case 'D':
+		cursor += 8;
+		break;
+
+		// 64 bit int
+	case 'L':
+		cursor += 8;
+		break;
+
+		// note: do not write cursor += ReadWord(...cursor) as this would be UB
+
+		// raw binary data
+	case 'R':	
+	{
+		const uint32_t length = ReadWord(input, cursor, end);
+		cursor += length;
+		break;
+	}
+
+	case 'b': 
+		// TODO: what is the 'b' type code? Right now we just skip over it /
+		// take the full range we could get
+		cursor = end;
+		break;
+
+		// array of *
+	case 'f':
+	case 'd':
+	case 'l':
+	case 'i':	{
+	
+		const uint32_t length = ReadWord(input, cursor, end);
+		const uint32_t encoding = ReadWord(input, cursor, end);
+
+		const uint32_t comp_len = ReadWord(input, cursor, end);
+
+		// compute length based on type and check against the stored value
+		if(encoding == 0) {
+			uint32_t stride = 0;
+			switch(type)
+			{
+			case 'f':
+			case 'i':
+				stride = 4;
+				break;
+
+			case 'd':
+			case 'l':
+				stride = 8;
+				break;
+
+			default:
+				ai_assert(false);
+			};
+            ai_assert(stride > 0);
+			if(length * stride != comp_len) {
+				TokenizeError("cannot ReadData, calculated data stride differs from what the file claims",input, cursor);
+			}
+		}
+		// zip/deflate algorithm (encoding==1)? take given length. anything else? die
+		else if (encoding != 1) {			
+			TokenizeError("cannot ReadData, unknown encoding",input, cursor);
+		}
+		cursor += comp_len;
+		break;
+	}
+
+		// string
+	case 'S': {
+		const char* sb, *se;
+		// 0 characters can legally happen in such strings
+		ReadString(sb, se, input, cursor, end, true, true);
+		break;
+	}
+	default:
+		TokenizeError("cannot ReadData, unexpected type code: " + std::string(&type, 1),input, cursor);
+	}
+
+	if(cursor > end) {
+		TokenizeError("cannot ReadData, the remaining size is too small for the data type: " + std::string(&type, 1),input, cursor);
+	} 
+
+	// the type code is contained in the returned range
+	send_out = cursor;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor, const char* end)
+{
+	// the first word contains the offset at which this block ends
+	const uint32_t end_offset = ReadWord(input, cursor, end);
+	
+	// we may get 0 if reading reached the end of the file -
+	// fbx files have a mysterious extra footer which I don't know 
+	// how to extract any information from, but at least it always 
+	// starts with a 0.
+	if(!end_offset) {
+		return false;
+	}
+
+	if(end_offset > Offset(input, end)) {
+		TokenizeError("block offset is out of range",input, cursor);
+	}
+	else if(end_offset < Offset(input, cursor)) {
+		TokenizeError("block offset is negative out of range",input, cursor);
+	}
+
+	// the second data word contains the number of properties in the scope
+	const uint32_t prop_count = ReadWord(input, cursor, end);
+
+	// the third data word contains the length of the property list
+	const uint32_t prop_length = ReadWord(input, cursor, end);
+
+	// now comes the name of the scope/key
+	const char* sbeg, *send;
+	ReadString(sbeg, send, input, cursor, end);
+
+	output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, cursor) ));
+
+	// now come the individual properties
+	const char* begin_cursor = cursor;
+	for (unsigned int i = 0; i < prop_count; ++i) {
+		ReadData(sbeg, send, input, cursor, begin_cursor + prop_length);
+
+		output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor) ));
+
+		if(i != prop_count-1) {
+			output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_COMMA, Offset(input, cursor) ));
+		}
+	}
+
+	if (Offset(begin_cursor, cursor) != prop_length) {
+		TokenizeError("property length not reached, something is wrong",input, cursor);
+	}
+
+	// at the end of each nested block, there is a NUL record to indicate
+	// that the sub-scope exists (i.e. to distinguish between P: and P : {})
+	// this NUL record is 13 bytes long.
+#define BLOCK_SENTINEL_LENGTH 13
+
+	if (Offset(input, cursor) < end_offset) {
+
+		if (end_offset - Offset(input, cursor) < BLOCK_SENTINEL_LENGTH) {
+			TokenizeError("insufficient padding bytes at block end",input, cursor);
+		}
+
+		output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_OPEN_BRACKET, Offset(input, cursor) ));
+
+		// XXX this is vulnerable to stack overflowing ..
+		while(Offset(input, cursor) < end_offset - BLOCK_SENTINEL_LENGTH) {
+			ReadScope(output_tokens, input, cursor, input + end_offset - BLOCK_SENTINEL_LENGTH);
+		}
+		output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor) ));
+
+		for (unsigned int i = 0; i < BLOCK_SENTINEL_LENGTH; ++i) {
+			if(cursor[i] != '\0') {
+				TokenizeError("failed to read nested block sentinel, expected all bytes to be 0",input, cursor);
+			}
+		}
+		cursor += BLOCK_SENTINEL_LENGTH;
+	}
+
+	if (Offset(input, cursor) != end_offset) {
+		TokenizeError("scope length not reached, something is wrong",input, cursor);
+	}
+
+	return true;
+}
+
+
+}
+
+// ------------------------------------------------------------------------------------------------
+void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int length)
+{
+	ai_assert(input);
+
+	if(length < 0x1b) {
+		TokenizeError("file is too short",0);
+	}
+
+	if (strncmp(input,"Kaydara FBX Binary",18)) {
+		TokenizeError("magic bytes not found",0);
+	}
+
+
+	//uint32_t offset = 0x1b;
+
+	const char* cursor = input + 0x1b;
+
+	while (cursor < input + length) {
+		if(!ReadScope(output_tokens, input, cursor, input + length)) {
+			break;
+		}
+	}
+}
+
+} // !FBX
+} // !Assimp
+
+#endif

+ 66 - 0
assimplib.mod/assimp/code/FBXCompileConfig.h

@@ -0,0 +1,66 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  FBXCompileConfig.h
+ *  @brief FBX importer compile-time switches
+ */
+#ifndef INCLUDED_AI_FBX_COMPILECONFIG_H
+#define INCLUDED_AI_FBX_COMPILECONFIG_H
+
+//
+#if _MSC_VER > 1500 || (defined __GNUC___)
+#	define ASSIMP_FBX_USE_UNORDERED_MULTIMAP
+#	else
+#	define fbx_unordered_map map
+#	define fbx_unordered_multimap multimap
+#endif
+
+#ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP
+#	include <unordered_map>
+#	if _MSC_VER > 1600
+#		define fbx_unordered_map unordered_map
+#		define fbx_unordered_multimap unordered_multimap
+#	else
+#		define fbx_unordered_map tr1::unordered_map
+#		define fbx_unordered_multimap tr1::unordered_multimap
+#	endif
+#endif
+
+#endif

+ 2982 - 0
assimplib.mod/assimp/code/FBXConverter.cpp

@@ -0,0 +1,2982 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  FBXConverter.cpp
+ *  @brief Implementation of the FBX DOM -> aiScene converter
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include <iterator>
+#include <sstream>
+#include <boost/tuple/tuple.hpp>
+
+#include "FBXParser.h"
+#include "FBXConverter.h"
+#include "FBXDocument.h"
+#include "FBXUtil.h"
+#include "FBXProperties.h"
+#include "FBXImporter.h"
+
+namespace Assimp {
+namespace FBX {
+
+	using namespace Util;
+
+
+#define MAGIC_NODE_TAG "_$AssimpFbx$"
+
+#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
+
+	// XXX vc9's debugger won't step into anonymous namespaces
+//namespace {
+
+/** Dummy class to encapsulate the conversion process */
+class Converter
+{
+public:
+
+	/** the different parts that make up the final local transformation of a fbx node */
+	enum TransformationComp
+	{
+		TransformationComp_Translation = 0,
+		TransformationComp_RotationOffset,
+		TransformationComp_RotationPivot,
+		TransformationComp_PreRotation,
+		TransformationComp_Rotation,
+		TransformationComp_PostRotation,
+		TransformationComp_RotationPivotInverse,
+		TransformationComp_ScalingOffset,
+		TransformationComp_ScalingPivot,
+		TransformationComp_Scaling,
+		TransformationComp_ScalingPivotInverse,
+		TransformationComp_GeometricTranslation,
+		TransformationComp_GeometricRotation,
+		TransformationComp_GeometricScaling,
+
+		TransformationComp_MAXIMUM
+	};
+
+public:
+
+	Converter(aiScene* out, const Document& doc)
+		: defaultMaterialIndex()
+		, out(out) 
+		, doc(doc)
+	{
+		// animations need to be converted first since this will
+		// populate the node_anim_chain_bits map, which is needed
+		// to determine which nodes need to be generated.
+		ConvertAnimations();
+		ConvertRootNode();
+
+		if(doc.Settings().readAllMaterials) {
+			// unfortunately this means we have to evaluate all objects
+			BOOST_FOREACH(const ObjectMap::value_type& v,doc.Objects()) {
+
+				const Object* ob = v.second->Get();
+				if(!ob) {
+					continue;
+				}
+
+				const Material* mat = dynamic_cast<const Material*>(ob);
+				if(mat) {
+
+					if (materials_converted.find(mat) == materials_converted.end()) {
+						ConvertMaterial(*mat, 0);
+					}
+				}
+			}
+		}
+
+		TransferDataToScene();
+
+		// if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
+		// to make sure the scene passes assimp's validation. FBX files
+		// need not contain geometry (i.e. camera animations, raw armatures).
+		if (out->mNumMeshes == 0) {
+			out->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
+		}
+	}
+
+
+	~Converter()
+	{
+		std::for_each(meshes.begin(),meshes.end(),Util::delete_fun<aiMesh>());
+		std::for_each(materials.begin(),materials.end(),Util::delete_fun<aiMaterial>());
+		std::for_each(animations.begin(),animations.end(),Util::delete_fun<aiAnimation>());
+		std::for_each(lights.begin(),lights.end(),Util::delete_fun<aiLight>());
+		std::for_each(cameras.begin(),cameras.end(),Util::delete_fun<aiCamera>());
+	}
+
+
+private:
+
+	// ------------------------------------------------------------------------------------------------
+	// find scene root and trigger recursive scene conversion
+	void ConvertRootNode() 
+	{
+		out->mRootNode = new aiNode();
+		out->mRootNode->mName.Set("RootNode");
+
+		// root has ID 0
+		ConvertNodes(0L, *out->mRootNode);
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// collect and assign child nodes
+	void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4())
+	{
+		const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
+
+		std::vector<aiNode*> nodes;
+		nodes.reserve(conns.size());
+
+		std::vector<aiNode*> nodes_chain;
+
+		try {
+			BOOST_FOREACH(const Connection* con, conns) {
+
+				// ignore object-property links
+				if(con->PropertyName().length()) {
+					continue;
+				}
+
+				const Object* const object = con->SourceObject();
+				if(!object) {
+					FBXImporter::LogWarn("failed to convert source object for Model link");
+					continue;
+				}
+
+				const Model* const model = dynamic_cast<const Model*>(object);
+
+				if(model) {
+					nodes_chain.clear();
+
+					aiMatrix4x4 new_abs_transform = parent_transform;
+
+					// even though there is only a single input node, the design of
+					// assimp (or rather: the complicated transformation chain that
+					// is employed by fbx) means that we may need multiple aiNode's
+					// to represent a fbx node's transformation.
+					GenerateTransformationNodeChain(*model,nodes_chain);
+
+					ai_assert(nodes_chain.size());
+
+					const std::string& original_name = FixNodeName(model->Name());
+
+					// check if any of the nodes in the chain has the name the fbx node
+					// is supposed to have. If there is none, add another node to 
+					// preserve the name - people might have scripts etc. that rely
+					// on specific node names.
+					aiNode* name_carrier = NULL;
+					BOOST_FOREACH(aiNode* prenode, nodes_chain) {
+						if ( !strcmp(prenode->mName.C_Str(), original_name.c_str()) ) {
+							name_carrier = prenode;
+							break;
+						}
+					}
+
+					if(!name_carrier) {
+						nodes_chain.push_back(new aiNode(original_name));
+						name_carrier = nodes_chain.back();
+					}
+
+					//setup metadata on newest node
+					SetupNodeMetadata(*model, *nodes_chain.back());
+
+					// link all nodes in a row
+					aiNode* last_parent = &parent;
+					BOOST_FOREACH(aiNode* prenode, nodes_chain) {
+						ai_assert(prenode);
+
+						if(last_parent != &parent) {
+							last_parent->mNumChildren = 1;
+							last_parent->mChildren = new aiNode*[1];
+							last_parent->mChildren[0] = prenode;
+						}
+
+						prenode->mParent = last_parent;
+						last_parent = prenode;
+
+						new_abs_transform *= prenode->mTransformation;
+					}
+
+					// attach geometry
+					ConvertModel(*model, *nodes_chain.back(), new_abs_transform);
+
+					// attach sub-nodes
+					ConvertNodes(model->ID(), *nodes_chain.back(), new_abs_transform);
+
+					if(doc.Settings().readLights) {
+						ConvertLights(*model);
+					}
+
+					if(doc.Settings().readCameras) {
+						ConvertCameras(*model);
+					}
+
+					nodes.push_back(nodes_chain.front());	
+					nodes_chain.clear();
+				}
+			}
+
+			if(nodes.size()) {
+				parent.mChildren = new aiNode*[nodes.size()]();
+				parent.mNumChildren = static_cast<unsigned int>(nodes.size());
+
+				std::swap_ranges(nodes.begin(),nodes.end(),parent.mChildren);
+			}
+		} 
+		catch(std::exception&)	{
+			Util::delete_fun<aiNode> deleter;
+			std::for_each(nodes.begin(),nodes.end(),deleter);
+			std::for_each(nodes_chain.begin(),nodes_chain.end(),deleter);
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertLights(const Model& model)
+	{
+		const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes();
+		BOOST_FOREACH(const NodeAttribute* attr, node_attrs) {
+			const Light* const light = dynamic_cast<const Light*>(attr);
+			if(light) {
+				ConvertLight(model, *light);
+			}
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertCameras(const Model& model)
+	{
+		const std::vector<const NodeAttribute*>& node_attrs = model.GetAttributes();
+		BOOST_FOREACH(const NodeAttribute* attr, node_attrs) {
+			const Camera* const cam = dynamic_cast<const Camera*>(attr);
+			if(cam) {
+				ConvertCamera(model, *cam);
+			}
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertLight(const Model& model, const Light& light)
+	{
+		lights.push_back(new aiLight());
+		aiLight* const out_light = lights.back();
+
+		out_light->mName.Set(FixNodeName(model.Name()));
+
+		const float intensity = light.Intensity();
+		const aiVector3D& col = light.Color();
+
+		out_light->mColorDiffuse = aiColor3D(col.x,col.y,col.z);
+		out_light->mColorDiffuse.r *= intensity;
+		out_light->mColorDiffuse.g *= intensity;
+		out_light->mColorDiffuse.b *= intensity;
+
+		out_light->mColorSpecular = out_light->mColorDiffuse;
+
+		switch(light.LightType())
+		{
+		case Light::Type_Point:
+			out_light->mType = aiLightSource_POINT;
+			break;
+
+		case Light::Type_Directional:
+			out_light->mType = aiLightSource_DIRECTIONAL;
+			break;
+
+		case Light::Type_Spot:
+			out_light->mType = aiLightSource_SPOT;
+			out_light->mAngleOuterCone = AI_DEG_TO_RAD(light.OuterAngle());
+			out_light->mAngleInnerCone = AI_DEG_TO_RAD(light.InnerAngle());
+			break;
+
+		case Light::Type_Area:
+			FBXImporter::LogWarn("cannot represent area light, set to UNDEFINED");
+			out_light->mType = aiLightSource_UNDEFINED;
+			break;
+
+		case Light::Type_Volume:
+			FBXImporter::LogWarn("cannot represent volume light, set to UNDEFINED");
+			out_light->mType = aiLightSource_UNDEFINED;
+			break;
+		default:
+			ai_assert(false);
+		}
+
+		// XXX: how to best convert the near and far decay ranges?
+		switch(light.DecayType())
+		{
+		case Light::Decay_None:
+			out_light->mAttenuationConstant = 1.0f;
+			break;
+		case Light::Decay_Linear:
+			out_light->mAttenuationLinear = 1.0f;
+			break;
+		case Light::Decay_Quadratic:
+			out_light->mAttenuationQuadratic = 1.0f;
+			break;
+		case Light::Decay_Cubic:
+			FBXImporter::LogWarn("cannot represent cubic attenuation, set to Quadratic");
+			out_light->mAttenuationQuadratic = 1.0f;
+			break;
+		default:
+			ai_assert(false);
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertCamera(const Model& model, const Camera& cam)
+	{
+		cameras.push_back(new aiCamera());
+		aiCamera* const out_camera = cameras.back();
+
+		out_camera->mName.Set(FixNodeName(model.Name()));
+
+		out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight();
+		out_camera->mPosition = cam.Position();
+		out_camera->mLookAt = cam.InterestPosition() - out_camera->mPosition;
+
+		// BUG HERE cam.FieldOfView() returns 1.0f every time.  1.0f is default value.
+		out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// this returns unified names usable within assimp identifiers (i.e. no space characters -
+	// while these would be allowed, they are a potential trouble spot so better not use them).
+	const char* NameTransformationComp(TransformationComp comp)
+	{
+		switch(comp)
+		{
+		case TransformationComp_Translation:
+			return "Translation";
+		case TransformationComp_RotationOffset:
+			return "RotationOffset";
+		case TransformationComp_RotationPivot:
+			return "RotationPivot";
+		case TransformationComp_PreRotation:
+			return "PreRotation";
+		case TransformationComp_Rotation:
+			return "Rotation";
+		case TransformationComp_PostRotation:
+			return "PostRotation";
+		case TransformationComp_RotationPivotInverse:
+			return "RotationPivotInverse";
+		case TransformationComp_ScalingOffset:
+			return "ScalingOffset";
+		case TransformationComp_ScalingPivot:
+			return "ScalingPivot";
+		case TransformationComp_Scaling:
+			return "Scaling";
+		case TransformationComp_ScalingPivotInverse:
+			return "ScalingPivotInverse";
+		case TransformationComp_GeometricScaling:
+			return "GeometricScaling";
+		case TransformationComp_GeometricRotation:
+			return "GeometricRotation";
+		case TransformationComp_GeometricTranslation:
+			return "GeometricTranslation";
+		case TransformationComp_MAXIMUM: // this is to silence compiler warnings
+			break;
+		}
+
+		ai_assert(false);
+		return NULL;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// note: this returns the REAL fbx property names
+	const char* NameTransformationCompProperty(TransformationComp comp)
+	{
+		switch(comp)
+		{
+		case TransformationComp_Translation:
+			return "Lcl Translation";
+		case TransformationComp_RotationOffset:
+			return "RotationOffset";
+		case TransformationComp_RotationPivot:
+			return "RotationPivot";
+		case TransformationComp_PreRotation:
+			return "PreRotation";
+		case TransformationComp_Rotation:
+			return "Lcl Rotation";
+		case TransformationComp_PostRotation:
+			return "PostRotation";
+		case TransformationComp_RotationPivotInverse:
+			return "RotationPivotInverse";
+		case TransformationComp_ScalingOffset:
+			return "ScalingOffset";
+		case TransformationComp_ScalingPivot:
+			return "ScalingPivot";
+		case TransformationComp_Scaling:
+			return "Lcl Scaling";
+		case TransformationComp_ScalingPivotInverse:
+			return "ScalingPivotInverse";
+		case TransformationComp_GeometricScaling:
+			return "GeometricScaling";
+		case TransformationComp_GeometricRotation:
+			return "GeometricRotation";
+		case TransformationComp_GeometricTranslation:
+			return "GeometricTranslation";
+		case TransformationComp_MAXIMUM: // this is to silence compiler warnings
+			break;
+		}
+
+		ai_assert(false);
+		return NULL;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	aiVector3D TransformationCompDefaultValue(TransformationComp comp)
+	{
+		// XXX a neat way to solve the never-ending special cases for scaling 
+		// would be to do everything in log space!
+		return comp == TransformationComp_Scaling ? aiVector3D(1.f,1.f,1.f) : aiVector3D();
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void GetRotationMatrix(Model::RotOrder mode, const aiVector3D& rotation, aiMatrix4x4& out)
+	{
+		if(mode == Model::RotOrder_SphericXYZ) {
+			FBXImporter::LogError("Unsupported RotationMode: SphericXYZ");
+			out = aiMatrix4x4();
+			return;
+		}
+
+		const float angle_epsilon = 1e-6f;
+
+		out = aiMatrix4x4();
+
+		bool is_id[3] = { true, true, true };
+
+		aiMatrix4x4 temp[3];
+		if(fabs(rotation.z) > angle_epsilon) {
+			aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(rotation.z),temp[2]);
+			is_id[2] = false;
+		}
+		if(fabs(rotation.y) > angle_epsilon) {
+			aiMatrix4x4::RotationY(AI_DEG_TO_RAD(rotation.y),temp[1]);
+			is_id[1] = false;
+		}
+		if(fabs(rotation.x) > angle_epsilon) {
+			aiMatrix4x4::RotationX(AI_DEG_TO_RAD(rotation.x),temp[0]);
+			is_id[0] = false;
+		}
+
+		int order[3] = {-1, -1, -1};
+
+		// note: rotation order is inverted since we're left multiplying as is usual in assimp
+		switch(mode)
+		{
+		case Model::RotOrder_EulerXYZ:
+			order[0] = 2;
+			order[1] = 1;
+			order[2] = 0;
+			break;
+
+		case Model::RotOrder_EulerXZY: 
+			order[0] = 1;
+			order[1] = 2;
+			order[2] = 0;
+			break;
+
+		case Model::RotOrder_EulerYZX:
+			order[0] = 0;
+			order[1] = 2;
+			order[2] = 1;
+			break;
+
+		case Model::RotOrder_EulerYXZ: 
+			order[0] = 2;
+			order[1] = 0;
+			order[2] = 1;
+			break;
+
+		case Model::RotOrder_EulerZXY: 
+			order[0] = 1;
+			order[1] = 0;
+			order[2] = 2;
+			break;
+
+		case Model::RotOrder_EulerZYX:
+			order[0] = 0;
+			order[1] = 1;
+			order[2] = 2;
+			break;
+
+			default:
+				ai_assert(false);
+		}
+        
+        ai_assert((order[0] >= 0) && (order[0] <= 2));
+        ai_assert((order[1] >= 0) && (order[1] <= 2));
+        ai_assert((order[2] >= 0) && (order[2] <= 2));
+
+		if(!is_id[order[0]]) {
+			out = temp[order[0]];
+		}
+
+		if(!is_id[order[1]]) {
+			out = out * temp[order[1]];
+		}
+
+		if(!is_id[order[2]]) {
+			out = out * temp[order[2]];
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	/** checks if a node has more than just scaling, rotation and translation components */
+	bool NeedsComplexTransformationChain(const Model& model)
+	{
+		const PropertyTable& props = model.Props();
+		bool ok;
+
+		const float zero_epsilon = 1e-6f;
+		for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
+			const TransformationComp comp = static_cast<TransformationComp>(i);
+
+			if( comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation ||
+				comp == TransformationComp_GeometricScaling || comp == TransformationComp_GeometricRotation || comp == TransformationComp_GeometricTranslation ) { 
+				continue;
+			}
+
+			const aiVector3D& v = PropertyGet<aiVector3D>(props,NameTransformationCompProperty(comp),ok);
+			if(ok && v.SquareLength() > zero_epsilon) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// note: name must be a FixNodeName() result
+	std::string NameTransformationChainNode(const std::string& name, TransformationComp comp)
+	{
+		return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp);
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	/** note: memory for output_nodes will be managed by the caller */
+	void GenerateTransformationNodeChain(const Model& model, 
+		std::vector<aiNode*>& output_nodes)
+	{
+		const PropertyTable& props = model.Props();
+		const Model::RotOrder rot = model.RotationOrder();
+
+		bool ok;
+
+		aiMatrix4x4 chain[TransformationComp_MAXIMUM];
+		std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
+		
+		// generate transformation matrices for all the different transformation components
+		const float zero_epsilon = 1e-6f;
+		bool is_complex = false;
+
+		const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props,"PreRotation",ok);
+		if(ok && PreRotation.SquareLength() > zero_epsilon) {
+			is_complex = true;
+
+			GetRotationMatrix(rot, PreRotation, chain[TransformationComp_PreRotation]);
+		}
+
+		const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props,"PostRotation",ok);
+		if(ok && PostRotation.SquareLength() > zero_epsilon) {
+			is_complex = true;
+			
+			GetRotationMatrix(rot, PostRotation, chain[TransformationComp_PostRotation]);
+		}
+
+		const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props,"RotationPivot",ok);
+		if(ok && RotationPivot.SquareLength() > zero_epsilon) {
+			is_complex = true;
+			
+			aiMatrix4x4::Translation(RotationPivot,chain[TransformationComp_RotationPivot]);
+			aiMatrix4x4::Translation(-RotationPivot,chain[TransformationComp_RotationPivotInverse]);
+		}
+
+		const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props,"RotationOffset",ok);
+		if(ok && RotationOffset.SquareLength() > zero_epsilon) {
+			is_complex = true;
+
+			aiMatrix4x4::Translation(RotationOffset,chain[TransformationComp_RotationOffset]);
+		}
+
+		const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props,"ScalingOffset",ok);
+		if(ok && ScalingOffset.SquareLength() > zero_epsilon) {
+			is_complex = true;
+			
+			aiMatrix4x4::Translation(ScalingOffset,chain[TransformationComp_ScalingOffset]);
+		}
+
+		const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props,"ScalingPivot",ok);
+		if(ok && ScalingPivot.SquareLength() > zero_epsilon) {
+			is_complex = true;
+
+			aiMatrix4x4::Translation(ScalingPivot,chain[TransformationComp_ScalingPivot]);
+			aiMatrix4x4::Translation(-ScalingPivot,chain[TransformationComp_ScalingPivotInverse]);
+		}
+
+		const aiVector3D& Translation = PropertyGet<aiVector3D>(props,"Lcl Translation",ok);
+		if(ok && Translation.SquareLength() > zero_epsilon) {
+			aiMatrix4x4::Translation(Translation,chain[TransformationComp_Translation]);
+		}
+
+		const aiVector3D& Scaling = PropertyGet<aiVector3D>(props,"Lcl Scaling",ok);
+		if(ok && fabs(Scaling.SquareLength()-1.0f) > zero_epsilon) {
+			aiMatrix4x4::Scaling(Scaling,chain[TransformationComp_Scaling]);
+		}
+
+		const aiVector3D& Rotation = PropertyGet<aiVector3D>(props,"Lcl Rotation",ok);
+		if(ok && Rotation.SquareLength() > zero_epsilon) {
+			GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
+		}
+		
+		const aiVector3D& GeometricScaling = PropertyGet<aiVector3D>(props, "GeometricScaling", ok);
+		if (ok && fabs(GeometricScaling.SquareLength() - 1.0f) > zero_epsilon) {
+			aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]);
+		}
+		
+		const aiVector3D& GeometricRotation = PropertyGet<aiVector3D>(props, "GeometricRotation", ok);
+		if (ok && GeometricRotation.SquareLength() > zero_epsilon) {
+			GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotation]);
+		}
+
+		const aiVector3D& GeometricTranslation = PropertyGet<aiVector3D>(props, "GeometricTranslation", ok);
+		if (ok && GeometricTranslation.SquareLength() > zero_epsilon){
+			aiMatrix4x4::Translation(GeometricTranslation, chain[TransformationComp_GeometricTranslation]);
+		}
+
+		// is_complex needs to be consistent with NeedsComplexTransformationChain()
+		// or the interplay between this code and the animation converter would
+		// not be guaranteed.
+		ai_assert(NeedsComplexTransformationChain(model) == is_complex);
+
+		const std::string& name = FixNodeName(model.Name());
+
+		// now, if we have more than just Translation, Scaling and Rotation,
+		// we need to generate a full node chain to accommodate for assimp's
+		// lack to express pivots and offsets.
+		if(is_complex && doc.Settings().preservePivots) {
+			FBXImporter::LogInfo("generating full transformation chain for node: " + name);
+
+			// query the anim_chain_bits dictionary to find out which chain elements
+			// have associated node animation channels. These can not be dropped 
+			// even if they have identity transform in bind pose.
+			NodeAnimBitMap::const_iterator it = node_anim_chain_bits.find(name);
+			const unsigned int anim_chain_bitmask = (it == node_anim_chain_bits.end() ? 0 : (*it).second);
+
+			unsigned int bit = 0x1;
+			for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
+				const TransformationComp comp = static_cast<TransformationComp>(i);
+				
+				if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) {
+					continue;
+				}
+
+				aiNode* nd = new aiNode();
+				output_nodes.push_back(nd);
+				
+				nd->mName.Set(NameTransformationChainNode(name, comp));
+				nd->mTransformation = chain[i];
+			}
+
+			ai_assert(output_nodes.size());
+			return;
+		}
+
+		// else, we can just multiply the matrices together
+		aiNode* nd = new aiNode();
+		output_nodes.push_back(nd);
+
+		nd->mName.Set(name);
+
+		for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
+			nd->mTransformation = nd->mTransformation * chain[i];
+		}
+	}
+	
+	// ------------------------------------------------------------------------------------------------
+
+	void SetupNodeMetadata(const Model& model, aiNode& nd)
+	{
+		const PropertyTable& props = model.Props();
+		DirectPropertyMap unparsedProperties = props.GetUnparsedProperties();
+
+		// create metadata on node
+		std::size_t numStaticMetaData = 2;
+		aiMetadata* data = new aiMetadata();
+		data->mNumProperties = unparsedProperties.size() + numStaticMetaData;
+		data->mKeys = new aiString[data->mNumProperties]();
+		data->mValues = new aiMetadataEntry[data->mNumProperties]();
+		nd.mMetaData = data;
+		int index = 0;
+
+		// find user defined properties (3ds Max)
+		data->Set(index++, "UserProperties", aiString(PropertyGet<std::string>(props, "UDP3DSMAX", "")));
+		unparsedProperties.erase("UDP3DSMAX");
+		// preserve the info that a node was marked as Null node in the original file.
+		data->Set(index++, "IsNull", model.IsNull() ? true : false);
+
+		// add unparsed properties to the node's metadata
+		BOOST_FOREACH(const DirectPropertyMap::value_type& prop, unparsedProperties) {
+
+			// Interpret the property as a concrete type
+			if (const TypedProperty<bool>* interpreted = prop.second->As<TypedProperty<bool> >())
+				data->Set(index++, prop.first, interpreted->Value());
+			else if (const TypedProperty<int>* interpreted = prop.second->As<TypedProperty<int> >())
+				data->Set(index++, prop.first, interpreted->Value());
+			else if (const TypedProperty<uint64_t>* interpreted = prop.second->As<TypedProperty<uint64_t> >())
+				data->Set(index++, prop.first, interpreted->Value());
+			else if (const TypedProperty<float>* interpreted = prop.second->As<TypedProperty<float> >())
+				data->Set(index++, prop.first, interpreted->Value());
+			else if (const TypedProperty<std::string>* interpreted = prop.second->As<TypedProperty<std::string> >())
+				data->Set(index++, prop.first, aiString(interpreted->Value()));
+			else if (const TypedProperty<aiVector3D>* interpreted = prop.second->As<TypedProperty<aiVector3D> >())
+				data->Set(index++, prop.first, interpreted->Value());
+			else
+				assert(false);
+		}
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform)
+	{
+		const std::vector<const Geometry*>& geos = model.GetGeometry();
+
+		std::vector<unsigned int> meshes;
+		meshes.reserve(geos.size());
+
+		BOOST_FOREACH(const Geometry* geo, geos) {
+
+			const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo);
+			if(mesh) {
+				const std::vector<unsigned int>& indices = ConvertMesh(*mesh, model, node_global_transform);
+				std::copy(indices.begin(),indices.end(),std::back_inserter(meshes) );
+			}
+			else {
+				FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name());
+			}
+		}
+
+		if(meshes.size()) {
+			nd.mMeshes = new unsigned int[meshes.size()]();
+			nd.mNumMeshes = static_cast<unsigned int>(meshes.size());
+
+			std::swap_ranges(meshes.begin(),meshes.end(),nd.mMeshes);
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
+	std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh,const Model& model, 
+		const aiMatrix4x4& node_global_transform)
+	{
+		std::vector<unsigned int> temp; 
+
+		MeshMap::const_iterator it = meshes_converted.find(&mesh);
+		if (it != meshes_converted.end()) {
+			std::copy((*it).second.begin(),(*it).second.end(),std::back_inserter(temp));
+			return temp;
+		}
+
+		const std::vector<aiVector3D>& vertices = mesh.GetVertices();
+		const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
+		if(vertices.empty() || faces.empty()) {
+			FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name());
+			return temp;
+		}
+
+		// one material per mesh maps easily to aiMesh. Multiple material 
+		// meshes need to be split.
+		const MatIndexArray& mindices = mesh.GetMaterialIndices();
+		if (doc.Settings().readMaterials && !mindices.empty()) {
+			const MatIndexArray::value_type base = mindices[0];
+			BOOST_FOREACH(MatIndexArray::value_type index, mindices) {
+				if(index != base) {
+					return ConvertMeshMultiMaterial(mesh, model, node_global_transform);
+				}
+			}
+		}
+
+		// faster codepath, just copy the data
+		temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform));
+		return temp;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	aiMesh* SetupEmptyMesh(const MeshGeometry& mesh)
+	{
+		aiMesh* const out_mesh = new aiMesh();
+		meshes.push_back(out_mesh);
+		meshes_converted[&mesh].push_back(static_cast<unsigned int>(meshes.size()-1));
+
+		// set name
+		std::string name = mesh.Name();
+		if (name.substr(0,10) == "Geometry::") {
+			name = name.substr(10);
+		}
+
+		if(name.length()) {
+			out_mesh->mName.Set(name);
+		}
+
+		return out_mesh;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, 
+		const aiMatrix4x4& node_global_transform)	
+	{
+		const MatIndexArray& mindices = mesh.GetMaterialIndices();
+		aiMesh* const out_mesh = SetupEmptyMesh(mesh); 
+
+		const std::vector<aiVector3D>& vertices = mesh.GetVertices();
+		const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
+
+		// copy vertices
+		out_mesh->mNumVertices = static_cast<unsigned int>(vertices.size());
+		out_mesh->mVertices = new aiVector3D[vertices.size()];
+		std::copy(vertices.begin(),vertices.end(),out_mesh->mVertices);
+
+		// generate dummy faces
+		out_mesh->mNumFaces = static_cast<unsigned int>(faces.size());
+		aiFace* fac = out_mesh->mFaces = new aiFace[faces.size()]();
+
+		unsigned int cursor = 0;
+		BOOST_FOREACH(unsigned int pcount, faces) {
+			aiFace& f = *fac++;
+			f.mNumIndices = pcount;
+			f.mIndices = new unsigned int[pcount];
+			switch(pcount) 
+			{
+			case 1:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+				break;
+			case 2:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+				break;
+			case 3:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+				break;
+			default:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+				break;
+			}
+			for (unsigned int i = 0; i < pcount; ++i) {
+				f.mIndices[i] = cursor++;
+			}
+		}
+
+		// copy normals
+		const std::vector<aiVector3D>& normals = mesh.GetNormals();
+		if(normals.size()) {
+			ai_assert(normals.size() == vertices.size());
+
+			out_mesh->mNormals = new aiVector3D[vertices.size()];
+			std::copy(normals.begin(),normals.end(),out_mesh->mNormals);
+		}
+
+		// copy tangents - assimp requires both tangents and bitangents (binormals)
+		// to be present, or neither of them. Compute binormals from normals
+		// and tangents if needed.
+		const std::vector<aiVector3D>& tangents = mesh.GetTangents();
+		const std::vector<aiVector3D>* binormals = &mesh.GetBinormals();
+
+		if(tangents.size()) {
+			std::vector<aiVector3D> tempBinormals;
+			if (!binormals->size()) {
+				if (normals.size()) {
+					tempBinormals.resize(normals.size());
+					for (unsigned int i = 0; i < tangents.size(); ++i) {
+						tempBinormals[i] = normals[i] ^ tangents[i];
+					}
+
+					binormals = &tempBinormals;
+				}
+				else {
+					binormals = NULL;	
+				}
+			}
+
+			if(binormals) {
+				ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size());
+
+				out_mesh->mTangents = new aiVector3D[vertices.size()];
+				std::copy(tangents.begin(),tangents.end(),out_mesh->mTangents);
+
+				out_mesh->mBitangents = new aiVector3D[vertices.size()];
+				std::copy(binormals->begin(),binormals->end(),out_mesh->mBitangents);
+			}
+		}
+
+		// copy texture coords
+		for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+			const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
+			if(uvs.empty()) {
+				break;
+			}
+
+			aiVector3D* out_uv = out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
+			BOOST_FOREACH(const aiVector2D& v, uvs) {
+				*out_uv++ = aiVector3D(v.x,v.y,0.0f);
+			}
+
+			out_mesh->mNumUVComponents[i] = 2;
+		}
+
+		// copy vertex colors
+		for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
+			const std::vector<aiColor4D>& colors = mesh.GetVertexColors(i);
+			if(colors.empty()) {
+				break;
+			}
+
+			out_mesh->mColors[i] = new aiColor4D[vertices.size()];
+			std::copy(colors.begin(),colors.end(),out_mesh->mColors[i]);
+		}
+
+		if(!doc.Settings().readMaterials || mindices.empty()) {
+			FBXImporter::LogError("no material assigned to mesh, setting default material");
+			out_mesh->mMaterialIndex = GetDefaultMaterial();
+		}
+		else {
+			ConvertMaterialForMesh(out_mesh,model,mesh,mindices[0]);
+		}
+
+		if(doc.Settings().readWeights && mesh.DeformerSkin() != NULL) {
+			ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION);
+		}
+
+		return static_cast<unsigned int>(meshes.size() - 1);
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, 
+		const aiMatrix4x4& node_global_transform)	
+	{
+		const MatIndexArray& mindices = mesh.GetMaterialIndices();
+		ai_assert(mindices.size());
+	
+		std::set<MatIndexArray::value_type> had;
+		std::vector<unsigned int> indices;
+
+		BOOST_FOREACH(MatIndexArray::value_type index, mindices) {
+			if(had.find(index) == had.end()) {
+
+				indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform));
+				had.insert(index);
+			}
+		}
+
+		return indices;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, 
+		MatIndexArray::value_type index, 
+		const aiMatrix4x4& node_global_transform)	
+	{
+		aiMesh* const out_mesh = SetupEmptyMesh(mesh);
+
+		const MatIndexArray& mindices = mesh.GetMaterialIndices();
+		const std::vector<aiVector3D>& vertices = mesh.GetVertices();
+		const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
+
+		const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL;
+
+		unsigned int count_faces = 0;
+		unsigned int count_vertices = 0;
+
+		// count faces
+		std::vector<unsigned int>::const_iterator itf = faces.begin();
+		for(MatIndexArray::const_iterator it = mindices.begin(), 
+			end = mindices.end(); it != end; ++it, ++itf) 
+		{	
+			if ((*it) != index) {
+				continue;
+			}
+			++count_faces;
+			count_vertices += *itf;
+		}
+
+		ai_assert(count_faces);
+		ai_assert(count_vertices);
+
+		// mapping from output indices to DOM indexing, needed to resolve weights
+		std::vector<unsigned int> reverseMapping;
+
+		if (process_weights) {
+			reverseMapping.resize(count_vertices);
+		}
+
+		// allocate output data arrays, but don't fill them yet
+		out_mesh->mNumVertices = count_vertices;
+		out_mesh->mVertices = new aiVector3D[count_vertices];
+
+		out_mesh->mNumFaces = count_faces;
+		aiFace* fac = out_mesh->mFaces = new aiFace[count_faces]();
+
+
+		// allocate normals
+		const std::vector<aiVector3D>& normals = mesh.GetNormals();
+		if(normals.size()) {
+			ai_assert(normals.size() == vertices.size());
+			out_mesh->mNormals = new aiVector3D[vertices.size()];
+		}
+
+		// allocate tangents, binormals. 
+		const std::vector<aiVector3D>& tangents = mesh.GetTangents();
+		const std::vector<aiVector3D>* binormals = &mesh.GetBinormals();
+
+		if(tangents.size()) {
+			std::vector<aiVector3D> tempBinormals;
+			if (!binormals->size()) {
+				if (normals.size()) {
+					// XXX this computes the binormals for the entire mesh, not only 
+					// the part for which we need them.
+					tempBinormals.resize(normals.size());
+					for (unsigned int i = 0; i < tangents.size(); ++i) {
+						tempBinormals[i] = normals[i] ^ tangents[i];
+					}
+
+					binormals = &tempBinormals;
+				}
+				else {
+					binormals = NULL;	
+				}
+			}
+
+			if(binormals) {
+				ai_assert(tangents.size() == vertices.size() && binormals->size() == vertices.size());
+
+				out_mesh->mTangents = new aiVector3D[vertices.size()];
+				out_mesh->mBitangents = new aiVector3D[vertices.size()];
+			}
+		}
+
+		// allocate texture coords
+		unsigned int num_uvs = 0;
+		for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i, ++num_uvs) {
+			const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
+			if(uvs.empty()) {
+				break;
+			}
+
+			out_mesh->mTextureCoords[i] = new aiVector3D[vertices.size()];
+			out_mesh->mNumUVComponents[i] = 2;
+		}
+
+		// allocate vertex colors
+		unsigned int num_vcs = 0;
+		for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i, ++num_vcs) {
+			const std::vector<aiColor4D>& colors = mesh.GetVertexColors(i);
+			if(colors.empty()) {
+				break;
+			}
+
+			out_mesh->mColors[i] = new aiColor4D[vertices.size()];
+		}
+
+		unsigned int cursor = 0, in_cursor = 0;
+
+		itf = faces.begin();
+		for(MatIndexArray::const_iterator it = mindices.begin(), 
+			end = mindices.end(); it != end; ++it, ++itf) 
+		{	
+			const unsigned int pcount = *itf;
+			if ((*it) != index) {
+				in_cursor += pcount;
+				continue;
+			}
+
+			aiFace& f = *fac++;
+
+			f.mNumIndices = pcount;
+			f.mIndices = new unsigned int[pcount];
+			switch(pcount) 
+			{
+			case 1:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
+				break;
+			case 2:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
+				break;
+			case 3:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
+				break;
+			default:
+				out_mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
+				break;
+			}
+			for (unsigned int i = 0; i < pcount; ++i, ++cursor, ++in_cursor) {
+				f.mIndices[i] = cursor;
+
+				if(reverseMapping.size()) {
+					reverseMapping[cursor] = in_cursor;
+				}
+
+				out_mesh->mVertices[cursor] = vertices[in_cursor];
+
+				if(out_mesh->mNormals) {
+					out_mesh->mNormals[cursor] = normals[in_cursor];
+				}
+
+				if(out_mesh->mTangents) {
+					out_mesh->mTangents[cursor] = tangents[in_cursor];
+					out_mesh->mBitangents[cursor] = (*binormals)[in_cursor];
+				}
+
+				for (unsigned int i = 0; i < num_uvs; ++i) {
+					const std::vector<aiVector2D>& uvs = mesh.GetTextureCoords(i);
+					out_mesh->mTextureCoords[i][cursor] = aiVector3D(uvs[in_cursor].x,uvs[in_cursor].y, 0.0f);
+				}
+
+				for (unsigned int i = 0; i < num_vcs; ++i) {
+					const std::vector<aiColor4D>& cols = mesh.GetVertexColors(i);
+					out_mesh->mColors[i][cursor] = cols[in_cursor];
+				}
+			}
+		}
+	
+		ConvertMaterialForMesh(out_mesh,model,mesh,index);
+
+		if(process_weights) {
+			ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping);
+		}
+
+		return static_cast<unsigned int>(meshes.size() - 1);
+	}
+
+	static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */ 
+		static_cast<unsigned int>(-1);
+
+
+	// ------------------------------------------------------------------------------------------------
+	/** - if materialIndex == NO_MATERIAL_SEPARATION, materials are not taken into
+	 *  account when determining which weights to include. 
+	 *  - outputVertStartIndices is only used when a material index is specified, it gives for
+	 *    each output vertex the DOM index it maps to. */
+	void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, 
+		const aiMatrix4x4& node_global_transform = aiMatrix4x4(),
+		unsigned int materialIndex = NO_MATERIAL_SEPARATION, 
+		std::vector<unsigned int>* outputVertStartIndices = NULL)
+	{
+		ai_assert(geo.DeformerSkin());
+
+		std::vector<size_t> out_indices;
+		std::vector<size_t> index_out_indices;
+		std::vector<size_t> count_out_indices;
+
+		const Skin& sk = *geo.DeformerSkin();
+
+		std::vector<aiBone*> bones;
+		bones.reserve(sk.Clusters().size());
+
+		const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION;
+		ai_assert(no_mat_check || outputVertStartIndices);
+
+		try {
+
+			BOOST_FOREACH(const Cluster* cluster, sk.Clusters()) {
+				ai_assert(cluster);
+
+				const WeightIndexArray& indices = cluster->GetIndices();
+
+				if(indices.empty()) {
+					continue;
+				}
+
+				const MatIndexArray& mats = geo.GetMaterialIndices();
+
+				bool ok = false;		
+
+				const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
+
+				count_out_indices.clear();
+				index_out_indices.clear();
+				out_indices.clear();
+
+				// now check if *any* of these weights is contained in the output mesh,
+				// taking notes so we don't need to do it twice.
+				BOOST_FOREACH(WeightIndexArray::value_type index, indices) {
+
+					unsigned int count;
+					const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count);
+
+					index_out_indices.push_back(no_index_sentinel);
+					count_out_indices.push_back(0);
+
+					for(unsigned int i = 0; i < count; ++i) {					
+						if (no_mat_check || static_cast<size_t>(mats[geo.FaceForVertexIndex(out_idx[i])]) == materialIndex) {
+							
+							if (index_out_indices.back() == no_index_sentinel) {
+								index_out_indices.back() = out_indices.size();
+								
+							}
+
+							if (no_mat_check) {
+								out_indices.push_back(out_idx[i]);
+							}
+							else {
+								// this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn)
+								const std::vector<unsigned int>::iterator it = std::lower_bound(
+									outputVertStartIndices->begin(),
+									outputVertStartIndices->end(),
+									out_idx[i]
+								);
+
+								out_indices.push_back(std::distance(outputVertStartIndices->begin(), it));
+							}
+
+							++count_out_indices.back();
+							ok = true;
+						}
+					}		
+				}
+
+				// if we found at least one, generate the output bones
+				// XXX this could be heavily simplified by collecting the bone
+				// data in a single step.
+				if (ok) {
+					ConvertCluster(bones, model, *cluster, out_indices, index_out_indices, 
+						count_out_indices, node_global_transform);
+				}
+			}
+		}
+		catch (std::exception&) {
+			std::for_each(bones.begin(),bones.end(),Util::delete_fun<aiBone>());
+			throw;
+		}
+
+		if(bones.empty()) {
+			return;
+		}
+
+		out->mBones = new aiBone*[bones.size()]();
+		out->mNumBones = static_cast<unsigned int>(bones.size());
+
+		std::swap_ranges(bones.begin(),bones.end(),out->mBones);
+	}
+
+
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertCluster(std::vector<aiBone*>& bones, const Model& model, const Cluster& cl, 		
+		std::vector<size_t>& out_indices,
+		std::vector<size_t>& index_out_indices,
+		std::vector<size_t>& count_out_indices,
+		const aiMatrix4x4& node_global_transform)
+	{
+
+		aiBone* const bone = new aiBone();
+		bones.push_back(bone);
+
+		bone->mName = FixNodeName(cl.TargetNode()->Name());
+
+		bone->mOffsetMatrix = cl.TransformLink();
+		bone->mOffsetMatrix.Inverse();
+
+		bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform;
+
+		bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
+		aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
+
+		const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
+		const WeightArray& weights = cl.GetWeights();
+
+		const size_t c = index_out_indices.size();
+		for (size_t i = 0; i < c; ++i) {
+			const size_t index_index =  index_out_indices[i];
+
+			if (index_index == no_index_sentinel) {
+				continue;
+			}
+
+			const size_t cc = count_out_indices[i];
+			for (size_t j = 0; j < cc; ++j) {
+				aiVertexWeight& out_weight = *cursor++;
+
+				out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
+				out_weight.mWeight = weights[i];
+			}			
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, 
+		MatIndexArray::value_type materialIndex)
+	{
+		// locate source materials for this mesh
+		const std::vector<const Material*>& mats = model.GetMaterials();
+		if (static_cast<unsigned int>(materialIndex) >= mats.size() || materialIndex < 0) {
+			FBXImporter::LogError("material index out of bounds, setting default material");
+			out->mMaterialIndex = GetDefaultMaterial();
+			return;
+		}
+
+		const Material* const mat = mats[materialIndex];
+		MaterialMap::const_iterator it = materials_converted.find(mat);
+		if (it != materials_converted.end()) {
+			out->mMaterialIndex = (*it).second;
+			return;
+		}
+
+		out->mMaterialIndex = ConvertMaterial(*mat, &geo);	
+		materials_converted[mat] = out->mMaterialIndex;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	unsigned int GetDefaultMaterial()
+	{
+		if (defaultMaterialIndex) {
+			return defaultMaterialIndex - 1; 
+		}
+
+		aiMaterial* out_mat = new aiMaterial();
+		materials.push_back(out_mat);
+
+		const aiColor3D diffuse = aiColor3D(0.8f,0.8f,0.8f);
+		out_mat->AddProperty(&diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
+
+		aiString s;
+		s.Set(AI_DEFAULT_MATERIAL_NAME);
+
+		out_mat->AddProperty(&s,AI_MATKEY_NAME);
+
+		defaultMaterialIndex = static_cast<unsigned int>(materials.size());
+		return defaultMaterialIndex - 1;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// Material -> aiMaterial
+	unsigned int ConvertMaterial(const Material& material, const MeshGeometry* const mesh)
+	{
+		const PropertyTable& props = material.Props();
+
+		// generate empty output material
+		aiMaterial* out_mat = new aiMaterial();
+		materials_converted[&material] = static_cast<unsigned int>(materials.size());
+
+		materials.push_back(out_mat);
+
+		aiString str;
+
+		// stip Material:: prefix
+		std::string name = material.Name();
+		if(name.substr(0,10) == "Material::") {
+			name = name.substr(10);
+		}
+
+		// set material name if not empty - this could happen
+		// and there should be no key for it in this case.
+		if(name.length()) {
+			str.Set(name);
+			out_mat->AddProperty(&str,AI_MATKEY_NAME);
+		}
+
+		// shading stuff and colors
+		SetShadingPropertiesCommon(out_mat,props);
+	
+		// texture assignments
+		SetTextureProperties(out_mat,material.Textures(), mesh);
+		SetTextureProperties(out_mat,material.LayeredTextures(), mesh);
+
+		return static_cast<unsigned int>(materials.size() - 1);
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, 
+		const std::string& propName, 
+		aiTextureType target, const MeshGeometry* const mesh)
+	{
+		TextureMap::const_iterator it = textures.find(propName);
+		if(it == textures.end()) {
+			return;
+		}
+
+		const Texture* const tex = (*it).second;
+		if(tex !=0 )
+		{
+			aiString path;
+			path.Set(tex->RelativeFilename());
+
+			out_mat->AddProperty(&path,_AI_MATKEY_TEXTURE_BASE,target,0);
+
+			aiUVTransform uvTrafo;
+			// XXX handle all kinds of UV transformations
+			uvTrafo.mScaling = tex->UVScaling();
+			uvTrafo.mTranslation = tex->UVTranslation();
+			out_mat->AddProperty(&uvTrafo,1,_AI_MATKEY_UVTRANSFORM_BASE,target,0);
+
+			const PropertyTable& props = tex->Props();
+
+			int uvIndex = 0;
+
+			bool ok;
+			const std::string& uvSet = PropertyGet<std::string>(props,"UVSet",ok);
+			if(ok) {
+				// "default" is the name which usually appears in the FbxFileTexture template
+				if(uvSet != "default" && uvSet.length()) {
+					// this is a bit awkward - we need to find a mesh that uses this
+					// material and scan its UV channels for the given UV name because
+					// assimp references UV channels by index, not by name.
+
+					// XXX: the case that UV channels may appear in different orders
+					// in meshes is unhandled. A possible solution would be to sort
+					// the UV channels alphabetically, but this would have the side
+					// effect that the primary (first) UV channel would sometimes
+					// be moved, causing trouble when users read only the first
+					// UV channel and ignore UV channel assignments altogether.
+
+					const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(), 
+						std::find(materials.begin(),materials.end(),out_mat)
+					));
+
+
+          uvIndex = -1;
+          if (!mesh)
+          {					
+					  BOOST_FOREACH(const MeshMap::value_type& v,meshes_converted) {
+						  const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*> (v.first);
+						  if(!mesh) {
+							  continue;
+						  }
+
+						  const MatIndexArray& mats = mesh->GetMaterialIndices();
+						  if(std::find(mats.begin(),mats.end(),matIndex) == mats.end()) {
+							  continue;
+						  }
+
+						  int index = -1;
+						  for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+							  if(mesh->GetTextureCoords(i).empty()) {
+								  break;
+							  }
+							  const std::string& name = mesh->GetTextureCoordChannelName(i);
+							  if(name == uvSet) {
+								  index = static_cast<int>(i);
+								  break;
+							  }
+						  }
+						  if(index == -1) {
+							  FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+							  continue;
+						  }
+
+						  if(uvIndex == -1) {
+							  uvIndex = index;
+						  }
+						  else {
+							  FBXImporter::LogWarn("the UV channel named " + uvSet + 
+								  " appears at different positions in meshes, results will be wrong");
+						  }
+					  }
+          }
+          else
+          {
+						int index = -1;
+						for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+							if(mesh->GetTextureCoords(i).empty()) {
+								break;
+							}
+							const std::string& name = mesh->GetTextureCoordChannelName(i);
+							if(name == uvSet) {
+								index = static_cast<int>(i);
+								break;
+							}
+						}
+						if(index == -1) {
+							FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+						}
+
+						if(uvIndex == -1) {
+							uvIndex = index;
+						}
+          }
+
+					if(uvIndex == -1) {
+						FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel");
+						uvIndex = 0;
+					}
+				}
+			}
+
+			out_mat->AddProperty(&uvIndex,1,_AI_MATKEY_UVWSRC_BASE,target,0);
+		}
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	void TrySetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, 
+		const std::string& propName, 
+		aiTextureType target, const MeshGeometry* const mesh)
+	{
+		LayeredTextureMap::const_iterator it = layeredTextures.find(propName);
+		if(it == layeredTextures.end()) {
+			return;
+		}
+
+		const Texture* const tex = (*it).second->getTexture();
+
+		aiString path;
+		path.Set(tex->RelativeFilename());
+
+		out_mat->AddProperty(&path,_AI_MATKEY_TEXTURE_BASE,target,0);
+
+		aiUVTransform uvTrafo;
+		// XXX handle all kinds of UV transformations
+		uvTrafo.mScaling = tex->UVScaling();
+		uvTrafo.mTranslation = tex->UVTranslation();
+		out_mat->AddProperty(&uvTrafo,1,_AI_MATKEY_UVTRANSFORM_BASE,target,0);
+
+		const PropertyTable& props = tex->Props();
+
+		int uvIndex = 0;
+
+		bool ok;
+		const std::string& uvSet = PropertyGet<std::string>(props,"UVSet",ok);
+		if(ok) {
+			// "default" is the name which usually appears in the FbxFileTexture template
+			if(uvSet != "default" && uvSet.length()) {
+				// this is a bit awkward - we need to find a mesh that uses this
+				// material and scan its UV channels for the given UV name because
+				// assimp references UV channels by index, not by name.
+
+				// XXX: the case that UV channels may appear in different orders
+				// in meshes is unhandled. A possible solution would be to sort
+				// the UV channels alphabetically, but this would have the side
+				// effect that the primary (first) UV channel would sometimes
+				// be moved, causing trouble when users read only the first
+				// UV channel and ignore UV channel assignments altogether.
+
+				const unsigned int matIndex = static_cast<unsigned int>(std::distance(materials.begin(), 
+					std::find(materials.begin(),materials.end(),out_mat)
+					));
+
+			  uvIndex = -1;
+        if (!mesh)
+        {					
+					BOOST_FOREACH(const MeshMap::value_type& v,meshes_converted) {
+						const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*> (v.first);
+						if(!mesh) {
+							continue;
+						}
+
+						const MatIndexArray& mats = mesh->GetMaterialIndices();
+						if(std::find(mats.begin(),mats.end(),matIndex) == mats.end()) {
+							continue;
+						}
+
+						int index = -1;
+						for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+							if(mesh->GetTextureCoords(i).empty()) {
+								break;
+							}
+							const std::string& name = mesh->GetTextureCoordChannelName(i);
+							if(name == uvSet) {
+								index = static_cast<int>(i);
+								break;
+							}
+						}
+						if(index == -1) {
+							FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+							continue;
+						}
+
+						if(uvIndex == -1) {
+							uvIndex = index;
+						}
+						else {
+							FBXImporter::LogWarn("the UV channel named " + uvSet + 
+								" appears at different positions in meshes, results will be wrong");
+						}
+					}
+        }
+        else
+        {
+					int index = -1;
+					for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
+						if(mesh->GetTextureCoords(i).empty()) {
+							break;
+						}
+						const std::string& name = mesh->GetTextureCoordChannelName(i);
+						if(name == uvSet) {
+							index = static_cast<int>(i);
+							break;
+						}
+					}
+					if(index == -1) {
+						FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material");
+					}
+
+					if(uvIndex == -1) {
+						uvIndex = index;
+					}
+        }
+
+				if(uvIndex == -1) {
+					FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel");
+					uvIndex = 0;
+				}
+			}
+		}
+
+		out_mat->AddProperty(&uvIndex,1,_AI_MATKEY_UVWSRC_BASE,target,0);
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	void SetTextureProperties(aiMaterial* out_mat, const TextureMap& textures, const MeshGeometry* const mesh)
+	{
+		TrySetTextureProperties(out_mat, textures, "DiffuseColor", aiTextureType_DIFFUSE, mesh);
+		TrySetTextureProperties(out_mat, textures, "AmbientColor", aiTextureType_AMBIENT, mesh);
+		TrySetTextureProperties(out_mat, textures, "EmissiveColor", aiTextureType_EMISSIVE, mesh);
+		TrySetTextureProperties(out_mat, textures, "SpecularColor", aiTextureType_SPECULAR, mesh);
+		TrySetTextureProperties(out_mat, textures, "TransparentColor", aiTextureType_OPACITY, mesh);
+		TrySetTextureProperties(out_mat, textures, "ReflectionColor", aiTextureType_REFLECTION, mesh);
+		TrySetTextureProperties(out_mat, textures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh);
+		TrySetTextureProperties(out_mat, textures, "NormalMap", aiTextureType_NORMALS, mesh);
+		TrySetTextureProperties(out_mat, textures, "Bump", aiTextureType_HEIGHT, mesh);
+		TrySetTextureProperties(out_mat, textures, "ShininessExponent", aiTextureType_SHININESS, mesh);
+	}
+
+	// ------------------------------------------------------------------------------------------------
+	void SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh)
+	{
+		TrySetTextureProperties(out_mat, layeredTextures, "DiffuseColor", aiTextureType_DIFFUSE, mesh);
+		TrySetTextureProperties(out_mat, layeredTextures, "AmbientColor", aiTextureType_AMBIENT, mesh);
+		TrySetTextureProperties(out_mat, layeredTextures, "EmissiveColor", aiTextureType_EMISSIVE, mesh);
+		TrySetTextureProperties(out_mat, layeredTextures, "SpecularColor", aiTextureType_SPECULAR, mesh);
+		TrySetTextureProperties(out_mat, layeredTextures, "TransparentColor", aiTextureType_OPACITY, mesh);
+		TrySetTextureProperties(out_mat, layeredTextures, "ReflectionColor", aiTextureType_REFLECTION, mesh);
+		TrySetTextureProperties(out_mat, layeredTextures, "DisplacementColor", aiTextureType_DISPLACEMENT, mesh);
+		TrySetTextureProperties(out_mat, layeredTextures, "NormalMap", aiTextureType_NORMALS, mesh);
+		TrySetTextureProperties(out_mat, layeredTextures, "Bump", aiTextureType_HEIGHT, mesh);
+		TrySetTextureProperties(out_mat, layeredTextures, "ShininessExponent", aiTextureType_SHININESS, mesh);
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	aiColor3D GetColorPropertyFromMaterial(const PropertyTable& props, const std::string& baseName, 
+		bool& result)
+	{
+		result = true;
+
+		bool ok;
+		const aiVector3D& Diffuse = PropertyGet<aiVector3D>(props,baseName,ok);
+		if(ok) {
+			return aiColor3D(Diffuse.x,Diffuse.y,Diffuse.z);
+		}
+		else {
+			aiVector3D DiffuseColor = PropertyGet<aiVector3D>(props,baseName + "Color",ok);
+			if(ok) {
+				float DiffuseFactor = PropertyGet<float>(props,baseName + "Factor",ok);
+				if(ok) {
+					DiffuseColor *= DiffuseFactor;
+				}
+
+				return aiColor3D(DiffuseColor.x,DiffuseColor.y,DiffuseColor.z);
+			}
+		}
+		result = false;
+		return aiColor3D(0.0f,0.0f,0.0f);
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void SetShadingPropertiesCommon(aiMaterial* out_mat, const PropertyTable& props)
+	{
+		// set shading properties. There are various, redundant ways in which FBX materials
+		// specify their shading settings (depending on shading models, prop
+		// template etc.). No idea which one is right in a particular context. 
+		// Just try to make sense of it - there's no spec to verify this against, 
+		// so why should we.
+		bool ok;
+		const aiColor3D& Diffuse = GetColorPropertyFromMaterial(props,"Diffuse",ok);
+		if(ok) {
+			out_mat->AddProperty(&Diffuse,1,AI_MATKEY_COLOR_DIFFUSE);
+		}
+
+		const aiColor3D& Emissive = GetColorPropertyFromMaterial(props,"Emissive",ok);
+		if(ok) {
+			out_mat->AddProperty(&Emissive,1,AI_MATKEY_COLOR_EMISSIVE);
+		}
+
+		const aiColor3D& Ambient = GetColorPropertyFromMaterial(props,"Ambient",ok);
+		if(ok) {
+			out_mat->AddProperty(&Ambient,1,AI_MATKEY_COLOR_AMBIENT);
+		}
+
+		const aiColor3D& Specular = GetColorPropertyFromMaterial(props,"Specular",ok);
+		if(ok) {
+			out_mat->AddProperty(&Specular,1,AI_MATKEY_COLOR_SPECULAR);
+		}
+
+		const float Opacity = PropertyGet<float>(props,"Opacity",ok);
+		if(ok) {
+			out_mat->AddProperty(&Opacity,1,AI_MATKEY_OPACITY);
+		}
+
+		const float Reflectivity = PropertyGet<float>(props,"Reflectivity",ok);
+		if(ok) {
+			out_mat->AddProperty(&Reflectivity,1,AI_MATKEY_REFLECTIVITY);
+		}
+
+		const float Shininess = PropertyGet<float>(props,"Shininess",ok);
+		if(ok) {
+			out_mat->AddProperty(&Shininess,1,AI_MATKEY_SHININESS_STRENGTH);
+		}
+
+		const float ShininessExponent = PropertyGet<float>(props,"ShininessExponent",ok);
+		if(ok) {
+			out_mat->AddProperty(&ShininessExponent,1,AI_MATKEY_SHININESS);
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// get the number of fps for a FrameRate enumerated value
+	static double FrameRateToDouble(FileGlobalSettings::FrameRate fp, double customFPSVal = -1.0)
+	{
+		switch(fp) {
+			case FileGlobalSettings::FrameRate_DEFAULT:
+				return 1.0;
+
+			case FileGlobalSettings::FrameRate_120:
+				return 120.0;
+
+			case FileGlobalSettings::FrameRate_100:
+				return 100.0;
+
+			case FileGlobalSettings::FrameRate_60:
+				return 60.0;
+
+			case FileGlobalSettings::FrameRate_50:
+				return 50.0;
+
+			case FileGlobalSettings::FrameRate_48:
+				return 48.0;
+
+			case FileGlobalSettings::FrameRate_30:
+			case FileGlobalSettings::FrameRate_30_DROP:
+				return 30.0;
+
+			case FileGlobalSettings::FrameRate_NTSC_DROP_FRAME:
+			case FileGlobalSettings::FrameRate_NTSC_FULL_FRAME:
+				return 29.9700262;
+
+			case FileGlobalSettings::FrameRate_PAL:
+				return 25.0;
+
+			case FileGlobalSettings::FrameRate_CINEMA:
+				return 24.0;
+
+			case FileGlobalSettings::FrameRate_1000:
+				return 1000.0;
+
+			case FileGlobalSettings::FrameRate_CINEMA_ND:
+				return 23.976;
+
+			case FileGlobalSettings::FrameRate_CUSTOM:
+				return customFPSVal;
+
+			case FileGlobalSettings::FrameRate_MAX: // this is to silence compiler warnings
+				break;
+		}
+
+		ai_assert(false);
+		return -1.0f;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// convert animation data to aiAnimation et al
+	void ConvertAnimations() 
+	{
+		// first of all determine framerate
+		const FileGlobalSettings::FrameRate fps = doc.GlobalSettings().TimeMode();
+		const float custom = doc.GlobalSettings().CustomFrameRate();
+		anim_fps = FrameRateToDouble(fps, custom);
+
+		const std::vector<const AnimationStack*>& animations = doc.AnimationStacks();
+		BOOST_FOREACH(const AnimationStack* stack, animations) {
+			ConvertAnimationStack(*stack);
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// rename a node already partially converted. fixed_name is a string previously returned by 
+	// FixNodeName, new_name specifies the string FixNodeName should return on all further invocations 
+	// which would previously have returned the old value.
+	//
+	// this also updates names in node animations, cameras and light sources and is thus slow.
+	//
+	// NOTE: the caller is responsible for ensuring that the new name is unique and does
+	// not collide with any other identifiers. The best way to ensure this is to only
+	// append to the old name, which is guaranteed to match these requirements.
+	void RenameNode(const std::string& fixed_name, const std::string& new_name)
+	{
+		ai_assert(node_names.find(fixed_name) != node_names.end());
+		ai_assert(node_names.find(new_name) == node_names.end());
+
+		renamed_nodes[fixed_name] = new_name;
+
+		const aiString fn(fixed_name);
+
+		BOOST_FOREACH(aiCamera* cam, cameras) {
+			if (cam->mName == fn) {
+				cam->mName.Set(new_name);
+				break;
+			}
+		}
+
+		BOOST_FOREACH(aiLight* light, lights) {
+			if (light->mName == fn) {
+				light->mName.Set(new_name);
+				break;
+			}
+		}
+
+		BOOST_FOREACH(aiAnimation* anim, animations) {
+			for (unsigned int i = 0; i < anim->mNumChannels; ++i) {
+				aiNodeAnim* const na = anim->mChannels[i];
+				if (na->mNodeName == fn) {
+					na->mNodeName.Set(new_name);
+					break;
+				}
+			}
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// takes a fbx node name and returns the identifier to be used in the assimp output scene.
+	// the function is guaranteed to provide consistent results over multiple invocations
+	// UNLESS RenameNode() is called for a particular node name.
+	std::string FixNodeName(const std::string& name)
+	{
+		// strip Model:: prefix, avoiding ambiguities (i.e. don't strip if 
+		// this causes ambiguities, well possible between empty identifiers,
+		// such as "Model::" and ""). Make sure the behaviour is consistent
+		// across multiple calls to FixNodeName().
+		if(name.substr(0,7) == "Model::") {
+			std::string temp = name.substr(7);
+
+			const NodeNameMap::const_iterator it = node_names.find(temp);
+			if (it != node_names.end()) {
+				if (!(*it).second) {
+					return FixNodeName(name + "_");
+				}
+			}
+			node_names[temp] = true;
+
+			const NameNameMap::const_iterator rit = renamed_nodes.find(temp);
+			return rit == renamed_nodes.end() ? temp : (*rit).second;
+		}
+
+		const NodeNameMap::const_iterator it = node_names.find(name);
+		if (it != node_names.end()) {
+			if ((*it).second) {
+				return FixNodeName(name + "_");
+			}
+		}
+		node_names[name] = false;
+
+		const NameNameMap::const_iterator rit = renamed_nodes.find(name);
+		return rit == renamed_nodes.end() ? name : (*rit).second;
+	}
+
+
+	typedef std::map<const AnimationCurveNode*, const AnimationLayer*> LayerMap;
+
+	// XXX: better use multi_map ..
+	typedef std::map<std::string, std::vector<const AnimationCurveNode*> > NodeMap;
+
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertAnimationStack(const AnimationStack& st)
+	{				
+		const AnimationLayerList& layers = st.Layers();
+		if(layers.empty()) {
+			return;
+		}
+
+		aiAnimation* const anim = new aiAnimation();
+		animations.push_back(anim);
+
+		// strip AnimationStack:: prefix
+		std::string name = st.Name();
+		if(name.substr(0,16) == "AnimationStack::") {
+			name = name.substr(16);
+		}
+
+		anim->mName.Set(name);
+		
+		// need to find all nodes for which we need to generate node animations -
+		// it may happen that we need to merge multiple layers, though.
+		NodeMap node_map;
+
+		// reverse mapping from curves to layers, much faster than querying 
+		// the FBX DOM for it.
+		LayerMap layer_map;
+
+		const char* prop_whitelist[] = {
+			"Lcl Scaling",
+			"Lcl Rotation",
+			"Lcl Translation"
+		};
+		
+		BOOST_FOREACH(const AnimationLayer* layer, layers) {
+			ai_assert(layer);
+
+			const AnimationCurveNodeList& nodes = layer->Nodes(prop_whitelist, 3);
+			BOOST_FOREACH(const AnimationCurveNode* node, nodes) {
+				ai_assert(node);
+
+				const Model* const model = dynamic_cast<const Model*>(node->Target());
+				// this can happen - it could also be a NodeAttribute (i.e. for camera animations)
+				if(!model) {
+					continue;
+				}
+
+				const std::string& name = FixNodeName(model->Name());
+				node_map[name].push_back(node);
+
+				layer_map[node] = layer;
+			}
+		}
+
+		// generate node animations
+		std::vector<aiNodeAnim*> node_anims;
+
+		double min_time = 1e10;
+		double max_time = -1e10;
+
+		try {
+			BOOST_FOREACH(const NodeMap::value_type& kv, node_map) {
+				GenerateNodeAnimations(node_anims, 
+					kv.first, 
+					kv.second, 
+					layer_map, 
+					max_time, 
+					min_time);
+			}
+		}
+		catch(std::exception&) {
+			std::for_each(node_anims.begin(), node_anims.end(), Util::delete_fun<aiNodeAnim>());
+			throw;
+		}
+
+		if(node_anims.size()) {
+			anim->mChannels = new aiNodeAnim*[node_anims.size()]();
+			anim->mNumChannels = static_cast<unsigned int>(node_anims.size());
+
+			std::swap_ranges(node_anims.begin(),node_anims.end(),anim->mChannels);
+		}
+		else {
+			// empty animations would fail validation, so drop them
+			delete anim;
+			animations.pop_back();
+			FBXImporter::LogInfo("ignoring empty AnimationStack (using IK?): " + name);
+			return;
+		}
+
+		// for some mysterious reason, mDuration is simply the maximum key -- the
+		// validator always assumes animations to start at zero.
+		anim->mDuration = max_time /*- min_time */;
+		anim->mTicksPerSecond = anim_fps;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void GenerateNodeAnimations(std::vector<aiNodeAnim*>& node_anims, 
+		const std::string& fixed_name, 
+		const std::vector<const AnimationCurveNode*>& curves, 
+		const LayerMap& layer_map, 
+		double& max_time,
+		double& min_time)
+	{
+
+		NodeMap node_property_map;
+		ai_assert(curves.size());
+
+		// sanity check whether the input is ok
+#ifdef ASSIMP_BUILD_DEBUG
+		{ const Object* target = NULL;
+		BOOST_FOREACH(const AnimationCurveNode* node, curves) {
+			if(!target) {
+				target = node->Target();
+			}
+			ai_assert(node->Target() == target);
+		}}
+#endif
+
+		const AnimationCurveNode* curve_node = NULL;
+		BOOST_FOREACH(const AnimationCurveNode* node, curves) {
+			ai_assert(node);
+
+			if (node->TargetProperty().empty()) {
+				FBXImporter::LogWarn("target property for animation curve not set: " + node->Name());
+				continue;
+			}
+
+			curve_node = node;
+			if (node->Curves().empty()) {
+				FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode: " + node->Name());
+				continue;
+			}
+
+			node_property_map[node->TargetProperty()].push_back(node);
+		}
+
+		ai_assert(curve_node);
+		ai_assert(curve_node->TargetAsModel());
+
+		const Model& target = *curve_node->TargetAsModel();
+
+		// check for all possible transformation components
+		NodeMap::const_iterator chain[TransformationComp_MAXIMUM];
+
+		bool has_any = false;
+		bool has_complex = false;
+
+		for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i) {
+			const TransformationComp comp = static_cast<TransformationComp>(i);
+
+			// inverse pivots don't exist in the input, we just generate them
+			if (comp == TransformationComp_RotationPivotInverse || comp == TransformationComp_ScalingPivotInverse) {
+				chain[i] = node_property_map.end();
+				continue;
+			}
+
+			chain[i] = node_property_map.find(NameTransformationCompProperty(comp));
+			if (chain[i] != node_property_map.end()) {
+
+				// check if this curves contains redundant information by looking
+				// up the corresponding node's transformation chain.
+				if (doc.Settings().optimizeEmptyAnimationCurves && 
+					IsRedundantAnimationData(target, comp, (*chain[i]).second)) {
+
+					FBXImporter::LogDebug("dropping redundant animation channel for node " + target.Name());
+					continue;
+				}
+
+				has_any = true;
+
+				if (comp != TransformationComp_Rotation && comp != TransformationComp_Scaling && comp != TransformationComp_Translation &&
+					comp != TransformationComp_GeometricScaling && comp != TransformationComp_GeometricRotation && comp != TransformationComp_GeometricTranslation )
+				{
+					has_complex = true;
+				}
+			}
+		}
+
+		if (!has_any) {
+			FBXImporter::LogWarn("ignoring node animation, did not find any transformation key frames");
+			return;
+		}
+
+		// this needs to play nicely with GenerateTransformationNodeChain() which will
+		// be invoked _later_ (animations come first). If this node has only rotation,
+		// scaling and translation _and_ there are no animated other components either,
+		// we can use a single node and also a single node animation channel.
+		if (!has_complex && !NeedsComplexTransformationChain(target)) {
+
+			aiNodeAnim* const nd = GenerateSimpleNodeAnim(fixed_name, target, chain, 
+				node_property_map.end(), 
+				layer_map,
+				max_time,
+				min_time,
+				true // input is TRS order, assimp is SRT
+				);
+
+			ai_assert(nd);
+			node_anims.push_back(nd);
+			return;
+		}
+
+		// otherwise, things get gruesome and we need separate animation channels
+		// for each part of the transformation chain. Remember which channels
+		// we generated and pass this information to the node conversion
+		// code to avoid nodes that have identity transform, but non-identity
+		// animations, being dropped.
+		unsigned int flags = 0, bit = 0x1;
+		for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
+			const TransformationComp comp = static_cast<TransformationComp>(i);
+
+			if (chain[i] != node_property_map.end()) {
+				flags |= bit;
+
+				ai_assert(comp != TransformationComp_RotationPivotInverse);
+				ai_assert(comp != TransformationComp_ScalingPivotInverse);
+
+				const std::string& chain_name = NameTransformationChainNode(fixed_name, comp);
+
+				aiNodeAnim* na;
+				switch(comp) 
+				{
+				case TransformationComp_Rotation:
+				case TransformationComp_PreRotation:
+				case TransformationComp_PostRotation:
+				case TransformationComp_GeometricRotation:
+					na = GenerateRotationNodeAnim(chain_name, 
+						target, 
+						(*chain[i]).second,
+						layer_map,
+						max_time,
+						min_time);
+
+					break;
+
+				case TransformationComp_RotationOffset:
+				case TransformationComp_RotationPivot:
+				case TransformationComp_ScalingOffset:
+				case TransformationComp_ScalingPivot:
+				case TransformationComp_Translation:
+				case TransformationComp_GeometricTranslation:
+					na = GenerateTranslationNodeAnim(chain_name, 
+						target, 
+						(*chain[i]).second,
+						layer_map,
+						max_time,
+						min_time);
+
+					// pivoting requires us to generate an implicit inverse channel to undo the pivot translation
+					if (comp == TransformationComp_RotationPivot) {
+						const std::string& invName = NameTransformationChainNode(fixed_name, 
+							TransformationComp_RotationPivotInverse);
+
+						aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName, 
+							target, 
+							(*chain[i]).second,
+							layer_map,
+							max_time,
+							min_time,
+							true);
+
+						ai_assert(inv);
+						node_anims.push_back(inv);
+
+						ai_assert(TransformationComp_RotationPivotInverse > i);
+						flags |= bit << (TransformationComp_RotationPivotInverse - i);
+					}
+					else if (comp == TransformationComp_ScalingPivot) {
+						const std::string& invName = NameTransformationChainNode(fixed_name, 
+							TransformationComp_ScalingPivotInverse);
+
+						aiNodeAnim* const inv = GenerateTranslationNodeAnim(invName, 
+							target, 
+							(*chain[i]).second,
+							layer_map,
+							max_time,
+							min_time,
+							true);
+
+						ai_assert(inv);
+						node_anims.push_back(inv);
+					
+						ai_assert(TransformationComp_RotationPivotInverse > i);
+						flags |= bit << (TransformationComp_RotationPivotInverse - i);
+					}
+
+					break;
+
+				case TransformationComp_Scaling:
+				case TransformationComp_GeometricScaling:
+					na = GenerateScalingNodeAnim(chain_name, 
+						target, 
+						(*chain[i]).second,
+						layer_map,
+						max_time,
+						min_time);
+
+					break;
+
+				default:
+					ai_assert(false);
+				}
+
+				ai_assert(na);
+				node_anims.push_back(na);
+				continue;
+			}
+		}
+
+		node_anim_chain_bits[fixed_name] = flags;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	bool IsRedundantAnimationData(const Model& target, 
+		TransformationComp comp, 
+		const std::vector<const AnimationCurveNode*>& curves)
+	{
+		ai_assert(curves.size());
+
+		// look for animation nodes with
+		//  * sub channels for all relevant components set
+		//  * one key/value pair per component
+		//  * combined values match up the corresponding value in the bind pose node transformation
+		// only such nodes are 'redundant' for this function.
+
+		if (curves.size() > 1) {
+			return false;
+		}
+
+		const AnimationCurveNode& nd = *curves.front();
+		const AnimationCurveMap& sub_curves = nd.Curves();
+
+		const AnimationCurveMap::const_iterator dx = sub_curves.find("d|X");
+		const AnimationCurveMap::const_iterator dy = sub_curves.find("d|Y");
+		const AnimationCurveMap::const_iterator dz = sub_curves.find("d|Z");
+
+		if (dx == sub_curves.end() || dy == sub_curves.end() || dz == sub_curves.end()) {
+			return false;
+		}
+
+		const KeyValueList& vx = (*dx).second->GetValues();
+		const KeyValueList& vy = (*dy).second->GetValues();
+		const KeyValueList& vz = (*dz).second->GetValues();
+
+		if(vx.size() != 1 || vy.size() != 1 || vz.size() != 1) {
+			return false;
+		}
+
+		const aiVector3D dyn_val = aiVector3D(vx[0], vy[0], vz[0]);
+		const aiVector3D& static_val = PropertyGet<aiVector3D>(target.Props(), 
+			NameTransformationCompProperty(comp), 
+			TransformationCompDefaultValue(comp)
+		);
+
+		const float epsilon = 1e-6f;
+		return (dyn_val - static_val).SquareLength() < epsilon;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	aiNodeAnim* GenerateRotationNodeAnim(const std::string& name, 
+		const Model& target, 
+		const std::vector<const AnimationCurveNode*>& curves,
+		const LayerMap& layer_map,
+		double& max_time,
+		double& min_time)
+	{
+		ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
+		na->mNodeName.Set(name);
+
+		ConvertRotationKeys(na, curves, layer_map, max_time,min_time, target.RotationOrder());
+
+		// dummy scaling key
+		na->mScalingKeys = new aiVectorKey[1];
+		na->mNumScalingKeys = 1;
+
+		na->mScalingKeys[0].mTime = 0.;
+		na->mScalingKeys[0].mValue = aiVector3D(1.0f,1.0f,1.0f);
+
+		// dummy position key
+		na->mPositionKeys = new aiVectorKey[1];
+		na->mNumPositionKeys = 1;
+
+		na->mPositionKeys[0].mTime = 0.;
+		na->mPositionKeys[0].mValue = aiVector3D();
+
+		return na.dismiss();
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	aiNodeAnim* GenerateScalingNodeAnim(const std::string& name, 
+		const Model& target, 
+		const std::vector<const AnimationCurveNode*>& curves,
+		const LayerMap& layer_map,
+		double& max_time,
+		double& min_time)
+	{
+		ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
+		na->mNodeName.Set(name);
+
+		ConvertScaleKeys(na, curves, layer_map, max_time,min_time);
+
+		// dummy rotation key
+		na->mRotationKeys = new aiQuatKey[1];
+		na->mNumRotationKeys = 1;
+
+		na->mRotationKeys[0].mTime = 0.;
+		na->mRotationKeys[0].mValue = aiQuaternion();
+
+		// dummy position key
+		na->mPositionKeys = new aiVectorKey[1];
+		na->mNumPositionKeys = 1;
+
+		na->mPositionKeys[0].mTime = 0.;
+		na->mPositionKeys[0].mValue = aiVector3D();
+
+		return na.dismiss();
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	aiNodeAnim* GenerateTranslationNodeAnim(const std::string& name, 
+		const Model& target, 
+		const std::vector<const AnimationCurveNode*>& curves,
+		const LayerMap& layer_map,
+		double& max_time,
+		double& min_time,
+		bool inverse = false)
+	{
+		ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
+		na->mNodeName.Set(name);
+
+		ConvertTranslationKeys(na, curves, layer_map, max_time,min_time);
+
+		if (inverse) {
+			for (unsigned int i = 0; i < na->mNumPositionKeys; ++i) {
+				na->mPositionKeys[i].mValue *= -1.0f;
+			}
+		}
+
+		// dummy scaling key
+		na->mScalingKeys = new aiVectorKey[1];
+		na->mNumScalingKeys = 1;
+
+		na->mScalingKeys[0].mTime = 0.;
+		na->mScalingKeys[0].mValue = aiVector3D(1.0f,1.0f,1.0f);
+
+		// dummy rotation key
+		na->mRotationKeys = new aiQuatKey[1];
+		na->mNumRotationKeys = 1;
+
+		na->mRotationKeys[0].mTime = 0.;
+		na->mRotationKeys[0].mValue = aiQuaternion();
+
+		return na.dismiss();
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// generate node anim, extracting only Rotation, Scaling and Translation from the given chain
+	aiNodeAnim* GenerateSimpleNodeAnim(const std::string& name, 
+		const Model& target, 
+		NodeMap::const_iterator chain[TransformationComp_MAXIMUM], 
+		NodeMap::const_iterator iter_end,
+		const LayerMap& layer_map,
+		double& max_time,
+		double& min_time,
+		bool reverse_order = false)
+
+	{
+		ScopeGuard<aiNodeAnim> na(new aiNodeAnim());
+		na->mNodeName.Set(name);
+
+		const PropertyTable& props = target.Props();
+
+		// need to convert from TRS order to SRT?
+		if(reverse_order) {
+		
+			aiVector3D def_scale, def_translate;
+			aiQuaternion def_rot;
+
+			KeyFrameListList scaling;
+			KeyFrameListList translation;
+			KeyFrameListList rotation;
+			
+			if(chain[TransformationComp_Scaling] != iter_end) {
+				scaling = GetKeyframeList((*chain[TransformationComp_Scaling]).second);
+			}
+			else {
+				def_scale = PropertyGet(props,"Lcl Scaling",aiVector3D(1.f,1.f,1.f));
+			}
+
+			if(chain[TransformationComp_Translation] != iter_end) {
+				translation = GetKeyframeList((*chain[TransformationComp_Translation]).second);
+			}
+			else {
+				def_translate = PropertyGet(props,"Lcl Translation",aiVector3D(0.f,0.f,0.f));
+			}
+			
+			if(chain[TransformationComp_Rotation] != iter_end) {
+				rotation = GetKeyframeList((*chain[TransformationComp_Rotation]).second);
+			}
+			else {
+				def_rot = EulerToQuaternion(PropertyGet(props,"Lcl Rotation",aiVector3D(0.f,0.f,0.f)),
+					target.RotationOrder());
+			}
+
+			KeyFrameListList joined;
+			joined.insert(joined.end(), scaling.begin(), scaling.end());
+			joined.insert(joined.end(), translation.begin(), translation.end());
+			joined.insert(joined.end(), rotation.begin(), rotation.end());
+
+			const KeyTimeList& times = GetKeyTimeList(joined);
+
+			aiQuatKey* out_quat = new aiQuatKey[times.size()];
+			aiVectorKey* out_scale = new aiVectorKey[times.size()];
+			aiVectorKey* out_translation = new aiVectorKey[times.size()];
+
+			ConvertTransformOrder_TRStoSRT(out_quat, out_scale, out_translation, 
+				scaling, 
+				translation, 
+				rotation, 
+				times,
+				max_time,
+				min_time,
+				target.RotationOrder(),
+				def_scale,
+				def_translate,
+				def_rot);
+
+			// XXX remove duplicates / redundant keys which this operation did
+			// likely produce if not all three channels were equally dense.
+
+			na->mNumScalingKeys = static_cast<unsigned int>(times.size());
+			na->mNumRotationKeys = na->mNumScalingKeys;
+			na->mNumPositionKeys = na->mNumScalingKeys;
+
+			na->mScalingKeys = out_scale;
+			na->mRotationKeys = out_quat;
+			na->mPositionKeys = out_translation;
+		}
+		else {
+
+			// if a particular transformation is not given, grab it from
+			// the corresponding node to meet the semantics of aiNodeAnim,
+			// which requires all of rotation, scaling and translation
+			// to be set.
+			if(chain[TransformationComp_Scaling] != iter_end) {
+				ConvertScaleKeys(na, (*chain[TransformationComp_Scaling]).second, 
+					layer_map, 
+					max_time, 
+					min_time);
+			}
+			else {
+				na->mScalingKeys = new aiVectorKey[1];
+				na->mNumScalingKeys = 1;
+
+				na->mScalingKeys[0].mTime = 0.;
+				na->mScalingKeys[0].mValue = PropertyGet(props,"Lcl Scaling",
+					aiVector3D(1.f,1.f,1.f));
+			}
+
+			if(chain[TransformationComp_Rotation] != iter_end) {
+				ConvertRotationKeys(na, (*chain[TransformationComp_Rotation]).second, 
+					layer_map, 
+					max_time,
+					min_time,
+					target.RotationOrder());
+			}
+			else {
+				na->mRotationKeys = new aiQuatKey[1];
+				na->mNumRotationKeys = 1;
+
+				na->mRotationKeys[0].mTime = 0.;
+				na->mRotationKeys[0].mValue = EulerToQuaternion(
+					PropertyGet(props,"Lcl Rotation",aiVector3D(0.f,0.f,0.f)),
+					target.RotationOrder());
+			}
+
+			if(chain[TransformationComp_Translation] != iter_end) {
+				ConvertTranslationKeys(na, (*chain[TransformationComp_Translation]).second, 
+					layer_map, 
+					max_time, 
+					min_time);
+			}
+			else {
+				na->mPositionKeys = new aiVectorKey[1];
+				na->mNumPositionKeys = 1;
+
+				na->mPositionKeys[0].mTime = 0.;
+				na->mPositionKeys[0].mValue = PropertyGet(props,"Lcl Translation",
+					aiVector3D(0.f,0.f,0.f));
+			}
+
+		}
+		return na.dismiss();
+	}
+
+
+
+	// key (time), value, mapto (component index)
+	typedef boost::tuple< const KeyTimeList*, const KeyValueList*, unsigned int > KeyFrameList;
+	typedef std::vector<KeyFrameList> KeyFrameListList;
+
+	
+
+	// ------------------------------------------------------------------------------------------------
+	KeyFrameListList GetKeyframeList(const std::vector<const AnimationCurveNode*>& nodes)
+	{
+		KeyFrameListList inputs;
+		inputs.reserve(nodes.size()*3);
+
+		BOOST_FOREACH(const AnimationCurveNode* node, nodes) {
+			ai_assert(node);
+
+			const AnimationCurveMap& curves = node->Curves();
+			BOOST_FOREACH(const AnimationCurveMap::value_type& kv, curves) {
+
+				unsigned int mapto;
+				if (kv.first == "d|X") {
+					mapto = 0;
+				}
+				else if (kv.first == "d|Y") {
+					mapto = 1;
+				}
+				else if (kv.first == "d|Z") {
+					mapto = 2;
+				}
+				else {
+					FBXImporter::LogWarn("ignoring scale animation curve, did not recognize target component");
+					continue;
+				}
+
+				const AnimationCurve* const curve = kv.second;
+				ai_assert(curve->GetKeys().size() == curve->GetValues().size() && curve->GetKeys().size());
+
+				inputs.push_back(boost::make_tuple(&curve->GetKeys(), &curve->GetValues(), mapto));
+			}
+		}
+		return inputs; // pray for NRVO :-)
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	KeyTimeList GetKeyTimeList(const KeyFrameListList& inputs)
+	{
+		ai_assert(inputs.size());
+
+		// reserve some space upfront - it is likely that the keyframe lists
+		// have matching time values, so max(of all keyframe lists) should 
+		// be a good estimate.
+		KeyTimeList keys;
+		
+		size_t estimate = 0;
+		BOOST_FOREACH(const KeyFrameList& kfl, inputs) {
+			estimate = std::max(estimate, kfl.get<0>()->size());
+		}
+
+		keys.reserve(estimate);
+
+		std::vector<unsigned int> next_pos;
+		next_pos.resize(inputs.size(),0);
+
+		const size_t count = inputs.size();
+		while(true) {
+
+			uint64_t min_tick = std::numeric_limits<uint64_t>::max();
+			for (size_t i = 0; i < count; ++i) {
+				const KeyFrameList& kfl = inputs[i];
+
+				if (kfl.get<0>()->size() > next_pos[i] && kfl.get<0>()->at(next_pos[i]) < min_tick) {
+					min_tick = kfl.get<0>()->at(next_pos[i]);
+				}
+			}
+
+			if (min_tick == std::numeric_limits<uint64_t>::max()) {
+				break;
+			}
+			keys.push_back(min_tick);
+
+			for (size_t i = 0; i < count; ++i) {
+				const KeyFrameList& kfl = inputs[i];
+
+
+				while(kfl.get<0>()->size() > next_pos[i] && kfl.get<0>()->at(next_pos[i]) == min_tick) {
+					++next_pos[i];
+				}
+			}
+		}	
+
+		return keys;
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void InterpolateKeys(aiVectorKey* valOut,const KeyTimeList& keys, const KeyFrameListList& inputs, 
+		const bool geom, 
+		double& max_time,
+		double& min_time)
+
+	{
+		ai_assert(keys.size());
+		ai_assert(valOut);
+
+		std::vector<unsigned int> next_pos;
+		const size_t count = inputs.size();
+
+		next_pos.resize(inputs.size(),0);
+
+		BOOST_FOREACH(KeyTimeList::value_type time, keys) {
+			float result[3] = {0.0f, 0.0f, 0.0f};
+			if(geom) {
+				result[0] = result[1] = result[2] = 1.0f;
+			}
+
+			for (size_t i = 0; i < count; ++i) {
+				const KeyFrameList& kfl = inputs[i];
+
+				const size_t ksize = kfl.get<0>()->size();
+				if (ksize > next_pos[i] && kfl.get<0>()->at(next_pos[i]) == time) {
+					++next_pos[i]; 
+				}
+
+				const size_t id0 = next_pos[i]>0 ? next_pos[i]-1 : 0;
+				const size_t id1 = next_pos[i]==ksize ? ksize-1 : next_pos[i];
+
+				// use lerp for interpolation
+				const KeyValueList::value_type valueA = kfl.get<1>()->at(id0);
+				const KeyValueList::value_type valueB = kfl.get<1>()->at(id1);
+
+				const KeyTimeList::value_type timeA = kfl.get<0>()->at(id0);
+				const KeyTimeList::value_type timeB = kfl.get<0>()->at(id1);
+
+				// do the actual interpolation in double-precision arithmetics
+				// because it is a bit sensitive to rounding errors.
+				const double factor = timeB == timeA ? 0. : static_cast<double>((time - timeA) / (timeB - timeA));
+				const float interpValue = static_cast<float>(valueA + (valueB - valueA) * factor);
+
+				if(geom) {
+					result[kfl.get<2>()] *= interpValue;
+				}
+				else {
+					result[kfl.get<2>()] += interpValue;
+				}
+			}
+
+			// magic value to convert fbx times to seconds
+			valOut->mTime = CONVERT_FBX_TIME(time) * anim_fps;
+
+			min_time = std::min(min_time, valOut->mTime);
+			max_time = std::max(max_time, valOut->mTime);
+
+			valOut->mValue.x = result[0];
+			valOut->mValue.y = result[1];
+			valOut->mValue.z = result[2];
+			
+			++valOut;
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void InterpolateKeys(aiQuatKey* valOut,const KeyTimeList& keys, const KeyFrameListList& inputs, 
+		const bool geom,
+		double& maxTime,
+		double& minTime,
+		Model::RotOrder order)
+	{
+		ai_assert(keys.size());
+		ai_assert(valOut);
+
+		boost::scoped_array<aiVectorKey> temp(new aiVectorKey[keys.size()]);
+		InterpolateKeys(temp.get(),keys,inputs,geom,maxTime, minTime);
+
+		aiMatrix4x4 m;
+
+		aiQuaternion lastq;
+
+		for (size_t i = 0, c = keys.size(); i < c; ++i) {
+
+			valOut[i].mTime = temp[i].mTime;
+
+			
+			GetRotationMatrix(order, temp[i].mValue, m);
+			aiQuaternion quat = aiQuaternion(aiMatrix3x3(m));
+
+			// take shortest path by checking the inner product
+			// http://www.3dkingdoms.com/weekly/weekly.php?a=36
+			if (quat.x * lastq.x + quat.y * lastq.y + quat.z * lastq.z + quat.w * lastq.w < 0)
+			{
+				quat.x = -quat.x;
+				quat.y = -quat.y;
+				quat.z = -quat.z;
+				quat.w = -quat.w;
+			} 
+			lastq = quat;
+
+			valOut[i].mValue = quat; 
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertTransformOrder_TRStoSRT(aiQuatKey* out_quat, aiVectorKey* out_scale,
+		aiVectorKey* out_translation, 
+		const KeyFrameListList& scaling, 
+		const KeyFrameListList& translation, 
+		const KeyFrameListList& rotation, 
+		const KeyTimeList& times,
+		double& maxTime,
+		double& minTime,
+		Model::RotOrder order,
+		const aiVector3D& def_scale,
+		const aiVector3D& def_translate,
+		const aiQuaternion& def_rotation)
+	{
+		if (rotation.size()) {
+			InterpolateKeys(out_quat, times, rotation, false, maxTime, minTime, order);
+		}
+		else {
+			for (size_t i = 0; i < times.size(); ++i) {
+				out_quat[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
+				out_quat[i].mValue = def_rotation;
+			}
+		}
+
+		if (scaling.size()) {
+			InterpolateKeys(out_scale, times, scaling, true, maxTime, minTime);
+		}
+		else {
+			for (size_t i = 0; i < times.size(); ++i) {
+				out_scale[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
+				out_scale[i].mValue = def_scale;
+			}
+		}
+
+		if (translation.size()) {
+			InterpolateKeys(out_translation, times, translation, false, maxTime, minTime);
+		}
+		else {
+			for (size_t i = 0; i < times.size(); ++i) {
+				out_translation[i].mTime = CONVERT_FBX_TIME(times[i]) * anim_fps;
+				out_translation[i].mValue = def_translate;
+			}
+		}
+
+		const size_t count = times.size();
+		for (size_t i = 0; i < count; ++i) {
+			aiQuaternion& r = out_quat[i].mValue;
+			aiVector3D& s = out_scale[i].mValue;
+			aiVector3D& t = out_translation[i].mValue;
+
+			aiMatrix4x4 mat, temp;
+			aiMatrix4x4::Translation(t, mat);
+			mat *= aiMatrix4x4( r.GetMatrix() );
+			mat *= aiMatrix4x4::Scaling(s, temp);
+
+			mat.Decompose(s, r, t);
+		}
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// euler xyz -> quat
+	aiQuaternion EulerToQuaternion(const aiVector3D& rot, Model::RotOrder order) 
+	{
+		aiMatrix4x4 m;
+		GetRotationMatrix(order, rot, m);
+
+		return aiQuaternion(aiMatrix3x3(m));
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertScaleKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& layers,
+		double& maxTime,
+		double& minTime)
+	{
+		ai_assert(nodes.size());
+
+		// XXX for now, assume scale should be blended geometrically (i.e. two
+		// layers should be multiplied with each other). There is a FBX 
+		// property in the layer to specify the behaviour, though.
+
+		const KeyFrameListList& inputs = GetKeyframeList(nodes);
+		const KeyTimeList& keys = GetKeyTimeList(inputs);
+
+		na->mNumScalingKeys = static_cast<unsigned int>(keys.size());
+		na->mScalingKeys = new aiVectorKey[keys.size()];
+		InterpolateKeys(na->mScalingKeys, keys, inputs, true, maxTime, minTime);
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, 
+		const LayerMap& layers,
+		double& maxTime,
+		double& minTime)
+	{
+		ai_assert(nodes.size());
+
+		// XXX see notes in ConvertScaleKeys()
+		const KeyFrameListList& inputs = GetKeyframeList(nodes);
+		const KeyTimeList& keys = GetKeyTimeList(inputs);
+
+		na->mNumPositionKeys = static_cast<unsigned int>(keys.size());
+		na->mPositionKeys = new aiVectorKey[keys.size()];
+		InterpolateKeys(na->mPositionKeys, keys, inputs, false, maxTime, minTime);
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	void ConvertRotationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, 
+		const LayerMap& layers, 
+		double& maxTime,
+		double& minTime,
+		Model::RotOrder order)
+	{
+		ai_assert(nodes.size());
+
+		// XXX see notes in ConvertScaleKeys()
+		const std::vector< KeyFrameList >& inputs = GetKeyframeList(nodes);
+		const KeyTimeList& keys = GetKeyTimeList(inputs);
+
+		na->mNumRotationKeys = static_cast<unsigned int>(keys.size());
+		na->mRotationKeys = new aiQuatKey[keys.size()];
+		InterpolateKeys(na->mRotationKeys, keys, inputs, false, maxTime, minTime, order);
+	}
+
+
+	// ------------------------------------------------------------------------------------------------
+	// copy generated meshes, animations, lights, cameras and textures to the output scene
+	void TransferDataToScene()
+	{
+		ai_assert(!out->mMeshes && !out->mNumMeshes);
+
+		// note: the trailing () ensures initialization with NULL - not
+		// many C++ users seem to know this, so pointing it out to avoid
+		// confusion why this code works.
+
+		if(meshes.size()) {
+			out->mMeshes = new aiMesh*[meshes.size()]();
+			out->mNumMeshes = static_cast<unsigned int>(meshes.size());
+
+			std::swap_ranges(meshes.begin(),meshes.end(),out->mMeshes);
+		}
+
+		if(materials.size()) {
+			out->mMaterials = new aiMaterial*[materials.size()]();
+			out->mNumMaterials = static_cast<unsigned int>(materials.size());
+
+			std::swap_ranges(materials.begin(),materials.end(),out->mMaterials);
+		}
+
+		if(animations.size()) {
+			out->mAnimations = new aiAnimation*[animations.size()]();
+			out->mNumAnimations = static_cast<unsigned int>(animations.size());
+
+			std::swap_ranges(animations.begin(),animations.end(),out->mAnimations);
+		}
+
+		if(lights.size()) {
+			out->mLights = new aiLight*[lights.size()]();
+			out->mNumLights = static_cast<unsigned int>(lights.size());
+
+			std::swap_ranges(lights.begin(),lights.end(),out->mLights);
+		}
+
+		if(cameras.size()) {
+			out->mCameras = new aiCamera*[cameras.size()]();
+			out->mNumCameras = static_cast<unsigned int>(cameras.size());
+
+			std::swap_ranges(cameras.begin(),cameras.end(),out->mCameras);
+		}
+	}
+
+
+private:
+
+	// 0: not assigned yet, others: index is value - 1
+	unsigned int defaultMaterialIndex;
+
+	std::vector<aiMesh*> meshes;
+	std::vector<aiMaterial*> materials;
+	std::vector<aiAnimation*> animations;
+	std::vector<aiLight*> lights;
+	std::vector<aiCamera*> cameras;
+
+	typedef std::map<const Material*, unsigned int> MaterialMap;
+	MaterialMap materials_converted;
+
+	typedef std::map<const Geometry*, std::vector<unsigned int> > MeshMap;
+	MeshMap meshes_converted;
+
+	// fixed node name -> which trafo chain components have animations?
+	typedef std::map<std::string, unsigned int> NodeAnimBitMap;
+	NodeAnimBitMap node_anim_chain_bits;
+
+	// name -> has had its prefix_stripped?
+	typedef std::map<std::string, bool> NodeNameMap;
+	NodeNameMap node_names;
+
+	typedef std::map<std::string, std::string> NameNameMap;
+	NameNameMap renamed_nodes;
+
+	double anim_fps;
+
+	aiScene* const out;
+	const FBX::Document& doc;
+};
+
+//} // !anon
+
+// ------------------------------------------------------------------------------------------------
+void ConvertToAssimpScene(aiScene* out, const Document& doc)
+{
+	Converter converter(out,doc);
+}
+
+} // !FBX
+} // !Assimp
+
+#endif

+ 63 - 0
assimplib.mod/assimp/code/FBXConverter.h

@@ -0,0 +1,63 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  FBXDConverter.h
+ *  @brief FBX DOM to aiScene conversion
+ */
+#ifndef INCLUDED_AI_FBX_CONVERTER_H
+#define INCLUDED_AI_FBX_CONVERTER_H
+
+namespace Assimp {
+namespace FBX {
+
+	class Document;
+
+
+/** Convert a FBX #Document to #aiScene
+ *  @param out Empty scene to be populated
+ *  @param doc Parsed FBX document */
+void ConvertToAssimpScene(aiScene* out, const Document& doc);
+
+
+}
+}
+
+
+#endif

+ 169 - 0
assimplib.mod/assimp/code/FBXDeformer.cpp

@@ -0,0 +1,169 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  FBXNoteAttribute.cpp
+ *  @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include "FBXParser.h"
+#include "FBXDocument.h"
+#include "FBXImporter.h"
+#include "FBXImportSettings.h"
+#include "FBXDocumentUtil.h"
+#include "FBXProperties.h"
+
+namespace Assimp {
+namespace FBX {
+
+	using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Deformer::Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+	: Object(id,element,name)
+{
+	const Scope& sc = GetRequiredScope(element);
+
+	const std::string& classname = ParseTokenAsString(GetRequiredToken(element,2));
+	props = GetPropertyTable(doc,"Deformer.Fbx" + classname,element,sc,true);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Deformer::~Deformer()
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Cluster::Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+: Deformer(id,element,doc,name)
+, node()
+{
+	const Scope& sc = GetRequiredScope(element);
+
+	const Element* const Indexes = sc["Indexes"];
+	const Element* const Weights = sc["Weights"];
+
+	const Element& Transform = GetRequiredElement(sc,"Transform",&element);
+	const Element& TransformLink = GetRequiredElement(sc,"TransformLink",&element);
+
+	transform = ReadMatrix(Transform);
+	transformLink = ReadMatrix(TransformLink);
+
+	// it is actually possible that there be Deformer's with no weights
+	if (!!Indexes != !!Weights) {
+		DOMError("either Indexes or Weights are missing from Cluster",&element);
+	}
+
+	if(Indexes) {
+		ParseVectorDataArray(indices,*Indexes);
+		ParseVectorDataArray(weights,*Weights);
+	}
+
+	if(indices.size() != weights.size()) {
+		DOMError("sizes of index and weight array don't match up",&element);
+	}
+
+	// read assigned node
+	const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Model");
+	BOOST_FOREACH(const Connection* con, conns) {
+		const Model* const mod = ProcessSimpleConnection<Model>(*con, false, "Model -> Cluster", element);
+		if(mod) {
+			node = mod;
+			break;
+		}
+	}
+
+	if (!node) {
+		DOMError("failed to read target Node for Cluster",&element);
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Cluster::~Cluster()
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Skin::Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+: Deformer(id,element,doc,name)
+{
+	const Scope& sc = GetRequiredScope(element);
+
+	const Element* const Link_DeformAcuracy = sc["Link_DeformAcuracy"];
+	if(Link_DeformAcuracy) {
+		accuracy = ParseTokenAsFloat(GetRequiredToken(*Link_DeformAcuracy,0));
+	}
+
+	// resolve assigned clusters 
+	const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer");
+
+	clusters.reserve(conns.size());
+	BOOST_FOREACH(const Connection* con, conns) {
+
+		const Cluster* const cluster = ProcessSimpleConnection<Cluster>(*con, false, "Cluster -> Skin", element);
+		if(cluster) {
+			clusters.push_back(cluster);
+			continue;
+		}
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Skin::~Skin()
+{
+
+}
+
+
+
+}
+}
+
+#endif
+

+ 721 - 0
assimplib.mod/assimp/code/FBXDocument.cpp

@@ -0,0 +1,721 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the*
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  FBXDocument.cpp
+ *  @brief Implementation of the FBX DOM classes
+ */
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
+
+#include <functional>
+
+#include "FBXParser.h"
+#include "FBXDocument.h"
+#include "FBXUtil.h"
+#include "FBXImporter.h"
+#include "FBXImportSettings.h"
+#include "FBXDocumentUtil.h"
+#include "FBXProperties.h"
+
+namespace Assimp {
+namespace FBX {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc)
+: doc(doc)
+, element(element)
+, id(id)
+, flags()
+{
+
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject::~LazyObject()
+{
+
+}
+
+// ------------------------------------------------------------------------------------------------
+const Object* LazyObject::Get(bool dieOnError)
+{
+	if(IsBeingConstructed() || FailedToConstruct()) {
+		return NULL;
+	}
+
+	if (object.get()) {
+		return object.get();
+	}
+
+	// if this is the root object, we return a dummy since there
+	// is no root object int he fbx file - it is just referenced
+	// with id 0.
+	if(id == 0L) {
+		object.reset(new Object(id, element, "Model::RootNode"));
+		return object.get();
+	}
+
+	const Token& key = element.KeyToken();
+	const TokenList& tokens = element.Tokens();
+
+	if(tokens.size() < 3) {
+		DOMError("expected at least 3 tokens: id, name and class tag",&element);
+	}
+
+	const char* err;
+	std::string name = ParseTokenAsString(*tokens[1],err);
+	if (err) {
+		DOMError(err,&element);
+	} 
+
+	// small fix for binary reading: binary fbx files don't use
+	// prefixes such as Model:: in front of their names. The
+	// loading code expects this at many places, though!
+	// so convert the binary representation (a 0x0001) to the
+	// double colon notation.
+	if(tokens[1]->IsBinary()) {
+		for (size_t i = 0; i < name.length(); ++i) {
+			if (name[i] == 0x0 && name[i+1] == 0x1) {
+				name = name.substr(i+2) + "::" + name.substr(0,i);
+			}
+		}
+	}
+
+	const std::string classtag = ParseTokenAsString(*tokens[2],err);
+	if (err) {
+		DOMError(err,&element);
+	} 
+
+	// prevent recursive calls
+	flags |= BEING_CONSTRUCTED;
+
+	try {
+		// this needs to be relatively fast since it happens a lot,
+		// so avoid constructing strings all the time.
+		const char* obtype = key.begin();
+		const size_t length = static_cast<size_t>(key.end()-key.begin());
+		if (!strncmp(obtype,"Geometry",length)) {
+			if (!strcmp(classtag.c_str(),"Mesh")) {
+				object.reset(new MeshGeometry(id,element,name,doc));
+			}
+		}
+		else if (!strncmp(obtype,"NodeAttribute",length)) {
+			if (!strcmp(classtag.c_str(),"Camera")) {
+				object.reset(new Camera(id,element,doc,name));
+			}
+			else if (!strcmp(classtag.c_str(),"CameraSwitcher")) {
+				object.reset(new CameraSwitcher(id,element,doc,name));
+			}
+			else if (!strcmp(classtag.c_str(),"Light")) {
+				object.reset(new Light(id,element,doc,name));
+			}
+			else if (!strcmp(classtag.c_str(),"Null")) {
+				object.reset(new Null(id,element,doc,name));
+			}
+			else if (!strcmp(classtag.c_str(),"LimbNode")) {
+				object.reset(new LimbNode(id,element,doc,name));
+			}
+		}
+		else if (!strncmp(obtype,"Deformer",length)) {
+			if (!strcmp(classtag.c_str(),"Cluster")) {
+				object.reset(new Cluster(id,element,doc,name));
+			}
+			else if (!strcmp(classtag.c_str(),"Skin")) {
+				object.reset(new Skin(id,element,doc,name));
+			}
+		}
+		else if (!strncmp(obtype,"Model",length)) {
+			// FK and IK effectors are not supported
+			if (strcmp(classtag.c_str(),"IKEffector") && strcmp(classtag.c_str(),"FKEffector")) {
+				object.reset(new Model(id,element,doc,name));
+			}
+		}
+		else if (!strncmp(obtype,"Material",length)) {
+			object.reset(new Material(id,element,doc,name));
+		}
+		else if (!strncmp(obtype,"Texture",length)) {
+			object.reset(new Texture(id,element,doc,name));
+		}
+		else if (!strncmp(obtype,"LayeredTexture",length)) {
+			object.reset(new LayeredTexture(id,element,doc,name));
+		}
+		else if (!strncmp(obtype,"AnimationStack",length)) {
+			object.reset(new AnimationStack(id,element,name,doc));
+		}
+		else if (!strncmp(obtype,"AnimationLayer",length)) {
+			object.reset(new AnimationLayer(id,element,name,doc));
+		}
+		// note: order matters for these two
+		else if (!strncmp(obtype,"AnimationCurve",length)) {
+			object.reset(new AnimationCurve(id,element,name,doc));
+		}
+		else if (!strncmp(obtype,"AnimationCurveNode",length)) {
+			object.reset(new AnimationCurveNode(id,element,name,doc));
+		}	
+	}
+	catch(std::exception& ex) {
+		flags &= ~BEING_CONSTRUCTED;
+		flags |= FAILED_TO_CONSTRUCT;
+
+		if(dieOnError || doc.Settings().strictMode) {
+			throw;
+		}
+
+		// note: the error message is already formatted, so raw logging is ok
+		if(!DefaultLogger::isNullLogger()) {
+			DefaultLogger::get()->error(ex.what());
+		}
+		return NULL;
+	}
+
+	if (!object.get()) {
+		//DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element);
+	}
+
+	flags &= ~BEING_CONSTRUCTED;
+	return object.get();
+}
+
+// ------------------------------------------------------------------------------------------------
+Object::Object(uint64_t id, const Element& element, const std::string& name)
+: element(element)
+, name(name)
+, id(id)
+{
+
+}
+
+// ------------------------------------------------------------------------------------------------
+Object::~Object()
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+FileGlobalSettings::FileGlobalSettings(const Document& doc, boost::shared_ptr<const PropertyTable> props)
+: props(props)
+, doc(doc) 
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+FileGlobalSettings::~FileGlobalSettings()
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Document::Document(const Parser& parser, const ImportSettings& settings)
+: settings(settings)
+, parser(parser)
+{
+	// cannot use array default initialization syntax because vc8 fails on it
+	for (unsigned int i = 0; i < 7; ++i) {
+		creationTimeStamp[i] = 0;
+	}
+
+	ReadHeader();
+	ReadPropertyTemplates();
+
+	ReadGlobalSettings();
+
+	// this order is important, connections need parsed objects to check
+	// whether connections are ok or not. Objects may not be evaluated yet,
+	// though, since this may require valid connections.
+	ReadObjects();
+	ReadConnections();
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Document::~Document()
+{
+	BOOST_FOREACH(ObjectMap::value_type& v, objects) {
+		delete v.second;
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadHeader()
+{
+	// read ID objects from "Objects" section
+	const Scope& sc = parser.GetRootScope();
+	const Element* const ehead = sc["FBXHeaderExtension"];
+	if(!ehead || !ehead->Compound()) {
+		DOMError("no FBXHeaderExtension dictionary found");
+	}
+
+	const Scope& shead = *ehead->Compound();
+	fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead,"FBXVersion",ehead),0));
+
+	// while we maye have some success with newer files, we don't support
+	// the older 6.n fbx format
+	if(fbxVersion < 7100) {
+		DOMError("unsupported, old format version, supported are only FBX 2011, FBX 2012 and FBX 2013");
+	}
+	if(fbxVersion > 7300) {
+		if(Settings().strictMode) {
+			DOMError("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013"
+				" (turn off strict mode to try anyhow) ");
+		}
+		else {
+			DOMWarning("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013,"
+				" trying to read it nevertheless");
+		}
+	}
+	
+
+	const Element* const ecreator = shead["Creator"];
+	if(ecreator) {
+		creator = ParseTokenAsString(GetRequiredToken(*ecreator,0));
+	}
+
+	const Element* const etimestamp = shead["CreationTimeStamp"];
+	if(etimestamp && etimestamp->Compound()) {
+		const Scope& stimestamp = *etimestamp->Compound();
+		creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Year"),0));
+		creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Month"),0));
+		creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Day"),0));
+		creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Hour"),0));
+		creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Minute"),0));
+		creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Second"),0));
+		creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Millisecond"),0));
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadGlobalSettings()
+{
+	const Scope& sc = parser.GetRootScope();
+	const Element* const ehead = sc["GlobalSettings"];
+	if(!ehead || !ehead->Compound()) {
+		DOMWarning("no GlobalSettings dictionary found");
+
+		globals.reset(new FileGlobalSettings(*this, boost::make_shared<const PropertyTable>()));
+		return;
+	}
+
+	boost::shared_ptr<const PropertyTable> props = GetPropertyTable(*this, "", *ehead, *ehead->Compound(), true);
+
+	if(!props) {
+		DOMError("GlobalSettings dictionary contains no property table");
+	}
+
+	globals.reset(new FileGlobalSettings(*this, props));
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadObjects()
+{
+	// read ID objects from "Objects" section
+	const Scope& sc = parser.GetRootScope();
+	const Element* const eobjects = sc["Objects"];
+	if(!eobjects || !eobjects->Compound()) {
+		DOMError("no Objects dictionary found");
+	}
+
+	// add a dummy entry to represent the Model::RootNode object (id 0),
+	// which is only indirectly defined in the input file
+	objects[0] = new LazyObject(0L, *eobjects, *this);
+
+	const Scope& sobjects = *eobjects->Compound();
+	BOOST_FOREACH(const ElementMap::value_type& el, sobjects.Elements()) {
+		
+		// extract ID 
+		const TokenList& tok = el.second->Tokens();
+		
+		if (tok.empty()) {
+			DOMError("expected ID after object key",el.second);
+		}
+
+		const char* err;
+
+		const uint64_t id = ParseTokenAsID(*tok[0], err);
+		if(err) {
+			DOMError(err,el.second);
+		}
+
+		// id=0 is normally implicit
+		if(id == 0L) {
+			DOMError("encountered object with implicitly defined id 0",el.second);
+		}
+
+		if(objects.find(id) != objects.end()) {
+			DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second);
+		}
+
+		objects[id] = new LazyObject(id, *el.second, *this);
+
+		// grab all animation stacks upfront since there is no listing of them
+		if(!strcmp(el.first.c_str(),"AnimationStack")) {
+			animationStacks.push_back(id);
+		}
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadPropertyTemplates()
+{
+	const Scope& sc = parser.GetRootScope();
+	// read property templates from "Definitions" section
+	const Element* const edefs = sc["Definitions"];
+	if(!edefs || !edefs->Compound()) {
+		DOMWarning("no Definitions dictionary found");
+		return;
+	}
+
+	const Scope& sdefs = *edefs->Compound();
+	const ElementCollection otypes = sdefs.GetCollection("ObjectType");
+	for(ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
+		const Element& el = *(*it).second;
+		const Scope* sc = el.Compound();
+		if(!sc) {
+			DOMWarning("expected nested scope in ObjectType, ignoring",&el);
+			continue;
+		}
+
+		const TokenList& tok = el.Tokens();
+		if(tok.empty()) {
+			DOMWarning("expected name for ObjectType element, ignoring",&el);
+			continue;
+		}
+
+		const std::string& oname = ParseTokenAsString(*tok[0]);
+
+		const ElementCollection templs = sc->GetCollection("PropertyTemplate");
+		for(ElementMap::const_iterator it = templs.first; it != templs.second; ++it) {
+			const Element& el = *(*it).second;
+			const Scope* sc = el.Compound();
+			if(!sc) {
+				DOMWarning("expected nested scope in PropertyTemplate, ignoring",&el);
+				continue;
+			}
+
+			const TokenList& tok = el.Tokens();
+			if(tok.empty()) {
+				DOMWarning("expected name for PropertyTemplate element, ignoring",&el);
+				continue;
+			}
+
+			const std::string& pname = ParseTokenAsString(*tok[0]);
+
+			const Element* Properties70 = (*sc)["Properties70"];
+			if(Properties70) {
+				boost::shared_ptr<const PropertyTable> props = boost::make_shared<const PropertyTable>(
+					*Properties70,boost::shared_ptr<const PropertyTable>(static_cast<const PropertyTable*>(NULL))
+				);
+
+				templates[oname+"."+pname] = props;
+			}
+		}
+	}
+}
+
+
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadConnections()
+{
+	const Scope& sc = parser.GetRootScope();
+	// read property templates from "Definitions" section
+	const Element* const econns = sc["Connections"];
+	if(!econns || !econns->Compound()) {
+		DOMError("no Connections dictionary found");
+	}
+
+	uint64_t insertionOrder = 0l;
+
+	const Scope& sconns = *econns->Compound();
+	const ElementCollection conns = sconns.GetCollection("C");
+	for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
+		const Element& el = *(*it).second;
+		const std::string& type = ParseTokenAsString(GetRequiredToken(el,0));
+		const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1));
+		const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2));
+
+		// OO = object-object connection
+		// OP = object-property connection, in which case the destination property follows the object ID
+		const std::string& prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el,3)) : "");
+
+		if(objects.find(src) == objects.end()) {
+			DOMWarning("source object for connection does not exist",&el);
+			continue;
+		}
+
+		// dest may be 0 (root node) but we added a dummy object before
+		if(objects.find(dest) == objects.end()) {
+			DOMWarning("destination object for connection does not exist",&el);
+			continue;
+		}
+
+		// add new connection
+		const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this);
+		src_connections.insert(ConnectionMap::value_type(src,c));  
+		dest_connections.insert(ConnectionMap::value_type(dest,c));  
+	}
+}
+
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<const AnimationStack*>& Document::AnimationStacks() const
+{
+	if (!animationStacksResolved.empty() || !animationStacks.size()) {
+		return animationStacksResolved;
+	}
+
+	animationStacksResolved.reserve(animationStacks.size());
+	BOOST_FOREACH(uint64_t id, animationStacks) {
+		LazyObject* const lazy = GetObject(id);
+		const AnimationStack* stack;
+		if(!lazy || !(stack = lazy->Get<AnimationStack>())) {
+			DOMWarning("failed to read AnimationStack object");
+			continue;
+		}
+		animationStacksResolved.push_back(stack);
+	}
+
+	return animationStacksResolved;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+LazyObject* Document::GetObject(uint64_t id) const
+{
+	ObjectMap::const_iterator it = objects.find(id);
+	return it == objects.end() ? NULL : (*it).second;
+}
+
+#define MAX_CLASSNAMES 6
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, 
+	const ConnectionMap& conns) const
+{
+	std::vector<const Connection*> temp;
+
+	const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range = 
+		conns.equal_range(id);
+
+	temp.reserve(std::distance(range.first,range.second));
+	for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
+		temp.push_back((*it).second);
+	}
+
+	std::sort(temp.begin(), temp.end(), std::mem_fun(&Connection::Compare));
+
+	return temp; // NRVO should handle this
+}
+
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bool is_src, 
+	const ConnectionMap& conns, 
+	const char* const* classnames, 
+	size_t count) const
+
+{
+	ai_assert(classnames);
+	ai_assert(count != 0 && count <= MAX_CLASSNAMES);
+
+	size_t lenghts[MAX_CLASSNAMES];
+
+	const size_t c = count;
+	for (size_t i = 0; i < c; ++i) {
+		lenghts[i] = strlen(classnames[i]);
+	}
+
+	std::vector<const Connection*> temp;
+
+	const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range = 
+		conns.equal_range(id);
+
+	temp.reserve(std::distance(range.first,range.second));
+	for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
+		const Token& key = (is_src 
+			? (*it).second->LazyDestinationObject()
+			: (*it).second->LazySourceObject()
+		).GetElement().KeyToken();
+
+		const char* obtype = key.begin();
+
+		for (size_t i = 0; i < c; ++i) {
+			ai_assert(classnames[i]);
+			if(static_cast<size_t>(std::distance(key.begin(),key.end())) == lenghts[i] && !strncmp(classnames[i],obtype,lenghts[i])) {
+				obtype = NULL;
+				break;
+			}
+		}
+
+		if(obtype) {
+			continue;
+		}
+
+		temp.push_back((*it).second);
+	}
+
+	std::sort(temp.begin(), temp.end(), std::mem_fun(&Connection::Compare));
+	return temp; // NRVO should handle this
+}
+
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source) const
+{
+	return GetConnectionsSequenced(source, ConnectionsBySource());
+}
+
+
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t dest, 
+	const char* classname) const
+{
+	const char* arr[] = {classname};
+	return GetConnectionsBySourceSequenced(dest, arr,1);
+}
+
+
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source, 
+	const char* const* classnames, size_t count) const
+{
+	return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest, 
+	const char* classname) const
+{
+	const char* arr[] = {classname};
+	return GetConnectionsByDestinationSequenced(dest, arr,1);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const
+{
+	return GetConnectionsSequenced(dest, ConnectionsByDestination());
+}
+
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest, 
+	const char* const* classnames, size_t count) const
+
+{
+	return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count);
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Connection::Connection(uint64_t insertionOrder,  uint64_t src, uint64_t dest, const std::string& prop, 
+	const Document& doc)
+
+: insertionOrder(insertionOrder)
+, prop(prop)
+, src(src)
+, dest(dest)
+, doc(doc)
+{
+	ai_assert(doc.Objects().find(src) != doc.Objects().end());
+	// dest may be 0 (root node)
+	ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
+}
+
+
+// ------------------------------------------------------------------------------------------------
+Connection::~Connection()
+{
+
+}
+
+
+// ------------------------------------------------------------------------------------------------
+LazyObject& Connection::LazySourceObject() const
+{
+	LazyObject* const lazy = doc.GetObject(src);
+	ai_assert(lazy);
+	return *lazy;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+LazyObject& Connection::LazyDestinationObject() const
+{
+	LazyObject* const lazy = doc.GetObject(dest);
+	ai_assert(lazy);
+	return *lazy;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+const Object* Connection::SourceObject() const
+{
+	LazyObject* const lazy = doc.GetObject(src);
+	ai_assert(lazy);
+	return lazy->Get();
+}
+
+
+// ------------------------------------------------------------------------------------------------
+const Object* Connection::DestinationObject() const
+{
+	LazyObject* const lazy = doc.GetObject(dest);
+	ai_assert(lazy);
+	return lazy->Get();
+}
+
+} // !FBX
+} // !Assimp
+
+#endif
+

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