2
0
Эх сурвалжийг харах

Merge pull request #1 from assimp/master

Update
Madrich 10 жил өмнө
parent
commit
45988cd238
100 өөрчлөгдсөн 4390 нэмэгдсэн , 895 устгасан
  1. 19 0
      .gitignore
  2. 25 7
      .travis.yml
  3. 23 0
      CHANGES
  4. 148 143
      CMakeLists.txt
  5. 3 0
      CREDITS
  6. 8 2
      Readme.md
  7. 11 11
      assimp-config.cmake.in
  8. 1 0
      assimp.pc.in
  9. 64 0
      cmake-modules/AddGTest.cmake
  10. 560 0
      code/3DSExporter.cpp
  11. 94 0
      code/3DSExporter.h
  12. 1 1
      code/3DSHelper.h
  13. 2 2
      code/3DSLoader.h
  14. 2 2
      code/ASEParser.cpp
  15. 765 0
      code/AssbinExporter.cpp
  16. 49 0
      code/AssbinExporter.h
  17. 672 0
      code/AssbinLoader.cpp
  18. 94 0
      code/AssbinLoader.h
  19. 24 1
      code/Assimp.cpp
  20. 2 0
      code/AssimpCExport.cpp
  21. 638 0
      code/AssxmlExporter.cpp
  22. 49 0
      code/AssxmlExporter.h
  23. 1 1
      code/B3DImporter.cpp
  24. 5 0
      code/BaseImporter.h
  25. 28 1
      code/BlenderBMesh.cpp
  26. 1 0
      code/BlenderBMesh.h
  27. 6 2
      code/BlenderLoader.cpp
  28. 2 2
      code/BlenderTessellator.cpp
  29. 3 3
      code/BoostWorkaround/boost/tuple/tuple.hpp
  30. 33 1
      code/CMakeLists.txt
  31. 3 3
      code/CalcTangentsProcess.cpp
  32. 39 31
      code/ColladaExporter.cpp
  33. 0 1
      code/ColladaHelper.h
  34. 7 8
      code/ColladaLoader.cpp
  35. 4 1
      code/ColladaLoader.h
  36. 87 37
      code/ColladaParser.cpp
  37. 10 1
      code/ColladaParser.h
  38. 3 3
      code/ComputeUVMappingProcess.cpp
  39. 19 9
      code/ConvertToLHProcess.cpp
  40. 2 2
      code/DeboneProcess.cpp
  41. 18 11
      code/DefaultIOStream.h
  42. 25 5
      code/Exporter.cpp
  43. 1 1
      code/FBXAnimation.cpp
  44. 6 5
      code/FBXBinaryTokenizer.cpp
  45. 11 14
      code/FBXConverter.cpp
  46. 10 5
      code/FBXDocument.cpp
  47. 2 2
      code/FBXDocument.h
  48. 3 1
      code/FBXImporter.cpp
  49. 1 1
      code/FBXMaterial.cpp
  50. 4 2
      code/FBXMeshGeometry.cpp
  51. 24 34
      code/FBXParser.cpp
  52. 2 2
      code/FBXProperties.cpp
  53. 2 4
      code/FBXProperties.h
  54. 5 4
      code/FBXTokenizer.cpp
  55. 2 4
      code/FindInstancesProcess.cpp
  56. 1 1
      code/FindInvalidDataProcess.cpp
  57. 2 2
      code/FixNormalsStep.cpp
  58. 2 2
      code/GenVertexNormalsProcess.cpp
  59. 1 1
      code/GenVertexNormalsProcess.h
  60. 6 6
      code/IFCBoolean.cpp
  61. 12 12
      code/IFCCurve.cpp
  62. 4 4
      code/IFCGeometry.cpp
  63. 39 39
      code/IFCOpenings.cpp
  64. 2 2
      code/IFCProfile.cpp
  65. 3 3
      code/IFCReaderGen.cpp
  66. 1 1
      code/IFCUtil.cpp
  67. 1 1
      code/IFCUtil.h
  68. 24 22
      code/IFF.h
  69. 3 3
      code/IRRLoader.cpp
  70. 13 4
      code/Importer.cpp
  71. 6 0
      code/ImporterRegistry.cpp
  72. 1 3
      code/JoinVerticesProcess.h
  73. 58 40
      code/LWOBLoader.cpp
  74. 57 45
      code/LWOLoader.cpp
  75. 9 3
      code/LWOLoader.h
  76. 37 37
      code/LWOMaterial.cpp
  77. 3 3
      code/LWSLoader.cpp
  78. 10 17
      code/LimitBoneWeightsProcess.cpp
  79. 3 3
      code/MD3FileData.h
  80. 1 1
      code/MD5Parser.h
  81. 36 32
      code/ObjExporter.cpp
  82. 38 29
      code/ObjFileImporter.cpp
  83. 2 2
      code/ObjFileImporter.h
  84. 24 28
      code/ObjFileMtlImporter.cpp
  85. 42 32
      code/ObjFileParser.cpp
  86. 15 35
      code/ObjTools.h
  87. 5 6
      code/OgreBinarySerializer.cpp
  88. 6 2
      code/OgreBinarySerializer.h
  89. 4 4
      code/OgreImporter.cpp
  90. 14 12
      code/OgreStructs.cpp
  91. 45 32
      code/OptimizeMeshes.cpp
  92. 60 27
      code/ParsingUtils.h
  93. 103 6
      code/PlyExporter.cpp
  94. 4 1
      code/PlyExporter.h
  95. 1 1
      code/PlyParser.cpp
  96. 6 6
      code/PolyTools.h
  97. 10 8
      code/Q3BSPFileImporter.cpp
  98. 5 5
      code/Q3BSPZipArchive.cpp
  99. 2 2
      code/STLLoader.cpp
  100. 46 0
      code/SceneCombiner.cpp

+ 19 - 0
.gitignore

@@ -32,3 +32,22 @@ test/results
 
 # Python
 __pycache__
+*.log
+*.vcxproj
+*.filters
+*.tlog
+Assimp.sdf
+test/gtest/tmp/gtest-gitupdate.cmake
+test/gtest/tmp/gtest-gitclone.cmake
+test/gtest/tmp/gtest-cfgcmd.txt.in
+test/gtest/tmp/gtest-cfgcmd.txt
+test/gtest/src/gtest-stamp/gtest-download.cmake
+test/gtest/src/gtest-stamp/gtest-configure.cmake
+test/gtest/src/gtest-stamp/gtest-build.cmake
+test/gtest/src/gtest-stamp/Debug/gtest-patch
+*.cache
+test/gtest/src/gtest-stamp/Debug/gtest-build
+*.suo
+*.lib
+test/gtest/src/gtest-stamp/Debug/
+tools/assimp_view/assimp_viewer.vcxproj.user

+ 25 - 7
.travis.yml

@@ -1,11 +1,16 @@
 before_install:
-  - sudo apt-get install cmake
+  - sudo apt-get install cmake python3
 
 env:
-  - TRAVIS_NO_EXPORT=YES
-  - TRAVIS_NO_EXPORT=NO
-  - TRAVIS_STATIC_BUILD=ON
-  - TRAVIS_STATIC_BUILD=OFF
+    matrix:
+    - LINUX=1 TRAVIS_NO_EXPORT=YES
+    - LINUX=1 TRAVIS_NO_EXPORT=NO
+    - LINUX=1 TRAVIS_STATIC_BUILD=ON
+    - LINUX=1 TRAVIS_STATIC_BUILD=OFF
+    - WINDOWS=1 TRAVIS_NO_EXPORT=YES
+    - WINDOWS=1 TRAVIS_NO_EXPORT=NO
+    - WINDOWS=1 TRAVIS_STATIC_BUILD=ON
+    - WINDOWS=1 TRAVIS_STATIC_BUILD=OFF
 
 language: cpp
 
@@ -13,7 +18,20 @@ compiler:
   - gcc
   - clang
 
-script: cmake -G "Unix Makefiles" -DASSIMP_ENABLE_BOOST_WORKAROUND=YES -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -STATIC_BUILD=$TRAVIS_STATIC_BUILD && make
-
+install:
+  - if [ $WINDOWS ]; then travis_retry sudo apt-get install -q -y gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 binutils-mingw-w64-x86-64; fi 
 
+script:
+  - cmake -G "Unix Makefiles" -DASSIMP_ENABLE_BOOST_WORKAROUND=YES -DASSIMP_NO_EXPORT=$TRAVIS_NO_EXPORT -STATIC_BUILD=$TRAVIS_STATIC_BUILD
+  - make
+  - sudo make install
+  - sudo ldconfig
+  - cd test/unit
+  - ../../bin/unit
+  - cd ../regression
+  - chmod 755 run.py
+  - ./run.py
+  - echo "=========================================================="
+  - echo "REGRESSION TEST FAILS (results/run_regression_suite_failures.csv)"
+  - cat ../results/run_regression_suite_failures.csv
 

+ 23 - 0
CHANGES

@@ -2,6 +2,29 @@
 CHANGELOG
 ----------------------------------------------------------------------
 
+3.1.1 (2014-06-15)
+
+FEATURES:
+   - Support for FBX 2013 and newer, binary and ASCII (this is partly
+     work from Google Summer of Code 2012)
+   - Support for OGRE binary mesh and skeleton format
+   - Updated BLEND support for newer Blender versions
+   - Support for arbitrary meta data, used to hold FBX and DAE metadata
+   - OBJ Export now produces smaller files
+   - Meshes can now have names, this is supported by the major importers
+   - Improved IFC geometry generation
+   - M3 support has been removed
+
+FIXES/HOUSEKEEPING:
+    - Hundreds of bugfixes in all parts of the library
+    - CMake is now the primary build system
+    
+API COMPATIBILITY:
+    - 3.1.1 is not binary compatible to 3.0 due to aiNode::mMetaData
+      and aiMesh::mName
+    - Export interface has been cleaned up and unified
+    - Other than that no relevant changes
+   
 
 3.0 (2012-07-07)
 

+ 148 - 143
CMakeLists.txt

@@ -3,7 +3,7 @@ PROJECT( Assimp )
 
 # Define here the needed parameters
 set (ASSIMP_VERSION_MAJOR 3)
-set (ASSIMP_VERSION_MINOR 0)
+set (ASSIMP_VERSION_MINOR 1)
 set (ASSIMP_VERSION_PATCH 1) # subversion revision?
 set (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH})
 set (ASSIMP_SOVERSION 3)
@@ -13,30 +13,30 @@ set(ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used f
 
 # Get the current working branch
 execute_process(
-  COMMAND git rev-parse --abbrev-ref HEAD
-  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
-  OUTPUT_VARIABLE GIT_BRANCH
-  OUTPUT_STRIP_TRAILING_WHITESPACE
+    COMMAND git rev-parse --abbrev-ref HEAD
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    OUTPUT_VARIABLE GIT_BRANCH
+    OUTPUT_STRIP_TRAILING_WHITESPACE
 )
 
 # Get the latest abbreviated commit hash of the working branch
 execute_process(
-  COMMAND git log -1 --format=%h
-  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
-  OUTPUT_VARIABLE GIT_COMMIT_HASH
-  OUTPUT_STRIP_TRAILING_WHITESPACE
+    COMMAND git log -1 --format=%h
+    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    OUTPUT_VARIABLE GIT_COMMIT_HASH
+    OUTPUT_STRIP_TRAILING_WHITESPACE
 )
 
 if(NOT GIT_COMMIT_HASH)
-  set(GIT_COMMIT_HASH 0)
+    set(GIT_COMMIT_HASH 0)
 endif(NOT GIT_COMMIT_HASH)
 
 configure_file(
-  ${CMAKE_SOURCE_DIR}/revision.h.in
-  ${CMAKE_BINARY_DIR}/revision.h
+    ${CMAKE_CURRENT_SOURCE_DIR}/revision.h.in
+    ${CMAKE_CURRENT_BINARY_DIR}/revision.h
 )
 
-include_directories(${CMAKE_BINARY_DIR})
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
 
 option(ASSIMP_OPT_BUILD_PACKAGES "Set to ON to generate CPack configuration files and packaging targets" OFF)
 set(CMAKE_MODULE_PATH       "${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules" )
@@ -45,14 +45,16 @@ set(LIBASSIMP-DEV_COMPONENT "libassimp${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_M
 set(CPACK_COMPONENTS_ALL assimp-bin ${LIBASSIMP_COMPONENT} ${LIBASSIMP-DEV_COMPONENT} assimp-dev)
 set(ASSIMP_LIBRARY_SUFFIX "" CACHE STRING "Suffix to append to library names")
 
+option(ASSIMP_ANDROID_JNIIOSYSTEM "Android JNI IOSystem support is active" OFF)
+
 if((CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) AND NOT CMAKE_COMPILER_IS_MINGW)
-  add_definitions(-fPIC) # this is a very important switch and some libraries seem now to have it....
-  # hide all not-exported symbols
-  add_definitions( -fvisibility=hidden -Wall )
+    add_definitions(-fPIC) # this is a very important switch and some libraries seem now to have it....
+    # hide all not-exported symbols
+    add_definitions( -fvisibility=hidden -Wall )
 elseif(MSVC)
-  # enable multi-core compilation with MSVC
-  add_definitions(/MP)
-endif()
+    # enable multi-core compilation with MSVC
+    add_definitions(/MP)
+    endif()
 
 INCLUDE (FindPkgConfig)
 INCLUDE_DIRECTORIES( include )
@@ -64,57 +66,54 @@ INCLUDE (PrecompiledHeader)
 # source tree. During an out-of-source build, however, do not litter this
 # directory, since that is probably what the user wanted to avoid.
 IF ( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR )
-	SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/lib" )
-	SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/lib" )
-	SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/bin" )
+    SET( CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/lib" )
+    SET( CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/lib" )
+    SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_HOME_DIRECTORY}/bin" )
 ENDIF ( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR )
 
 # Cache these to allow the user to override them manually.
 SET( ASSIMP_LIB_INSTALL_DIR "lib" CACHE PATH
-	"Path the built library files are installed to." )
+    "Path the built library files are installed to." )
 SET( ASSIMP_INCLUDE_INSTALL_DIR "include" CACHE PATH
-	"Path the header files are installed to." )
+    "Path the header files are installed to." )
 SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE PATH
-	"Path the tool executables are installed to." )
+    "Path the tool executables are installed to." )
 
 SET(ASSIMP_DEBUG_POSTFIX "d" CACHE STRING "Debug Postfitx for lib, samples and tools")
 
-# Allow the user to build a static library
+# Allow the user to build a shared or static library
 option ( BUILD_SHARED_LIBS "Build a shared version of the library" ON )
 
-# Generate a pkg-config .pc for the Assimp library.
-CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/assimp.pc.in" "${PROJECT_BINARY_DIR}/assimp.pc" @ONLY )
-INSTALL( FILES "${PROJECT_BINARY_DIR}/assimp.pc" DESTINATION ${ASSIMP_LIB_INSTALL_DIR}/pkgconfig/ COMPONENT ${LIBASSIMP-DEV_COMPONENT})
-
 # Only generate this target if no higher-level project already has
 IF (NOT TARGET uninstall)
-	# add make uninstall capability
-	configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
-	add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
+    # add make uninstall capability
+    configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
+    add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
 ENDIF()
 
 # Globally enable Boost resp. the Boost workaround – it is also needed by the
 # tools which include the Assimp headers.
-SET ( ASSIMP_ENABLE_BOOST_WORKAROUND ON CACHE BOOL
-	"If a simple implementation of the used Boost functions is used. Slightly reduces functionality, but enables builds without Boost available."
+option ( ASSIMP_ENABLE_BOOST_WORKAROUND
+    "If a simple implementation of the used Boost functions is used. Slightly reduces functionality, but enables builds without Boost available."
+    ON
 )
 IF ( ASSIMP_ENABLE_BOOST_WORKAROUND )
-	INCLUDE_DIRECTORIES( code/BoostWorkaround )
-	ADD_DEFINITIONS( -DASSIMP_BUILD_BOOST_WORKAROUND )
-	MESSAGE( STATUS "Building a non-boost version of Assimp." )
+    INCLUDE_DIRECTORIES( code/BoostWorkaround )
+    ADD_DEFINITIONS( -DASSIMP_BUILD_BOOST_WORKAROUND )
+    MESSAGE( STATUS "Building a non-boost version of Assimp." )
 ELSE ( ASSIMP_ENABLE_BOOST_WORKAROUND )
-	SET( Boost_DETAILED_FAILURE_MSG ON )
-	SET( Boost_ADDITIONAL_VERSIONS "1.47" "1.47.0" "1.48.0" "1.48" "1.49" "1.49.0" "1.50" "1.50.0" "1.51" "1.51.0" "1.52.0" "1.53.0" "1.54.0")	
-	FIND_PACKAGE( Boost )
-	IF ( NOT Boost_FOUND )
-		MESSAGE( FATAL_ERROR
-			"Boost libraries (http://www.boost.org/) not found. "
-			"You can build a non-boost version of Assimp with slightly reduced "
+    SET( Boost_DETAILED_FAILURE_MSG ON )
+    SET( Boost_ADDITIONAL_VERSIONS "1.47" "1.47.0" "1.48.0" "1.48" "1.49" "1.49.0" "1.50" "1.50.0" "1.51" "1.51.0" "1.52.0" "1.53.0" "1.54.0" "1.55" "1.55.0" "1.56" "1.56.0" "1.57" "1.57.0" )	
+    FIND_PACKAGE( Boost )
+    IF ( NOT Boost_FOUND )
+        MESSAGE( FATAL_ERROR
+            "Boost libraries (http://www.boost.org/) not found. "
+            "You can build a non-boost version of Assimp with slightly reduced "
       "functionality by specifying -DASSIMP_ENABLE_BOOST_WORKAROUND=ON."
-		)
-	ENDIF ( NOT Boost_FOUND )
+        )
+    ENDIF ( NOT Boost_FOUND )
 
-	INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIRS} )
+    INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIRS} )
 ENDIF ( ASSIMP_ENABLE_BOOST_WORKAROUND )
 
 # cmake configuration files
@@ -122,146 +121,152 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in"         "${C
 configure_file("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config-version.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" @ONLY IMMEDIATE)
 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake"             "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" DESTINATION "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}" COMPONENT ${LIBASSIMP-DEV_COMPONENT})
 
-SET ( ASSIMP_NO_EXPORT OFF CACHE BOOL
-	"Disable Assimp's export functionality." 
+option ( ASSIMP_NO_EXPORT
+    "Disable Assimp's export functionality."
+    OFF
 )
 
+if( CMAKE_COMPILER_IS_GNUCXX )
+  set(LIBSTDC++_LIBRARIES -lstdc++)
+endif( CMAKE_COMPILER_IS_GNUCXX )
+
 # Search for external dependencies, and build them from source if not found
 # Search for zlib
 find_package(ZLIB)
 if( NOT ZLIB_FOUND )
-  message(STATUS "compiling zlib from souces")
-  include(CheckIncludeFile)
-  include(CheckTypeSize)
-  include(CheckFunctionExists)
-  # compile from sources
-  add_subdirectory(contrib/zlib)
-  set(ZLIB_FOUND 1)
-  set(ZLIB_LIBRARIES zlibstatic)
-  set(ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/contrib/zlib ${CMAKE_CURRENT_BINARY_DIR}/contrib/zlib)
+    message(STATUS "compiling zlib from souces")
+    include(CheckIncludeFile)
+    include(CheckTypeSize)
+    include(CheckFunctionExists)
+    # compile from sources
+    add_subdirectory(contrib/zlib)
+    set(ZLIB_FOUND 1)
+    set(ZLIB_LIBRARIES zlibstatic)
+    set(ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/contrib/zlib ${CMAKE_CURRENT_BINARY_DIR}/contrib/zlib)
 else(NOT ZLIB_FOUND)
-  ADD_DEFINITIONS(-DASSIMP_BUILD_NO_OWN_ZLIB)
+    ADD_DEFINITIONS(-DASSIMP_BUILD_NO_OWN_ZLIB)
+    set(ZLIB_LIBRARIES_LINKED -lz)
 endif(NOT ZLIB_FOUND)
 INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
 
 # Search for unzip
 if (PKG_CONFIG_FOUND)
-	PKG_CHECK_MODULES(UNZIP minizip)
+    PKG_CHECK_MODULES(UNZIP minizip)
 endif (PKG_CONFIG_FOUND)
 
 IF ( ASSIMP_NO_EXPORT )
-	ADD_DEFINITIONS( -DASSIMP_BUILD_NO_EXPORT)
-	MESSAGE( STATUS "Build an import-only version of Assimp." )
+    ADD_DEFINITIONS( -DASSIMP_BUILD_NO_EXPORT)
+    MESSAGE( STATUS "Build an import-only version of Assimp." )
 ENDIF( ASSIMP_NO_EXPORT )
 
-# if(CMAKE_CL_64)
-# 	set(ASSIMP_BUILD_ARCHITECTURE "amd64")
-# else(CMAKE_CL_64)
-# 	set(ASSIMP_BUILD_ARCHITECTURE "x86")
-# endif(CMAKE_CL_64)
 SET ( ASSIMP_BUILD_ARCHITECTURE "" CACHE STRING 
-	"describe the current architecture."
+    "describe the current architecture."
 )
 IF    ( ASSIMP_BUILD_ARCHITECTURE STREQUAL "")
 ELSE  ( ASSIMP_BUILD_ARCHITECTURE STREQUAL "")
-	ADD_DEFINITIONS ( -D'ASSIMP_BUILD_ARCHITECTURE="${ASSIMP_BUILD_ARCHITECTURE}"' )
+    ADD_DEFINITIONS ( -D'ASSIMP_BUILD_ARCHITECTURE="${ASSIMP_BUILD_ARCHITECTURE}"' )
 ENDIF ( ASSIMP_BUILD_ARCHITECTURE STREQUAL "")
 
 # ${CMAKE_GENERATOR}
 SET ( ASSIMP_BUILD_COMPILER "" CACHE STRING 
-	"describe the current compiler."
+    "describe the current compiler."
 )
 IF    ( ASSIMP_BUILD_COMPILER STREQUAL "")
 ELSE  ( ASSIMP_BUILD_COMPILER STREQUAL "")
-	ADD_DEFINITIONS ( -D'ASSIMP_BUILD_COMPILER="${ASSIMP_BUILD_COMPILER}"' )
+    ADD_DEFINITIONS ( -D'ASSIMP_BUILD_COMPILER="${ASSIMP_BUILD_COMPILER}"' )
 ENDIF ( ASSIMP_BUILD_COMPILER STREQUAL "")
 
 MARK_AS_ADVANCED ( ASSIMP_BUILD_ARCHITECTURE ASSIMP_BUILD_COMPILER )
 
 ADD_SUBDIRECTORY( code/ )
-SET ( ASSIMP_BUILD_ASSIMP_TOOLS ON CACHE BOOL
-	"If the supplementary tools for Assimp are built in addition to the library."
+option ( ASSIMP_BUILD_ASSIMP_TOOLS
+    "If the supplementary tools for Assimp are built in addition to the library."
+    ON
 )
 IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
-	IF ( WIN32 )
-		ADD_SUBDIRECTORY( tools/assimp_view/ )
-	ENDIF ( WIN32 )
-	ADD_SUBDIRECTORY( tools/assimp_cmd/ )
+    IF ( WIN32 )
+        ADD_SUBDIRECTORY( tools/assimp_view/ )
+    ENDIF ( WIN32 )
+    ADD_SUBDIRECTORY( tools/assimp_cmd/ )
 ENDIF ( ASSIMP_BUILD_ASSIMP_TOOLS )
 
-SET ( ASSIMP_BUILD_SAMPLES OFF CACHE BOOL
-	"If the official samples are built as well (needs Glut)."
+option ( ASSIMP_BUILD_SAMPLES
+    "If the official samples are built as well (needs Glut)."
+    OFF
 )
 
 IF ( ASSIMP_BUILD_SAMPLES)
-	IF ( WIN32 )
-		ADD_SUBDIRECTORY( samples/SimpleTexturedOpenGL/ )
-	ENDIF ( WIN32 )
-	ADD_SUBDIRECTORY( samples/SimpleOpenGL/ )
+    IF ( WIN32 )
+        ADD_SUBDIRECTORY( samples/SimpleTexturedOpenGL/ )
+    ENDIF ( WIN32 )
+    ADD_SUBDIRECTORY( samples/SimpleOpenGL/ )
 ENDIF ( ASSIMP_BUILD_SAMPLES )
 
-IF ( WIN32 )
-	SET ( ASSIMP_BUILD_TESTS ON CACHE BOOL
-		"If the test suite for Assimp is built in addition to the library."
-	)
-	
-	IF ( ASSIMP_BUILD_TESTS )
-		ADD_SUBDIRECTORY( test/ )
-	ENDIF ( ASSIMP_BUILD_TESTS )
-ENDIF ( WIN32 )
+option ( ASSIMP_BUILD_TESTS
+    "If the test suite for Assimp is built in addition to the library."
+    ON
+)
+    
+IF ( ASSIMP_BUILD_TESTS )
+    ADD_SUBDIRECTORY( test/ )
+ENDIF ( ASSIMP_BUILD_TESTS )
 
 IF(MSVC)
-	SET ( ASSIMP_INSTALL_PDB ON CACHE BOOL
-		"Install MSVC debug files."
-	)
+    option ( ASSIMP_INSTALL_PDB
+        "Install MSVC debug files."
+        ON
+    )
 ENDIF(MSVC)
 
+# Generate a pkg-config .pc for the Assimp library.
+CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/assimp.pc.in" "${PROJECT_BINARY_DIR}/assimp.pc" @ONLY )
+INSTALL( FILES "${PROJECT_BINARY_DIR}/assimp.pc" DESTINATION ${ASSIMP_LIB_INSTALL_DIR}/pkgconfig/ COMPONENT ${LIBASSIMP-DEV_COMPONENT})
+
 if(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES)
-  # Packing information
-  set(CPACK_PACKAGE_NAME                    "assimp{ASSIMP_VERSION_MAJOR}")
-  set(CPACK_PACKAGE_CONTACT "" CACHE STRING "Package maintainer and PGP signer.")
-  set(CPACK_PACKAGE_VENDOR                  "http://assimp.sourceforge.net/")
-  set(CPACK_PACKAGE_DISPLAY_NAME            "Assimp ${ASSIMP_VERSION}")
-  set(CPACK_PACKAGE_DESCRIPTION_SUMMARY     " - Open Asset Import Library ${ASSIMP_VERSION}")
-  set(CPACK_PACKAGE_VERSION                 "${ASSIMP_VERSION}.${ASSIMP_PACKAGE_VERSION}" )
-  set(CPACK_PACKAGE_VERSION_MAJOR           "${ASSIMP_VERSION_MAJOR}")
-  set(CPACK_PACKAGE_VERSION_MINOR           "${ASSIMP_VERSION_MINOR}")
-  set(CPACK_PACKAGE_VERSION_PATCH           "${ASSIMP_VERSION_PATCH}")
-  set(CPACK_PACKAGE_INSTALL_DIRECTORY       "assimp${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}")
-  #set(CPACK_PACKAGE_DESCRIPTION_FILE       "${CMAKE_CURRENT_SOURCE_DIR}/description")
-  set(CPACK_RESOURCE_FILE_LICENSE           "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
-
-  string(TOUPPER ${LIBASSIMP_COMPONENT}     "LIBASSIMP_COMPONENT_UPPER")
-  string(TOUPPER ${LIBASSIMP-DEV_COMPONENT} "LIBASSIMP-DEV_COMPONENT_UPPER")
-
-  set(CPACK_COMPONENT_ASSIMP-BIN_DISPLAY_NAME                       "tools")
-  set(CPACK_COMPONENT_ASSIMP-BIN_DEPENDS                            "${LIBASSIMP_COMPONENT}" )
-  set(CPACK_COMPONENT_${LIBASSIMP_COMPONENT_UPPER}_DISPLAY_NAME     "libraries")
-  set(CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT_UPPER}_DISPLAY_NAME "common headers and installs")
-  set(CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT_UPPER}_DEPENDS $    "{LIBASSIMP_COMPONENT}" )
-  set(CPACK_COMPONENT_ASSIMP-DEV_DISPLAY_NAME                       "${CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT}_DISPLAY_NAME}" )
-  set(CPACK_COMPONENT_ASSIMP-DEV_DEPENDS                            "${LIBASSIMP-DEV_COMPONENT}" )
-  set(CPACK_DEBIAN_BUILD_DEPENDS debhelper cmake libboost-dev libboost-thread-dev libboost-math-dev zlib1g-dev pkg-config)
-
-  # debian
-  set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
-  set(CPACK_DEBIAN_CMAKE_OPTIONS    "-DBUILD_ASSIMP_SAMPLES:BOOL=${ASSIMP_BUILD_SAMPLES}")
-  set(CPACK_DEBIAN_PACKAGE_SECTION  "libs" )
-  set(CPACK_DEBIAN_PACKAGE_DEPENDS  "${CPACK_COMPONENTS_ALL}")
-  set(CPACK_DEBIAN_PACKAGE_SUGGESTS)
-  set(CPACK_DEBIAN_PACKAGE_NAME     "assimp")
-  set(CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES contrib/cppunit-1.12.1 contrib/cppunit_note.txt contrib/zlib workspaces test doc obj samples packaging)
-  set(CPACK_DEBIAN_PACKAGE_SOURCE_COPY svn export --force)
-  set(CPACK_DEBIAN_CHANGELOG)
-  execute_process(COMMAND lsb_release -is
-    OUTPUT_VARIABLE _lsb_distribution OUTPUT_STRIP_TRAILING_WHITESPACE
-    RESULT_VARIABLE _lsb_release_failed)
-  set(CPACK_DEBIAN_DISTRIBUTION_NAME ${_lsb_distribution} CACHE STRING "Name of the distrubiton")
-  string(TOLOWER ${CPACK_DEBIAN_DISTRIBUTION_NAME} CPACK_DEBIAN_DISTRIBUTION_NAME)
-  if( ${CPACK_DEBIAN_DISTRIBUTION_NAME} STREQUAL "ubuntu" )
-    set(CPACK_DEBIAN_DISTRIBUTION_RELEASES lucid maverick natty oneiric precise CACHE STRING "Release code-names of the distrubiton release")
-  endif()
-  set(DPUT_HOST "" CACHE STRING "PPA repository to upload the debian sources")
-  include(CPack)
-  include(DebSourcePPA)
+    # Packing information
+    set(CPACK_PACKAGE_NAME                    "assimp{ASSIMP_VERSION_MAJOR}")
+    set(CPACK_PACKAGE_CONTACT "" CACHE STRING "Package maintainer and PGP signer.")
+    set(CPACK_PACKAGE_VENDOR                  "http://assimp.sourceforge.net/")
+    set(CPACK_PACKAGE_DISPLAY_NAME            "Assimp ${ASSIMP_VERSION}")
+    set(CPACK_PACKAGE_DESCRIPTION_SUMMARY     " - Open Asset Import Library ${ASSIMP_VERSION}")
+    set(CPACK_PACKAGE_VERSION                 "${ASSIMP_VERSION}.${ASSIMP_PACKAGE_VERSION}" )
+    set(CPACK_PACKAGE_VERSION_MAJOR           "${ASSIMP_VERSION_MAJOR}")
+    set(CPACK_PACKAGE_VERSION_MINOR           "${ASSIMP_VERSION_MINOR}")
+    set(CPACK_PACKAGE_VERSION_PATCH           "${ASSIMP_VERSION_PATCH}")
+    set(CPACK_PACKAGE_INSTALL_DIRECTORY       "assimp${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}")
+    set(CPACK_RESOURCE_FILE_LICENSE           "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
+
+    string(TOUPPER ${LIBASSIMP_COMPONENT}     "LIBASSIMP_COMPONENT_UPPER")
+    string(TOUPPER ${LIBASSIMP-DEV_COMPONENT} "LIBASSIMP-DEV_COMPONENT_UPPER")
+
+    set(CPACK_COMPONENT_ASSIMP-BIN_DISPLAY_NAME                       "tools")
+    set(CPACK_COMPONENT_ASSIMP-BIN_DEPENDS                            "${LIBASSIMP_COMPONENT}" )
+    set(CPACK_COMPONENT_${LIBASSIMP_COMPONENT_UPPER}_DISPLAY_NAME     "libraries")
+    set(CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT_UPPER}_DISPLAY_NAME "common headers and installs")
+    set(CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT_UPPER}_DEPENDS $    "{LIBASSIMP_COMPONENT}" )
+    set(CPACK_COMPONENT_ASSIMP-DEV_DISPLAY_NAME                       "${CPACK_COMPONENT_${LIBASSIMP-DEV_COMPONENT}_DISPLAY_NAME}" )
+    set(CPACK_COMPONENT_ASSIMP-DEV_DEPENDS                            "${LIBASSIMP-DEV_COMPONENT}" )
+    set(CPACK_DEBIAN_BUILD_DEPENDS debhelper cmake libboost-dev libboost-thread-dev libboost-math-dev zlib1g-dev pkg-config)
+
+    # debian
+    set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
+    set(CPACK_DEBIAN_CMAKE_OPTIONS    "-DBUILD_ASSIMP_SAMPLES:BOOL=${ASSIMP_BUILD_SAMPLES}")
+    set(CPACK_DEBIAN_PACKAGE_SECTION  "libs" )
+    set(CPACK_DEBIAN_PACKAGE_DEPENDS  "${CPACK_COMPONENTS_ALL}")
+    set(CPACK_DEBIAN_PACKAGE_SUGGESTS)
+    set(CPACK_DEBIAN_PACKAGE_NAME     "assimp")
+    set(CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES contrib/cppunit-1.12.1 contrib/cppunit_note.txt contrib/zlib workspaces test doc obj samples packaging)
+    set(CPACK_DEBIAN_PACKAGE_SOURCE_COPY svn export --force)
+    set(CPACK_DEBIAN_CHANGELOG)
+    execute_process(COMMAND lsb_release -is
+        OUTPUT_VARIABLE _lsb_distribution OUTPUT_STRIP_TRAILING_WHITESPACE
+        RESULT_VARIABLE _lsb_release_failed)
+    set(CPACK_DEBIAN_DISTRIBUTION_NAME ${_lsb_distribution} CACHE STRING "Name of the distrubiton")
+    string(TOLOWER ${CPACK_DEBIAN_DISTRIBUTION_NAME} CPACK_DEBIAN_DISTRIBUTION_NAME)
+    if( ${CPACK_DEBIAN_DISTRIBUTION_NAME} STREQUAL "ubuntu" )
+        set(CPACK_DEBIAN_DISTRIBUTION_RELEASES lucid maverick natty oneiric precise CACHE STRING "Release code-names of the distrubiton release")
+    endif()
+    set(DPUT_HOST "" CACHE STRING "PPA repository to upload the debian sources")
+    include(CPack)
+    include(DebSourcePPA)
 endif()

+ 3 - 0
CREDITS

@@ -148,3 +148,6 @@ Bugfixes for uv-tanget calculation.
 
 - Jonne Nauha
 Ogre Binary format support
+
+- Filip Wasil, Tieto Poland Sp. z o.o.
+Android JNI asset extraction support

+ 8 - 2
Readme.md

@@ -1,10 +1,12 @@
 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.
+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 __40 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)
 
+[open3mod](https://github.com/acgessler/open3mod) is an Open Source 3D model viewer based off Assimp's import and export abilities.
+
 #### Supported file formats ####
 
 The library provides importers for a lot of file formats, including:
@@ -44,6 +46,7 @@ The library provides importers for a lot of file formats, including:
 - Ogre Binary
 - Ogre XML
 - Q3D
+- ASSBIN (Assimp scene serialization)
  
 Additionally, the following formats are also supported, but not part of the core library as they depend on proprietary libraries.
 
@@ -55,7 +58,10 @@ Exporters include:
 - STL
 - OBJ
 - PLY
+- X
+- 3DS
 - JSON (for WebGl, via https://github.com/acgessler/assimp2json)
+- ASSBIN
 	
 See [the full list here](http://assimp.sourceforge.net/main_features_formats.html).
 
@@ -77,7 +83,7 @@ C++ish interface). The directory structure is:
 	/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`)
+	/tools		Tools (old assimp viewer, command line `assimp`)
 	/samples	A small number of samples to illustrate possible 
                         use cases for Assimp
 	/workspaces	Build enviroments for vc,xcode,... (deprecated,

+ 11 - 11
assimp-config.cmake.in

@@ -44,20 +44,20 @@ if (CMAKE_BUILD_TYPE EQUAL "DEBUG")
 	set( ASSIMP_LIBRARIES ${ASSIMP_LIBRARIES}D)
 endif (CMAKE_BUILD_TYPE EQUAL "DEBUG")
 
+# search for the boost version assimp was compiled with
+#set(Boost_USE_MULTITHREAD ON)
+#set(Boost_USE_STATIC_LIBS OFF)
+#set(Boost_USE_STATIC_RUNTIME OFF)
+#find_package(Boost ${ASSIMP_Boost_VERSION} EXACT COMPONENTS thread date_time)
+#if(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0")
+#	set( ASSIMP_INCLUDE_DIRS "${ASSIMP_INCLUDE_DIRS}" ${Boost_INCLUDE_DIRS})
+#else(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0")
+#	message(WARNING "Failed to find Boost ${ASSIMP_Boost_VERSION} necessary for assimp")
+#endif(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0")
+
 # the boost version assimp was compiled with
 set( ASSIMP_Boost_VERSION "@Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@")
 
-# search for the boost version assimp was compiled with
-set(Boost_USE_MULTITHREAD ON)
-set(Boost_USE_STATIC_LIBS OFF)
-set(Boost_USE_STATIC_RUNTIME OFF)
-find_package(Boost ${ASSIMP_Boost_VERSION} EXACT COMPONENTS thread date_time)
-if(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0")
-	set( ASSIMP_INCLUDE_DIRS "${ASSIMP_INCLUDE_DIRS}" ${Boost_INCLUDE_DIRS})
-else(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0")
-	message(WARNING "Failed to find Boost ${ASSIMP_Boost_VERSION} necessary for assimp")
-endif(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0")
-
 # for compatibility wiht pkg-config
 set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}")
 set(ASSIMP_LDFLAGS_OTHER "${ASSIMP_LINK_FLAGS}")

+ 1 - 0
assimp.pc.in

@@ -7,4 +7,5 @@ Name: @CMAKE_PROJECT_NAME@
 Description: Import various well-known 3D model formats in an uniform manner.
 Version: @PROJECT_VERSION@
 Libs: -L${libdir} -lassimp@ASSIMP_LIBRARY_SUFFIX@
+Libs.private: @LIBSTDC++_LIBRARIES@ @ZLIB_LIBRARIES_LINKED@
 Cflags: -I${includedir}

+ 64 - 0
cmake-modules/AddGTest.cmake

@@ -0,0 +1,64 @@
+find_package(Threads REQUIRED)
+include(ExternalProject)
+
+if(MSYS OR MINGW)
+	set(DISABLE_PTHREADS ON)
+else()
+	set(DISABLE_PTHREADS OFF)
+endif()
+
+if (MSVC)
+	set(RELEASE_LIB_DIR ReleaseLibs)
+	set(DEBUG_LIB_DIR DebugLibs)
+else()
+	set(RELEASE_LIB_DIR "")
+	set(DEBUG_LIB_DIR "")
+endif()
+
+set(GTEST_CMAKE_ARGS
+	"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
+	"-Dgtest_force_shared_crt=ON"
+	"-Dgtest_disable_pthreads:BOOL=${DISABLE_PTHREADS}")
+set(GTEST_RELEASE_LIB_DIR "")
+set(GTEST_DEBUGLIB_DIR "")
+if (MSVC)
+	set(GTEST_CMAKE_ARGS ${GTEST_CMAKE_ARGS}
+		"-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG:PATH=${DEBUG_LIB_DIR}"
+		"-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE:PATH=${RELEASE_LIB_DIR}")
+	set(GTEST_LIB_DIR)
+endif()
+
+set(GTEST_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/gtest")
+
+ExternalProject_Add(gtest
+	GIT_REPOSITORY https://chromium.googlesource.com/external/googletest
+	TIMEOUT 10
+	PREFIX "${GTEST_PREFIX}"
+	CMAKE_ARGS "${GTEST_CMAKE_ARGS}"
+	LOG_DOWNLOAD ON
+	LOG_CONFIGURE ON
+	LOG_BUILD ON
+	# Disable install
+	INSTALL_COMMAND ""
+)
+
+set(LIB_PREFIX "${CMAKE_STATIC_LIBRARY_PREFIX}")
+set(LIB_SUFFIX "${CMAKE_STATIC_LIBRARY_SUFFIX}")
+set(GTEST_LOCATION "${GTEST_PREFIX}/src/gtest-build")
+set(GTEST_DEBUG_LIBRARIES
+	"${GTEST_LOCATION}/${DEBUG_LIB_DIR}/${LIB_PREFIX}gtest${LIB_SUFFIX}"
+	"${CMAKE_THREAD_LIBS_INIT}")
+SET(GTEST_RELEASE_LIBRARIES
+	"${GTEST_LOCATION}/${RELEASE_LIB_DIR}/${LIB_PREFIX}gtest${LIB_SUFFIX}"
+	"${CMAKE_THREAD_LIBS_INIT}")
+
+if(MSVC_VERSION EQUAL 1700)
+  add_definitions(-D_VARIADIC_MAX=10)
+endif()
+
+ExternalProject_Get_Property(gtest source_dir)
+include_directories(${source_dir}/include)
+include_directories(${source_dir}/gtest/include)
+
+ExternalProject_Get_Property(gtest binary_dir)
+link_directories(${binary_dir})

+ 560 - 0
code/3DSExporter.cpp

@@ -0,0 +1,560 @@
+/*
+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_3DS_EXPORTER
+
+#include "3DSExporter.h"
+#include "3DSLoader.h"
+#include "SceneCombiner.h"
+#include "SplitLargeMeshes.h"
+
+using namespace Assimp;
+namespace Assimp	{
+
+namespace {
+
+	//////////////////////////////////////////////////////////////////////////////////////
+	// Scope utility to write a 3DS file chunk.
+	//
+	// Upon construction, the chunk header is written with the chunk type (flags)
+	// filled out, but the chunk size left empty. Upon destruction, the correct chunk
+	// size based on the then-position of the output stream cursor is filled in.
+	class ChunkWriter {
+		enum {
+			  CHUNK_SIZE_NOT_SET = 0xdeadbeef
+			, SIZE_OFFSET        = 2
+		};
+	public:
+
+		ChunkWriter(StreamWriterLE& writer, uint16_t chunk_type)
+			: writer(writer)
+		{
+			chunk_start_pos = writer.GetCurrentPos();
+			writer.PutU2(chunk_type);
+			writer.PutU4(CHUNK_SIZE_NOT_SET);
+		}
+
+		~ChunkWriter() {
+			std::size_t head_pos = writer.GetCurrentPos();
+
+			ai_assert(head_pos > chunk_start_pos);
+			const std::size_t chunk_size = head_pos - chunk_start_pos;
+
+			writer.SetCurrentPos(chunk_start_pos + SIZE_OFFSET);
+			writer.PutU4(chunk_size);
+			writer.SetCurrentPos(head_pos);
+		}
+		
+	private:
+		StreamWriterLE& writer;
+		std::size_t chunk_start_pos;
+	};
+
+
+	// Return an unique name for a given |mesh| attached to |node| that
+	// preserves the mesh's given name if it has one. |index| is the index
+	// of the mesh in |aiScene::mMeshes|.
+	std::string GetMeshName(const aiMesh& mesh, unsigned int index, const aiNode& node) {
+		static const std::string underscore = "_";
+		char postfix[10] = {0};
+		ASSIMP_itoa10(postfix, index);
+
+		std::string result = node.mName.C_Str();
+		if (mesh.mName.length > 0) {
+			result += underscore + mesh.mName.C_Str();
+		}
+		return result + underscore + postfix;
+	}
+
+	// Return an unique name for a given |mat| with original position |index|
+	// in |aiScene::mMaterials|. The name preserves the original material
+	// name if possible.
+	std::string GetMaterialName(const aiMaterial& mat, unsigned int index) {
+		static const std::string underscore = "_";
+		char postfix[10] = {0};
+		ASSIMP_itoa10(postfix, index);
+
+		aiString mat_name;
+		if (AI_SUCCESS == mat.Get(AI_MATKEY_NAME, mat_name)) {
+			return mat_name.C_Str() + underscore + postfix;
+		}
+
+		return "Material" + underscore + postfix;
+	}
+
+	// Collect world transformations for each node
+	void CollectTrafos(const aiNode* node, std::map<const aiNode*, aiMatrix4x4>& trafos) {
+		const aiMatrix4x4& parent = node->mParent ? trafos[node->mParent] : aiMatrix4x4();
+		trafos[node] = parent * node->mTransformation;
+		for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+			CollectTrafos(node->mChildren[i], trafos);
+		}
+	}
+
+	// Generate a flat list of the meshes (by index) assigned to each node
+	void CollectMeshes(const aiNode* node, std::multimap<const aiNode*, unsigned int>& meshes) {
+		for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
+			meshes.insert(std::make_pair(node, node->mMeshes[i]));
+		}
+		for (unsigned int i = 0; i < node->mNumChildren; ++i) {
+			CollectMeshes(node->mChildren[i], meshes);
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Worker function for exporting a scene to 3DS. Prototyped and registered in Exporter.cpp
+void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
+{
+	boost::shared_ptr<IOStream> outfile (pIOSystem->Open(pFile, "wb"));
+	if(!outfile) {
+		throw DeadlyExportError("Could not open output .3ds file: " + std::string(pFile));
+	} 
+
+	// TODO: This extra copy should be avoided and all of this made a preprocess
+	// requirement of the 3DS exporter.
+	//
+	// 3DS meshes can be max 0xffff (16 Bit) vertices and faces, respectively.
+	// SplitLargeMeshes can do this, but it requires the correct limit to be set
+	// which is not possible with the current way of specifying preprocess steps
+	// in |Exporter::ExportFormatEntry|.
+	aiScene* scenecopy_tmp;
+	SceneCombiner::CopyScene(&scenecopy_tmp,pScene);
+	std::auto_ptr<aiScene> scenecopy(scenecopy_tmp);
+
+	SplitLargeMeshesProcess_Triangle tri_splitter;
+	tri_splitter.SetLimit(0xffff);
+	tri_splitter.Execute(scenecopy.get());
+
+	SplitLargeMeshesProcess_Vertex vert_splitter;
+	vert_splitter.SetLimit(0xffff);
+	vert_splitter.Execute(scenecopy.get());
+
+	// Invoke the actual exporter 
+	Discreet3DSExporter exporter(outfile, scenecopy.get());
+}
+
+} // end of namespace Assimp
+
+// ------------------------------------------------------------------------------------------------
+Discreet3DSExporter:: Discreet3DSExporter(boost::shared_ptr<IOStream> outfile, const aiScene* scene)
+: scene(scene)
+, writer(outfile)
+{
+	CollectTrafos(scene->mRootNode, trafos);
+	CollectMeshes(scene->mRootNode, meshes);
+
+	ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAIN);
+
+	{
+		ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJMESH);
+		WriteMeshes();
+		WriteMaterials();
+
+		{
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MASTER_SCALE);
+			writer.PutF4(1.0f);
+		}
+	}
+
+	{
+		ChunkWriter chunk(writer, Discreet3DS::CHUNK_KEYFRAMER);
+		WriteHierarchy(*scene->mRootNode, -1, -1);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+int Discreet3DSExporter::WriteHierarchy(const aiNode& node, int seq, int sibling_level)
+{
+	// 3DS scene hierarchy is serialized as in http://www.martinreddy.net/gfx/3d/3DS.spec
+	{
+		ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRACKINFO);
+		{
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRACKOBJNAME);
+
+			// Assimp node names are unique and distinct from all mesh-node
+			// names we generate; thus we can use them as-is
+			WriteString(node.mName);
+
+			// Two unknown int16 values - it is even unclear if 0 is a safe value
+			// but luckily importers do not know better either.
+			writer.PutI4(0);
+
+			int16_t hierarchy_pos = static_cast<int16_t>(seq);
+			if (sibling_level != -1) {
+				hierarchy_pos = sibling_level;
+			}
+
+			// Write the hierarchy position
+			writer.PutI2(hierarchy_pos);
+		}
+	}
+
+	// TODO: write transformation chunks
+
+	++seq;
+	sibling_level = seq;
+
+	// Write all children
+	for (unsigned int i = 0; i < node.mNumChildren; ++i) {
+		seq = WriteHierarchy(*node.mChildren[i], seq, i == 0 ? -1 : sibling_level);
+	}
+
+	// Write all meshes as separate nodes to be able to reference the meshes by name
+	for (unsigned int i = 0; i < node.mNumMeshes; ++i) {
+		const bool first_child = node.mNumChildren == 0 && i == 0;
+
+		const unsigned int mesh_idx = node.mMeshes[i];
+		const aiMesh& mesh = *scene->mMeshes[mesh_idx];
+
+		ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRACKINFO);
+		{
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRACKOBJNAME);
+			WriteString(GetMeshName(mesh, mesh_idx, node));
+
+			writer.PutI4(0);
+			writer.PutI2(static_cast<int16_t>(first_child ? seq : sibling_level));
+			++seq;
+		}
+	}
+	return seq;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteMaterials()
+{
+	for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
+		ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_MATERIAL);
+		const aiMaterial& mat = *scene->mMaterials[i];
+
+		{
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_MATNAME);
+			const std::string& name = GetMaterialName(mat, i);
+			WriteString(name);
+		}
+
+		aiColor3D color;
+		if (mat.Get(AI_MATKEY_COLOR_DIFFUSE, color) == AI_SUCCESS) {
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_DIFFUSE);
+			WriteColor(color);
+		}
+
+		if (mat.Get(AI_MATKEY_COLOR_SPECULAR, color) == AI_SUCCESS) {
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR);
+			WriteColor(color);
+		}
+
+		if (mat.Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS) {
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT);
+			WriteColor(color);
+		}
+
+		if (mat.Get(AI_MATKEY_COLOR_EMISSIVE, color) == AI_SUCCESS) {
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SELF_ILLUM);
+			WriteColor(color);
+		}
+
+		aiShadingMode shading_mode;
+		if (mat.Get(AI_MATKEY_SHADING_MODEL, shading_mode) == AI_SUCCESS) {
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHADING);
+
+			Discreet3DS::shadetype3ds shading_mode_out;
+			switch(shading_mode) {
+			case aiShadingMode_Flat:
+			case aiShadingMode_NoShading:
+				shading_mode_out = Discreet3DS::Flat;
+				break;
+
+			case aiShadingMode_Gouraud:
+			case aiShadingMode_Toon:
+			case aiShadingMode_OrenNayar:
+			case aiShadingMode_Minnaert:
+				shading_mode_out = Discreet3DS::Gouraud;
+				break;
+
+			case aiShadingMode_Phong:
+			case aiShadingMode_Blinn:
+			case aiShadingMode_CookTorrance:
+			case aiShadingMode_Fresnel:
+				shading_mode_out = Discreet3DS::Phong;
+				break;
+
+			default:
+				ai_assert(false);
+			};
+			writer.PutU2(static_cast<uint16_t>(shading_mode_out));
+		}
+
+
+		float f;
+		if (mat.Get(AI_MATKEY_SHININESS, f) == AI_SUCCESS) {
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHININESS);
+			WritePercentChunk(f);
+		}
+
+		if (mat.Get(AI_MATKEY_SHININESS_STRENGTH, f) == AI_SUCCESS) {
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHININESS_PERCENT);
+			WritePercentChunk(f);
+		}
+
+		int twosided;
+		if (mat.Get(AI_MATKEY_TWOSIDED, twosided) == AI_SUCCESS && twosided != 0) {
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_TWO_SIDE);
+			writer.PutI2(1);
+		}
+		
+		WriteTexture(mat, aiTextureType_DIFFUSE, Discreet3DS::CHUNK_MAT_TEXTURE);
+		WriteTexture(mat, aiTextureType_HEIGHT, Discreet3DS::CHUNK_MAT_BUMPMAP);
+		WriteTexture(mat, aiTextureType_OPACITY, Discreet3DS::CHUNK_MAT_OPACMAP);
+		WriteTexture(mat, aiTextureType_SHININESS, Discreet3DS::CHUNK_MAT_MAT_SHINMAP);
+		WriteTexture(mat, aiTextureType_SPECULAR, Discreet3DS::CHUNK_MAT_SPECMAP);
+		WriteTexture(mat, aiTextureType_EMISSIVE, Discreet3DS::CHUNK_MAT_SELFIMAP);
+		WriteTexture(mat, aiTextureType_REFLECTION, Discreet3DS::CHUNK_MAT_REFLMAP);
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags) 
+{
+	aiString path;
+	aiTextureMapMode map_mode[2] = {
+		aiTextureMapMode_Wrap, aiTextureMapMode_Wrap
+	};
+	float blend = 1.0f;
+	if (mat.GetTexture(type, 0, &path, NULL, NULL, &blend, NULL, map_mode) != AI_SUCCESS || !path.length) {
+		return;
+	}
+
+	// TODO: handle embedded textures properly
+	if (path.data[0] == '*') {
+		DefaultLogger::get()->error("Ignoring embedded texture for export: " + std::string(path.C_Str()));
+		return;
+	}
+
+	ChunkWriter chunk(writer, chunk_flags);
+	{
+		ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAPFILE);
+		WriteString(path);
+	}
+
+	WritePercentChunk(blend);
+
+	{
+		ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_MAP_TILING);
+		uint16_t val = 0; // WRAP
+		if (map_mode[0] == aiTextureMapMode_Mirror) {
+			val = 0x2;
+		}
+		else if (map_mode[0] == aiTextureMapMode_Decal) {
+			val = 0x10;
+		}
+		writer.PutU2(val);
+	}
+	// TODO: export texture transformation (i.e. UV offset, scale, rotation)
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteMeshes()
+{
+	// NOTE: 3DS allows for instances. However:
+	//   i)  not all importers support reading them
+	//   ii) instances are not as flexible as they are in assimp, in particular,
+	//        nodes can carry (and instance) only one mesh.
+	//
+	// This exporter currently deep clones all instanced meshes, i.e. for each mesh
+	// attached to a node a full TRIMESH chunk is written to the file.
+	//
+	// Furthermore, the TRIMESH is transformed into world space so that it will
+	// appear correctly if importers don't read the scene hierarchy at all.
+	for (MeshesByNodeMap::const_iterator it = meshes.begin(); it != meshes.end(); ++it) {
+		const aiNode& node = *(*it).first;
+		const unsigned int mesh_idx = (*it).second;
+
+		const aiMesh& mesh = *scene->mMeshes[mesh_idx];
+
+		// This should not happen if the SLM step is correctly executed
+		// before the scene is handed to the exporter
+		ai_assert(mesh.mNumVertices <= 0xffff);
+		ai_assert(mesh.mNumFaces <= 0xffff);
+
+		const aiMatrix4x4& trafo = trafos[&node];
+
+		ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJBLOCK);
+
+		// Mesh name is tied to the node it is attached to so it can later be referenced
+		const std::string& name = GetMeshName(mesh, mesh_idx, node);
+		WriteString(name);
+
+
+		// TRIMESH chunk
+		ChunkWriter chunk2(writer, Discreet3DS::CHUNK_TRIMESH);
+
+		// Vertices in world space
+		{
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_VERTLIST);
+
+			const uint16_t count = static_cast<uint16_t>(mesh.mNumVertices);
+			writer.PutU2(count);
+			for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
+				const aiVector3D& v = trafo * mesh.mVertices[i];
+				writer.PutF4(v.x);
+				writer.PutF4(v.y);
+				writer.PutF4(v.z);
+			}
+		}
+
+		// UV coordinates
+		if (mesh.HasTextureCoords(0)) {
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAPLIST);
+			const uint16_t count = static_cast<uint16_t>(mesh.mNumVertices);
+			writer.PutU2(count);
+
+			for (unsigned int i = 0; i < mesh.mNumVertices; ++i) {
+				const aiVector3D& v = mesh.mTextureCoords[0][i];
+				writer.PutF4(v.x);
+				writer.PutF4(v.y);
+			}
+		}
+
+		// Faces (indices)
+		{
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_FACELIST);
+
+			ai_assert(mesh.mNumFaces <= 0xffff);
+
+			// Count triangles, discard lines and points
+			uint16_t count = 0;
+			for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
+				const aiFace& f = mesh.mFaces[i];
+				if (f.mNumIndices < 3) {
+					continue;
+				}
+				// TRIANGULATE step is a pre-requisite so we should not see polys here
+				ai_assert(f.mNumIndices == 3);
+				++count;
+			}
+
+			writer.PutU2(count);
+			for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
+				const aiFace& f = mesh.mFaces[i];
+				if (f.mNumIndices < 3) {
+					continue;
+				}
+
+				for (unsigned int j = 0; j < 3; ++j) {
+					ai_assert(f.mIndices[j] <= 0xffff);
+					writer.PutI2(static_cast<uint16_t>(f.mIndices[j]));
+				}
+
+				// Edge visibility flag
+				writer.PutI2(0x0);
+			}
+
+			// TODO: write smoothing groups (CHUNK_SMOOLIST)
+
+			WriteFaceMaterialChunk(mesh);
+		}
+
+		// Transformation matrix by which the mesh vertices have been pre-transformed with.
+		{
+			ChunkWriter chunk(writer, Discreet3DS::CHUNK_TRMATRIX);
+			for (unsigned int r = 0; r < 4; ++r) {
+				for (unsigned int c = 0; c < 3; ++c) {
+					writer.PutF4(trafo[r][c]);
+				}
+			}
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteFaceMaterialChunk(const aiMesh& mesh)
+{
+	ChunkWriter chunk(writer, Discreet3DS::CHUNK_FACEMAT);
+	const std::string& name = GetMaterialName(*scene->mMaterials[mesh.mMaterialIndex], mesh.mMaterialIndex);
+	WriteString(name);
+
+	// Because assimp splits meshes by material, only a single
+	// FACEMAT chunk needs to be written
+	ai_assert(mesh.mNumFaces <= 0xffff);
+	const uint16_t count = static_cast<uint16_t>(mesh.mNumFaces);
+	writer.PutU2(count);
+
+	for (unsigned int i = 0; i < mesh.mNumFaces; ++i) {
+		writer.PutU2(static_cast<uint16_t>(i));
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteString(const std::string& s) {
+	for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
+		writer.PutI1(*it);
+	}
+	writer.PutI1('\0');
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteString(const aiString& s) {
+	for (std::size_t i = 0; i < s.length; ++i) {
+		writer.PutI1(s.data[i]);
+	}
+	writer.PutI1('\0');
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WriteColor(const aiColor3D& color) {
+	ChunkWriter chunk(writer, Discreet3DS::CHUNK_RGBF);
+	writer.PutF4(color.r);
+	writer.PutF4(color.g);
+	writer.PutF4(color.b);
+}
+
+// ------------------------------------------------------------------------------------------------
+void Discreet3DSExporter::WritePercentChunk(float f) {
+	ChunkWriter chunk(writer, Discreet3DS::CHUNK_PERCENTF);
+	writer.PutF4(f);
+}
+
+
+#endif // ASSIMP_BUILD_NO_3DS_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 94 - 0
code/3DSExporter.h

@@ -0,0 +1,94 @@
+/*
+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 3DSExporter.h
+ * 3DS Exporter Main Header
+ */
+#ifndef AI_3DSEXPORTER_H_INC
+#define AI_3DSEXPORTER_H_INC
+
+#include <map>
+
+#include "StreamWriter.h"
+
+struct aiScene;
+struct aiNode;
+
+namespace Assimp	
+{
+
+// ------------------------------------------------------------------------------------------------
+/** Helper class to export a given scene to a 3DS file. */
+// ------------------------------------------------------------------------------------------------
+class Discreet3DSExporter
+{
+public:
+	Discreet3DSExporter(boost::shared_ptr<IOStream> outfile, const aiScene* pScene);
+
+private:
+
+	void WriteMeshes();
+	void WriteMaterials();
+	void WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags);
+
+	void WriteFaceMaterialChunk(const aiMesh& mesh);
+
+	int WriteHierarchy(const aiNode& node, int level, int sibling_level);
+
+	void WriteString(const std::string& s);
+	void WriteString(const aiString& s);
+	void WriteColor(const aiColor3D& color);
+	void WritePercentChunk(float f);
+
+private:
+
+	const aiScene* const scene;
+	StreamWriterLE writer;
+
+	std::map<const aiNode*, aiMatrix4x4> trafos;
+
+	typedef std::multimap<const aiNode*, unsigned int> MeshesByNodeMap;
+	MeshesByNodeMap meshes;
+
+};
+
+}
+
+#endif

+ 1 - 1
code/3DSHelper.h

@@ -468,7 +468,7 @@ struct aiFloatKey
 		{return mTime < o.mTime;}
 
 	bool operator > (const aiFloatKey& o) const
-		{return mTime < o.mTime;}
+		{return mTime > o.mTime;}
 
 #endif
 };

+ 2 - 2
code/3DSLoader.h

@@ -273,8 +273,8 @@ protected:
 	bool bIsPrj;
 };
 
-#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
-
 } // end of namespace Assimp
 
+#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER
+
 #endif // AI_3DSIMPORTER_H_INC

+ 2 - 2
code/ASEParser.cpp

@@ -1525,7 +1525,7 @@ void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh& mesh)
 				continue;
 			}
 			// Number of bones
-			if (TokenMatch(filePtr,"MESH_NUMBONE" ,11))
+			if (TokenMatch(filePtr,"MESH_NUMBONE" ,12))
 			{
 				ParseLV4MeshLong(iNumBones);
 				continue;
@@ -1559,7 +1559,7 @@ void Parser::ParseLV4MeshBones(unsigned int iNumBones,ASE::Mesh& mesh)
 			++filePtr;
 
 			// Mesh bone with name ...
-			if (TokenMatch(filePtr,"MESH_BONE_NAME" ,16))
+			if (TokenMatch(filePtr,"MESH_BONE_NAME" ,14))
 			{
 				// parse an index ...
 				if(SkipSpaces(&filePtr))

+ 765 - 0
code/AssbinExporter.cpp

@@ -0,0 +1,765 @@
+/*
+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  AssbinExporter.cpp
+ *  ASSBIN exporter main code
+ */
+#include "AssimpPCH.h"
+#include "assbin_chunks.h"
+#include "./../include/assimp/version.h"
+#include "ProcessHelper.h"
+
+#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+#	include <zlib.h>
+#else
+#	include "../contrib/zlib/zlib.h"
+#endif
+
+#include <time.h>
+
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
+
+using namespace Assimp;
+
+namespace Assimp	{
+
+template <typename T> 
+size_t Write(IOStream * stream, const T& v)
+{
+	return stream->Write( &v, sizeof(T), 1 );
+}
+
+
+// -----------------------------------------------------------------------------------
+// Serialize an aiString
+template <>
+inline size_t Write<aiString>(IOStream * stream, const aiString& s)
+{
+	const size_t s2 = (uint32_t)s.length;
+	stream->Write(&s,4,1);
+	stream->Write(s.data,s2,1);
+	return s2+4;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an unsigned int as uint32_t
+template <>
+inline size_t Write<unsigned int>(IOStream * stream, const unsigned int& w)
+{
+	const uint32_t t = (uint32_t)w;
+	if (w > t) {
+		// this shouldn't happen, integers in Assimp data structures never exceed 2^32
+		throw new DeadlyExportError("loss of data due to 64 -> 32 bit integer conversion");
+	}
+
+	stream->Write(&t,4,1);
+	return 4;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an unsigned int as uint16_t
+template <>
+inline size_t Write<uint16_t>(IOStream * stream, const uint16_t& w)
+{
+	BOOST_STATIC_ASSERT(sizeof(uint16_t)==2);
+	stream->Write(&w,2,1);
+	return 2;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a float
+template <>
+inline size_t Write<float>(IOStream * stream, const float& f)
+{
+	BOOST_STATIC_ASSERT(sizeof(float)==4);
+	stream->Write(&f,4,1);
+	return 4;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a double
+template <>
+inline size_t Write<double>(IOStream * stream, const double& f)
+{
+	BOOST_STATIC_ASSERT(sizeof(double)==8);
+	stream->Write(&f,8,1);
+	return 8;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a vec3
+template <>
+inline size_t Write<aiVector3D>(IOStream * stream, const aiVector3D& v)
+{
+	size_t t = Write<float>(stream,v.x);
+	t += Write<float>(stream,v.y);
+	t += Write<float>(stream,v.z);
+	return t;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a color value
+template <>
+inline size_t Write<aiColor4D>(IOStream * stream, const aiColor4D& v)
+{
+	size_t t = Write<float>(stream,v.r);
+	t += Write<float>(stream,v.g);
+	t += Write<float>(stream,v.b);
+	t += Write<float>(stream,v.a);
+	return t;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a quaternion
+template <>
+inline size_t Write<aiQuaternion>(IOStream * stream, const aiQuaternion& v)
+{
+	size_t t = Write<float>(stream,v.w);
+	t += Write<float>(stream,v.x);
+	t += Write<float>(stream,v.y);
+	t += Write<float>(stream,v.z);
+	return 16;
+}
+
+
+// -----------------------------------------------------------------------------------
+// Serialize a vertex weight
+template <>
+inline size_t Write<aiVertexWeight>(IOStream * stream, const aiVertexWeight& v)
+{
+	size_t t = Write<unsigned int>(stream,v.mVertexId);
+	return t+Write<float>(stream,v.mWeight);
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize a mat4x4
+template <>
+inline size_t Write<aiMatrix4x4>(IOStream * stream, const aiMatrix4x4& m)
+{
+	for (unsigned int i = 0; i < 4;++i) {
+		for (unsigned int i2 = 0; i2 < 4;++i2) {
+			Write<float>(stream,m[i][i2]);
+		}
+	}
+	return 64;
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an aiVectorKey
+template <>
+inline size_t Write<aiVectorKey>(IOStream * stream, const aiVectorKey& v)
+{
+	const size_t t = Write<double>(stream,v.mTime);
+	return t + Write<aiVector3D>(stream,v.mValue);
+}
+
+// -----------------------------------------------------------------------------------
+// Serialize an aiQuatKey
+template <>
+inline size_t Write<aiQuatKey>(IOStream * stream, const aiQuatKey& v)
+{
+	const size_t t = Write<double>(stream,v.mTime);
+	return t + Write<aiQuaternion>(stream,v.mValue);
+}
+
+template <typename T>
+inline size_t WriteBounds(IOStream * stream, const T* in, unsigned int size)
+{
+	T minc,maxc;
+	ArrayBounds(in,size,minc,maxc);
+
+	const size_t t = Write<T>(stream,minc);
+	return t + Write<T>(stream,maxc);
+}
+
+// We use this to write out non-byte arrays so that we write using the specializations.
+// This way we avoid writing out extra bytes that potentially come from struct alignment.
+template <typename T>
+inline size_t WriteArray(IOStream * stream, const T* in, unsigned int size)
+{
+	size_t n = 0;
+	for (unsigned int i=0; i<size; i++) n += Write<T>(stream,in[i]);
+	return n;
+}
+
+	// ----------------------------------------------------------------------------------
+	/**	@class	AssbinChunkWriter
+	 *	@brief	Chunk writer mechanism for the .assbin file structure
+	 *
+	 *  This is a standard in-memory IOStream (most of the code is based on BlobIOStream),
+	 *  the difference being that this takes another IOStream as a "container" in the
+	 *  constructor, and when it is destroyed, it appends the magic number, the chunk size,
+	 *  and the chunk contents to the container stream. This allows relatively easy chunk
+	 *  chunk construction, even recursively.
+	 */
+	class AssbinChunkWriter : public IOStream
+	{
+	private:
+
+		uint8_t* buffer;
+		uint32_t magic;
+		IOStream * container;
+		size_t cur_size, cursor, initial;
+
+	private:
+		// -------------------------------------------------------------------
+		void Grow(size_t need = 0) 
+		{
+			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;
+		}
+
+	public:
+
+		AssbinChunkWriter( IOStream * container, uint32_t magic, size_t initial = 4096) 
+			: buffer(NULL), magic(magic), container(container), cur_size(0), cursor(0), initial(initial)
+		{
+		}
+
+		virtual ~AssbinChunkWriter() 
+		{
+			if (container) {
+				container->Write( &magic, sizeof(uint32_t), 1 );
+				container->Write( &cursor, sizeof(uint32_t), 1 );
+				container->Write( buffer, 1, cursor );
+			}
+			if (buffer) delete[] buffer;
+		}
+
+		void * GetBufferPointer() { return buffer; };
+
+		// -------------------------------------------------------------------
+		virtual size_t Read(void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) { return 0; };
+		virtual aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) { return aiReturn_FAILURE; };
+		virtual size_t Tell() const { return cursor; };
+		virtual void Flush() { };
+
+		virtual size_t FileSize() const
+		{
+			return cursor;
+		}
+
+		// -------------------------------------------------------------------
+		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;
+
+			return pCount; 
+		}
+
+	};
+
+	// ----------------------------------------------------------------------------------
+	/**	@class	AssbinExport
+	 *	@brief	Assbin exporter class
+	 *
+	 *  This class performs the .assbin exporting, and is responsible for the file layout.
+	 */
+	class AssbinExport
+	{
+	private:
+		bool shortened;
+		bool compressed;
+
+	protected:
+
+		// -----------------------------------------------------------------------------------
+		void WriteBinaryNode( IOStream * container, const aiNode* node)
+		{
+			AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AINODE );
+
+			Write<aiString>(&chunk,node->mName);
+			Write<aiMatrix4x4>(&chunk,node->mTransformation);
+			Write<unsigned int>(&chunk,node->mNumChildren);
+			Write<unsigned int>(&chunk,node->mNumMeshes);
+
+			for (unsigned int i = 0; i < node->mNumMeshes;++i) {
+				Write<unsigned int>(&chunk,node->mMeshes[i]);
+			}
+
+			for (unsigned int i = 0; i < node->mNumChildren;++i) {
+				WriteBinaryNode( &chunk, node->mChildren[i] );
+			}
+		}
+
+		// -----------------------------------------------------------------------------------
+		void WriteBinaryTexture(IOStream * container, const aiTexture* tex)
+		{
+			AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AITEXTURE );
+
+			Write<unsigned int>(&chunk,tex->mWidth);
+			Write<unsigned int>(&chunk,tex->mHeight);
+			chunk.Write( tex->achFormatHint, sizeof(char), 4 );
+
+			if(!shortened) {
+				if (!tex->mHeight) {
+					chunk.Write(tex->pcData,1,tex->mWidth);
+				}
+				else {
+					chunk.Write(tex->pcData,1,tex->mWidth*tex->mHeight*4);
+				}
+			}
+
+		}
+
+		// -----------------------------------------------------------------------------------
+		void WriteBinaryBone(IOStream * container, const aiBone* b)
+		{
+			AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIBONE );
+
+			Write<aiString>(&chunk,b->mName);
+			Write<unsigned int>(&chunk,b->mNumWeights);
+			Write<aiMatrix4x4>(&chunk,b->mOffsetMatrix);
+
+			// for the moment we write dumb min/max values for the bones, too.
+			// maybe I'll add a better, hash-like solution later
+			if (shortened) {
+				WriteBounds(&chunk,b->mWeights,b->mNumWeights);
+			} // else write as usual
+			else WriteArray<aiVertexWeight>(&chunk,b->mWeights,b->mNumWeights);
+		}
+
+		// -----------------------------------------------------------------------------------
+		void WriteBinaryMesh(IOStream * container, const aiMesh* mesh)
+		{
+			AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIMESH );
+
+			Write<unsigned int>(&chunk,mesh->mPrimitiveTypes);
+			Write<unsigned int>(&chunk,mesh->mNumVertices);
+			Write<unsigned int>(&chunk,mesh->mNumFaces);
+			Write<unsigned int>(&chunk,mesh->mNumBones);
+			Write<unsigned int>(&chunk,mesh->mMaterialIndex);
+
+			// first of all, write bits for all existent vertex components
+			unsigned int c = 0;
+			if (mesh->mVertices) {
+				c |= ASSBIN_MESH_HAS_POSITIONS;
+			}
+			if (mesh->mNormals) {
+				c |= ASSBIN_MESH_HAS_NORMALS;
+			}
+			if (mesh->mTangents && mesh->mBitangents) {
+				c |= ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS;
+			}
+			for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
+				if (!mesh->mTextureCoords[n]) {
+					break;
+				}
+				c |= ASSBIN_MESH_HAS_TEXCOORD(n);
+			}
+			for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS;++n) {
+				if (!mesh->mColors[n]) {
+					break;
+				}
+				c |= ASSBIN_MESH_HAS_COLOR(n);
+			}
+			Write<unsigned int>(&chunk,c);
+
+			aiVector3D minVec, maxVec;
+			if (mesh->mVertices) {
+				if (shortened) {
+					WriteBounds(&chunk,mesh->mVertices,mesh->mNumVertices);
+				} // else write as usual
+				else WriteArray<aiVector3D>(&chunk,mesh->mVertices,mesh->mNumVertices);
+			}
+			if (mesh->mNormals) {
+				if (shortened) {
+					WriteBounds(&chunk,mesh->mNormals,mesh->mNumVertices);
+				} // else write as usual
+				else WriteArray<aiVector3D>(&chunk,mesh->mNormals,mesh->mNumVertices);
+			}
+			if (mesh->mTangents && mesh->mBitangents) {
+				if (shortened) {
+					WriteBounds(&chunk,mesh->mTangents,mesh->mNumVertices);
+					WriteBounds(&chunk,mesh->mBitangents,mesh->mNumVertices);
+				} // else write as usual
+				else {
+					WriteArray<aiVector3D>(&chunk,mesh->mTangents,mesh->mNumVertices);
+					WriteArray<aiVector3D>(&chunk,mesh->mBitangents,mesh->mNumVertices);
+				}
+			}
+			for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS;++n) {
+				if (!mesh->mColors[n])
+					break;
+
+				if (shortened) {
+					WriteBounds(&chunk,mesh->mColors[n],mesh->mNumVertices);
+				} // else write as usual
+				else WriteArray<aiColor4D>(&chunk,mesh->mColors[n],mesh->mNumVertices);
+			}
+			for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) {
+				if (!mesh->mTextureCoords[n])
+					break;
+
+				// write number of UV components
+				Write<unsigned int>(&chunk,mesh->mNumUVComponents[n]);
+
+				if (shortened) {
+					WriteBounds(&chunk,mesh->mTextureCoords[n],mesh->mNumVertices);
+				} // else write as usual
+				else WriteArray<aiVector3D>(&chunk,mesh->mTextureCoords[n],mesh->mNumVertices);
+			}
+
+			// write faces. There are no floating-point calculations involved
+			// in these, so we can write a simple hash over the face data
+			// to the dump file. We generate a single 32 Bit hash for 512 faces
+			// using Assimp's standard hashing function.
+			if (shortened) {
+				unsigned int processed = 0;
+				for (unsigned int job;(job = std::min(mesh->mNumFaces-processed,512u));processed += job) {
+
+					uint32_t hash = 0;
+					for (unsigned int a = 0; a < job;++a) {
+
+						const aiFace& f = mesh->mFaces[processed+a];
+						uint32_t tmp = f.mNumIndices;
+						hash = SuperFastHash(reinterpret_cast<const char*>(&tmp),sizeof tmp,hash);
+						for (unsigned int i = 0; i < f.mNumIndices; ++i) {
+							BOOST_STATIC_ASSERT(AI_MAX_VERTICES <= 0xffffffff);
+							tmp = static_cast<uint32_t>( f.mIndices[i] );
+							hash = SuperFastHash(reinterpret_cast<const char*>(&tmp),sizeof tmp,hash);
+						}
+					}
+					Write<unsigned int>(&chunk,hash);
+				}
+			}
+			else // else write as usual
+			{
+				// if there are less than 2^16 vertices, we can simply use 16 bit integers ...
+				for (unsigned int i = 0; i < mesh->mNumFaces;++i) {
+					const aiFace& f = mesh->mFaces[i];
+
+					BOOST_STATIC_ASSERT(AI_MAX_FACE_INDICES <= 0xffff);
+					Write<uint16_t>(&chunk,f.mNumIndices);
+
+					for (unsigned int a = 0; a < f.mNumIndices;++a) {
+						if (mesh->mNumVertices < (1u<<16)) {
+							Write<uint16_t>(&chunk,f.mIndices[a]);
+						}
+						else Write<unsigned int>(&chunk,f.mIndices[a]);
+					}
+				}
+			}
+
+			// write bones
+			if (mesh->mNumBones) {
+				for (unsigned int a = 0; a < mesh->mNumBones;++a) {
+					const aiBone* b = mesh->mBones[a];
+					WriteBinaryBone(&chunk,b);
+				}
+			}
+		}
+
+		// -----------------------------------------------------------------------------------
+		void WriteBinaryMaterialProperty(IOStream * container, const aiMaterialProperty* prop)
+		{
+			AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIMATERIALPROPERTY );
+
+			Write<aiString>(&chunk,prop->mKey);
+			Write<unsigned int>(&chunk,prop->mSemantic);
+			Write<unsigned int>(&chunk,prop->mIndex);
+
+			Write<unsigned int>(&chunk,prop->mDataLength);
+			Write<unsigned int>(&chunk,(unsigned int)prop->mType);
+			chunk.Write(prop->mData,1,prop->mDataLength);
+		}
+
+		// -----------------------------------------------------------------------------------
+		void WriteBinaryMaterial(IOStream * container, const aiMaterial* mat)
+		{
+			AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIMATERIAL);
+
+			Write<unsigned int>(&chunk,mat->mNumProperties);
+			for (unsigned int i = 0; i < mat->mNumProperties;++i) {
+				WriteBinaryMaterialProperty( &chunk, mat->mProperties[i]);
+			}
+		}
+
+		// -----------------------------------------------------------------------------------
+		void WriteBinaryNodeAnim(IOStream * container, const aiNodeAnim* nd)
+		{
+			AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AINODEANIM );
+
+			Write<aiString>(&chunk,nd->mNodeName);
+			Write<unsigned int>(&chunk,nd->mNumPositionKeys);
+			Write<unsigned int>(&chunk,nd->mNumRotationKeys);
+			Write<unsigned int>(&chunk,nd->mNumScalingKeys);
+			Write<unsigned int>(&chunk,nd->mPreState);
+			Write<unsigned int>(&chunk,nd->mPostState);
+
+			if (nd->mPositionKeys) {
+				if (shortened) {
+					WriteBounds(&chunk,nd->mPositionKeys,nd->mNumPositionKeys);
+
+				} // else write as usual
+				else WriteArray<aiVectorKey>(&chunk,nd->mPositionKeys,nd->mNumPositionKeys);
+			}
+			if (nd->mRotationKeys) {
+				if (shortened) {
+					WriteBounds(&chunk,nd->mRotationKeys,nd->mNumRotationKeys);
+
+				} // else write as usual
+				else WriteArray<aiQuatKey>(&chunk,nd->mRotationKeys,nd->mNumRotationKeys);
+			}
+			if (nd->mScalingKeys) {
+				if (shortened) {
+					WriteBounds(&chunk,nd->mScalingKeys,nd->mNumScalingKeys);
+
+				} // else write as usual
+				else WriteArray<aiVectorKey>(&chunk,nd->mScalingKeys,nd->mNumScalingKeys);
+			}
+		}
+
+
+		// -----------------------------------------------------------------------------------
+		void WriteBinaryAnim( IOStream * container, const aiAnimation* anim )
+		{
+			AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIANIMATION );
+
+			Write<aiString>(&chunk,anim->mName);
+			Write<double>(&chunk,anim->mDuration);
+			Write<double>(&chunk,anim->mTicksPerSecond);
+			Write<unsigned int>(&chunk,anim->mNumChannels);
+
+			for (unsigned int a = 0; a < anim->mNumChannels;++a) {
+				const aiNodeAnim* nd = anim->mChannels[a];
+				WriteBinaryNodeAnim(&chunk,nd);	
+			}
+		}
+
+		// -----------------------------------------------------------------------------------
+		void WriteBinaryLight( IOStream * container, const aiLight* l )
+		{
+			AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AILIGHT );
+
+			Write<aiString>(&chunk,l->mName);
+			Write<unsigned int>(&chunk,l->mType);
+
+			if (l->mType != aiLightSource_DIRECTIONAL) { 
+				Write<float>(&chunk,l->mAttenuationConstant);
+				Write<float>(&chunk,l->mAttenuationLinear);
+				Write<float>(&chunk,l->mAttenuationQuadratic);
+			}
+
+			Write<aiVector3D>(&chunk,(const aiVector3D&)l->mColorDiffuse);
+			Write<aiVector3D>(&chunk,(const aiVector3D&)l->mColorSpecular);
+			Write<aiVector3D>(&chunk,(const aiVector3D&)l->mColorAmbient);
+
+			if (l->mType == aiLightSource_SPOT) {
+				Write<float>(&chunk,l->mAngleInnerCone);
+				Write<float>(&chunk,l->mAngleOuterCone);
+			}
+
+		}
+
+		// -----------------------------------------------------------------------------------
+		void WriteBinaryCamera( IOStream * container, const aiCamera* cam )
+		{
+			AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AICAMERA );
+
+			Write<aiString>(&chunk,cam->mName);
+			Write<aiVector3D>(&chunk,cam->mPosition);
+			Write<aiVector3D>(&chunk,cam->mLookAt);
+			Write<aiVector3D>(&chunk,cam->mUp);
+			Write<float>(&chunk,cam->mHorizontalFOV);
+			Write<float>(&chunk,cam->mClipPlaneNear);
+			Write<float>(&chunk,cam->mClipPlaneFar);
+			Write<float>(&chunk,cam->mAspect);
+		}
+
+		// -----------------------------------------------------------------------------------
+		void WriteBinaryScene( IOStream * container, const aiScene* scene)
+		{
+			AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AISCENE );
+
+			// basic scene information
+			Write<unsigned int>(&chunk,scene->mFlags);
+			Write<unsigned int>(&chunk,scene->mNumMeshes);
+			Write<unsigned int>(&chunk,scene->mNumMaterials);
+			Write<unsigned int>(&chunk,scene->mNumAnimations);
+			Write<unsigned int>(&chunk,scene->mNumTextures);
+			Write<unsigned int>(&chunk,scene->mNumLights);
+			Write<unsigned int>(&chunk,scene->mNumCameras);
+
+			// write node graph
+			WriteBinaryNode( &chunk, scene->mRootNode );
+
+			// write all meshes
+			for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
+				const aiMesh* mesh = scene->mMeshes[i];
+				WriteBinaryMesh( &chunk,mesh);
+			}
+
+			// write materials
+			for (unsigned int i = 0; i< scene->mNumMaterials; ++i) {
+				const aiMaterial* mat = scene->mMaterials[i];
+				WriteBinaryMaterial(&chunk,mat);
+			}
+
+			// write all animations
+			for (unsigned int i = 0; i < scene->mNumAnimations;++i) {
+				const aiAnimation* anim = scene->mAnimations[i];
+				WriteBinaryAnim(&chunk,anim);
+			}
+
+
+			// write all textures
+			for (unsigned int i = 0; i < scene->mNumTextures;++i) {
+				const aiTexture* mesh = scene->mTextures[i];
+				WriteBinaryTexture(&chunk,mesh);
+			}
+
+			// write lights
+			for (unsigned int i = 0; i < scene->mNumLights;++i) {
+				const aiLight* l = scene->mLights[i];
+				WriteBinaryLight(&chunk,l);
+			}
+
+			// write cameras
+			for (unsigned int i = 0; i < scene->mNumCameras;++i) {
+				const aiCamera* cam = scene->mCameras[i];
+				WriteBinaryCamera(&chunk,cam);
+			}
+
+		}
+
+	public:
+		AssbinExport() 
+			: shortened(false), compressed(false) // temporary settings until properties are introduced for exporters
+		{
+		}
+
+		// -----------------------------------------------------------------------------------
+		// Write a binary model dump
+		void WriteBinaryDump(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
+		{
+			IOStream * out = pIOSystem->Open( pFile, "wb" );
+			if (!out) return;
+
+			time_t tt = time(NULL);
+			tm* p     = gmtime(&tt);
+
+			// header
+			char s[64];
+			memset( s, 0, 64 );
+#if _MSC_VER >= 1400
+			sprintf_s(s,"ASSIMP.binary-dump.%s",asctime(p));
+#else
+			snprintf(s,64,"ASSIMP.binary-dump.%s",asctime(p));
+#endif
+			out->Write( s, 44, 1 );
+			// == 44 bytes
+
+			Write<unsigned int>( out, ASSBIN_VERSION_MAJOR );
+			Write<unsigned int>( out, ASSBIN_VERSION_MINOR );
+			Write<unsigned int>( out, aiGetVersionRevision() );
+			Write<unsigned int>( out, aiGetCompileFlags() );
+			Write<uint16_t>( out, shortened );
+			Write<uint16_t>( out, compressed );
+			// ==  20 bytes
+
+			char buff[256]; 
+			strncpy(buff,pFile,256);
+			out->Write(buff,sizeof(char),256);
+
+			char cmd[] = "\0";
+			strncpy(buff,cmd,128);
+			out->Write(buff,sizeof(char),128);
+
+			// leave 64 bytes free for future extensions
+			memset(buff,0xcd,64);
+			out->Write(buff,sizeof(char),64);
+			// == 435 bytes
+
+			// ==== total header size: 512 bytes
+			ai_assert( out->Tell() == ASSBIN_HEADER_LENGTH );
+
+			// Up to here the data is uncompressed. For compressed files, the rest
+			// is compressed using standard DEFLATE from zlib.
+			if (compressed)
+			{
+				AssbinChunkWriter uncompressedStream( NULL, 0 );
+				WriteBinaryScene( &uncompressedStream, pScene );
+
+				uLongf uncompressedSize = uncompressedStream.Tell();
+				uLongf compressedSize = (uLongf)(uncompressedStream.Tell() * 1.001 + 12.);
+				uint8_t* compressedBuffer = new uint8_t[ compressedSize ];
+
+				compress2( compressedBuffer, &compressedSize, (const Bytef*)uncompressedStream.GetBufferPointer(), uncompressedSize, 9 );
+
+				out->Write( &uncompressedSize, sizeof(uint32_t), 1 );
+				out->Write( compressedBuffer, sizeof(char), compressedSize );
+
+				delete[] compressedBuffer;
+			}
+			else
+			{
+				WriteBinaryScene( out, pScene );
+			}
+
+			pIOSystem->Close( out );
+		}
+	};
+
+void ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
+{
+	AssbinExport exporter;
+	exporter.WriteBinaryDump( pFile, pIOSystem, pScene );
+}
+} // end of namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_ASSBIN_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 49 - 0
code/AssbinExporter.h

@@ -0,0 +1,49 @@
+/*
+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 AssbinExporter.h
+ * ASSBIN Exporter Main Header
+ */
+#ifndef AI_ASSBINEXPORTER_H_INC
+#define AI_ASSBINEXPORTER_H_INC
+
+// nothing really needed here - reserved for future use like properties
+
+#endif

+ 672 - 0
code/AssbinLoader.cpp

@@ -0,0 +1,672 @@
+/*
+---------------------------------------------------------------------------
+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  AssbinLoader.cpp
+ *  @brief Implementation of the .assbin importer class
+ *
+ *  see assbin_chunks.h
+ */
+
+#include "AssimpPCH.h"
+#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
+
+// internal headers
+#include "AssbinLoader.h"
+#include "assbin_chunks.h"
+#include "MemoryIOWrapper.h"
+#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+#	include <zlib.h>
+#else
+#	include "../contrib/zlib/zlib.h"
+#endif
+
+using namespace Assimp;
+
+static const aiImporterDesc desc = {
+	".assbin Importer",
+	"Gargaj / Conspiracy",
+	"",
+	"",
+	aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour,
+	0,
+	0,
+	0,
+	0,
+	"assbin" 
+};
+
+const aiImporterDesc* AssbinImporter::GetInfo() const
+{
+	return &desc;
+}
+
+bool AssbinImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/ ) const
+{
+	IOStream * in = pIOHandler->Open(pFile);
+	if (!in)
+		return false;
+
+	char s[32];
+	in->Read( s, sizeof(char), 32 );
+
+	pIOHandler->Close(in);
+
+	return strncmp( s, "ASSIMP.binary-dump.", 19 ) == 0;
+}
+
+template <typename T>
+T Read(IOStream * stream)
+{
+	T t;
+	stream->Read( &t, sizeof(T), 1 );
+	return t;
+}
+
+template <>
+aiVector3D Read<aiVector3D>(IOStream * stream)
+{
+	aiVector3D v;
+	v.x = Read<float>(stream);
+	v.y = Read<float>(stream);
+	v.z = Read<float>(stream);
+	return v;
+}
+
+template <>
+aiColor4D Read<aiColor4D>(IOStream * stream)
+{
+	aiColor4D c;
+	c.r = Read<float>(stream);
+	c.g = Read<float>(stream);
+	c.b = Read<float>(stream);
+	c.a = Read<float>(stream);
+	return c;
+}
+
+template <>
+aiQuaternion Read<aiQuaternion>(IOStream * stream)
+{
+	aiQuaternion v;
+	v.w = Read<float>(stream);
+	v.x = Read<float>(stream);
+	v.y = Read<float>(stream);
+	v.z = Read<float>(stream);
+	return v;
+}
+
+template <>
+aiString Read<aiString>(IOStream * stream)
+{
+	aiString s;
+	stream->Read(&s.length,4,1);
+	stream->Read(s.data,s.length,1);
+	s.data[s.length] = 0;
+	return s;
+}
+
+template <>
+aiVertexWeight Read<aiVertexWeight>(IOStream * stream)
+{
+	aiVertexWeight w;
+	w.mVertexId = Read<unsigned int>(stream);
+	w.mWeight = Read<float>(stream);
+	return w;
+}
+
+template <>
+aiMatrix4x4 Read<aiMatrix4x4>(IOStream * stream)
+{
+	aiMatrix4x4 m;
+	for (unsigned int i = 0; i < 4;++i) {
+		for (unsigned int i2 = 0; i2 < 4;++i2) {
+			m[i][i2] = Read<float>(stream);
+		}
+	}
+	return m;
+}
+
+template <>
+aiVectorKey Read<aiVectorKey>(IOStream * stream)
+{
+	aiVectorKey v;
+	v.mTime = Read<double>(stream);
+	v.mValue = Read<aiVector3D>(stream);
+	return v;
+}
+
+template <>
+aiQuatKey Read<aiQuatKey>(IOStream * stream)
+{
+	aiQuatKey v;
+	v.mTime = Read<double>(stream);
+	v.mValue = Read<aiQuaternion>(stream);
+	return v;
+}
+
+template <typename T>
+void ReadArray(IOStream * stream, T * out, unsigned int size)
+{ 
+	for (unsigned int i=0; i<size; i++) out[i] = Read<T>(stream);
+}
+
+template <typename T> void ReadBounds( IOStream * stream, T* /*p*/, unsigned int n )
+{
+	// not sure what to do here, the data isn't really useful.
+	stream->Seek( sizeof(T) * n, aiOrigin_CUR );
+}
+
+void AssbinImporter::ReadBinaryNode( IOStream * stream, aiNode** node )
+{
+	ai_assert( Read<uint32_t>(stream) == ASSBIN_CHUNK_AINODE);
+	/*uint32_t size =*/ Read<uint32_t>(stream);
+
+	*node = new aiNode();
+
+	(*node)->mName = Read<aiString>(stream);
+	(*node)->mTransformation = Read<aiMatrix4x4>(stream);
+	(*node)->mNumChildren = Read<unsigned int>(stream);
+	(*node)->mNumMeshes = Read<unsigned int>(stream);
+
+	if ((*node)->mNumMeshes)
+	{
+		(*node)->mMeshes = new unsigned int[(*node)->mNumMeshes];
+		for (unsigned int i = 0; i < (*node)->mNumMeshes; ++i) {
+			(*node)->mMeshes[i] = Read<unsigned int>(stream);
+		}
+	}
+
+	if ((*node)->mNumChildren)
+	{
+		(*node)->mChildren = new aiNode*[(*node)->mNumChildren];
+		for (unsigned int i = 0; i < (*node)->mNumChildren; ++i) {
+			ReadBinaryNode( stream, &(*node)->mChildren[i] );
+		}
+	}
+
+}
+
+// -----------------------------------------------------------------------------------
+void AssbinImporter::ReadBinaryBone( IOStream * stream, aiBone* b )
+{
+	ai_assert( Read<uint32_t>(stream) == ASSBIN_CHUNK_AIBONE );
+	/*uint32_t size =*/ Read<uint32_t>(stream);
+
+	b->mName = Read<aiString>(stream);
+	b->mNumWeights = Read<unsigned int>(stream);
+	b->mOffsetMatrix = Read<aiMatrix4x4>(stream);
+
+	// for the moment we write dumb min/max values for the bones, too.
+	// maybe I'll add a better, hash-like solution later
+	if (shortened) 
+	{
+		ReadBounds(stream,b->mWeights,b->mNumWeights);
+	} // else write as usual
+	else 
+	{
+		b->mWeights = new aiVertexWeight[b->mNumWeights];
+		ReadArray<aiVertexWeight>(stream,b->mWeights,b->mNumWeights);
+	}
+}
+
+
+void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh )
+{
+	ai_assert( Read<uint32_t>(stream) == ASSBIN_CHUNK_AIMESH);
+	/*uint32_t size =*/ Read<uint32_t>(stream);
+
+	mesh->mPrimitiveTypes = Read<unsigned int>(stream);
+	mesh->mNumVertices = Read<unsigned int>(stream);
+	mesh->mNumFaces = Read<unsigned int>(stream);
+	mesh->mNumBones = Read<unsigned int>(stream);
+	mesh->mMaterialIndex = Read<unsigned int>(stream);
+
+	// first of all, write bits for all existent vertex components
+	unsigned int c = Read<unsigned int>(stream);
+
+	if (c & ASSBIN_MESH_HAS_POSITIONS) 
+	{
+		if (shortened) {
+			ReadBounds(stream,mesh->mVertices,mesh->mNumVertices);
+		} // else write as usual
+		else 
+		{
+			mesh->mVertices = new aiVector3D[mesh->mNumVertices];
+			ReadArray<aiVector3D>(stream,mesh->mVertices,mesh->mNumVertices);
+		}
+	}
+	if (c & ASSBIN_MESH_HAS_NORMALS) 
+	{
+		if (shortened) {
+			ReadBounds(stream,mesh->mNormals,mesh->mNumVertices);
+		} // else write as usual
+		else 
+		{
+			mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+			ReadArray<aiVector3D>(stream,mesh->mNormals,mesh->mNumVertices);
+		}
+	}
+	if (c & ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS) 
+	{
+		if (shortened) {
+			ReadBounds(stream,mesh->mTangents,mesh->mNumVertices);
+			ReadBounds(stream,mesh->mBitangents,mesh->mNumVertices);
+		} // else write as usual
+		else 
+		{
+			mesh->mTangents = new aiVector3D[mesh->mNumVertices];
+			ReadArray<aiVector3D>(stream,mesh->mTangents,mesh->mNumVertices);
+			mesh->mBitangents = new aiVector3D[mesh->mNumVertices];
+			ReadArray<aiVector3D>(stream,mesh->mBitangents,mesh->mNumVertices);
+		}
+	}
+	for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS;++n) 
+	{
+		if (!(c & ASSBIN_MESH_HAS_COLOR(n)))
+			break;
+
+		if (shortened) 
+		{
+			ReadBounds(stream,mesh->mColors[n],mesh->mNumVertices);
+		} // else write as usual
+		else 
+		{
+			mesh->mColors[n] = new aiColor4D[mesh->mNumVertices];
+			ReadArray<aiColor4D>(stream,mesh->mColors[n],mesh->mNumVertices);
+		}
+	}
+	for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS;++n) 
+	{
+		if (!(c & ASSBIN_MESH_HAS_TEXCOORD(n)))
+			break;
+
+		// write number of UV components
+		mesh->mNumUVComponents[n] = Read<unsigned int>(stream);
+
+		if (shortened) {
+			ReadBounds(stream,mesh->mTextureCoords[n],mesh->mNumVertices);
+		} // else write as usual
+		else 
+		{
+			mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices];
+			ReadArray<aiVector3D>(stream,mesh->mTextureCoords[n],mesh->mNumVertices);
+		}
+	}
+
+	// write faces. There are no floating-point calculations involved
+	// in these, so we can write a simple hash over the face data
+	// to the dump file. We generate a single 32 Bit hash for 512 faces
+	// using Assimp's standard hashing function.
+	if (shortened) {
+		Read<unsigned int>(stream);
+	}
+	else // else write as usual
+	{
+		// if there are less than 2^16 vertices, we can simply use 16 bit integers ...
+		mesh->mFaces = new aiFace[mesh->mNumFaces];
+		for (unsigned int i = 0; i < mesh->mNumFaces;++i) {
+			aiFace& f = mesh->mFaces[i];
+
+			BOOST_STATIC_ASSERT(AI_MAX_FACE_INDICES <= 0xffff);
+			f.mNumIndices = Read<uint16_t>(stream);
+			f.mIndices = new unsigned int[f.mNumIndices];
+
+			for (unsigned int a = 0; a < f.mNumIndices;++a) {
+				if (mesh->mNumVertices < (1u<<16)) 
+				{
+					f.mIndices[a] = Read<uint16_t>(stream);
+				}
+				else 
+				{
+					f.mIndices[a] = Read<unsigned int>(stream);
+				}
+			}
+		}
+	}
+
+	// write bones
+	if (mesh->mNumBones) {
+		mesh->mBones = new C_STRUCT aiBone*[mesh->mNumBones];
+		for (unsigned int a = 0; a < mesh->mNumBones;++a) {
+			mesh->mBones[a] = new aiBone();
+			ReadBinaryBone(stream,mesh->mBones[a]);
+		}
+	}
+}
+
+void AssbinImporter::ReadBinaryMaterialProperty(IOStream * stream, aiMaterialProperty* prop)
+{
+	ai_assert( Read<uint32_t>(stream) == ASSBIN_CHUNK_AIMATERIALPROPERTY);
+	/*uint32_t size =*/ Read<uint32_t>(stream);
+
+	prop->mKey = Read<aiString>(stream);
+	prop->mSemantic = Read<unsigned int>(stream);
+	prop->mIndex = Read<unsigned int>(stream);
+
+	prop->mDataLength = Read<unsigned int>(stream);
+	prop->mType = (aiPropertyTypeInfo)Read<unsigned int>(stream);
+	prop->mData = new char [ prop->mDataLength ];
+	stream->Read(prop->mData,1,prop->mDataLength);
+}
+
+// -----------------------------------------------------------------------------------
+void AssbinImporter::ReadBinaryMaterial(IOStream * stream, aiMaterial* mat)
+{
+	ai_assert( Read<uint32_t>(stream) == ASSBIN_CHUNK_AIMATERIAL);
+	/*uint32_t size =*/ Read<uint32_t>(stream);
+
+	mat->mNumAllocated = mat->mNumProperties = Read<unsigned int>(stream);
+	if (mat->mNumProperties)
+	{
+		if (mat->mProperties) 
+		{
+			delete[] mat->mProperties;
+		}
+		mat->mProperties = new aiMaterialProperty*[mat->mNumProperties];
+		for (unsigned int i = 0; i < mat->mNumProperties;++i) {
+			mat->mProperties[i] = new aiMaterialProperty();
+			ReadBinaryMaterialProperty( stream, mat->mProperties[i]);
+		}
+	}
+}
+
+// -----------------------------------------------------------------------------------
+void AssbinImporter::ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd)
+{
+	ai_assert( Read<uint32_t>(stream) == ASSBIN_CHUNK_AINODEANIM);
+	/*uint32_t size =*/ Read<uint32_t>(stream);
+
+	nd->mNodeName = Read<aiString>(stream);
+	nd->mNumPositionKeys = Read<unsigned int>(stream);
+	nd->mNumRotationKeys = Read<unsigned int>(stream);
+	nd->mNumScalingKeys = Read<unsigned int>(stream);
+	nd->mPreState = (aiAnimBehaviour)Read<unsigned int>(stream);
+	nd->mPostState = (aiAnimBehaviour)Read<unsigned int>(stream);
+
+	if (nd->mNumPositionKeys) {
+		if (shortened) {
+			ReadBounds(stream,nd->mPositionKeys,nd->mNumPositionKeys);
+
+		} // else write as usual
+		else {
+			nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];
+			ReadArray<aiVectorKey>(stream,nd->mPositionKeys,nd->mNumPositionKeys);
+		}
+	}
+	if (nd->mNumRotationKeys) {
+		if (shortened) {
+			ReadBounds(stream,nd->mRotationKeys,nd->mNumRotationKeys);
+
+		} // else write as usual
+		else 
+		{
+			nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys];
+			ReadArray<aiQuatKey>(stream,nd->mRotationKeys,nd->mNumRotationKeys);
+		}
+	}
+	if (nd->mNumScalingKeys) {
+		if (shortened) {
+			ReadBounds(stream,nd->mScalingKeys,nd->mNumScalingKeys);
+
+		} // else write as usual
+		else 
+		{
+			nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys];
+			ReadArray<aiVectorKey>(stream,nd->mScalingKeys,nd->mNumScalingKeys);
+		}
+	}
+}
+
+
+// -----------------------------------------------------------------------------------
+void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim )
+{
+	ai_assert( Read<uint32_t>(stream) == ASSBIN_CHUNK_AIANIMATION);
+	/*uint32_t size =*/ Read<uint32_t>(stream);
+
+	anim->mName = Read<aiString> (stream);
+	anim->mDuration = Read<double> (stream);
+	anim->mTicksPerSecond = Read<double> (stream);
+	anim->mNumChannels = Read<unsigned int>(stream);
+
+	if (anim->mNumChannels)
+	{
+		anim->mChannels = new aiNodeAnim*[ anim->mNumChannels ];
+		for (unsigned int a = 0; a < anim->mNumChannels;++a) {
+			anim->mChannels[a] = new aiNodeAnim();
+			ReadBinaryNodeAnim(stream,anim->mChannels[a]);
+		}
+	}
+}
+
+void AssbinImporter::ReadBinaryTexture(IOStream * stream, aiTexture* tex)
+{
+	ai_assert( Read<uint32_t>(stream) == ASSBIN_CHUNK_AITEXTURE);
+	/*uint32_t size =*/ Read<uint32_t>(stream);
+
+	tex->mWidth = Read<unsigned int>(stream);
+	tex->mHeight = Read<unsigned int>(stream);
+	stream->Read( tex->achFormatHint, sizeof(char), 4 );
+
+	if(!shortened) {
+		if (!tex->mHeight) {
+			tex->pcData = new aiTexel[ tex->mWidth ];
+			stream->Read(tex->pcData,1,tex->mWidth);
+		}
+		else {
+			tex->pcData = new aiTexel[ tex->mWidth*tex->mHeight ];
+			stream->Read(tex->pcData,1,tex->mWidth*tex->mHeight*4);
+		}
+	}
+
+}
+
+// -----------------------------------------------------------------------------------
+void AssbinImporter::ReadBinaryLight( IOStream * stream, aiLight* l )
+{
+	ai_assert( Read<uint32_t>(stream) == ASSBIN_CHUNK_AILIGHT);
+	/*uint32_t size =*/ Read<uint32_t>(stream);
+
+	l->mName = Read<aiString>(stream);
+	l->mType = (aiLightSourceType)Read<unsigned int>(stream);
+
+	if (l->mType != aiLightSource_DIRECTIONAL) { 
+		l->mAttenuationConstant = Read<float>(stream);
+		l->mAttenuationLinear = Read<float>(stream);
+		l->mAttenuationQuadratic = Read<float>(stream);
+	}
+
+	l->mColorDiffuse = Read<aiColor3D>(stream);
+	l->mColorSpecular = Read<aiColor3D>(stream);
+	l->mColorAmbient = Read<aiColor3D>(stream);
+
+	if (l->mType == aiLightSource_SPOT) {
+		l->mAngleInnerCone = Read<float>(stream);
+		l->mAngleOuterCone = Read<float>(stream);
+	}
+
+}
+
+// -----------------------------------------------------------------------------------
+void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam )
+{
+	ai_assert( Read<uint32_t>(stream) == ASSBIN_CHUNK_AICAMERA);
+	/*uint32_t size =*/ Read<uint32_t>(stream);
+
+	cam->mName = Read<aiString>(stream);
+	cam->mPosition = Read<aiVector3D>(stream);
+	cam->mLookAt = Read<aiVector3D>(stream);
+	cam->mUp = Read<aiVector3D>(stream);
+	cam->mHorizontalFOV = Read<float>(stream);
+	cam->mClipPlaneNear = Read<float>(stream);
+	cam->mClipPlaneFar = Read<float>(stream);
+	cam->mAspect = Read<float>(stream);
+}
+
+void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene )
+{
+	ai_assert( Read<uint32_t>(stream) == ASSBIN_CHUNK_AISCENE);
+	/*uint32_t size =*/ Read<uint32_t>(stream);
+
+	scene->mFlags         = Read<unsigned int>(stream);
+	scene->mNumMeshes     = Read<unsigned int>(stream);
+	scene->mNumMaterials  = Read<unsigned int>(stream);
+	scene->mNumAnimations = Read<unsigned int>(stream);
+	scene->mNumTextures   = Read<unsigned int>(stream);
+	scene->mNumLights     = Read<unsigned int>(stream);
+	scene->mNumCameras    = Read<unsigned int>(stream);
+
+	// Read node graph
+	scene->mRootNode = new aiNode[1];
+	ReadBinaryNode( stream, &scene->mRootNode );
+
+	// Read all meshes
+	if (scene->mNumMeshes)
+	{
+		scene->mMeshes = new aiMesh*[scene->mNumMeshes];
+		for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
+			scene->mMeshes[i] = new aiMesh();
+			ReadBinaryMesh( stream,scene->mMeshes[i]);
+		}
+	}
+
+	// Read materials
+	if (scene->mNumMaterials)
+	{
+		scene->mMaterials = new aiMaterial*[scene->mNumMaterials];
+		for (unsigned int i = 0; i< scene->mNumMaterials; ++i) {
+			scene->mMaterials[i] = new aiMaterial();
+			ReadBinaryMaterial(stream,scene->mMaterials[i]);
+		}
+	}
+
+	// Read all animations
+	if (scene->mNumAnimations)
+	{
+		scene->mAnimations = new aiAnimation*[scene->mNumAnimations];
+		for (unsigned int i = 0; i < scene->mNumAnimations;++i) {
+			scene->mAnimations[i] = new aiAnimation();
+			ReadBinaryAnim(stream,scene->mAnimations[i]);
+		}
+	}
+
+	// Read all textures
+	if (scene->mNumTextures)
+	{
+		scene->mTextures = new aiTexture*[scene->mNumTextures];
+		for (unsigned int i = 0; i < scene->mNumTextures;++i) {
+			scene->mTextures[i] = new aiTexture();
+			ReadBinaryTexture(stream,scene->mTextures[i]);
+		}
+	}
+
+	// Read lights
+	if (scene->mNumLights)
+	{
+		scene->mLights = new aiLight*[scene->mNumLights];
+		for (unsigned int i = 0; i < scene->mNumLights;++i) {
+			scene->mLights[i] = new aiLight();
+			ReadBinaryLight(stream,scene->mLights[i]);
+		}
+	}
+
+	// Read cameras
+	if (scene->mNumCameras)
+	{
+		scene->mCameras = new aiCamera*[scene->mNumCameras];
+		for (unsigned int i = 0; i < scene->mNumCameras;++i) {
+			scene->mCameras[i] = new aiCamera();
+			ReadBinaryCamera(stream,scene->mCameras[i]);
+		}
+	}
+
+}
+
+void AssbinImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler )
+{
+	IOStream * stream = pIOHandler->Open(pFile,"rb");
+	if (!stream)
+		return;
+
+	stream->Seek( 44, aiOrigin_CUR ); // signature
+
+	/*unsigned int versionMajor =*/ Read<unsigned int>(stream);
+	/*unsigned int versionMinor =*/ Read<unsigned int>(stream);
+	/*unsigned int versionRevision =*/ Read<unsigned int>(stream);
+	/*unsigned int compileFlags =*/ Read<unsigned int>(stream);
+
+	shortened = Read<uint16_t>(stream) > 0;
+	compressed = Read<uint16_t>(stream) > 0;
+
+	if (shortened)
+		throw DeadlyImportError( "Shortened binaries are not supported!" );
+
+	stream->Seek( 256, aiOrigin_CUR ); // original filename
+	stream->Seek( 128, aiOrigin_CUR ); // options
+	stream->Seek( 64, aiOrigin_CUR ); // padding
+
+	if (compressed)
+	{
+		uLongf uncompressedSize = Read<uint32_t>(stream);
+		uLongf compressedSize = stream->FileSize() - stream->Tell();
+
+		unsigned char * compressedData = new unsigned char[ compressedSize ];
+		stream->Read( compressedData, 1, compressedSize );
+
+		unsigned char * uncompressedData = new unsigned char[ uncompressedSize ];
+
+		uncompress( uncompressedData, &uncompressedSize, compressedData, compressedSize );
+
+		MemoryIOStream io( uncompressedData, uncompressedSize );
+
+		ReadBinaryScene(&io,pScene);
+
+		delete[] uncompressedData;
+		delete[] compressedData;
+	}
+	else
+	{
+		ReadBinaryScene(stream,pScene);
+	}
+	
+	pIOHandler->Close(stream);
+}
+
+#endif // !! ASSIMP_BUILD_NO_ASSBIN_IMPORTER

+ 94 - 0
code/AssbinLoader.h

@@ -0,0 +1,94 @@
+
+/*
+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  AssbinLoader.h
+ *  @brief .assbin File format loader
+ */
+#ifndef AI_ASSBINIMPORTER_H_INC
+#define AI_ASSBINIMPORTER_H_INC
+
+#include "BaseImporter.h"
+#include "../include/assimp/types.h"
+
+#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
+
+namespace Assimp	{
+
+// ---------------------------------------------------------------------------------
+/** Importer class for 3D Studio r3 and r4 3DS files
+ */
+class AssbinImporter : public BaseImporter
+{
+private:
+  bool shortened;
+  bool compressed;
+protected:
+
+public:
+  virtual bool CanRead( 
+    const std::string& pFile, 
+    IOSystem* pIOHandler, 
+    bool checkSig
+    ) const;
+  virtual const aiImporterDesc* GetInfo() const;
+  virtual void InternReadFile( 
+    const std::string& pFile, 
+    aiScene* pScene, 
+    IOSystem* pIOHandler
+    );
+  void ReadBinaryScene( IOStream * stream, aiScene* pScene );
+  void ReadBinaryNode( IOStream * stream, aiNode** mRootNode );
+  void ReadBinaryMesh( IOStream * stream, aiMesh* mesh );
+  void ReadBinaryBone( IOStream * stream, aiBone* bone );
+  void ReadBinaryMaterial(IOStream * stream, aiMaterial* mat);
+  void ReadBinaryMaterialProperty(IOStream * stream, aiMaterialProperty* prop);
+  void ReadBinaryNodeAnim(IOStream * stream, aiNodeAnim* nd);
+  void ReadBinaryAnim( IOStream * stream, aiAnimation* anim );
+  void ReadBinaryTexture(IOStream * stream, aiTexture* tex);
+  void ReadBinaryLight( IOStream * stream, aiLight* l );
+  void ReadBinaryCamera( IOStream * stream, aiCamera* cam );
+};
+
+} // end of namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_ASSBIN_IMPORTER
+
+#endif // AI_ASSBINIMPORTER_H_INC

+ 24 - 1
code/Assimp.cpp

@@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "GenericProperty.h"
 #include "CInterfaceIOWrapper.h"
+#include "../include/assimp/importerdesc.h"
 #include "Importer.h"
 
 // ------------------------------------------------------------------------------------------------
@@ -84,7 +85,11 @@ namespace Assimp
 
 	/** Verbose logging active or not? */
 	static aiBool gVerboseLogging = false;
-}
+
+    /** will return all registered importers. */
+    void GetImporterInstanceList(std::vector< BaseImporter* >& out);
+
+} // namespace assimp
 
 
 #ifndef ASSIMP_BUILD_SINGLETHREADED
@@ -606,4 +611,22 @@ ASSIMP_API void aiIdentityMatrix4(
 	*mat = aiMatrix4x4();
 }
 
+// ------------------------------------------------------------------------------------------------
+ASSIMP_API C_STRUCT const aiImporterDesc* aiGetImporterDesc( const char *extension ) {
+    if( NULL == extension ) {
+        return NULL;
+    }
+    const aiImporterDesc *desc( NULL );
+    std::vector< BaseImporter* > out;
+    GetImporterInstanceList( out );
+    for( size_t i = 0; i < out.size(); ++i ) {
+        if( 0 == strncmp( out[ i ]->GetInfo()->mFileExtensions, extension, strlen( extension ) ) ) {
+            desc = out[ i ]->GetInfo();
+            break;
+        }
+    }
 
+    return desc;
+}
+
+// ------------------------------------------------------------------------------------------------

+ 2 - 0
code/AssimpCExport.cpp

@@ -61,6 +61,8 @@ ASSIMP_API size_t aiGetExportFormatCount(void)
 // ------------------------------------------------------------------------------------------------
 ASSIMP_API const aiExportFormatDesc* aiGetExportFormatDescription( size_t pIndex)
 {
+	// Note: this is valid as the index always pertains to a builtin exporter,
+	// for which the returned structure is guaranteed to be of static storage duration.
 	return Exporter().GetExportFormatDescription(pIndex);
 }
 

+ 638 - 0
code/AssxmlExporter.cpp

@@ -0,0 +1,638 @@
+/*
+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  AssxmlExporter.cpp
+ *  ASSXML exporter main code
+ */
+#include <stdarg.h>
+#include "AssimpPCH.h"
+#include "./../include/assimp/version.h"
+#include "ProcessHelper.h"
+
+#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+#	include <zlib.h>
+#else
+#	include "../contrib/zlib/zlib.h"
+#endif
+
+#include <time.h>
+
+#ifndef ASSIMP_BUILD_NO_EXPORT
+#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
+
+using namespace Assimp;
+
+namespace Assimp	{
+
+namespace AssxmlExport	{
+
+int ioprintf( IOStream * io, const char * format, ... )
+{
+	char sz[4096];
+	va_list va;
+	va_start( va, format );
+	int nSize = vsnprintf( sz, 4096, format, va );
+  ai_assert( nSize < 4096 );
+	va_end( va );
+
+	io->Write( sz, sizeof(char), nSize );
+
+	return nSize;
+}
+
+// -----------------------------------------------------------------------------------
+// Convert a name to standard XML format
+void ConvertName(aiString& out, const aiString& in)
+{
+	out.length = 0;
+	for (unsigned int i = 0; i < in.length; ++i)  {
+		switch (in.data[i]) {
+			case '<':
+				out.Append("&lt;");break;
+			case '>':
+				out.Append("&gt;");break;
+			case '&':
+				out.Append("&amp;");break;
+			case '\"':
+				out.Append("&quot;");break;
+			case '\'':
+				out.Append("&apos;");break;
+			default:
+				out.data[out.length++] = in.data[i];
+		}
+	}
+	out.data[out.length] = 0;
+}
+
+// -----------------------------------------------------------------------------------
+// Write a single node as text dump
+void WriteNode(const aiNode* node, IOStream * io, unsigned int depth)
+{
+	char prefix[512];
+	for (unsigned int i = 0; i < depth;++i)
+		prefix[i] = '\t';
+	prefix[depth] = '\0';
+
+	const aiMatrix4x4& m = node->mTransformation;
+
+	aiString name;
+	ConvertName(name,node->mName);
+	ioprintf(io,"%s<Node name=\"%s\"> \n"
+		"%s\t<Matrix4> \n"
+		"%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+		"%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+		"%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+		"%s\t\t%0 6f %0 6f %0 6f %0 6f\n"
+		"%s\t</Matrix4> \n",
+		prefix,name.data,prefix,
+		prefix,m.a1,m.a2,m.a3,m.a4,
+		prefix,m.b1,m.b2,m.b3,m.b4,
+		prefix,m.c1,m.c2,m.c3,m.c4,
+		prefix,m.d1,m.d2,m.d3,m.d4,prefix);
+
+	if (node->mNumMeshes) {
+		ioprintf(io, "%s\t<MeshRefs num=\"%i\">\n%s\t",
+			prefix,node->mNumMeshes,prefix);
+
+		for (unsigned int i = 0; i < node->mNumMeshes;++i) {
+			ioprintf(io,"%i ",node->mMeshes[i]);
+		}
+		ioprintf(io,"\n%s\t</MeshRefs>\n",prefix);
+	}
+
+	if (node->mNumChildren) {
+		ioprintf(io,"%s\t<NodeList num=\"%i\">\n",
+			prefix,node->mNumChildren);
+
+		for (unsigned int i = 0; i < node->mNumChildren;++i) {
+			WriteNode(node->mChildren[i],io,depth+2);
+		}
+		ioprintf(io,"%s\t</NodeList>\n",prefix);
+	}
+	ioprintf(io,"%s</Node>\n",prefix);
+}
+
+
+// -----------------------------------------------------------------------------------
+// Some chuncks of text will need to be encoded for XML
+// http://stackoverflow.com/questions/5665231/most-efficient-way-to-escape-xml-html-in-c-string#5665377
+static std::string encodeXML(const std::string& data) {
+		std::string buffer;
+		buffer.reserve(data.size());
+		for(size_t pos = 0; pos != data.size(); ++pos) {
+				switch(data[pos]) {
+						case '&':  buffer.append("&amp;");				break;
+						case '\"': buffer.append("&quot;");				break;
+						case '\'': buffer.append("&apos;");				break;
+						case '<':  buffer.append("&lt;");					break;
+						case '>':  buffer.append("&gt;");					break;
+						default:   buffer.append(&data[pos], 1);	break;
+				}
+		}
+		return buffer;
+}
+
+
+
+// -----------------------------------------------------------------------------------
+// Write a text model dump
+void WriteDump(const aiScene* scene, IOStream* io, bool shortened)
+{
+	time_t tt = ::time(NULL);
+	tm* p     = ::gmtime(&tt);
+
+	aiString name;
+
+	// write header
+	ioprintf(io,
+		"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+		"<ASSIMP format_id=\"1\">\n\n"
+
+		"<!-- XML Model dump produced by assimp dump\n"
+		"  Library version: %i.%i.%i\n"
+		"  %s\n"
+		"-->"
+		" \n\n"
+		"<Scene flags=\"%i\" postprocessing=\"%i\">\n",
+		
+		aiGetVersionMajor(),aiGetVersionMinor(),aiGetVersionRevision(),asctime(p),
+		scene->mFlags,
+		0 /*globalImporter->GetEffectivePostProcessing()*/);
+
+	// write the node graph
+	WriteNode(scene->mRootNode, io, 0);
+
+#if 0
+		// write cameras
+	for (unsigned int i = 0; i < scene->mNumCameras;++i) {
+		aiCamera* cam  = scene->mCameras[i];
+		ConvertName(name,cam->mName);
+
+		// camera header
+		ioprintf(io,"\t<Camera parent=\"%s\">\n"
+			"\t\t<Vector3 name=\"up\"        > %0 8f %0 8f %0 8f </Vector3>\n"
+			"\t\t<Vector3 name=\"lookat\"    > %0 8f %0 8f %0 8f </Vector3>\n"
+			"\t\t<Vector3 name=\"pos\"       > %0 8f %0 8f %0 8f </Vector3>\n"
+			"\t\t<Float   name=\"fov\"       > %f </Float>\n"
+			"\t\t<Float   name=\"aspect\"    > %f </Float>\n"
+			"\t\t<Float   name=\"near_clip\" > %f </Float>\n"
+			"\t\t<Float   name=\"far_clip\"  > %f </Float>\n"
+			"\t</Camera>\n",
+			name.data,
+			cam->mUp.x,cam->mUp.y,cam->mUp.z,
+			cam->mLookAt.x,cam->mLookAt.y,cam->mLookAt.z,
+			cam->mPosition.x,cam->mPosition.y,cam->mPosition.z,
+			cam->mHorizontalFOV,cam->mAspect,cam->mClipPlaneNear,cam->mClipPlaneFar,i);
+	}
+
+	// write lights
+	for (unsigned int i = 0; i < scene->mNumLights;++i) {
+		aiLight* l  = scene->mLights[i];
+		ConvertName(name,l->mName);
+
+		// light header
+		ioprintf(io,"\t<Light parent=\"%s\"> type=\"%s\"\n"
+			"\t\t<Vector3 name=\"diffuse\"   > %0 8f %0 8f %0 8f </Vector3>\n"
+			"\t\t<Vector3 name=\"specular\"  > %0 8f %0 8f %0 8f </Vector3>\n"
+			"\t\t<Vector3 name=\"ambient\"   > %0 8f %0 8f %0 8f </Vector3>\n",
+			name.data,
+			(l->mType == aiLightSource_DIRECTIONAL ? "directional" :
+			(l->mType == aiLightSource_POINT ? "point" : "spot" )),
+			l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b,
+			l->mColorSpecular.r,l->mColorSpecular.g,l->mColorSpecular.b,
+			l->mColorAmbient.r, l->mColorAmbient.g, l->mColorAmbient.b);
+
+		if (l->mType != aiLightSource_DIRECTIONAL) {
+			ioprintf(io,
+				"\t\t<Vector3 name=\"pos\"       > %0 8f %0 8f %0 8f </Vector3>\n"
+				"\t\t<Float   name=\"atten_cst\" > %f </Float>\n"
+				"\t\t<Float   name=\"atten_lin\" > %f </Float>\n"
+				"\t\t<Float   name=\"atten_sqr\" > %f </Float>\n",
+				l->mPosition.x,l->mPosition.y,l->mPosition.z,
+				l->mAttenuationConstant,l->mAttenuationLinear,l->mAttenuationQuadratic);
+		}
+
+		if (l->mType != aiLightSource_POINT) {
+			ioprintf(io,
+				"\t\t<Vector3 name=\"lookat\"    > %0 8f %0 8f %0 8f </Vector3>\n",
+				l->mDirection.x,l->mDirection.y,l->mDirection.z);
+		}
+
+		if (l->mType == aiLightSource_SPOT) {
+			ioprintf(io,
+				"\t\t<Float   name=\"cone_out\" > %f </Float>\n"
+				"\t\t<Float   name=\"cone_inn\" > %f </Float>\n",
+				l->mAngleOuterCone,l->mAngleInnerCone);
+		}
+		ioprintf(io,"\t</Light>\n");
+	}
+#endif
+
+	// write textures
+	if (scene->mNumTextures) {
+		ioprintf(io,"<TextureList num=\"%i\">\n",scene->mNumTextures);
+		for (unsigned int i = 0; i < scene->mNumTextures;++i) {
+			aiTexture* tex  = scene->mTextures[i];
+			bool compressed = (tex->mHeight == 0);
+
+			// mesh header
+			ioprintf(io,"\t<Texture width=\"%i\" height=\"%i\" compressed=\"%s\"> \n",
+				(compressed ? -1 : tex->mWidth),(compressed ? -1 : tex->mHeight),
+				(compressed ? "true" : "false"));
+
+			if (compressed) {
+				ioprintf(io,"\t\t<Data length=\"%i\"> \n",tex->mWidth);
+
+				if (!shortened) {
+					for (unsigned int n = 0; n < tex->mWidth;++n) {
+						ioprintf(io,"\t\t\t%2x",reinterpret_cast<uint8_t*>(tex->pcData)[n]);
+						if (n && !(n % 50)) {
+							ioprintf(io,"\n");
+						}
+					}
+				}
+			}
+			else if (!shortened){
+				ioprintf(io,"\t\t<Data length=\"%i\"> \n",tex->mWidth*tex->mHeight*4);
+
+				// const unsigned int width = (unsigned int)log10((double)std::max(tex->mHeight,tex->mWidth))+1;
+				for (unsigned int y = 0; y < tex->mHeight;++y) {
+					for (unsigned int x = 0; x < tex->mWidth;++x) {
+						aiTexel* tx = tex->pcData + y*tex->mWidth+x;
+						unsigned int r = tx->r,g=tx->g,b=tx->b,a=tx->a;
+						ioprintf(io,"\t\t\t%2x %2x %2x %2x",r,g,b,a);
+
+						// group by four for readibility
+						if (0 == (x+y*tex->mWidth) % 4)
+							ioprintf(io,"\n");
+					}
+				}
+			}
+			ioprintf(io,"\t\t</Data>\n\t</Texture>\n");
+		}
+		ioprintf(io,"</TextureList>\n");
+	}
+
+	// write materials
+	if (scene->mNumMaterials) {
+		ioprintf(io,"<MaterialList num=\"%i\">\n",scene->mNumMaterials);
+		for (unsigned int i = 0; i< scene->mNumMaterials; ++i) {
+			const aiMaterial* mat = scene->mMaterials[i];
+
+			ioprintf(io,"\t<Material>\n");
+			ioprintf(io,"\t\t<MatPropertyList  num=\"%i\">\n",mat->mNumProperties);
+			for (unsigned int n = 0; n < mat->mNumProperties;++n) {
+
+				const aiMaterialProperty* prop = mat->mProperties[n];
+				const char* sz = "";
+				if (prop->mType == aiPTI_Float) {
+					sz = "float";
+				}
+				else if (prop->mType == aiPTI_Integer) {
+					sz = "integer";
+				}
+				else if (prop->mType == aiPTI_String) {
+					sz = "string";
+				}
+				else if (prop->mType == aiPTI_Buffer) {
+					sz = "binary_buffer";
+				}
+
+				ioprintf(io,"\t\t\t<MatProperty key=\"%s\" \n\t\t\ttype=\"%s\" tex_usage=\"%s\" tex_index=\"%i\"",
+					prop->mKey.data, sz,
+					::TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex);
+
+				if (prop->mType == aiPTI_Float) {
+					ioprintf(io," size=\"%i\">\n\t\t\t\t",
+						static_cast<int>(prop->mDataLength/sizeof(float)));
+
+					for (unsigned int p = 0; p < prop->mDataLength/sizeof(float);++p) {
+						ioprintf(io,"%f ",*((float*)(prop->mData+p*sizeof(float))));
+					}
+				}
+				else if (prop->mType == aiPTI_Integer) {
+					ioprintf(io," size=\"%i\">\n\t\t\t\t",
+						static_cast<int>(prop->mDataLength/sizeof(int)));
+
+					for (unsigned int p = 0; p < prop->mDataLength/sizeof(int);++p) {
+						ioprintf(io,"%i ",*((int*)(prop->mData+p*sizeof(int))));
+					}
+				}
+				else if (prop->mType == aiPTI_Buffer) {
+					ioprintf(io," size=\"%i\">\n\t\t\t\t",
+						static_cast<int>(prop->mDataLength));
+
+					for (unsigned int p = 0; p < prop->mDataLength;++p) {
+						ioprintf(io,"%2x ",prop->mData[p]);
+						if (p && 0 == p%30) {
+							ioprintf(io,"\n\t\t\t\t");
+						}
+					}
+				}
+				else if (prop->mType == aiPTI_String) {
+					ioprintf(io,">\n\t\t\t\t\"%s\"",encodeXML(prop->mData+4).c_str() /* skip length */);
+				}
+				ioprintf(io,"\n\t\t\t</MatProperty>\n");
+			}
+			ioprintf(io,"\t\t</MatPropertyList>\n");
+			ioprintf(io,"\t</Material>\n");
+		}
+		ioprintf(io,"</MaterialList>\n");
+	}
+
+	// write animations
+	if (scene->mNumAnimations) {
+		ioprintf(io,"<AnimationList num=\"%i\">\n",scene->mNumAnimations);
+		for (unsigned int i = 0; i < scene->mNumAnimations;++i) {
+			aiAnimation* anim = scene->mAnimations[i];
+
+			// anim header
+			ConvertName(name,anim->mName);
+			ioprintf(io,"\t<Animation name=\"%s\" duration=\"%e\" tick_cnt=\"%e\">\n",
+				name.data, anim->mDuration, anim->mTicksPerSecond);
+
+			// write bone animation channels
+			if (anim->mNumChannels) {
+				ioprintf(io,"\t\t<NodeAnimList num=\"%i\">\n",anim->mNumChannels);
+				for (unsigned int n = 0; n < anim->mNumChannels;++n) {
+					aiNodeAnim* nd = anim->mChannels[n];
+
+					// node anim header
+					ConvertName(name,nd->mNodeName);
+					ioprintf(io,"\t\t\t<NodeAnim node=\"%s\">\n",name.data);
+
+					if (!shortened) {
+						// write position keys
+						if (nd->mNumPositionKeys) {
+							ioprintf(io,"\t\t\t\t<PositionKeyList num=\"%i\">\n",nd->mNumPositionKeys);
+							for (unsigned int a = 0; a < nd->mNumPositionKeys;++a) {
+								aiVectorKey* vc = nd->mPositionKeys+a;
+								ioprintf(io,"\t\t\t\t\t<PositionKey time=\"%e\">\n"
+									"\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t</PositionKey>\n",
+									vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z);
+							}
+							ioprintf(io,"\t\t\t\t</PositionKeyList>\n");
+						}
+
+						// write scaling keys
+						if (nd->mNumScalingKeys) {
+							ioprintf(io,"\t\t\t\t<ScalingKeyList num=\"%i\">\n",nd->mNumScalingKeys);
+							for (unsigned int a = 0; a < nd->mNumScalingKeys;++a) {
+								aiVectorKey* vc = nd->mScalingKeys+a;
+								ioprintf(io,"\t\t\t\t\t<ScalingKey time=\"%e\">\n"
+									"\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t</ScalingKey>\n",
+									vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z);
+							}
+							ioprintf(io,"\t\t\t\t</ScalingKeyList>\n");
+						}
+
+						// write rotation keys
+						if (nd->mNumRotationKeys) {
+							ioprintf(io,"\t\t\t\t<RotationKeyList num=\"%i\">\n",nd->mNumRotationKeys);
+							for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) {
+								aiQuatKey* vc = nd->mRotationKeys+a;
+								ioprintf(io,"\t\t\t\t\t<RotationKey time=\"%e\">\n"
+									"\t\t\t\t\t\t%0 8f %0 8f %0 8f %0 8f\n\t\t\t\t\t</RotationKey>\n",
+									vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z,vc->mValue.w);
+							}
+							ioprintf(io,"\t\t\t\t</RotationKeyList>\n");
+						}
+					}
+					ioprintf(io,"\t\t\t</NodeAnim>\n");
+				}
+				ioprintf(io,"\t\t</NodeAnimList>\n");
+			}
+			ioprintf(io,"\t</Animation>\n");
+		}
+		ioprintf(io,"</AnimationList>\n");
+	}
+
+	// write meshes
+	if (scene->mNumMeshes) {
+		ioprintf(io,"<MeshList num=\"%i\">\n",scene->mNumMeshes);
+		for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
+			aiMesh* mesh = scene->mMeshes[i];
+			// const unsigned int width = (unsigned int)log10((double)mesh->mNumVertices)+1;
+
+			// mesh header
+			ioprintf(io,"\t<Mesh types=\"%s %s %s %s\" material_index=\"%i\">\n",
+				(mesh->mPrimitiveTypes & aiPrimitiveType_POINT    ? "points"    : ""),
+				(mesh->mPrimitiveTypes & aiPrimitiveType_LINE     ? "lines"     : ""),
+				(mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE ? "triangles" : ""),
+				(mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON  ? "polygons"  : ""),
+				mesh->mMaterialIndex);
+
+			// bones
+			if (mesh->mNumBones) {
+				ioprintf(io,"\t\t<BoneList num=\"%i\">\n",mesh->mNumBones);
+
+				for (unsigned int n = 0; n < mesh->mNumBones;++n) {
+					aiBone* bone = mesh->mBones[n];
+
+					ConvertName(name,bone->mName);
+					// bone header
+					ioprintf(io,"\t\t\t<Bone name=\"%s\">\n"
+						"\t\t\t\t<Matrix4> \n"
+						"\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+						"\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+						"\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+						"\t\t\t\t\t%0 6f %0 6f %0 6f %0 6f\n"
+						"\t\t\t\t</Matrix4> \n",
+						name.data,
+						bone->mOffsetMatrix.a1,bone->mOffsetMatrix.a2,bone->mOffsetMatrix.a3,bone->mOffsetMatrix.a4,
+						bone->mOffsetMatrix.b1,bone->mOffsetMatrix.b2,bone->mOffsetMatrix.b3,bone->mOffsetMatrix.b4,
+						bone->mOffsetMatrix.c1,bone->mOffsetMatrix.c2,bone->mOffsetMatrix.c3,bone->mOffsetMatrix.c4,
+						bone->mOffsetMatrix.d1,bone->mOffsetMatrix.d2,bone->mOffsetMatrix.d3,bone->mOffsetMatrix.d4);
+
+					if (!shortened && bone->mNumWeights) {
+						ioprintf(io,"\t\t\t\t<WeightList num=\"%i\">\n",bone->mNumWeights);
+
+						// bone weights
+						for (unsigned int a = 0; a < bone->mNumWeights;++a) {
+							aiVertexWeight* wght = bone->mWeights+a;
+
+							ioprintf(io,"\t\t\t\t\t<Weight index=\"%i\">\n\t\t\t\t\t\t%f\n\t\t\t\t\t</Weight>\n",
+								wght->mVertexId,wght->mWeight);
+						}
+						ioprintf(io,"\t\t\t\t</WeightList>\n");
+					}
+					ioprintf(io,"\t\t\t</Bone>\n");
+				}
+				ioprintf(io,"\t\t</BoneList>\n");
+			}
+
+			// faces
+			if (!shortened && mesh->mNumFaces) {
+				ioprintf(io,"\t\t<FaceList num=\"%i\">\n",mesh->mNumFaces);
+				for (unsigned int n = 0; n < mesh->mNumFaces; ++n) {
+					aiFace& f = mesh->mFaces[n];
+					ioprintf(io,"\t\t\t<Face num=\"%i\">\n"
+						"\t\t\t\t",f.mNumIndices);
+
+					for (unsigned int j = 0; j < f.mNumIndices;++j)
+						ioprintf(io,"%i ",f.mIndices[j]);
+
+					ioprintf(io,"\n\t\t\t</Face>\n");
+				}
+				ioprintf(io,"\t\t</FaceList>\n");
+			}
+
+			// vertex positions
+			if (mesh->HasPositions()) {
+				ioprintf(io,"\t\t<Positions num=\"%i\" set=\"0\" num_components=\"3\"> \n",mesh->mNumVertices);
+				if (!shortened) {
+					for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+						ioprintf(io,"\t\t%0 8f %0 8f %0 8f\n",
+							mesh->mVertices[n].x,
+							mesh->mVertices[n].y,
+							mesh->mVertices[n].z);
+					}
+				}
+				ioprintf(io,"\t\t</Positions>\n");
+			}
+
+			// vertex normals
+			if (mesh->HasNormals()) {
+				ioprintf(io,"\t\t<Normals num=\"%i\" set=\"0\" num_components=\"3\"> \n",mesh->mNumVertices);
+				if (!shortened) {
+					for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+						ioprintf(io,"\t\t%0 8f %0 8f %0 8f\n",
+							mesh->mNormals[n].x,
+							mesh->mNormals[n].y,
+							mesh->mNormals[n].z);
+					}
+				}
+				else {
+				}
+				ioprintf(io,"\t\t</Normals>\n");
+			}
+
+			// vertex tangents and bitangents
+			if (mesh->HasTangentsAndBitangents()) {
+				ioprintf(io,"\t\t<Tangents num=\"%i\" set=\"0\" num_components=\"3\"> \n",mesh->mNumVertices);
+				if (!shortened) {
+					for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+						ioprintf(io,"\t\t%0 8f %0 8f %0 8f\n",
+							mesh->mTangents[n].x,
+							mesh->mTangents[n].y,
+							mesh->mTangents[n].z);
+					}
+				}
+				ioprintf(io,"\t\t</Tangents>\n");
+
+				ioprintf(io,"\t\t<Bitangents num=\"%i\" set=\"0\" num_components=\"3\"> \n",mesh->mNumVertices);
+				if (!shortened) {
+					for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+						ioprintf(io,"\t\t%0 8f %0 8f %0 8f\n",
+							mesh->mBitangents[n].x,
+							mesh->mBitangents[n].y,
+							mesh->mBitangents[n].z);
+					}
+				}
+				ioprintf(io,"\t\t</Bitangents>\n");
+			}
+
+			// texture coordinates
+			for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) {
+				if (!mesh->mTextureCoords[a])
+					break;
+
+				ioprintf(io,"\t\t<TextureCoords num=\"%i\" set=\"%i\" num_components=\"%i\"> \n",mesh->mNumVertices,
+					a,mesh->mNumUVComponents[a]);
+				
+				if (!shortened) {
+					if (mesh->mNumUVComponents[a] == 3) {
+						for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+							ioprintf(io,"\t\t%0 8f %0 8f %0 8f\n",
+								mesh->mTextureCoords[a][n].x,
+								mesh->mTextureCoords[a][n].y,
+								mesh->mTextureCoords[a][n].z);
+						}
+					}
+					else {
+						for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+							ioprintf(io,"\t\t%0 8f %0 8f\n",
+								mesh->mTextureCoords[a][n].x,
+								mesh->mTextureCoords[a][n].y);
+						}
+					}
+				}
+				ioprintf(io,"\t\t</TextureCoords>\n");
+			}
+
+			// vertex colors
+			for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) {
+				if (!mesh->mColors[a])
+					break;
+				ioprintf(io,"\t\t<Colors num=\"%i\" set=\"%i\" num_components=\"4\"> \n",mesh->mNumVertices,a);
+				if (!shortened) {
+					for (unsigned int n = 0; n < mesh->mNumVertices; ++n) {
+						ioprintf(io,"\t\t%0 8f %0 8f %0 8f %0 8f\n",
+							mesh->mColors[a][n].r,
+							mesh->mColors[a][n].g,
+							mesh->mColors[a][n].b,
+							mesh->mColors[a][n].a);
+					}
+				}
+				ioprintf(io,"\t\t</Colors>\n");
+			}
+			ioprintf(io,"\t</Mesh>\n");
+		}
+		ioprintf(io,"</MeshList>\n");
+	}
+	ioprintf(io,"</Scene>\n</ASSIMP>");
+}
+
+} // end of namespace AssxmlExport
+
+void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
+{
+	IOStream * out = pIOSystem->Open( pFile, "wt" );
+	if (!out) return;
+
+	bool shortened = false;
+	AssxmlExport::WriteDump( pScene, out, shortened );
+
+	pIOSystem->Close( out );
+}
+
+} // end of namespace Assimp
+
+#endif // ASSIMP_BUILD_NO_ASSXML_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 49 - 0
code/AssxmlExporter.h

@@ -0,0 +1,49 @@
+/*
+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 AssxmlExporter.h
+ * ASSXML Exporter Main Header
+ */
+#ifndef AI_ASSXMLEXPORTER_H_INC
+#define AI_ASSXMLEXPORTER_H_INC
+
+// nothing really needed here - reserved for future use like properties
+
+#endif

+ 1 - 1
code/B3DImporter.cpp

@@ -548,7 +548,7 @@ aiNode *B3DImporter::ReadNODE( aiNode *parent ){
 void B3DImporter::ReadBB3D( aiScene *scene ){
 
 	_textures.clear();
-	_materials.size();
+	_materials.clear();
 
 	_vertices.clear();
 	_meshes.clear();

+ 5 - 0
code/BaseImporter.h

@@ -90,6 +90,11 @@ struct ScopeGuard
 	}
 
 private:
+    // no copying allowed.
+    ScopeGuard();
+    ScopeGuard( const ScopeGuard & );
+    ScopeGuard &operator = ( const ScopeGuard & );
+
 	T* obj;
 	bool mdismiss;
 };

+ 28 - 1
code/BlenderBMesh.cpp

@@ -65,7 +65,6 @@ BlenderBMeshConverter::BlenderBMeshConverter( const Mesh* mesh ):
 	BMesh( mesh ),
 	triMesh( NULL )
 {
-	AssertValidMesh( );
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -143,9 +142,21 @@ void BlenderBMeshConverter::DestroyTriMesh( )
 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 );
+
+		// UVs are optional, so only convert when present.
+		if ( BMesh->mloopuv.size() )
+		{
+			if ( (poly.loopstart + poly.totloop ) > static_cast<int>( BMesh->mloopuv.size() ) )
+			{
+				ThrowException( "BMesh uv loop array has incorrect size" );
+			}
+			const MLoopUV* loopUV = &BMesh->mloopuv[ poly.loopstart ];
+			AddTFace( loopUV[ 0 ].uv, loopUV[ 1 ].uv, loopUV[ 2 ].uv, poly.totloop == 4 ? loopUV[ 3 ].uv : 0 );
+		}
 	}
 	else if ( poly.totloop > 4 )
 	{
@@ -173,4 +184,20 @@ void BlenderBMeshConverter::AddFace( int v1, int v2, int v3, int v4 )
 	triMesh->totface = triMesh->mface.size( );
 }
 
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::AddTFace( const float* uv1, const float *uv2, const float *uv3, const float* uv4 )
+{
+	MTFace mtface;
+	memcpy( &mtface.uv[ 0 ], uv1, sizeof(float) * 2 );
+	memcpy( &mtface.uv[ 1 ], uv2, sizeof(float) * 2 );
+	memcpy( &mtface.uv[ 2 ], uv3, sizeof(float) * 2 );
+	
+	if ( uv4 )
+	{
+		memcpy( &mtface.uv[ 3 ], uv4, sizeof(float) * 2 );
+	}
+	
+	triMesh->mtface.push_back( mtface );
+}
+
 #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 1 - 0
code/BlenderBMesh.h

@@ -80,6 +80,7 @@ namespace Assimp
 		void DestroyTriMesh( );
 		void ConvertPolyToFaces( const Blender::MPoly& poly );
 		void AddFace( int v1, int v2, int v3, int v4 = 0 );
+		void AddTFace( const float* uv1, const float* uv2, const float *uv3, const float* uv4 = 0 );
 
 		const Blender::Mesh* BMesh;
 		Blender::Mesh* triMesh;

+ 6 - 2
code/BlenderLoader.cpp

@@ -659,11 +659,15 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
 	ConversionData& conv_data, TempArray<std::vector,aiMesh>&  temp
 	) 
 {
+	// TODO: Resolve various problems with BMesh triangluation before re-enabling.
+	//       See issues #400, #373, #318  #315 and #132.
+#if defined(TODO_FIX_BMESH_CONVERSION)
 	BlenderBMeshConverter BMeshConverter( mesh );
 	if ( BMeshConverter.ContainsBMesh( ) )
 	{
 		mesh = BMeshConverter.TriangulateBMesh( );
 	}
+#endif
 
 	typedef std::pair<const int,size_t> MyPair;
 	if ((!mesh->totface && !mesh->totloop) || !mesh->totvert) {
@@ -999,7 +1003,7 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
 }
 
 // ------------------------------------------------------------------------------------------------
-aiCamera* BlenderImporter::ConvertCamera(const Scene& /*in*/, const Object* obj, const Camera* camera, ConversionData& /*conv_data*/)
+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;
@@ -1010,7 +1014,7 @@ aiCamera* BlenderImporter::ConvertCamera(const Scene& /*in*/, const Object* obj,
 }
 
 // ------------------------------------------------------------------------------------------------
-aiLight* BlenderImporter::ConvertLight(const Scene& in, const Object* obj, const Lamp* lamp, ConversionData& conv_data)
+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;

+ 2 - 2
code/BlenderTessellator.cpp

@@ -324,7 +324,7 @@ void BlenderTessellatorP2T::Copy3DVertices( const MLoop* polyLoop, int vertexCou
 aiMatrix4x4 BlenderTessellatorP2T::GeneratePointTransformMatrix( const Blender::PlaneP2T& plane ) const
 {
 	aiVector3D sideA( 1.0f, 0.0f, 0.0f );
-	if ( fabs( plane.normal * sideA ) > 0.999f )
+	if ( std::fabs( plane.normal * sideA ) > 0.999f )
 	{
 		sideA = aiVector3D( 0.0f, 1.0f, 0.0f );
 	}
@@ -420,7 +420,7 @@ float BlenderTessellatorP2T::FindLargestMatrixElem( const aiMatrix3x3& mtx ) con
 	{
 		for ( int y = 0; y < 3; ++y )
 		{
-			result = p2tMax( fabs( mtx[ x ][ y ] ), result );
+			result = p2tMax( std::fabs( mtx[ x ][ y ] ), result );
 		}
 	}
 

+ 3 - 3
code/BoostWorkaround/boost/tuple/tuple.hpp

@@ -100,7 +100,7 @@ namespace boost	{
 			};
 
 			// dummy
-			list_elem& operator = (const list_elem& other)	{
+			list_elem& operator = (const list_elem& /*other*/)	{
 				return *this;
 			}
 
@@ -142,7 +142,7 @@ namespace boost	{
 				return me.me;
 			}
 		};
-	};
+	}
 
 	// A very minimal implementation for up to 5 elements
 	template <typename T0  = detail::nulltype,
@@ -278,6 +278,6 @@ namespace boost	{
 		tuple <> t;
 		return t;
 	}
-};
+}
 
 #endif // !! BOOST_TUPLE_INCLUDED

+ 33 - 1
code/CMakeLists.txt

@@ -111,6 +111,7 @@ SET( Common_SRCS
 	MemoryIOWrapper.h
 	ParsingUtils.h
 	StreamReader.h
+	StreamWriter.h
 	StringComparison.h
 	SGSpatialSort.cpp
 	SGSpatialSort.h
@@ -143,6 +144,7 @@ SET( Common_SRCS
 	LogAux.h
 	Bitmap.cpp
 	Bitmap.h
+	XMLTools.h
 )
 SOURCE_GROUP(Common FILES ${Common_SRCS})
 
@@ -151,6 +153,8 @@ SET( 3DS_SRCS
 	3DSHelper.h
 	3DSLoader.cpp
 	3DSLoader.h
+	3DSExporter.h
+	3DSExporter.cpp
 )
 SOURCE_GROUP(3DS FILES ${3DS_SRCS})
 
@@ -168,6 +172,20 @@ SET( ASE_SRCS
 )
 SOURCE_GROUP( ASE FILES ${ASE_SRCS})
 
+SET( ASSBIN_SRCS
+	AssbinExporter.h
+	AssbinExporter.cpp
+	AssbinLoader.h
+	AssbinLoader.cpp
+)
+SOURCE_GROUP( Assbin FILES ${ASSBIN_SRCS})
+
+SET( ASSXML_SRCS
+	AssxmlExporter.h
+	AssxmlExporter.cpp
+)
+SOURCE_GROUP( Assxml FILES ${ASSXML_SRCS})
+
 SET( B3D_SRCS
 	B3DImporter.cpp
 	B3DImporter.h
@@ -612,7 +630,7 @@ SOURCE_GROUP( unzip FILES ${unzip_SRCS})
 
 # VC2010 fixes
 if(MSVC10)
-	OPTION( VC10_STDINT_FIX "Fix for VC10 Compiler regarding pstdint.h redefinition errors" OFF )
+	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 )
@@ -643,6 +661,8 @@ SET( assimp_src
 	${3DS_SRCS}
 	${AC_SRCS}
 	${ASE_SRCS}
+	${ASSBIN_SRCS}
+	${ASSXML_SRCS}
 	${B3D_SRCS}
 	${BVH_SRCS}
 	${Collada_SRCS}
@@ -705,6 +725,13 @@ ADD_LIBRARY( assimp ${assimp_src} )
 SET_PROPERTY(TARGET assimp PROPERTY DEBUG_POSTFIX ${ASSIMP_DEBUG_POSTFIX})
 
 TARGET_LINK_LIBRARIES(assimp ${ZLIB_LIBRARIES})
+
+if(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
+	set(ASSIMP_ANDROID_JNIIOSYSTEM_PATH port/AndroidJNI)
+	add_subdirectory(../${ASSIMP_ANDROID_JNIIOSYSTEM_PATH}/ ../${ASSIMP_ANDROID_JNIIOSYSTEM_PATH}/)
+	target_link_libraries(assimp android_jniiosystem)
+endif(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
+
 SET_TARGET_PROPERTIES( assimp PROPERTIES
 	VERSION ${ASSIMP_VERSION}
 	SOVERSION ${ASSIMP_SOVERSION} # use full version 
@@ -731,6 +758,11 @@ INSTALL( TARGETS assimp
          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 (ASSIMP_ANDROID_JNIIOSYSTEM)
+	INSTALL(FILES ${HEADER_PATH}/../${ASSIMP_ANDROID_JNIIOSYSTEM_PATH}/AndroidJNIIOSystem.h
+			DESTINATION ${ASSIMP_INCLUDE_INSTALL_DIR}
+			COMPONENT assimp-dev)
+endif(ASSIMP_ANDROID_JNIIOSYSTEM)
 
 if(MSVC AND ASSIMP_INSTALL_PDB)
 	install(FILES ${Assimp_BINARY_DIR}/code/Debug/assimp${ASSIMP_DEBUG_POSTFIX}.pdb

+ 3 - 3
code/CalcTangentsProcess.cpp

@@ -115,9 +115,9 @@ 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);
+    // assert( must be verbose, dammit);
 
-	if (pMesh->mTangents) // thisimplies that mBitangents is also there
+	if (pMesh->mTangents) // this implies that mBitangents is also there
 		return false;
 
 	// If the mesh consists of lines and/or points but not of
@@ -271,7 +271,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
 		const aiVector3D& origNorm = pMesh->mNormals[a];
 		const aiVector3D& origTang = pMesh->mTangents[a];
 		const aiVector3D& origBitang = pMesh->mBitangents[a];
-		closeVertices.clear();
+		closeVertices.resize( 0 );
 
 		// find all vertices close to that position
 		vertexFinder->FindPositions( origPos, posEpsilon, verticesFound);

+ 39 - 31
code/ColladaExporter.cpp

@@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "Bitmap.h"
 #include "fast_atof.h"
 #include "SceneCombiner.h" 
+#include "XMLTools.h"
 
 #include <ctime>
 #include <set>
@@ -93,6 +94,7 @@ void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* p
 } // 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)
@@ -140,7 +142,7 @@ void ColladaExporter::WriteFile()
 	// useless Collada fu at the end, just in case we haven't had enough indirections, yet. 
 	mOutput << startstr << "<scene>" << endstr;
 	PushTag();
-	mOutput << startstr << "<instance_visual_scene url=\"#" + std::string(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr;
+	mOutput << startstr << "<instance_visual_scene url=\"#" + XMLEscape(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr;
 	PopTag();
 	mOutput << startstr << "</scene>" << endstr;
 	PopTag();
@@ -236,12 +238,12 @@ void ColladaExporter::WriteHeader()
 	if (!meta || !meta->Get("Author", value))		
 		mOutput << startstr << "<author>" << "Assimp" << "</author>" << endstr;
 	else		
-		mOutput << startstr << "<author>" << value.C_Str() << "</author>" << endstr;
+		mOutput << startstr << "<author>" << XMLEscape(value.C_Str()) << "</author>" << endstr;
 
 	if (!meta || !meta->Get("AuthoringTool", value))
 		mOutput << startstr << "<authoring_tool>" << "Assimp Exporter" << "</authoring_tool>" << endstr;
 	else		
-		mOutput << startstr << "<authoring_tool>" << value.C_Str() << "</authoring_tool>" << endstr;
+		mOutput << startstr << "<authoring_tool>" << XMLEscape(value.C_Str()) << "</authoring_tool>" << endstr;
 
 	//mOutput << startstr << "<author>" << mScene->author.C_Str() << "</author>" << endstr;
 	//mOutput << startstr << "<authoring_tool>" << mScene->authoringTool.C_Str() << "</authoring_tool>" << endstr;
@@ -342,16 +344,20 @@ void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::strin
 {
   if( !pSurface.texture.empty() )
   {
-    mOutput << startstr << "<image id=\"" << pNameAdd << "\">" << endstr;
+    mOutput << startstr << "<image id=\"" << XMLEscape(pNameAdd) << "\">" << endstr;
     PushTag(); 
     mOutput << startstr << "<init_from>";
+	
+	// URL encode image file name first, then XML encode on top
+	std::stringstream imageUrlEncoded;
     for( std::string::const_iterator it = pSurface.texture.begin(); it != pSurface.texture.end(); ++it )
     {
       if( isalnum( *it) || *it == '_' || *it == '.' || *it == '/' || *it == '\\' )
-        mOutput << *it;
+        imageUrlEncoded << *it;
       else
-        mOutput << '%' << std::hex << size_t( (unsigned char) *it) << std::dec;
+        imageUrlEncoded << '%' << std::hex << size_t( (unsigned char) *it) << std::dec;
     }
+	mOutput << XMLEscape(imageUrlEncoded.str());
     mOutput << "</init_from>" << endstr;
     PopTag();
     mOutput << startstr << "</image>" << endstr;
@@ -371,7 +377,7 @@ void ColladaExporter::WriteTextureColorEntry( const Surface& pSurface, const std
     }
 	else
     {
-      mOutput << startstr << "<texture texture=\"" << pImageName << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
+      mOutput << startstr << "<texture texture=\"" << XMLEscape(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
     }
     PopTag();
     mOutput << startstr << "</" << pTypeName << ">" << endstr;
@@ -385,21 +391,21 @@ void ColladaExporter::WriteTextureParamEntry( const Surface& pSurface, const std
   // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture
   if( !pSurface.texture.empty() )
   {
-    mOutput << startstr << "<newparam sid=\"" << pMatName << "-" << pTypeName << "-surface\">" << endstr;
+    mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-surface\">" << endstr;
     PushTag();
     mOutput << startstr << "<surface type=\"2D\">" << endstr;
     PushTag();
-    mOutput << startstr << "<init_from>" << pMatName << "-" << pTypeName << "-image</init_from>" << endstr;
+    mOutput << startstr << "<init_from>" << XMLEscape(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr;
     PopTag();
     mOutput << startstr << "</surface>" << endstr;
     PopTag();
     mOutput << startstr << "</newparam>" << endstr;
 
-    mOutput << startstr << "<newparam sid=\"" << pMatName << "-" << pTypeName << "-sampler\">" << endstr;
+    mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-sampler\">" << endstr;
     PushTag();
     mOutput << startstr << "<sampler2D>" << endstr;
     PushTag();
-    mOutput << startstr << "<source>" << pMatName << "-" << pTypeName << "-surface</source>" << endstr;
+    mOutput << startstr << "<source>" << XMLEscape(pMatName) << "-" << pTypeName << "-surface</source>" << endstr;
     PopTag();
     mOutput << startstr << "</sampler2D>" << endstr;
     PopTag();
@@ -439,7 +445,7 @@ void ColladaExporter::WriteMaterials()
       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
+		// isalnum on MSVC asserts for code points outside [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 = '_';
@@ -510,7 +516,7 @@ void ColladaExporter::WriteMaterials()
     {
       const Material& mat = *it;
       // this is so ridiculous it must be right
-      mOutput << startstr << "<effect id=\"" << mat.name << "-fx\" name=\"" << mat.name << "\">" << endstr;
+      mOutput << startstr << "<effect id=\"" << XMLEscape(mat.name) << "-fx\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
       PushTag();
       mOutput << startstr << "<profile_COMMON>" << endstr;
       PushTag();
@@ -561,9 +567,9 @@ void ColladaExporter::WriteMaterials()
     for( std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it )
     {
       const Material& mat = *it;
-      mOutput << startstr << "<material id=\"" << mat.name << "\" name=\"" << mat.name << "\">" << endstr;
+      mOutput << startstr << "<material id=\"" << XMLEscape(mat.name) << "\" name=\"" << mat.name << "\">" << endstr;
       PushTag();
-      mOutput << startstr << "<instance_effect url=\"#" << mat.name << "-fx\"/>" << endstr;
+      mOutput << startstr << "<instance_effect url=\"#" << XMLEscape(mat.name) << "-fx\"/>" << endstr;
       PopTag();
       mOutput << startstr << "</material>" << endstr;
     }
@@ -591,13 +597,14 @@ void ColladaExporter::WriteGeometryLibrary()
 void ColladaExporter::WriteGeometry( size_t pIndex)
 {
 	const aiMesh* mesh = mScene->mMeshes[pIndex];
-	std::string idstr = GetMeshId( pIndex);
+	const std::string idstr = GetMeshId( pIndex);
+	const std::string idstrEscaped = XMLEscape(idstr);
 
   if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
     return;
 
 	// opening tag
-	mOutput << startstr << "<geometry id=\"" << idstr << "\" name=\"" << idstr << "_name\" >" << endstr;
+	mOutput << startstr << "<geometry id=\"" << idstrEscaped << "\" name=\"" << idstrEscaped << "_name\" >" << endstr;
 	PushTag();
 
 	mOutput << startstr << "<mesh>" << endstr;
@@ -627,20 +634,20 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
 	}
 
 	// assemble vertex structure
-	mOutput << startstr << "<vertices id=\"" << idstr << "-vertices" << "\">" << endstr;
+	mOutput << startstr << "<vertices id=\"" << idstrEscaped << "-vertices" << "\">" << endstr;
 	PushTag();
-	mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << idstr << "-positions\" />" << endstr;
+	mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << idstrEscaped << "-positions\" />" << endstr;
 	if( mesh->HasNormals() )
-		mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << idstr << "-normals\" />" << endstr;
+		mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << idstrEscaped << "-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;
+			mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << idstrEscaped << "-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;
+			mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << idstrEscaped << "-color" << a << "\" " /*<< set=\"" << a << "\"" */ << " />" << endstr;
 	}
 	
 	PopTag();
@@ -660,7 +667,7 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
 	{
 		mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr;
 		PushTag();
-		mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstr << "-vertices\" />" << endstr;
+		mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstrEscaped << "-vertices\" />" << endstr;
 		mOutput << startstr << "<p>";
 		for( size_t a = 0; a < mesh->mNumFaces; ++a )
 		{
@@ -681,7 +688,7 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
 	{		
 		mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr;
 		PushTag();
-		mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstr << "-vertices\" />" << endstr;
+		mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstrEscaped << "-vertices\" />" << endstr;
 	
 		mOutput << startstr << "<vcount>";
 		for( size_t a = 0; a < mesh->mNumFaces; ++a )
@@ -728,11 +735,11 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
 
 	std::string arrayId = pIdString + "-array";
 
-	mOutput << startstr << "<source id=\"" << pIdString << "\" name=\"" << pIdString << "\">" << endstr;
+	mOutput << startstr << "<source id=\"" << XMLEscape(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr;
 	PushTag();
 
 	// source array
-	mOutput << startstr << "<float_array id=\"" << arrayId << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
+	mOutput << startstr << "<float_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
 	PushTag();
 
 	if( pType == FloatType_TexCoord2 )
@@ -804,11 +811,11 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
 // Writes the scene library
 void ColladaExporter::WriteSceneLibrary()
 {
-	std::string scene_name = mScene->mRootNode->mName.C_Str();
+	const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str());
 
 	mOutput << startstr << "<library_visual_scenes>" << endstr;
 	PushTag();
-	mOutput << startstr << "<visual_scene id=\"" + scene_name + "\" name=\"" + scene_name + "\">" << endstr;
+	mOutput << startstr << "<visual_scene id=\"" + scene_name_escaped + "\" name=\"" + scene_name_escaped + "\">" << endstr;
 	PushTag();
 
 	// start recursive write at the root node
@@ -833,7 +840,8 @@ void ColladaExporter::WriteNode(aiNode* pNode)
 		pNode->mName.Set(ss.str());
 	}
 
-	mOutput << startstr << "<node id=\"" << pNode->mName.data << "\" name=\"" << pNode->mName.data << "\">" << endstr;
+	const std::string node_name_escaped = XMLEscape(pNode->mName.data);
+	mOutput << startstr << "<node id=\"" << node_name_escaped << "\" name=\"" << node_name_escaped << "\">" << endstr;
 	PushTag();
 
 	// write transformation - we can directly put the matrix there
@@ -854,13 +862,13 @@ void ColladaExporter::WriteNode(aiNode* pNode)
 	if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
 		continue;
 
-		mOutput << startstr << "<instance_geometry url=\"#" << GetMeshId( pNode->mMeshes[a]) << "\">" << endstr;
+		mOutput << startstr << "<instance_geometry url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "\">" << endstr;
 		PushTag();
 	mOutput << startstr << "<bind_material>" << endstr;
 	PushTag();
 	mOutput << startstr << "<technique_common>" << endstr;
 	PushTag();
-	mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << materials[mesh->mMaterialIndex].name << "\" />" << endstr;
+	mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLEscape(materials[mesh->mMaterialIndex].name) << "\" />" << endstr;
 		PopTag();
 	mOutput << startstr << "</technique_common>" << endstr;
 	PopTag();

+ 0 - 1
code/ColladaHelper.h

@@ -118,7 +118,6 @@ struct Camera
 	float mZNear, mZFar;
 };
 
-#define aiLightSource_AMBIENT 0xdeaddead
 #define ASSIMP_COLLADA_LIGHT_ANGLE_NOT_SET 1e9f
 
 /** A collada light source. */

+ 7 - 8
code/ColladaLoader.cpp

@@ -73,7 +73,7 @@ static const aiImporterDesc desc = {
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 ColladaLoader::ColladaLoader()
-: noSkeletonMesh(), ignoreUpDirection(false)
+: noSkeletonMesh(), ignoreUpDirection(false), mNodeNameCounter()
 {}
 
 // ------------------------------------------------------------------------------------------------
@@ -133,6 +133,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
 	mLights.clear();
 	mCameras.clear();
 	mTextures.clear();
+	mAnims.clear();
 
 	// parse the input file
 	ColladaParser parser( pIOHandler, pFile);
@@ -307,10 +308,6 @@ void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Coll
 			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();
@@ -340,7 +337,7 @@ void ColladaLoader::BuildLightsForNode( const ColladaParser& pParser, const Coll
 				{
 					// 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))+
+					out->mAngleOuterCone = AI_DEG_TO_RAD (std::acos(std::pow(0.1f,1.f/srcLight->mFalloffExponent))+
 						srcLight->mFalloffAngle);
 				}
 				else {
@@ -904,6 +901,8 @@ void ColladaLoader::StoreAnimations( aiScene* pScene, const ColladaParser& pPars
 		pScene->mAnimations = new aiAnimation*[mAnims.size()];
 		std::copy( mAnims.begin(), mAnims.end(), pScene->mAnimations);
 	}
+
+	mAnims.clear();
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -1544,7 +1543,7 @@ const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, c
 
 // ------------------------------------------------------------------------------------------------
 // Finds a proper name for a node derived from the collada-node's properties
-std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) const
+std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode)
 {
 	// 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.
@@ -1558,7 +1557,7 @@ std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) const
 	{
 		// 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());
+    return boost::str( boost::format( "$ColladaAutoName$_%d") % mNodeNameCounter++);
 	}
 }
 

+ 4 - 1
code/ColladaLoader.h

@@ -203,7 +203,7 @@ protected:
 	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;
+	std::string FindNameForNode( const Collada::Node* pNode);
 
 protected:
 	/** Filename, for a verbose error message */
@@ -235,6 +235,9 @@ protected:
 
 	bool noSkeletonMesh;
 	bool ignoreUpDirection;
+
+	/** Used by FindNameForNode() to generate unique node names */
+	unsigned int mNodeNameCounter;
 };
 
 } // end of namespace Assimp

+ 87 - 37
code/ColladaParser.cpp

@@ -1163,6 +1163,19 @@ void ColladaParser::ReadEffectProfileCommon( Collada::Effect& pEffect)
 				// just syntactic sugar
 			}
 
+			else if( mFormat == FV_1_4_n && IsElement( "image"))
+			{
+				// read ID. Another entry which is "optional" by design but obligatory in reality
+				int attrID = GetAttribute( "id");
+				std::string id = mReader->getAttributeValue( attrID);
+
+				// create an entry and store it in the library under its ID
+				mImageLibrary[id] = Image();
+
+				// read on from there
+				ReadImage( mImageLibrary[id]);
+			}
+
 			/* Shading modes */
 			else if( IsElement( "phong"))
 				pEffect.mShadeType = Shade_Phong;
@@ -1854,14 +1867,15 @@ void ColladaParser::ReadIndexData( Mesh* pMesh)
 	// read primitive count from the attribute
 	int attrCount = GetAttribute( "count");
 	size_t numPrimitives = (size_t) mReader->getAttributeValueAsInt( attrCount);
+	// some mesh types (e.g. tristrips) don't specify primitive count upfront,
+	// so we need to sum up the actual number of primitives while we read the <p>-tags
+	size_t actualPrimitives = 0;
 
-	// material subgroup 
+	// 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();
@@ -1920,7 +1934,7 @@ void ColladaParser::ReadIndexData( Mesh* pMesh)
 				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);
+					actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType);
 				}
 			} else
 			{
@@ -1935,6 +1949,14 @@ void ColladaParser::ReadIndexData( Mesh* pMesh)
 			break;
 		}
 	}
+
+	// small sanity check
+	if (primType != Prim_TriFans && primType != Prim_TriStrips)
+		ai_assert(actualPrimitives == numPrimitives);
+
+	// only when we're done reading all <p> tags (and thus know the final vertex count) can we commit the submesh
+	subgroup.mNumFaces = actualPrimitives;
+	pMesh->mSubMeshes.push_back(subgroup);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -1982,7 +2004,7 @@ void ColladaParser::ReadInputChannel( std::vector<InputChannel>& poChannels)
 
 // ------------------------------------------------------------------------------------------------
 // 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 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 
@@ -2080,70 +2102,98 @@ void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPer
 			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;
+	// For continued primitives, the given count is actually the number of <p>'s inside the parent tag
+	if ( pPrimType == Prim_TriStrips){
+		size_t numberOfVertices = indices.size() / numOffsets;
+		numPrimitives = numberOfVertices - 2;
+	}
 
 	pMesh->mFaceSize.reserve( numPrimitives);
 	pMesh->mFacePosIndices.reserve( indices.size() / numOffsets);
 
-	for( size_t a = 0; a < numPrimitives; a++)
+	size_t polylistStartVertex = 0;
+	for (size_t currentPrimitive = 0; currentPrimitive < numPrimitives; currentPrimitive++)
 	{
 		// determine number of points for this primitive
 		size_t numPoints = 0;
 		switch( pPrimType)
 		{
 			case Prim_Lines:
-				numPoints = 2; 
+				numPoints = 2;
+				for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+					CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+				break;
+			case Prim_Triangles:
+				numPoints = 3;
+				for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+					CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
 				break;
-			case Prim_Triangles: 
-				numPoints = 3; 
+			case Prim_TriStrips:
+				numPoints = 3;
+				ReadPrimTriStrips(numOffsets, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
 				break;
 			case Prim_Polylist: 
-				numPoints = pVCount[a];
+				numPoints = pVCount[currentPrimitive];
+				for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+					CopyVertex(polylistStartVertex + currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, 0, indices);
+				polylistStartVertex += numPoints;
 				break;
 			case Prim_TriFans: 
 			case Prim_Polygon:
-				numPoints = indices.size() / numOffsets; 
+				numPoints = indices.size() / numOffsets;
+				for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+					CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
 				break;
 			default:
-				// LineStrip and TriStrip not supported due to expected index unmangling
+				// LineStrip is not supported due to expected index unmangling
 				ThrowException( "Unsupported primitive type.");
 				break;
 		}
 
 		// store the face size to later reconstruct the face from
 		pMesh->mFaceSize.push_back( numPoints);
-
-		// 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");
+	return numPrimitives;
+}
+
+void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices){
+	// calculate the base offset of the vertex whose attributes we ant to copy
+	size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets;
+
+	// don't overrun the boundaries of the index list
+	size_t maxIndexRequested = baseOffset + numOffsets - 1;
+	ai_assert(maxIndexRequested < indices.size());
+
+	// extract per-vertex channels using the global per-vertex offset
+	for (std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
+		ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
+	// and extract per-index channels using there specified offset
+	for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
+		ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
+
+	// store the vertex-data index for later assignment of bone vertex weights
+	pMesh->mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]);
+}
+
+void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices){
+	if (currentPrimitive % 2 != 0){
+		//odd tristrip triangles need their indices mangled, to preserve winding direction
+		CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+		CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+		CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+	}
+	else {//for non tristrips or even tristrip triangles
+		CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+		CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+		CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+	}
 }
 
 // ------------------------------------------------------------------------------------------------

+ 10 - 1
code/ColladaParser.h

@@ -177,9 +177,18 @@ protected:
 	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 ReadPrimitives( Collada::Mesh* pMesh, std::vector<Collada::InputChannel>& pPerIndexChannels,
 		size_t pNumPrimitives, const std::vector<size_t>& pVCount, Collada::PrimitiveType pPrimType);
 
+	/** Copies the data for a single primitive into the mesh, based on the InputChannels */
+	void CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset,
+		Collada::Mesh* pMesh, std::vector<Collada::InputChannel>& pPerIndexChannels,
+		size_t currentPrimitive, const std::vector<size_t>& indices);
+
+	/** Reads one triangle of a tristrip into the mesh */
+	void ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Collada::Mesh* pMesh,
+		std::vector<Collada::InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices);
+
 	/** 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);
 

+ 3 - 3
code/ComputeUVMappingProcess.cpp

@@ -207,7 +207,7 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
 		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);
+				(std::asin  (diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.f);
 		}
 	}
 	else if (axis * base_axis_y >= angle_epsilon)	{
@@ -215,7 +215,7 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
 		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);
+				(std::asin  (diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.f);
 		}
 	}
 	else if (axis * base_axis_z >= angle_epsilon)	{
@@ -223,7 +223,7 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
 		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);
+				(std::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

+ 19 - 9
code/ConvertToLHProcess.cpp

@@ -57,12 +57,15 @@ using namespace Assimp;
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 MakeLeftHandedProcess::MakeLeftHandedProcess()
-{}
+: BaseProcess() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
-MakeLeftHandedProcess::~MakeLeftHandedProcess()
-{}
+MakeLeftHandedProcess::~MakeLeftHandedProcess() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
@@ -121,8 +124,9 @@ void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pPare
 	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);
+    for( size_t a = 0; a < pNode->mNumChildren; ++a ) {
+        ProcessNode( pNode->mChildren[ a ], pParentGlobalRotation * pNode->mTransformation );
+    }
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -244,6 +248,10 @@ void FlipUVsProcess::ProcessMaterial (aiMaterial* _mat)
 	aiMaterial* mat = (aiMaterial*)_mat;
 	for (unsigned int a = 0; a < mat->mNumProperties;++a)	{
 		aiMaterialProperty* prop = mat->mProperties[a];
+        if( !prop ) {
+            DefaultLogger::get()->debug( "Property is null" );
+            continue;
+        }
 
 		// UV transformation key?
 		if (!::strcmp( prop->mKey.data, "$tex.uvtrafo"))	{
@@ -263,11 +271,13 @@ 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;
+        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;
+        for( unsigned int b = 0; b < pMesh->mNumVertices; b++ ) {
+            pMesh->mTextureCoords[ a ][ b ].y = 1.0f - pMesh->mTextureCoords[ a ][ b ].y;
+        }
 	}
 }
 

+ 2 - 2
code/DeboneProcess.cpp

@@ -111,8 +111,8 @@ void DeboneProcess::Execute( aiScene* pScene)
 
 	if(numSplits)	{
 		// we need to do something. Let's go.
-		mSubMeshIndices.clear();
-		mSubMeshIndices.resize(pScene->mNumMeshes);
+		//mSubMeshIndices.clear();                  // really needed?
+		mSubMeshIndices.resize(pScene->mNumMeshes); // because we're doing it here anyway
 
 		// build a new array of meshes for the scene
 		std::vector<aiMesh*> meshes;

+ 18 - 11
code/DefaultIOStream.h

@@ -56,52 +56,59 @@ namespace Assimp	{
 class DefaultIOStream : public IOStream
 {
 	friend class DefaultIOSystem;
+#if __ANDROID__ 
+#if __ANDROID_API__ > 9
+#if defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT)
+    friend class AndroidJNIIOSystem;
+#endif // defined(AI_CONFIG_ANDROID_JNI_ASSIMP_MANAGER_SUPPORT)
+#endif // __ANDROID_API__ > 9
+#endif // __ANDROID__ 
 
 protected:
-	DefaultIOStream ();
-	DefaultIOStream (FILE* pFile, const std::string &strFilename);
+	DefaultIOStream();
+	DefaultIOStream(FILE* pFile, const std::string &strFilename);
 
 public:
 	/** Destructor public to allow simple deletion to close the file. */
 	~DefaultIOStream ();
 
 	// -------------------------------------------------------------------
-	// Read from stream
+	/// Read from stream
     size_t Read(void* pvBuffer, 
 		size_t pSize, 
 		size_t pCount);
 
 
 	// -------------------------------------------------------------------
-	// Write to stream
+	/// Write to stream
     size_t Write(const void* pvBuffer, 
 		size_t pSize,
 		size_t pCount);
 
 	// -------------------------------------------------------------------
-	// Seek specific position
+	/// Seek specific position
 	aiReturn Seek(size_t pOffset,
 		aiOrigin pOrigin);
 
 	// -------------------------------------------------------------------
-	// Get current seek position
+	/// Get current seek position
     size_t Tell() const;
 
 	// -------------------------------------------------------------------
-	// Get size of file
+	/// Get size of file
 	size_t FileSize() const;
 
 	// -------------------------------------------------------------------
-	// Flush file contents
+	/// Flush file contents
 	void Flush();
 
 private:
-	//!	File datastructure, using clib
+	//	File datastructure, using clib
 	FILE* mFile;
-	//!	Filename
+	//	Filename
 	std::string	mFilename;
 
-	//! Cached file size
+	// Cached file size
 	mutable size_t cachedSize;
 };
 

+ 25 - 5
code/Exporter.cpp

@@ -78,7 +78,10 @@ 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*) {}
+void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*);
+void ExportScene3DS(const char*, IOSystem*, const aiScene*);
+void ExportSceneAssbin(const char*, IOSystem*, const aiScene*);
+void ExportSceneAssxml(const char*, IOSystem*, const aiScene*);
 
 // ------------------------------------------------------------------------------------------------
 // global array of all export formats which Assimp supports in its current build
@@ -90,7 +93,7 @@ Exporter::ExportFormatEntry gExporters[] =
 
 #ifndef ASSIMP_BUILD_NO_FXILE_EXPORTER
 	Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile,
-	aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs),
+		aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs),
 #endif
 
 #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
@@ -111,11 +114,23 @@ Exporter::ExportFormatEntry gExporters[] =
 	Exporter::ExportFormatEntry( "ply", "Stanford Polygon Library", "ply" , &ExportScenePly, 
 		aiProcess_PreTransformVertices
 	),
+	Exporter::ExportFormatEntry( "plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary,
+		aiProcess_PreTransformVertices
+	),
+#endif
+
+#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
+	Exporter::ExportFormatEntry( "3ds", "Autodesk 3DS (legacy)", "3ds" , &ExportScene3DS,
+		aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices),
 #endif
 
-//#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
-//	ExportFormatEntry( "3ds", "Autodesk 3DS (legacy format)", "3ds" , &ExportScene3DS),
-//#endif
+#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
+	Exporter::ExportFormatEntry( "assbin", "Assimp Binary", "assbin" , &ExportSceneAssbin, 0),
+#endif
+
+#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
+	Exporter::ExportFormatEntry( "assxml", "Assxml Document", "assxml" , &ExportSceneAssxml, 0),
+#endif
 };
 
 #define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0]))
@@ -443,6 +458,11 @@ const aiExportFormatDesc* Exporter :: GetExportFormatDescription( size_t pIndex
 	if (pIndex >= GetExportFormatCount()) {
 		return NULL;
 	}
+	
+	// Return from static storage if the requested index is built-in.
+	if (pIndex < sizeof(gExporters) / sizeof(gExporters[0])) {
+		return &gExporters[pIndex].mDescription;
+	}
 
 	return &pimpl->mExporters[pIndex].mDescription;
 }

+ 1 - 1
code/FBXAnimation.cpp

@@ -59,7 +59,7 @@ namespace FBX {
 	using namespace Util;
 
 // ------------------------------------------------------------------------------------------------
-AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc)
+AnimationCurve::AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& /*doc*/)
 : Object(id, element, name)
 {
 	const Scope& sc = GetRequiredScope(element);

+ 6 - 5
code/FBXBinaryTokenizer.cpp

@@ -55,14 +55,15 @@ namespace FBX {
 
 // ------------------------------------------------------------------------------------------------
 Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int offset)
-	: sbegin(sbegin)
+	: 
+	#ifdef DEBUG
+	contents(sbegin, static_cast<size_t>(send-sbegin)),
+	#endif
+	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);
@@ -395,4 +396,4 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int le
 } // !FBX
 } // !Assimp
 
-#endif
+#endif

+ 11 - 14
code/FBXConverter.cpp

@@ -381,8 +381,6 @@ private:
 		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());
 	}
 
@@ -499,15 +497,15 @@ private:
 		bool is_id[3] = { true, true, true };
 
 		aiMatrix4x4 temp[3];
-		if(fabs(rotation.z) > angle_epsilon) {
+		if(std::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) {
+		if(std::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) {
+		if(std::fabs(rotation.x) > angle_epsilon) {
 			aiMatrix4x4::RotationX(AI_DEG_TO_RAD(rotation.x),temp[0]);
 			is_id[0] = false;
 		}
@@ -676,7 +674,7 @@ private:
 		}
 
 		const aiVector3D& Scaling = PropertyGet<aiVector3D>(props,"Lcl Scaling",ok);
-		if(ok && fabs(Scaling.SquareLength()-1.0f) > zero_epsilon) {
+		if(ok && std::fabs(Scaling.SquareLength()-1.0f) > zero_epsilon) {
 			aiMatrix4x4::Scaling(Scaling,chain[TransformationComp_Scaling]);
 		}
 
@@ -686,7 +684,7 @@ private:
 		}
 		
 		const aiVector3D& GeometricScaling = PropertyGet<aiVector3D>(props, "GeometricScaling", ok);
-		if (ok && fabs(GeometricScaling.SquareLength() - 1.0f) > zero_epsilon) {
+		if (ok && std::fabs(GeometricScaling.SquareLength() - 1.0f) > zero_epsilon) {
 			aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]);
 		}
 		
@@ -767,7 +765,6 @@ private:
 
 		// 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);
 
@@ -1320,7 +1317,7 @@ private:
 
 
 	// ------------------------------------------------------------------------------------------------
-	void ConvertCluster(std::vector<aiBone*>& bones, const Model& model, const Cluster& cl, 		
+	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,
@@ -2348,7 +2345,7 @@ private:
 
 	// ------------------------------------------------------------------------------------------------
 	aiNodeAnim* GenerateScalingNodeAnim(const std::string& name, 
-		const Model& target, 
+		const Model& /*target*/,
 		const std::vector<const AnimationCurveNode*>& curves,
 		const LayerMap& layer_map,
 		double& max_time,
@@ -2379,7 +2376,7 @@ private:
 
 	// ------------------------------------------------------------------------------------------------
 	aiNodeAnim* GenerateTranslationNodeAnim(const std::string& name, 
-		const Model& target, 
+		const Model& /*target*/,
 		const std::vector<const AnimationCurveNode*>& curves,
 		const LayerMap& layer_map,
 		double& max_time,
@@ -2831,7 +2828,7 @@ private:
 
 
 	// ------------------------------------------------------------------------------------------------
-	void ConvertScaleKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& layers,
+	void ConvertScaleKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, const LayerMap& /*layers*/,
 		double& maxTime,
 		double& minTime)
 	{
@@ -2852,7 +2849,7 @@ private:
 
 	// ------------------------------------------------------------------------------------------------
 	void ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, 
-		const LayerMap& layers,
+		const LayerMap& /*layers*/,
 		double& maxTime,
 		double& minTime)
 	{
@@ -2870,7 +2867,7 @@ private:
 
 	// ------------------------------------------------------------------------------------------------
 	void ConvertRotationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes, 
-		const LayerMap& layers, 
+		const LayerMap& /*layers*/,
 		double& maxTime,
 		double& minTime,
 		Model::RotOrder order)

+ 10 - 5
code/FBXDocument.cpp

@@ -253,8 +253,8 @@ 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) {
+	// Cannot use array default initialization syntax because vc8 fails on it
+	for (unsigned int i = 0; i < sizeof(creationTimeStamp) / sizeof(creationTimeStamp[0]); ++i) {
 		creationTimeStamp[i] = 0;
 	}
 
@@ -263,7 +263,7 @@ Document::Document(const Parser& parser, const ImportSettings& settings)
 
 	ReadGlobalSettings();
 
-	// this order is important, connections need parsed objects to check
+	// 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();
@@ -277,13 +277,18 @@ Document::~Document()
 	BOOST_FOREACH(ObjectMap::value_type& v, objects) {
 		delete v.second;
 	}
+
+	BOOST_FOREACH(ConnectionMap::value_type& v, src_connections) {
+		delete v.second;
+	}
+	// |dest_connections| contain the same Connection objects as the |src_connections|
 }
 
 
 // ------------------------------------------------------------------------------------------------
 void Document::ReadHeader()
 {
-	// read ID objects from "Objects" section
+	// Read ID objects from "Objects" section
 	const Scope& sc = parser.GetRootScope();
 	const Element* const ehead = sc["FBXHeaderExtension"];
 	if(!ehead || !ehead->Compound()) {
@@ -293,7 +298,7 @@ void Document::ReadHeader()
 	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
+	// 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");

+ 2 - 2
code/FBXDocument.h

@@ -696,7 +696,7 @@ public:
 public:
 
 	/** Get the Skin attached to this geometry or NULL */
-	const Skin* const DeformerSkin() const {
+	const Skin* DeformerSkin() const {
 		return skin;
 	}
 
@@ -1096,7 +1096,7 @@ public:
 		return transformLink;
 	}
 
-	const Model* const TargetNode() const {
+	const Model* TargetNode() const {
 		return node;
 	}
 

+ 3 - 1
code/FBXImporter.cpp

@@ -105,7 +105,7 @@ bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool
 
 	else if ((!extension.length() || checkSig) && pIOHandler)	{
 		// at least ascii FBX files usually have a 'FBX' somewhere in their head
-		const char* tokens[] = {"FBX"};
+		const char* tokens[] = {"fbx"};
 		return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
 	}
 	return false;
@@ -179,6 +179,8 @@ void FBXImporter::InternReadFile( const std::string& pFile,
 
 		// convert the FBX DOM to aiScene
 		ConvertToAssimpScene(pScene,doc);
+
+		std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
 	}
 	catch(std::exception&) {
 		std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());

+ 1 - 1
code/FBXMaterial.cpp

@@ -207,7 +207,7 @@ Texture::~Texture()
 
 }
 
-LayeredTexture::LayeredTexture(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+LayeredTexture::LayeredTexture(uint64_t id, const Element& element, const Document& /*doc*/, const std::string& name)
 : Object(id,element,name)
 ,texture(0)
 ,blendMode(BlendMode_Modulate)

+ 4 - 2
code/FBXMeshGeometry.cpp

@@ -466,8 +466,9 @@ void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out,
 	const std::string& MappingInformationType,
 	const std::string& ReferenceInformationType)
 {
+	const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent";
 	ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
-		"Tangent",
+		str,
 		"TangentIndex",
 		vertices.size(),
 		mapping_counts,
@@ -481,8 +482,9 @@ void MeshGeometry::ReadVertexDataBinormals(std::vector<aiVector3D>& binormals_ou
 	const std::string& MappingInformationType,
 	const std::string& ReferenceInformationType)
 {
+	const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal";
 	ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType,
-		"Binormal",
+		str,
 		"BinormalIndex",
 		vertices.size(),
 		mapping_counts,

+ 24 - 34
code/FBXParser.cpp

@@ -93,7 +93,7 @@ namespace {
 	}
 
 	// ------------------------------------------------------------------------------------------------
-	void ParseWarning(const std::string& message, const Element* element = NULL)
+/*	void ParseWarning(const std::string& message, const Element* element = NULL)
 	{
 		if(element) {
 			ParseWarning(message,element->KeyToken());
@@ -103,7 +103,7 @@ namespace {
 			DefaultLogger::get()->warn("FBX-Parser: " + message);
 		}
 	}
-
+*/
 	// ------------------------------------------------------------------------------------------------
 	void ParseError(const std::string& message, TokenPtr token)
 	{
@@ -113,6 +113,18 @@ namespace {
 		ParseError(message);
 	}
 
+	// Initially, we did reinterpret_cast, breaking strict aliasing rules.
+	// This actually caused trouble on Android, so let's be safe this time.
+	// https://github.com/assimp/assimp/issues/24
+	template <typename T>
+	T SafeParse(const char* data, const char* end) {
+		// Actual size validation happens during Tokenization so
+		// this is valid as an assertion.
+		ai_assert(static_cast<size_t>(end - data) >= sizeof(T));
+		T result = static_cast<T>(0);
+		::memcpy(&result, data, sizeof(T));
+		return result;
+	}
 }
 
 namespace Assimp {
@@ -275,9 +287,7 @@ uint64_t ParseTokenAsID(const Token& t, const char*& err_out)
 			return 0L;
 		}
 
-		ai_assert(t.end() - data == 9);
-
-		BE_NCONST uint64_t id = *reinterpret_cast<const uint64_t*>(data+1);
+		BE_NCONST uint64_t id = SafeParse<uint64_t>(data+1, t.end());
 		AI_SWAP8(id);
 		return id;
 	}
@@ -316,8 +326,7 @@ size_t ParseTokenAsDim(const Token& t, const char*& err_out)
 			return 0;
 		}
 
-		ai_assert(t.end() - data == 9);
-		BE_NCONST uint64_t id = *reinterpret_cast<const uint64_t*>(data+1);
+		BE_NCONST uint64_t id = SafeParse<uint64_t>(data+1, t.end());
 		AI_SWAP8(id);
 		return static_cast<size_t>(id);
 	}
@@ -364,24 +373,10 @@ float ParseTokenAsFloat(const Token& t, const char*& err_out)
 		}
 
 		if (data[0] == 'F') {
-			// Actual size validation happens during Tokenization so
-			// this is valid as an assertion.
-			ai_assert(t.end() - data == sizeof(float) + 1);
-			// Initially, we did reinterpret_cast, breaking strict aliasing rules.
-			// This actually caused trouble on Android, so let's be safe this time.
-			// https://github.com/assimp/assimp/issues/24
-			
-			float out_float;
-			::memcpy(&out_float, data+1, sizeof(float));
-			return out_float;
+			return SafeParse<float>(data+1, t.end());
 		}
 		else {
-			ai_assert(t.end() - data == sizeof(double) + 1);
-			
-			// Same
-			double out_double;
-			::memcpy(&out_double, data+1, sizeof(double));
-			return out_double;
+			return static_cast<float>( SafeParse<double>(data+1, t.end()) );
 		}
 	}
 
@@ -416,8 +411,7 @@ int ParseTokenAsInt(const Token& t, const char*& err_out)
 			return 0;
 		}
 
-		ai_assert(t.end() - data == 5);
-		BE_NCONST int32_t ival = *reinterpret_cast<const int32_t*>(data+1);
+		BE_NCONST int32_t ival = SafeParse<int32_t>(data+1, t.end());
 		AI_SWAP4(ival);
 		return static_cast<int>(ival);
 	}
@@ -453,10 +447,8 @@ std::string ParseTokenAsString(const Token& t, const char*& err_out)
 			return "";
 		}
 
-		ai_assert(t.end() - data >= 5);
-
 		// read string length
-		BE_NCONST int32_t len = *reinterpret_cast<const int32_t*>(data+1);
+		BE_NCONST int32_t len = SafeParse<int32_t>(data+1, t.end());
 		AI_SWAP4(len);
 
 		ai_assert(t.end() - data == 5 + len);
@@ -494,7 +486,7 @@ void ReadBinaryDataArrayHead(const char*& data, const char* end, char& type, uin
 	type = *data;
 
 	// read number of elements
-	BE_NCONST uint32_t len = *reinterpret_cast<const uint32_t*>(data+1);
+	BE_NCONST uint32_t len = SafeParse<uint32_t>(data+1, end);
 	AI_SWAP4(len);
 
 	count = len;
@@ -506,16 +498,14 @@ void ReadBinaryDataArrayHead(const char*& data, const char* end, char& type, uin
 // read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header)
 void ReadBinaryDataArray(char type, uint32_t count, const char*& data, const char* end, 
 	std::vector<char>& buff, 
-	const Element& el)
+	const Element& /*el*/)
 {
-	ai_assert(static_cast<size_t>(end-data) >= 4); // runtime check for this happens at tokenization stage
-
-	BE_NCONST uint32_t encmode = *reinterpret_cast<const uint32_t*>(data);
+	BE_NCONST uint32_t encmode = SafeParse<uint32_t>(data, end);
 	AI_SWAP4(encmode);
 	data += 4;
 
 	// next comes the compressed length
-	BE_NCONST uint32_t comp_len = *reinterpret_cast<const uint32_t*>(data);
+	BE_NCONST uint32_t comp_len = SafeParse<uint32_t>(data, end);
 	AI_SWAP4(comp_len);
 	data += 4;
 

+ 2 - 2
code/FBXProperties.cpp

@@ -85,7 +85,7 @@ Property* ReadTypedProperty(const Element& element)
 	else if (!strcmp(cs,"bool") || !strcmp(cs,"Bool")) {
 		return new TypedProperty<bool>(ParseTokenAsInt(*tok[4]) != 0);
 	}
-	else if (!strcmp(cs,"int") || !strcmp(cs,"enum")) {
+	else if (!strcmp(cs, "int") || !strcmp(cs, "Int") || !strcmp(cs, "enum") || !strcmp(cs, "Enum")) {
 		return new TypedProperty<int>(ParseTokenAsInt(*tok[4]));
 	}
 	else if (!strcmp(cs,"ULongLong")) {
@@ -105,7 +105,7 @@ Property* ReadTypedProperty(const Element& element)
 			ParseTokenAsFloat(*tok[6]))
 		);
 	}
-	else if (!strcmp(cs,"double") || !strcmp(cs,"Number") || !strcmp(cs,"KTime") || !strcmp(cs,"Float")) {
+	else if (!strcmp(cs,"double") || !strcmp(cs,"Number") || !strcmp(cs,"KTime") || !strcmp(cs,"Float") || !strcmp(cs,"FieldOfView")) {
 		return new TypedProperty<float>(ParseTokenAsFloat(*tok[4]));
 	}
 	return NULL;

+ 2 - 4
code/FBXProperties.h

@@ -143,8 +143,7 @@ private:
 // ------------------------------------------------------------------------------------------------
 template <typename T>
 inline T PropertyGet(const PropertyTable& in, const std::string& name, 
-	const T& defaultValue, 
-	bool ignoreTemplate = false)
+	const T& defaultValue)
 {
 	const Property* const prop = in.Get(name);
 	if(!prop) {
@@ -164,8 +163,7 @@ inline T PropertyGet(const PropertyTable& in, const std::string& name,
 // ------------------------------------------------------------------------------------------------
 template <typename T>
 inline T PropertyGet(const PropertyTable& in, const std::string& name, 
-	bool& result, 
-	bool ignoreTemplate = false)
+	bool& result)
 {
 	const Property* const prop = in.Get(name);
 	if(!prop) {

+ 5 - 4
code/FBXTokenizer.cpp

@@ -58,14 +58,15 @@ namespace FBX {
 
 // ------------------------------------------------------------------------------------------------
 Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column)
-	: sbegin(sbegin)
+	:
+#ifdef DEBUG
+	contents(sbegin, static_cast<size_t>(send-sbegin)),
+#endif
+	sbegin(sbegin)
 	, send(send)
 	, type(type)
 	, line(line)
 	, column(column)
-#ifdef DEBUG
-	, contents(sbegin, static_cast<size_t>(send-sbegin))
-#endif
 {
 	ai_assert(sbegin);
 	ai_assert(send);

+ 2 - 4
code/FindInstancesProcess.cpp

@@ -85,8 +85,7 @@ bool CompareBones(const aiMesh* orig, const aiMesh* inst)
 		aiBone* oha = inst->mBones[i];
 
 		if (aha->mNumWeights   != oha->mNumWeights   ||
-			aha->mOffsetMatrix != oha->mOffsetMatrix ||
-			aha->mNumWeights   != oha->mNumWeights) {
+			aha->mOffsetMatrix != oha->mOffsetMatrix) {
 			return false;
 		}
 
@@ -174,7 +173,6 @@ void FindInstancesProcess::Execute( aiScene* pScene)
 
 					// use a constant epsilon for colors and UV coordinates
 					static const float uvEpsilon = 10e-4f;
-
 					{
 						unsigned int i, end = orig->GetNumUVChannels();
 						for(i = 0; i < end; ++i) {
@@ -260,7 +258,7 @@ void FindInstancesProcess::Execute( aiScene* pScene)
 					pScene->mMeshes[real++] = pScene->mMeshes[i];
 			}
 
-			// And update the nodegraph with our nice lookup table
+			// And update the node graph with our nice lookup table
 			UpdateMeshIndices(pScene->mRootNode,remapping.get());
 
 			// write to log

+ 1 - 1
code/FindInvalidDataProcess.cpp

@@ -221,7 +221,7 @@ AI_FORCE_INLINE bool EpsilonCompare(const T& n, const T& s, float epsilon);
 
 // ------------------------------------------------------------------------------------------------
 AI_FORCE_INLINE bool EpsilonCompare(float n, float s, float epsilon) {
-	return fabs(n-s)>epsilon;
+	return std::fabs(n-s)>epsilon;
 }
 
 // ------------------------------------------------------------------------------------------------

+ 2 - 2
code/FixNormalsStep.cpp

@@ -149,8 +149,8 @@ bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index)
 	if (fDelta1_z < 0.05f * sqrtf( fDelta1_y * fDelta1_x ))return false;
 
 	// now compare the volumes of the bounding boxes
-	if (::fabsf(fDelta0_x * fDelta1_yz) <
-		::fabsf(fDelta1_x * fDelta1_y * fDelta1_z))
+	if (std::fabs(fDelta0_x * fDelta1_yz) <
+		std::fabs(fDelta1_x * fDelta1_y * fDelta1_z))
 	{
 		if (!DefaultLogger::isNullLogger())
 		{

+ 2 - 2
code/GenVertexNormalsProcess.cpp

@@ -93,7 +93,7 @@ void GenVertexNormalsProcess::Execute( aiScene* pScene)
 	bool bHas = false;
 	for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
 	{
-		if(GenMeshVertexNormals( pScene->mMeshes[a],a))
+        if(GenMeshVertexNormals( pScene->mMeshes[a],a))
 			bHas = true;
 	}
 
@@ -204,7 +204,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int
 	// Slower code path if a smooth angle is set. There are many ways to achieve
 	// the effect, this one is the most straightforward one.
 	else	{
-		const float fLimit = ::cos(configMaxAngle); 
+		const float fLimit = std::cos(configMaxAngle);
 		for (unsigned int i = 0; i < pMesh->mNumVertices;++i)	{
 			// Get all vertices that share this one ...
 			vertexFinder->FindPositions( pMesh->mVertices[i] , posEpsilon, verticesFound);

+ 1 - 1
code/GenVertexNormalsProcess.h

@@ -53,7 +53,7 @@ namespace Assimp {
 // ---------------------------------------------------------------------------
 /** The GenFaceNormalsProcess computes vertex normals for all vertizes
 */
-class ASSIMP_API_WINONLY GenVertexNormalsProcess : public BaseProcess
+class ASSIMP_API GenVertexNormalsProcess : public BaseProcess
 {
 public:
 

+ 6 - 6
code/IFCBoolean.cpp

@@ -69,8 +69,8 @@ Intersect IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const I
 	const IfcVector3 pdelta = e0 - p, seg = e1-e0;
 	const IfcFloat dotOne = n*seg, dotTwo = -(n*pdelta);
 
-	if (fabs(dotOne) < 1e-6) {
-		return fabs(dotTwo) < 1e-6f ? Intersect_LiesOnPlane : Intersect_No;
+	if (std::fabs(dotOne) < 1e-6) {
+		return std::fabs(dotTwo) < 1e-6f ? Intersect_LiesOnPlane : Intersect_No;
 	}
 
 	const IfcFloat t = dotTwo/dotOne;
@@ -85,7 +85,7 @@ Intersect IntersectSegmentPlane(const IfcVector3& p,const IfcVector3& n, const I
 // ------------------------------------------------------------------------------------------------
 void ProcessBooleanHalfSpaceDifference(const IfcHalfSpaceSolid* hs, TempMesh& result, 
 									   const TempMesh& first_operand, 
-									   ConversionData& conv)
+									   ConversionData& /*conv*/)
 {
 	ai_assert(hs != NULL);
 
@@ -210,7 +210,7 @@ bool IntersectsBoundaryProfile( const IfcVector3& e0, const IfcVector3& e1, cons
 		// segment-segment intersection
 		// solve b0 + b*s = e0 + e*t for (s,t)
 		const IfcFloat det = (-b.x * e.y + e.x * b.y);
-		if(fabs(det) < 1e-6) {
+		if(std::fabs(det) < 1e-6) {
 			// no solutions (parallel lines)
 			continue;
 		}
@@ -234,7 +234,7 @@ bool IntersectsBoundaryProfile( const IfcVector3& e0, const IfcVector3& e1, cons
 		if (t >= -epsilon && (t <= 1.0+epsilon || half_open) && s >= -epsilon && s <= 1.0) {
 
 			if (e0_hits_border && !*e0_hits_border) {
-				*e0_hits_border = fabs(t) < 1e-5f;
+				*e0_hits_border = std::fabs(t) < 1e-5f;
 			}
 	
 			const IfcVector3& p = e0 + e*t;
@@ -419,7 +419,7 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const IfcPolygonalBounded
 
 #ifdef ASSIMP_BUILD_DEBUG
 			if (isect == Intersect_Yes) {
-				const IfcFloat f = fabs((isectpos - p)*n);
+				const IfcFloat f = std::fabs((isectpos - p)*n);
 				ai_assert(f < 1e-5);
 			}
 #endif

+ 12 - 12
code/IFCCurve.cpp

@@ -88,10 +88,10 @@ public:
 		a *= conv.angle_scale;
 		b *= conv.angle_scale;
 
-		a = fmod(a,static_cast<IfcFloat>( AI_MATH_TWO_PI ));
-		b = fmod(b,static_cast<IfcFloat>( AI_MATH_TWO_PI ));
+		a = std::fmod(a,static_cast<IfcFloat>( AI_MATH_TWO_PI ));
+		b = std::fmod(b,static_cast<IfcFloat>( AI_MATH_TWO_PI ));
 		const IfcFloat setting = static_cast<IfcFloat>( AI_MATH_PI * conv.settings.conicSamplingAngle / 180.0 );
-		return static_cast<size_t>( ceil(abs( b-a)) / setting);
+		return static_cast<size_t>( std::ceil(abs( b-a)) / setting);
 	}
 
 	// --------------------------------------------------
@@ -124,8 +124,8 @@ public:
 	// --------------------------------------------------
 	IfcVector3 Eval(IfcFloat u) const {
 		u = -conv.angle_scale * u;
-		return location + static_cast<IfcFloat>(entity.Radius)*(static_cast<IfcFloat>(::cos(u))*p[0] + 
-			static_cast<IfcFloat>(::sin(u))*p[1]);
+		return location + static_cast<IfcFloat>(entity.Radius)*(static_cast<IfcFloat>(std::cos(u))*p[0] +
+			static_cast<IfcFloat>(std::sin(u))*p[1]);
 	}
 
 private:
@@ -153,8 +153,8 @@ public:
 	// --------------------------------------------------
 	IfcVector3 Eval(IfcFloat u) const {
 		u = -conv.angle_scale * u;
-		return location + static_cast<IfcFloat>(entity.SemiAxis1)*static_cast<IfcFloat>(::cos(u))*p[0] +
-			static_cast<IfcFloat>(entity.SemiAxis2)*static_cast<IfcFloat>(::sin(u))*p[1];
+		return location + static_cast<IfcFloat>(entity.SemiAxis1)*static_cast<IfcFloat>(std::cos(u))*p[0] +
+			static_cast<IfcFloat>(entity.SemiAxis2)*static_cast<IfcFloat>(std::sin(u))*p[1];
 	}
 
 private:
@@ -486,7 +486,7 @@ public:
 	IfcVector3 Eval(IfcFloat p) const {
 		ai_assert(InRange(p));
 		
-		const size_t b = static_cast<size_t>(floor(p));  
+		const size_t b = static_cast<size_t>(std::floor(p));
 		if (b == points.size()-1) {
 			return points.back();
 		}
@@ -498,7 +498,7 @@ public:
 	// --------------------------------------------------
 	size_t EstimateSampleCount(IfcFloat a, IfcFloat b) const {
 		ai_assert(InRange(a) && InRange(b));
-		return static_cast<size_t>( ceil(b) - floor(a) );
+		return static_cast<size_t>( std::ceil(b) - std::floor(a) );
 	}
 
 	// --------------------------------------------------
@@ -558,7 +558,7 @@ bool Curve :: InRange(IfcFloat u) const
 	if (IsClosed()) {
 		return true;
 		//ai_assert(range.first != std::numeric_limits<IfcFloat>::infinity() && range.second != std::numeric_limits<IfcFloat>::infinity());
-		//u = range.first + fmod(u-range.first,range.second-range.first);
+		//u = range.first + std::fmod(u-range.first,range.second-range.first);
 	}
 	const IfcFloat epsilon = 1e-5;
 	return u - range.first > -epsilon && range.second - u > -epsilon;
@@ -606,12 +606,12 @@ IfcFloat RecursiveSearch(const Curve* cv, const IfcVector3& val, IfcFloat a, Ifc
 	}
 
 	ai_assert(min_diff[0] != inf && min_diff[1] != inf);
-	if ( fabs(a-min_point[0]) < threshold || recurse >= max_recurse) {
+	if ( std::fabs(a-min_point[0]) < threshold || recurse >= max_recurse) {
 		return min_point[0];
 	}
 
 	// fix for closed curves to take their wrap-over into account
-	if (cv->IsClosed() && fabs(min_point[0]-min_point[1]) > cv->GetParametricRangeDelta()*0.5  ) {
+	if (cv->IsClosed() && std::fabs(min_point[0]-min_point[1]) > cv->GetParametricRangeDelta()*0.5  ) {
 		const Curve::ParamRange& range = cv->GetParametricRange();
 		const IfcFloat wrapdiff = (cv->Eval(range.first)-val).SquareLength();
 

+ 4 - 4
code/IFCGeometry.cpp

@@ -250,17 +250,17 @@ void ProcessRevolvedAreaSolid(const IfcRevolvedAreaSolid& solid, TempMesh& resul
 	
 	bool has_area = solid.SweptArea->ProfileType == "AREA" && size>2;
 	const IfcFloat max_angle = solid.Angle*conv.angle_scale;
-	if(fabs(max_angle) < 1e-3) {
+	if(std::fabs(max_angle) < 1e-3) {
 		if(has_area) {
 			result = meshout;
 		}
 		return;
 	}
 
-	const unsigned int cnt_segments = std::max(2u,static_cast<unsigned int>(16 * fabs(max_angle)/AI_MATH_HALF_PI_F));
+	const unsigned int cnt_segments = std::max(2u,static_cast<unsigned int>(16 * std::fabs(max_angle)/AI_MATH_HALF_PI_F));
 	const IfcFloat delta = max_angle/cnt_segments;
 
-	has_area = has_area && fabs(max_angle) < AI_MATH_TWO_PI_F*0.99;
+	has_area = has_area && std::fabs(max_angle) < AI_MATH_TWO_PI_F*0.99;
 	
 	result.verts.reserve(size*((cnt_segments+1)*4+(has_area?2:0)));
 	result.vertcnt.reserve(size*cnt_segments+2);
@@ -480,7 +480,7 @@ IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVect
 	for (i = 0; !done && i < s-2; done || ++i) {
 		for (j = i+1; j < s-1; ++j) {
 			nor = -((out[i]-any_point)^(out[j]-any_point));
-			if(fabs(nor.Length()) > 1e-8f) {
+			if(std::fabs(nor.Length()) > 1e-8f) {
 				done = true;
 				break;
 			}

+ 39 - 39
code/IFCOpenings.cpp

@@ -259,7 +259,7 @@ BoundingBox GetBoundingBox(const ClipperLib::Polygon& poly)
 
 // ------------------------------------------------------------------------------------------------
 void InsertWindowContours(const ContourVector& contours,
-	const std::vector<TempOpening>& openings,
+	const std::vector<TempOpening>& /*openings*/,
 	TempMesh& curmesh)
 {
 	// fix windows - we need to insert the real, polygonal shapes into the quadratic holes that we have now
@@ -303,20 +303,20 @@ void InsertWindowContours(const ContourVector& contours,
 			const IfcVector2& v = contour[n];
 
 			bool hit = false;
-			if (fabs(v.x-bb.first.x)<epsilon) {
+			if (std::fabs(v.x-bb.first.x)<epsilon) {
 				edge.x = bb.first.x;
 				hit = true;
 			}
-			else if (fabs(v.x-bb.second.x)<epsilon) {
+			else if (std::fabs(v.x-bb.second.x)<epsilon) {
 				edge.x = bb.second.x;
 				hit = true;
 			}
 
-			if (fabs(v.y-bb.first.y)<epsilon) {
+			if (std::fabs(v.y-bb.first.y)<epsilon) {
 				edge.y = bb.first.y;
 				hit = true;
 			}
-			else if (fabs(v.y-bb.second.y)<epsilon) {
+			else if (std::fabs(v.y-bb.second.y)<epsilon) {
 				edge.y = bb.second.y;
 				hit = true;
 			}
@@ -343,17 +343,17 @@ void InsertWindowContours(const ContourVector& contours,
 
 						IfcVector2 corner = edge;
 
-						if (fabs(contour[last_hit].x-bb.first.x)<epsilon) {
+						if (std::fabs(contour[last_hit].x-bb.first.x)<epsilon) {
 							corner.x = bb.first.x;
 						}
-						else if (fabs(contour[last_hit].x-bb.second.x)<epsilon) {
+						else if (std::fabs(contour[last_hit].x-bb.second.x)<epsilon) {
 							corner.x = bb.second.x;
 						}
 
-						if (fabs(contour[last_hit].y-bb.first.y)<epsilon) {
+						if (std::fabs(contour[last_hit].y-bb.first.y)<epsilon) {
 							corner.y = bb.first.y;
 						}
-						else if (fabs(contour[last_hit].y-bb.second.y)<epsilon) {
+						else if (std::fabs(contour[last_hit].y-bb.second.y)<epsilon) {
 							corner.y = bb.second.y;
 						}
 
@@ -590,10 +590,10 @@ bool BoundingBoxesAdjacent(const BoundingBox& bb, const BoundingBox& ibb)
 {
 	// TODO: I'm pretty sure there is a much more compact way to check this
 	const IfcFloat epsilon = 1e-5f;
-	return	(fabs(bb.second.x - ibb.first.x) < epsilon && bb.first.y <= ibb.second.y && bb.second.y >= ibb.first.y) ||
-		(fabs(bb.first.x - ibb.second.x) < epsilon && ibb.first.y <= bb.second.y && ibb.second.y >= bb.first.y) || 
-		(fabs(bb.second.y - ibb.first.y) < epsilon && bb.first.x <= ibb.second.x && bb.second.x >= ibb.first.x) ||
-		(fabs(bb.first.y - ibb.second.y) < epsilon && ibb.first.x <= bb.second.x && ibb.second.x >= bb.first.x);
+	return	(std::fabs(bb.second.x - ibb.first.x) < epsilon && bb.first.y <= ibb.second.y && bb.second.y >= ibb.first.y) ||
+		(std::fabs(bb.first.x - ibb.second.x) < epsilon && ibb.first.y <= bb.second.y && ibb.second.y >= bb.first.y) || 
+		(std::fabs(bb.second.y - ibb.first.y) < epsilon && bb.first.x <= ibb.second.x && bb.second.x >= ibb.first.x) ||
+		(std::fabs(bb.first.y - ibb.second.y) < epsilon && ibb.first.x <= bb.second.x && ibb.second.x >= bb.first.x);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -615,11 +615,11 @@ bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1,
 
 	static const IfcFloat inf = std::numeric_limits<IfcFloat>::infinity();
 
-	if (!(n0_to_m0.SquareLength() < e*e || fabs(n0_to_m0 * n0_to_n1) / (n0_to_m0.Length() * n0_to_n1.Length()) > 1-1e-5 )) {
+	if (!(n0_to_m0.SquareLength() < e*e || std::fabs(n0_to_m0 * n0_to_n1) / (n0_to_m0.Length() * n0_to_n1.Length()) > 1-1e-5 )) {
 		return false;
 	}
 
-	if (!(n1_to_m1.SquareLength() < e*e || fabs(n1_to_m1 * n0_to_n1) / (n1_to_m1.Length() * n0_to_n1.Length()) > 1-1e-5 )) {
+	if (!(n1_to_m1.SquareLength() < e*e || std::fabs(n1_to_m1 * n0_to_n1) / (n1_to_m1.Length() * n0_to_n1.Length()) > 1-1e-5 )) {
 		return false;
 	}
 
@@ -631,14 +631,14 @@ bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1,
 	// the higher absolute difference is big enough as to avoid
 	// divisions by zero, the case 0/0 ~ infinity is detected and
 	// handled separately.
-	if(fabs(n0_to_n1.x) > fabs(n0_to_n1.y)) {
+	if(std::fabs(n0_to_n1.x) > std::fabs(n0_to_n1.y)) {
 		s0 = n0_to_m0.x / n0_to_n1.x;
 		s1 = n0_to_m1.x / n0_to_n1.x;
 
-		if (fabs(s0) == inf && fabs(n0_to_m0.x) < smalle) {
+		if (std::fabs(s0) == inf && std::fabs(n0_to_m0.x) < smalle) {
 			s0 = 0.;
 		}
-		if (fabs(s1) == inf && fabs(n0_to_m1.x) < smalle) {
+		if (std::fabs(s1) == inf && std::fabs(n0_to_m1.x) < smalle) {
 			s1 = 0.;
 		}
 	}
@@ -646,10 +646,10 @@ bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1,
 		s0 = n0_to_m0.y / n0_to_n1.y;
 		s1 = n0_to_m1.y / n0_to_n1.y;
 
-		if (fabs(s0) == inf && fabs(n0_to_m0.y) < smalle) {
+		if (std::fabs(s0) == inf && std::fabs(n0_to_m0.y) < smalle) {
 			s0 = 0.;
 		}
-		if (fabs(s1) == inf && fabs(n0_to_m1.y) < smalle) {
+		if (std::fabs(s1) == inf && std::fabs(n0_to_m1.y) < smalle) {
 			s1 = 0.;
 		}
 	}
@@ -664,7 +664,7 @@ bool IntersectingLineSegments(const IfcVector2& n0, const IfcVector2& n1,
 	s0 = std::min(1.0,s0);
 	s1 = std::min(1.0,s1);
 
-	if (fabs(s1-s0) < e) {
+	if (std::fabs(s1-s0) < e) {
 		return false;
 	}
 
@@ -755,7 +755,7 @@ void FindAdjacentContours(ContourVector::iterator current, const ContourVector&
 AI_FORCE_INLINE bool LikelyBorder(const IfcVector2& vdelta)
 {
 	const IfcFloat dot_point_epsilon = static_cast<IfcFloat>(1e-5);
-	return fabs(vdelta.x * vdelta.y) < dot_point_epsilon;
+	return std::fabs(vdelta.x * vdelta.y) < dot_point_epsilon;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -812,9 +812,9 @@ void FindBorderContours(ContourVector::iterator current)
 // ------------------------------------------------------------------------------------------------
 AI_FORCE_INLINE bool LikelyDiagonal(IfcVector2 vdelta)
 {
-	vdelta.x = fabs(vdelta.x);
-	vdelta.y = fabs(vdelta.y);
-	return (fabs(vdelta.x-vdelta.y) < 0.8 * std::max(vdelta.x, vdelta.y));
+	vdelta.x = std::fabs(vdelta.x);
+	vdelta.y = std::fabs(vdelta.y);
+	return (std::fabs(vdelta.x-vdelta.y) < 0.8 * std::max(vdelta.x, vdelta.y));
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -926,7 +926,7 @@ size_t CloseWindows(ContourVector& contours,
 				/* debug code to check for unwanted diagonal lines in window contours
 				if (cit != cbegin) {
 					const IfcVector2& vdelta = proj_point - last_proj;
-					if (fabs(vdelta.x-vdelta.y) < 0.5 * std::max(vdelta.x, vdelta.y)) {
+					if (std::fabs(vdelta.x-vdelta.y) < 0.5 * std::max(vdelta.x, vdelta.y)) {
 						//continue;
 					}
 				} */
@@ -1065,7 +1065,7 @@ IfcMatrix4 ProjectOntoPlane(std::vector<IfcVector2>& out_contour, const TempMesh
 	}
 #ifdef ASSIMP_BUILD_DEBUG
 	const IfcFloat det = m.Determinant();
-	ai_assert(fabs(det-1) < 1e-5);
+	ai_assert(std::fabs(det-1) < 1e-5);
 #endif
 
 	IfcFloat zcoord = 0;
@@ -1085,7 +1085,7 @@ IfcMatrix4 ProjectOntoPlane(std::vector<IfcVector2>& out_contour, const TempMesh
 		// XXX this should be guarded, but we somehow need to pick a suitable
 		// epsilon
 		// if(coord != -1.0f) {
-		//	assert(fabs(coord - vv.z) < 1e-3f);
+		//	assert(std::fabs(coord - vv.z) < 1e-3f);
 		// }
 		zcoord += vv.z;
 		vmin = std::min(vv, vmin);
@@ -1125,7 +1125,7 @@ IfcMatrix4 ProjectOntoPlane(std::vector<IfcVector2>& out_contour, const TempMesh
 		const IfcVector3& vv = m * x;
 
 		out_contour2.push_back(IfcVector2(vv.x,vv.y));
-		ai_assert(fabs(vv.z) < vmax.z + 1e-8);
+		ai_assert(std::fabs(vv.z) < vmax.z + 1e-8);
 	} 
 
 	for(size_t i = 0; i < out_contour.size(); ++i) {
@@ -1188,9 +1188,9 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
 		bool is_2d_source = false;
 		if (opening.profileMesh2D && norm_extrusion_dir.SquareLength() > 0) {
 			
-			if(fabs(norm_extrusion_dir * wall_extrusion_axis_norm) < 0.1) {
+			if(std::fabs(norm_extrusion_dir * wall_extrusion_axis_norm) < 0.1) {
 				// horizontal extrusion
-				if (fabs(norm_extrusion_dir * nor) > 0.9) {
+				if (std::fabs(norm_extrusion_dir * nor) > 0.9) {
 					profile_data = opening.profileMesh2D.get();
 					is_2d_source = true;
 				}
@@ -1200,7 +1200,7 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
 			}
 			else {
 				// vertical extrusion
-				if (fabs(norm_extrusion_dir * nor) > 0.9) {
+				if (std::fabs(norm_extrusion_dir * nor) > 0.9) {
 					continue;
 				}
 				continue;
@@ -1289,7 +1289,7 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
 			ai_assert(!is_2d_source);
 			const IfcVector2 area = vpmax-vpmin;
 			const IfcVector2 area2 = vpmax2-vpmin2;
-			if (temp_contour.size() <= 2 || fabs(area2.x * area2.y) > fabs(area.x * area.y)) {
+			if (temp_contour.size() <= 2 || std::fabs(area2.x * area2.y) > std::fabs(area.x * area.y)) {
 				temp_contour.swap(temp_contour2);
 
 				vpmax = vpmax2;
@@ -1301,7 +1301,7 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
 		}
 
 		// TODO: This epsilon may be too large
-		const IfcFloat epsilon = fabs(dmax-dmin) * 0.0001;
+		const IfcFloat epsilon = std::fabs(dmax-dmin) * 0.0001;
 		if (!is_2d_source && check_intersection && (0 < dmin-epsilon || 0 > dmax+epsilon)) {
 			continue;
 		}
@@ -1310,7 +1310,7 @@ bool GenerateOpenings(std::vector<TempOpening>& openings,
 
 		// Skip over very small openings - these are likely projection errors
 		// (i.e. they don't belong to this side of the wall)
-		if(fabs(vpmax.x - vpmin.x) * fabs(vpmax.y - vpmin.y) < static_cast<IfcFloat>(1e-10)) {
+		if(std::fabs(vpmax.x - vpmin.x) * std::fabs(vpmax.y - vpmin.y) < static_cast<IfcFloat>(1e-10)) {
 			continue;
 		}
 		std::vector<TempOpening*> joined_openings(1, &opening);
@@ -1480,7 +1480,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
 		// XXX this should be guarded, but we somehow need to pick a suitable
 		// epsilon
 		// if(coord != -1.0f) {
-		//	assert(fabs(coord - vv.z) < 1e-3f);
+		//	assert(std::fabs(coord - vv.z) < 1e-3f);
 		// }
 
 		coord = vv.z;
@@ -1515,7 +1515,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
 		BOOST_FOREACH(const TempOpening& t,openings) {
 			const IfcVector3& outernor = nors[c++];
 			const IfcFloat dot = nor * outernor;
-			if (fabs(dot)<1.f-1e-6f) {
+			if (std::fabs(dot)<1.f-1e-6f) {
 				continue;
 			}
 
@@ -1529,7 +1529,7 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
 			BOOST_FOREACH(const IfcVector3& xx, t.profileMesh->verts) {
 				IfcVector3 vv = m *  xx, vv_extr = m * (xx + t.extrusionDir);
 				
-				const bool is_extruded_side = fabs(vv.z - coord) > fabs(vv_extr.z - coord);
+				const bool is_extruded_side = std::fabs(vv.z - coord) > std::fabs(vv_extr.z - coord);
 				if (first) {
 					first = false;
 					if (dot > 0.f) {
@@ -1741,4 +1741,4 @@ bool TryAddOpenings_Poly2Tri(const std::vector<TempOpening>& openings,const std:
 #undef from_int64
 #undef one_vec
 
-#endif 
+#endif 

+ 2 - 2
code/IFCProfile.cpp

@@ -101,7 +101,7 @@ void ProcessOpenProfile(const IfcArbitraryOpenProfileDef& def, TempMesh& meshout
 }
 
 // ------------------------------------------------------------------------------------------------
-void ProcessParametrizedProfile(const IfcParameterizedProfileDef& def, TempMesh& meshout, ConversionData& conv)
+void ProcessParametrizedProfile(const IfcParameterizedProfileDef& def, TempMesh& meshout, ConversionData& /*conv*/)
 {
 	if(const IfcRectangleProfileDef* const cprofile = def.ToPtr<IfcRectangleProfileDef>()) {
 		const IfcFloat x = cprofile->XDim*0.5f, y = cprofile->YDim*0.5f;
@@ -124,7 +124,7 @@ void ProcessParametrizedProfile(const IfcParameterizedProfileDef& def, TempMesh&
 
 		IfcFloat angle = 0.f;
 		for(size_t i = 0; i < segments; ++i, angle += delta) {
-			meshout.verts.push_back( IfcVector3( cos(angle)*radius, sin(angle)*radius, 0.f ));
+			meshout.verts.push_back( IfcVector3( std::cos(angle)*radius, std::sin(angle)*radius, 0.f ));
 		}
 
 		meshout.vertcnt.push_back(segments);

+ 3 - 3
code/IFCReaderGen.cpp

@@ -1045,7 +1045,7 @@ void IFC::GetSchema(EXPRESS::ConversionSchema& out)
 namespace STEP {
 
 // -----------------------------------------------------------------------------------------------------------
-template <> size_t GenericFill<NotImplemented>(const STEP::DB& db, const LIST& params, NotImplemented* in)
+template <> size_t GenericFill<NotImplemented>(const STEP::DB& /*db*/, const LIST& /*params*/, NotImplemented* /*in*/)
 {
 	return 0;
 }
@@ -1253,7 +1253,7 @@ template <> size_t GenericFill<IfcPerformanceHistory>(const DB& db, const LIST&
 	return base;
 }
 // -----------------------------------------------------------------------------------------------------------
-template <> size_t GenericFill<IfcRepresentationItem>(const DB& db, const LIST& params, IfcRepresentationItem* in)
+template <> size_t GenericFill<IfcRepresentationItem>(const DB& /*db*/, const LIST& /*params*/, IfcRepresentationItem* /*in*/)
 {
 	size_t base = 0;
 	return base;
@@ -1715,7 +1715,7 @@ template <> size_t GenericFill<IfcPlateType>(const DB& db, const LIST& params, I
 	return base;
 }
 // -----------------------------------------------------------------------------------------------------------
-template <> size_t GenericFill<IfcObjectPlacement>(const DB& db, const LIST& params, IfcObjectPlacement* in)
+template <> size_t GenericFill<IfcObjectPlacement>(const DB& /*db*/, const LIST& /*params*/, IfcObjectPlacement* /*in*/)
 {
 	size_t base = 0;
 	return base;

+ 1 - 1
code/IFCUtil.cpp

@@ -278,7 +278,7 @@ void TempMesh::RemoveAdjacentDuplicates()
 		//		continue;
 		//	}
 
-		//	const IfcFloat d = (d0/sqrt(l0))*(d1/sqrt(l1));
+		//	const IfcFloat d = (d0/std::sqrt(l0))*(d1/std::sqrt(l1));
 
 		//	if ( d >= 1.f-dotepsilon ) {
 		//		v1 = v0;

+ 1 - 1
code/IFCUtil.h

@@ -220,7 +220,7 @@ struct FuzzyVectorCompare {
 
 	FuzzyVectorCompare(IfcFloat epsilon) : epsilon(epsilon) {}
 	bool operator()(const IfcVector3& a, const IfcVector3& b) {
-		return fabs((a-b).SquareLength()) < epsilon;
+		return std::fabs((a-b).SquareLength()) < epsilon;
 	}
 
 	const IfcFloat epsilon;

+ 24 - 22
code/IFF.h

@@ -12,8 +12,6 @@
 namespace Assimp	{
 namespace IFF		{
 
-#include "./../include/assimp/Compiler/pushpack1.h"
-
 /////////////////////////////////////////////////////////////////////////////////
 //! Describes an IFF chunk header
 /////////////////////////////////////////////////////////////////////////////////
@@ -24,7 +22,7 @@ struct ChunkHeader
 
 	//! Length of the chunk data, in bytes
 	uint32_t length;
-} PACK_STRUCT;
+};
 
 
 /////////////////////////////////////////////////////////////////////////////////
@@ -37,9 +35,7 @@ struct SubChunkHeader
 
 	//! Length of the chunk data, in bytes
 	uint16_t length;
-} PACK_STRUCT;
-
-#include "./../include/assimp/Compiler/poppack1.h"
+};
 
 
 #define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \
@@ -52,28 +48,34 @@ struct SubChunkHeader
 /////////////////////////////////////////////////////////////////////////////////
 //! Load a chunk header
 //! @param outFile Pointer to the file data - points to the chunk data afterwards
-//! @return Pointer to the chunk header
+//! @return Copy of the chunk header
 /////////////////////////////////////////////////////////////////////////////////
-inline ChunkHeader* LoadChunk(uint8_t*& outFile)
+inline ChunkHeader LoadChunk(uint8_t*& outFile)
 {
-	ChunkHeader* head = (ChunkHeader*) outFile;
-	AI_LSWAP4(head->length);
-	AI_LSWAP4(head->type);
-	outFile += sizeof(ChunkHeader);
+	ChunkHeader head;
+	::memcpy(&head.type, outFile, 4);
+	outFile += 4;
+	::memcpy(&head.length, outFile, 4);
+	outFile += 4;
+	AI_LSWAP4(head.length);
+	AI_LSWAP4(head.type);
 	return head;
 }
 
 /////////////////////////////////////////////////////////////////////////////////
 //! Load a sub chunk header
 //! @param outFile Pointer to the file data - points to the chunk data afterwards
-//! @return Pointer to the sub chunk header
+//! @return Copy of the sub chunk header
 /////////////////////////////////////////////////////////////////////////////////
-inline SubChunkHeader* LoadSubChunk(uint8_t*& outFile)
+inline SubChunkHeader LoadSubChunk(uint8_t*& outFile)
 {
-	SubChunkHeader* head = (SubChunkHeader*) outFile;
-	AI_LSWAP2(head->length);
-	AI_LSWAP4(head->type);
-	outFile += sizeof(SubChunkHeader);
+	SubChunkHeader head;
+	::memcpy(&head.type, outFile, 4);
+	outFile += 4;
+	::memcpy(&head.length, outFile, 2);
+	outFile += 2;
+	AI_LSWAP2(head.length);
+	AI_LSWAP4(head.type);
 	return head;
 }
 
@@ -84,14 +86,14 @@ inline SubChunkHeader* LoadSubChunk(uint8_t*& outFile)
 //! @param fileType Receives the type of the file
 //! @return 0 if everything was OK, otherwise an error message
 /////////////////////////////////////////////////////////////////////////////////
-inline const char* ReadHeader(uint8_t* outFile,uint32_t& fileType) 
+inline const char* ReadHeader(uint8_t* outFile, uint32_t& fileType) 
 {
-	ChunkHeader* head = LoadChunk(outFile);
-	if(AI_IFF_FOURCC_FORM != head->type)
+	ChunkHeader head = LoadChunk(outFile);
+	if(AI_IFF_FOURCC_FORM != head.type)
 	{
 		return "The file is not an IFF file: FORM chunk is missing";
 	}
-	fileType = *((uint32_t*)(head+1));
+	::memcpy(&fileType, outFile, 4);
 	AI_LSWAP4(fileType);
 	return 0;
 }

+ 3 - 3
code/IRRLoader.cpp

@@ -470,7 +470,7 @@ void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vector<aiNode
 					key.mTime = i * tdelta;
 
 					const float t = (float) ( in.speed * key.mTime );
-					key.mValue = in.circleCenter  + in.circleRadius * ((vecU*::cosf(t)) + (vecV*::sinf(t)));
+					key.mValue = in.circleCenter  + in.circleRadius * ((vecU * std::cos(t)) + (vecV * std::sin(t)));
 				}
 
 				// This animation is repeated and repeated ...
@@ -533,8 +533,8 @@ void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vector<aiNode
 					aiVectorKey& key = anim->mPositionKeys[i];
 
 					const float dt = (i * in.speed * 0.001f );
-					const float u = dt - floor(dt);
-					const int idx = (int)floor(dt) % size;
+					const float u = dt - std::floor(dt);
+					const int idx = (int)std::floor(dt) % size;
 
 					// get the 4 current points to evaluate the spline
 					const aiVector3D& p0 = in.splineKeys[ ClampSpline( idx - 1, size ) ].mValue;

+ 13 - 4
code/Importer.cpp

@@ -640,16 +640,25 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
 			}
 		}
 
+		// Get file size for progress handler
+		IOStream * fileIO = pimpl->mIOHandler->Open( pFile );
+		uint32_t fileSize = 0;
+		if (fileIO)
+		{
+			fileSize = fileIO->FileSize();
+			pimpl->mIOHandler->Close( fileIO );
+		}
+
 		// Dispatch the reading to the worker class for this format
 		DefaultLogger::get()->info("Found a matching importer for this file format");
-		pimpl->mProgressHandler->Update();
+		pimpl->mProgressHandler->UpdateFileRead( 0, fileSize );
 
 		if (profiler) {
 			profiler->BeginRegion("import");
 		}
 
 		pimpl->mScene = imp->ReadFile( this, pFile, pimpl->mIOHandler);
-		pimpl->mProgressHandler->Update();
+		pimpl->mProgressHandler->UpdateFileRead( fileSize, fileSize );
 
 		if (profiler) {
 			profiler->EndRegion("import");
@@ -678,7 +687,6 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
 			ScenePreprocessor pre(pimpl->mScene);
 			pre.ProcessScene();
 
-			pimpl->mProgressHandler->Update();
 			if (profiler) {
 				profiler->EndRegion("preprocess");
 			}
@@ -768,6 +776,7 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
 	for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++)	{
 
 		BaseProcess* process = pimpl->mPostProcessingSteps[a];
+		pimpl->mProgressHandler->UpdatePostProcess( a, pimpl->mPostProcessingSteps.size() );
 		if( process->IsActive( pFlags))	{
 
 			if (profiler) {
@@ -775,7 +784,6 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
 			}
 
 			process->ExecuteOnScene	( this );
-			pimpl->mProgressHandler->Update();
 
 			if (profiler) {
 				profiler->EndRegion("postprocess");
@@ -803,6 +811,7 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
 		}
 #endif // ! DEBUG
 	}
+	pimpl->mProgressHandler->UpdatePostProcess( pimpl->mPostProcessingSteps.size(), pimpl->mPostProcessingSteps.size() );
 
 	// update private scene flags
   if( pimpl->mScene )

+ 6 - 0
code/ImporterRegistry.cpp

@@ -166,6 +166,9 @@ corresponding preprocessor flag to selectively disable formats.
 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
 #   include "FBXImporter.h"
 #endif 
+#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
+#   include "AssbinLoader.h"
+#endif 
 
 namespace Assimp {
 
@@ -291,6 +294,9 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
 #if ( !defined ASSIMP_BUILD_NO_FBX_IMPORTER )
 	out.push_back( new FBXImporter() );
 #endif
+#if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER )
+	out.push_back( new AssbinImporter() );
+#endif
 }
 
 }

+ 1 - 3
code/JoinVerticesProcess.h

@@ -49,8 +49,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp
 {
 
-class JoinVerticesTest;
-
 // ---------------------------------------------------------------------------
 /** The JoinVerticesProcess unites identical vertices in all imported meshes. 
  * By default the importer returns meshes where each face addressed its own 
@@ -59,7 +57,7 @@ class JoinVerticesTest;
  * erases all but one of the copies. This usually reduces the number of vertices
  * in a mesh by a serious amount and is the standard form to render a mesh.
  */
-class ASSIMP_API_WINONLY JoinVerticesProcess : public BaseProcess
+class ASSIMP_API JoinVerticesProcess : public BaseProcess
 {
 public:
 

+ 58 - 40
code/LWOBLoader.cpp

@@ -58,30 +58,31 @@ void LWOImporter::LoadLWOBFile()
 	while (running)
 	{
 		if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break;
-		LE_NCONST IFF::ChunkHeader* const head = IFF::LoadChunk(mFileBuffer);
+		const IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
 
-		if (mFileBuffer + head->length > end)
+		if (mFileBuffer + head.length > end)
 		{
 			throw DeadlyImportError("LWOB: Invalid chunk length");
 			break;
 		}
-		uint8_t* const next = mFileBuffer+head->length;
-		switch (head->type)
+		uint8_t* const next = mFileBuffer+head.length;
+		switch (head.type)
 		{
 			// vertex list
 		case AI_LWO_PNTS:
 			{
 				if (!mCurLayer->mTempPoints.empty())
 					DefaultLogger::get()->warn("LWO: PNTS chunk encountered twice");
-				else LoadLWOPoints(head->length);
+				else LoadLWOPoints(head.length);
 				break;
 			}
 			// face list
 		case AI_LWO_POLS:
 			{
+
 				if (!mCurLayer->mFaces.empty())
 					DefaultLogger::get()->warn("LWO: POLS chunk encountered twice");
-				else LoadLWOBPolygons(head->length);
+				else LoadLWOBPolygons(head.length);
 				break;
 			}
 			// list of tags
@@ -89,14 +90,14 @@ void LWOImporter::LoadLWOBFile()
 			{
 				if (!mTags->empty())
 					DefaultLogger::get()->warn("LWO: SRFS chunk encountered twice");
-				else LoadLWOTags(head->length);
+				else LoadLWOTags(head.length);
 				break;
 			}
 
 			// surface chunk
 		case AI_LWO_SURF:
 			{
-				LoadLWOBSurface(head->length);
+				LoadLWOBSurface(head.length);
 				break;
 			}
 		}
@@ -137,14 +138,17 @@ void LWOImporter::CountVertsAndFacesLWOB(unsigned int& verts, unsigned int& face
 {
 	while (cursor < end && max--)
 	{
-		uint16_t numIndices = *cursor++;
-		verts += numIndices;faces++;
+		uint16_t numIndices;
+		::memcpy(&numIndices, cursor++, 2);
+		verts += numIndices;
+		faces++;
 		cursor += numIndices;
-		int16_t surface = *cursor++;
+		int16_t surface;
+		::memcpy(&surface, cursor++, 2);
 		if (surface < 0)
 		{
 			// there are detail polygons
-			numIndices = *cursor++;
+			::memcpy(&numIndices, cursor++, 2);
 			CountVertsAndFacesLWOB(verts,faces,cursor,end,numIndices);
 		}
 	}
@@ -159,13 +163,22 @@ void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator& it,
 	while (cursor < end && max--)
 	{
 		LWO::Face& face = *it;++it;
-		if((face.mNumIndices = *cursor++))
+		uint16_t numIndices;
+		::memcpy(&numIndices, cursor++, 2);
+		face.mNumIndices = numIndices;
+		if(face.mNumIndices)
 		{
-			if (cursor + face.mNumIndices >= end)break;
+			if (cursor + face.mNumIndices >= end)
+			{
+				break;
+			}
 			face.mIndices = new unsigned int[face.mNumIndices];
 			for (unsigned int i = 0; i < face.mNumIndices;++i)
 			{
-				unsigned int & mi = face.mIndices[i] = *cursor++;
+				unsigned int & mi = face.mIndices[i];
+				uint16_t index;
+				::memcpy(&index, cursor++, 2);
+				mi = index;
 				if (mi > mCurLayer->mTempPoints.size())
 				{
 					DefaultLogger::get()->warn("LWOB: face index is out of range");
@@ -174,14 +187,19 @@ void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator& it,
 			}
 		}
 		else DefaultLogger::get()->warn("LWOB: Face has 0 indices");
-		int16_t surface = *cursor++;
+		int16_t surface;
+		::memcpy(&surface, cursor++, 2);
 		if (surface < 0)
 		{
 			surface = -surface;
 
 			// there are detail polygons. 
-			const uint16_t numPolygons = *cursor++;
-			if (cursor < end)CopyFaceIndicesLWOB(it,cursor,end,numPolygons);
+			uint16_t numPolygons;
+			::memcpy(&numPolygons, cursor++, 2);
+			if (cursor < end)
+			{
+				CopyFaceIndicesLWOB(it,cursor,end,numPolygons);
+			}
 		}
 		face.surfaceIndex = surface-1;
 	}
@@ -235,7 +253,7 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
 		if (mFileBuffer + 6 >= end)
 			break;
 
-		IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+		IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
 
 		/*  A single test file (sonycam.lwo) seems to have invalid surface chunks.
 		 *  I'm assuming it's the fault of a single, unknown exporter so there are
@@ -244,18 +262,18 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
 		 *  We don't break if the chunk limit is exceeded. Instead, we're computing 
 		 *  how much storage is actually left and work with this value from now on.
 		 */
-		if (mFileBuffer + head->length > end) {
+		if (mFileBuffer + head.length > end) {
 			DefaultLogger::get()->error("LWOB: Invalid surface chunk length. Trying to continue.");
-			head->length = (uint16_t) (end - mFileBuffer);
+			head.length = (uint16_t) (end - mFileBuffer);
 		}
 
-		uint8_t* const next = mFileBuffer+head->length;
-		switch (head->type)
+		uint8_t* const next = mFileBuffer+head.length;
+		switch (head.type)
 		{
 		// diffuse color
 		case AI_LWO_COLR:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,COLR,3);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,COLR,3);
 				surf.mColor.r = GetU1() / 255.0f;
 				surf.mColor.g = GetU1() / 255.0f;
 				surf.mColor.b = GetU1() / 255.0f;
@@ -264,35 +282,35 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
 		// diffuse strength ...
 		case AI_LWO_DIFF:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,DIFF,2);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,DIFF,2);
 				surf.mDiffuseValue = GetU2() / 255.0f;
 				break;
 			}
 		// specular strength ... 
 		case AI_LWO_SPEC:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPEC,2);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SPEC,2);
 				surf.mSpecularValue = GetU2() / 255.0f;
 				break;
 			}
 		// luminosity ... 
 		case AI_LWO_LUMI:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LUMI,2);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,LUMI,2);
 				surf.mLuminosity = GetU2() / 255.0f;
 				break;
 			}
 		// transparency
 		case AI_LWO_TRAN:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TRAN,2);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TRAN,2);
 				surf.mTransparency = GetU2() / 255.0f;
 				break;
 			}
 		// surface flags
 		case AI_LWO_FLAG:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,FLAG,2);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,FLAG,2);
 				uint16_t flag = GetU2();
 				if (flag & 0x4 )   surf.mMaximumSmoothAngle = 1.56207f;
 				if (flag & 0x8 )   surf.mColorHighlights = 1.f;
@@ -302,14 +320,14 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
 		// maximum smoothing angle
 		case AI_LWO_SMAN:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
-				surf.mMaximumSmoothAngle = fabs( GetF4() );
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SMAN,4);
+				surf.mMaximumSmoothAngle = std::fabs( GetF4() );
 				break;
 			}
 		// glossiness
 		case AI_LWO_GLOS:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,GLOS,2);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,GLOS,2);
 				surf.mGlossiness = (float)GetU2();
 				break;
 			}
@@ -317,42 +335,42 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
 		case AI_LWO_CTEX:
 			{
 				pTex = SetupNewTextureLWOB(surf.mColorTextures,
-					head->length);
+					head.length);
 				break;
 			}
 		// diffuse texture
 		case AI_LWO_DTEX:
 			{
 				pTex = SetupNewTextureLWOB(surf.mDiffuseTextures,
-					head->length);
+					head.length);
 				break;
 			}
 		// specular texture
 		case AI_LWO_STEX:
 			{
 				pTex = SetupNewTextureLWOB(surf.mSpecularTextures,
-					head->length);
+					head.length);
 				break;
 			}
 		// bump texture
 		case AI_LWO_BTEX:
 			{
 				pTex = SetupNewTextureLWOB(surf.mBumpTextures,
-					head->length);
+					head.length);
 				break;
 			}
 		// transparency texture
 		case AI_LWO_TTEX:
 			{
 				pTex = SetupNewTextureLWOB(surf.mOpacityTextures,
-					head->length);
+					head.length);
 				break;
 			}
 		// texture path
 		case AI_LWO_TIMG:
 			{
 				if (pTex)	{
-					GetS0(pTex->mFileName,head->length);	
+					GetS0(pTex->mFileName,head.length);	
 				}
 				else DefaultLogger::get()->warn("LWOB: Unexpected TIMG chunk");
 				break;
@@ -360,7 +378,7 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
 		// texture strength
 		case AI_LWO_TVAL:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TVAL,1);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TVAL,1);
 				if (pTex)	{
 					pTex->mStrength = (float)GetU1()/ 255.f;
 				}
@@ -370,7 +388,7 @@ void LWOImporter::LoadLWOBSurface(unsigned int size)
 		// texture flags
 		case AI_LWO_TFLG:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TFLG,2);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TFLG,2);
 
 				if (pTex) 
 				{

+ 57 - 45
code/LWOLoader.cpp

@@ -503,7 +503,7 @@ void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>&
 	// Generate vertex normals. We have O(logn) for the binary lookup, which we need
 	// for n elements, thus the EXPECTED complexity is O(nlogn)
 	if (surface.mMaximumSmoothAngle < 3.f && !configSpeedFlag)	{
-		const float fLimit = cos(surface.mMaximumSmoothAngle);
+		const float fLimit = std::cos(surface.mMaximumSmoothAngle);
 
 		for( begin =  mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it)	{
 			const aiFace& face = *begin;
@@ -787,7 +787,8 @@ void LWOImporter::LoadLWO2Polygons(unsigned int length)
 	CountVertsAndFacesLWO2(iNumVertices,iNumFaces,cursor,end);
 
 	// allocate the output array and copy face indices
-	if (iNumFaces)	{
+	if (iNumFaces)
+	{
 		cursor = (uint16_t*)mFileBuffer;
 
 		mCurLayer->mFaces.resize(iNumFaces,LWO::Face(type));
@@ -802,13 +803,18 @@ void LWOImporter::CountVertsAndFacesLWO2(unsigned int& verts, unsigned int& face
 {
 	while (cursor < end && max--)
 	{
-		AI_LSWAP2P(cursor);
-		uint16_t numIndices = *cursor++;
+		uint16_t numIndices;
+		::memcpy(&numIndices, cursor++, 2);
+		AI_LSWAP2(numIndices);
 		numIndices &= 0x03FF;
-		verts += numIndices;++faces;
+
+		verts += numIndices;
+		++faces;
 
 		for(uint16_t i = 0; i < numIndices; i++)
+		{
 			ReadVSizedIntLWO2((uint8_t*&)cursor);
+		}
 	}
 }
 
@@ -817,10 +823,16 @@ void LWOImporter::CopyFaceIndicesLWO2(FaceList::iterator& it,
 	uint16_t*& cursor, 
 	const uint16_t* const end)
 {
-	while (cursor < end)	{
-
-		LWO::Face& face = *it++;;
-		if((face.mNumIndices = (*cursor++) & 0x03FF)) /* byte swapping has already been done */ {
+	while (cursor < end)
+	{
+		LWO::Face& face = *it++;
+		uint16_t numIndices;
+		::memcpy(&numIndices, cursor++, 2);
+		AI_LSWAP2(numIndices);
+		face.mNumIndices = numIndices & 0x03FF;
+		
+		if(face.mNumIndices) /* byte swapping has already been done */
+		{
 			face.mIndices = new unsigned int[face.mNumIndices];
 			for(unsigned int i = 0; i < face.mNumIndices; i++)
 			{
@@ -848,8 +860,8 @@ void LWOImporter::LoadLWO2PolygonTags(unsigned int length)
 	if (type != AI_LWO_SURF && type != AI_LWO_SMGP)
 		return;
 
-	while (mFileBuffer < end)	{
-
+	while (mFileBuffer < end)
+	{
 		unsigned int i = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
 		unsigned int j = GetU2();
 
@@ -1106,19 +1118,19 @@ void LWOImporter::LoadLWO2Clip(unsigned int length)
 	// first - get the index of the clip
 	clip.idx = GetU4();
 
-	IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
-	switch (head->type)
+	IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
+	switch (head.type)
 	{
 	case AI_LWO_STIL:
-		AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,STIL,1);
+		AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,STIL,1);
 
 		// "Normal" texture
-		GetS0(clip.path,head->length);
+		GetS0(clip.path,head.length);
 		clip.type = Clip::STILL;
 		break;
 
 	case AI_LWO_ISEQ:
-		AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ISEQ,16);
+		AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,ISEQ,16);
 		// Image sequence. We'll later take the first.
 		{
 			uint8_t digits = GetU1();  mFileBuffer++;
@@ -1127,12 +1139,12 @@ void LWOImporter::LoadLWO2Clip(unsigned int length)
 
 			std::string s;
 			std::ostringstream ss;
-			GetS0(s,head->length);
+			GetS0(s,head.length);
 
-			head->length -= (unsigned int)s.length()+1;
+			head.length -= (unsigned int)s.length()+1;
 			ss << s;
 			ss << std::setw(digits) << offset + start;
-			GetS0(s,head->length);
+			GetS0(s,head.length);
 			ss << s;
 			clip.path = ss.str();
 			clip.type = Clip::SEQ;
@@ -1148,7 +1160,7 @@ void LWOImporter::LoadLWO2Clip(unsigned int length)
 		break;
 
 	case AI_LWO_XREF:
-		AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,XREF,4);
+		AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,XREF,4);
 
 		// Just a cross-reference to another CLIp
 		clip.type = Clip::REF;
@@ -1156,7 +1168,7 @@ void LWOImporter::LoadLWO2Clip(unsigned int length)
 		break;
 
 	case AI_LWO_NEGA:
-		AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,NEGA,2);
+		AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,NEGA,2);
 		clip.negate = (0 != GetU2());
 		break;
 
@@ -1194,17 +1206,17 @@ void LWOImporter::LoadLWO2Envelope(unsigned int length)
 	while (true)
 	{
 		if (mFileBuffer + 6 >= end)break;
-		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+		LE_NCONST IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
 
-		if (mFileBuffer + head->length > end)
+		if (mFileBuffer + head.length > end)
 			throw DeadlyImportError("LWO2: Invalid envelope chunk length");
 
-		uint8_t* const next = mFileBuffer+head->length;
-		switch (head->type)
+		uint8_t* const next = mFileBuffer+head.length;
+		switch (head.type)
 		{
 			// Type & representation of the envelope
 		case AI_LWO_TYPE:
-			AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TYPE,2);
+			AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TYPE,2);
 			mFileBuffer++; // skip user format
 
 			// Determine type of envelope
@@ -1214,20 +1226,20 @@ void LWOImporter::LoadLWO2Envelope(unsigned int length)
 
 			// precondition
 		case AI_LWO_PRE:
-			AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,PRE,2);
+			AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,PRE,2);
 			envelope.pre = (LWO::PrePostBehaviour)GetU2();
 			break;
 		
 			// postcondition
 		case AI_LWO_POST:
-			AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,POST,2);
+			AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,POST,2);
 			envelope.post = (LWO::PrePostBehaviour)GetU2();
 			break;
 
 			// keyframe
 		case AI_LWO_KEY: 
 			{
-			AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,KEY,8);
+			AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,KEY,8);
 			
 			envelope.keys.push_back(LWO::Key());
 			LWO::Key& key = envelope.keys.back();
@@ -1240,7 +1252,7 @@ void LWOImporter::LoadLWO2Envelope(unsigned int length)
 			// interval interpolation
 		case AI_LWO_SPAN: 
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPAN,4);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SPAN,4);
 				if (envelope.keys.size()<2)
 					DefaultLogger::get()->warn("LWO2: Unexpected SPAN chunk");
 				else {
@@ -1286,22 +1298,22 @@ void LWOImporter::LoadLWO2File()
 	while (true)
 	{
 		if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break;
-		IFF::ChunkHeader* const head = IFF::LoadChunk(mFileBuffer);
+		const IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
 
-		if (mFileBuffer + head->length > end)
+		if (mFileBuffer + head.length > end)
 		{
 			throw DeadlyImportError("LWO2: Chunk length points behind the file");
 			break;
 		}
-		uint8_t* const next = mFileBuffer+head->length;
+		uint8_t* const next = mFileBuffer+head.length;
 		unsigned int iUnnamed = 0;
 
-		if(!head->length) {
+		if(!head.length) {
 			mFileBuffer = next;
 			continue;
 		}
 
-		switch (head->type)
+		switch (head.type)
 		{
 			// new layer
 		case AI_LWO_LAYR:
@@ -1311,7 +1323,7 @@ void LWOImporter::LoadLWO2File()
 				LWO::Layer& layer = mLayers->back();
 				mCurLayer = &layer;
 
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LAYR,16);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,LAYR,16);
 
 				// layer index.
 				layer.mIndex = GetU2();
@@ -1327,7 +1339,7 @@ void LWOImporter::LoadLWO2File()
 				mCurLayer->mPivot.x = GetF4();
 				mCurLayer->mPivot.y = GetF4();
 				mCurLayer->mPivot.z = GetF4();
-				GetS0(layer.mName,head->length-16);
+				GetS0(layer.mName,head.length-16);
 
 				// if the name is empty, generate a default name
 				if (layer.mName.empty())	{
@@ -1360,7 +1372,7 @@ void LWOImporter::LoadLWO2File()
 					break;
 
 				unsigned int old = (unsigned int)mCurLayer->mTempPoints.size();
-				LoadLWOPoints(head->length);
+				LoadLWOPoints(head.length);
 				mCurLayer->mPointIDXOfs = old;
 				break;
 			}
@@ -1379,7 +1391,7 @@ void LWOImporter::LoadLWO2File()
 
 				if (mCurLayer->mTempPoints.empty())
 					DefaultLogger::get()->warn("LWO2: Unexpected VMAP chunk");
-				else LoadLWO2VertexMap(head->length,head->type == AI_LWO_VMAD);
+				else LoadLWO2VertexMap(head.length,head.type == AI_LWO_VMAD);
 				break;
 			}
 			// face list
@@ -1389,7 +1401,7 @@ void LWOImporter::LoadLWO2File()
 					break;
 
 				unsigned int old = (unsigned int)mCurLayer->mFaces.size();
-				LoadLWO2Polygons(head->length);
+				LoadLWO2Polygons(head.length);
 				mCurLayer->mFaceIDXOfs = old;
 				break;
 			}
@@ -1401,7 +1413,7 @@ void LWOImporter::LoadLWO2File()
 
 				if (mCurLayer->mFaces.empty())
 					DefaultLogger::get()->warn("LWO2: Unexpected PTAG");
-				else LoadLWO2PolygonTags(head->length);
+				else LoadLWO2PolygonTags(head.length);
 				break;
 			}
 			// list of tags
@@ -1409,28 +1421,28 @@ void LWOImporter::LoadLWO2File()
 			{
 				if (!mTags->empty())
 					DefaultLogger::get()->warn("LWO2: SRFS chunk encountered twice");
-				else LoadLWOTags(head->length);
+				else LoadLWOTags(head.length);
 				break;
 			}
 
 			// surface chunk
 		case AI_LWO_SURF:
 			{
-				LoadLWO2Surface(head->length);
+				LoadLWO2Surface(head.length);
 				break;
 			}
 
 			// clip chunk
 		case AI_LWO_CLIP:
 			{
-				LoadLWO2Clip(head->length);
+				LoadLWO2Clip(head.length);
 				break;
 			}
 
 			// envelope chunk
 		case AI_LWO_ENVL:
 			{
-				LoadLWO2Envelope(head->length);
+				LoadLWO2Envelope(head.length);
 				break;
 			}
 		}

+ 9 - 3
code/LWOLoader.h

@@ -402,7 +402,9 @@ protected:
 // ------------------------------------------------------------------------------------------------
 inline float LWOImporter::GetF4()
 {
-	float f = *((float*)mFileBuffer);mFileBuffer += 4;
+	float f;
+	::memcpy(&f, mFileBuffer, 4);
+	mFileBuffer += 4;
 	AI_LSWAP4(f);
 	return f;
 }
@@ -410,7 +412,9 @@ inline float LWOImporter::GetF4()
 // ------------------------------------------------------------------------------------------------
 inline uint32_t LWOImporter::GetU4()
 {
-	uint32_t f = *((uint32_t*)mFileBuffer);mFileBuffer += 4;
+	uint32_t f;
+	::memcpy(&f, mFileBuffer, 4);
+	mFileBuffer += 4;
 	AI_LSWAP4(f);
 	return f;
 }
@@ -418,7 +422,9 @@ inline uint32_t LWOImporter::GetU4()
 // ------------------------------------------------------------------------------------------------
 inline uint16_t LWOImporter::GetU2()
 {
-	uint16_t f = *((uint16_t*)mFileBuffer);mFileBuffer += 2;
+	uint16_t f;
+	::memcpy(&f, mFileBuffer, 2);
+	mFileBuffer += 2;
 	AI_LSWAP2(f);
 	return f;
 }

+ 37 - 37
code/LWOMaterial.cpp

@@ -285,7 +285,7 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,aiMaterial* pcMat)
 	{
 		float fGloss;
 		if (mIsLWO2)	{
-			fGloss = pow( surf.mGlossiness*10.0f+2.0f, 2.0f);
+			fGloss = std::pow( surf.mGlossiness*10.0f+2.0f, 2.0f);
 		}
 		else
 		{
@@ -527,13 +527,13 @@ void LWOImporter::LoadLWO2ImageMap(unsigned int size, LWO::Texture& tex )
 	while (true)
 	{
 		if (mFileBuffer + 6 >= end)break;
-		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+		LE_NCONST IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
 
-		if (mFileBuffer + head->length > end)
+		if (mFileBuffer + head.length > end)
 			throw DeadlyImportError("LWO2: Invalid SURF.BLOCK chunk length");
 
-		uint8_t* const next = mFileBuffer+head->length;
-		switch (head->type)
+		uint8_t* const next = mFileBuffer+head.length;
+		switch (head.type)
 		{
 		case AI_LWO_PROJ:
 			tex.mapMode = (Texture::MappingMode)GetU2();
@@ -549,7 +549,7 @@ void LWOImporter::LoadLWO2ImageMap(unsigned int size, LWO::Texture& tex )
 			tex.mClipIdx = GetU2();
 			break;
 		case AI_LWO_VMAP:
-			GetS0(tex.mUVChannelIndex,head->length);
+			GetS0(tex.mUVChannelIndex,head.length);
 			break;
 		case AI_LWO_WRPH:
 			tex.wrapAmountH = GetF4();
@@ -595,13 +595,13 @@ void LWOImporter::LoadLWO2TextureHeader(unsigned int size, LWO::Texture& tex )
 	while (true)
 	{
 		if (mFileBuffer + 6 >= end)break;
-		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+		const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
 
-		if (mFileBuffer + head->length > end)
+		if (mFileBuffer + head.length > end)
 			throw DeadlyImportError("LWO2: Invalid texture header chunk length");
 
-		uint8_t* const next = mFileBuffer+head->length;
-		switch (head->type)
+		uint8_t* const next = mFileBuffer+head.length;
+		switch (head.type)
 		{
 		case AI_LWO_CHAN:
 			tex.type = GetU4();
@@ -698,20 +698,20 @@ void LWOImporter::LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader* /*head*/, u
 	while (true)
 	{
 		if (mFileBuffer + 6 >= end)break;
-		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+		const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
 
-		if (mFileBuffer + head->length > end)
+		if (mFileBuffer + head.length > end)
 			throw DeadlyImportError("LWO2: Invalid shader header chunk length");
 
-		uint8_t* const next = mFileBuffer+head->length;
-		switch (head->type)
+		uint8_t* const next = mFileBuffer+head.length;
+		switch (head.type)
 		{
 		case AI_LWO_ENAB:
 			shader.enabled = GetU2() ? true : false;
 			break;
 
 		case AI_LWO_FUNC:
-			GetS0( shader.functionName, head->length );
+			GetS0( shader.functionName, head.length );
 		}
 		mFileBuffer = next;
 	}
@@ -756,18 +756,18 @@ void LWOImporter::LoadLWO2Surface(unsigned int size)
 	{
 		if (mFileBuffer + 6 >= end)
 			break;
-		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
+		const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
 
-		if (mFileBuffer + head->length > end)
+		if (mFileBuffer + head.length > end)
 			throw DeadlyImportError("LWO2: Invalid surface chunk length");
 
-		uint8_t* const next = mFileBuffer+head->length;
-		switch (head->type)
+		uint8_t* const next = mFileBuffer+head.length;
+		switch (head.type)
 		{
 			// diffuse color
 		case AI_LWO_COLR:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,COLR,12);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,COLR,12);
 				surf.mColor.r = GetF4();
 				surf.mColor.g = GetF4();
 				surf.mColor.b = GetF4();
@@ -776,14 +776,14 @@ void LWOImporter::LoadLWO2Surface(unsigned int size)
 			// diffuse strength ... hopefully
 		case AI_LWO_DIFF:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,DIFF,4);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,DIFF,4);
 				surf.mDiffuseValue = GetF4();
 				break;
 			}
 			// specular strength ... hopefully
 		case AI_LWO_SPEC:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPEC,4);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SPEC,4);
 				surf.mSpecularValue = GetF4();
 				break;
 			}
@@ -794,21 +794,21 @@ void LWOImporter::LoadLWO2Surface(unsigned int size)
 				if (surf.mTransparency == 10e10f)
 					break;
 
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TRAN,4);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TRAN,4);
 				surf.mTransparency = GetF4();
 				break;
 			}
 			// additive transparency
 		case AI_LWO_ADTR:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ADTR,4);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,ADTR,4);
 				surf.mAdditiveTransparency = GetF4();
 				break;
 			}
 			// wireframe mode
 		case AI_LWO_LINE:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LINE,2);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,LINE,2);
 				if (GetU2() & 0x1)
 					surf.mWireframe = true;
 				break;
@@ -816,49 +816,49 @@ void LWOImporter::LoadLWO2Surface(unsigned int size)
 			// glossiness
 		case AI_LWO_GLOS:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,GLOS,4);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,GLOS,4);
 				surf.mGlossiness = GetF4();
 				break;
 			}
 			// bump intensity
 		case AI_LWO_BUMP:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,BUMP,4);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,BUMP,4);
 				surf.mBumpIntensity = GetF4();
 				break;
 			}
 			// color highlights
 		case AI_LWO_CLRH:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,CLRH,4);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,CLRH,4);
 				surf.mColorHighlights = GetF4();
 				break;
 			}
 			// index of refraction
 		case AI_LWO_RIND:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,RIND,4);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,RIND,4);
 				surf.mIOR = GetF4();
 				break;
 			}
 			// polygon sidedness
 		case AI_LWO_SIDE:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SIDE,2);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SIDE,2);
 				surf.bDoubleSided = (3 == GetU2());
 				break;
 			}
 			// maximum smoothing angle
 		case AI_LWO_SMAN:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SMAN,4);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SMAN,4);
 				surf.mMaximumSmoothAngle = fabs( GetF4() );
 				break;
 			}
 			// vertex color channel to be applied to the surface
 		case AI_LWO_VCOL:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,VCOL,12);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,VCOL,12);
 				surf.mDiffuseValue *= GetF4();				// strength
 				ReadVSizedIntLWO2(mFileBuffer);             // skip envelope
 				surf.mVCMapType = GetU4();					// type of the channel
@@ -870,18 +870,18 @@ void LWOImporter::LoadLWO2Surface(unsigned int size)
 			// surface bock entry
 		case AI_LWO_BLOK:
 			{
-				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,BLOK,4);
-				LE_NCONST IFF::SubChunkHeader* head2 = IFF::LoadSubChunk(mFileBuffer);
+				AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,BLOK,4);
+				IFF::SubChunkHeader head2 = IFF::LoadSubChunk(mFileBuffer);
 
-				switch (head2->type)
+				switch (head2.type)
 				{
 				case AI_LWO_PROC:
 				case AI_LWO_GRAD:
 				case AI_LWO_IMAP:
-					LoadLWO2TextureBlock(head2, head->length);
+					LoadLWO2TextureBlock(&head2, head.length);
 					break;
 				case AI_LWO_SHDR:
-					LoadLWO2ShaderBlock(head2, head->length);
+					LoadLWO2ShaderBlock(&head2, head.length);
 					break;
 
 				default:

+ 3 - 3
code/LWSLoader.cpp

@@ -464,7 +464,7 @@ std::string LWSImporter::FindLWOFile(const std::string& in)
 	std::string tmp;
 	if (in.length() > 3 && in[1] == ':'&& in[2] != '\\' && in[2] != '/')
 	{
-		tmp = in[0] + (":\\" + in.substr(2));
+		tmp = in[0] + (std::string(":\\") + in.substr(2));
 	}
 	else tmp = in;
 
@@ -480,12 +480,12 @@ std::string LWSImporter::FindLWOFile(const std::string& in)
 	// <folder>\Scenes\<hh>\<*>.lws
 	// where <hh> is optional.
 
-	std::string test = ".." + (io->getOsSeparator() + tmp); 
+	std::string test = std::string("..") + (io->getOsSeparator() + tmp);
 	if (io->Exists(test)) {
 		return test;
 	}
 
-	test = ".." + (io->getOsSeparator() + test); 
+	test = std::string("..") + (io->getOsSeparator() + test);
 	if (io->Exists(test)) {
 		return test;
 	}

+ 10 - 17
code/LimitBoneWeightsProcess.cpp

@@ -131,10 +131,15 @@ void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
 
 		// and renormalize the weights
 		float sum = 0.0f;
-		for( std::vector<Weight>::const_iterator it = vit->begin(); it != vit->end(); ++it)
-			sum += it->mWeight;
-		for( std::vector<Weight>::iterator it = vit->begin(); it != vit->end(); ++it)
-			it->mWeight /= sum;
+        for( std::vector<Weight>::const_iterator it = vit->begin(); it != vit->end(); ++it ) {
+            sum += it->mWeight;
+        }
+        if( 0.0f != sum ) {
+            const float invSum = 1.0f / sum;
+            for( std::vector<Weight>::iterator it = vit->begin(); it != vit->end(); ++it ) {
+                it->mWeight *= invSum;
+            }
+        }
 	}
 
 	if (bChanged)	{
@@ -157,18 +162,6 @@ void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
 			const std::vector<aiVertexWeight>& bw = boneWeights[a];
 			aiBone* bone = pMesh->mBones[a];
 
-			// ignore the bone if no vertex weights were removed there
-
-			// FIX (Aramis, 07|22|08)
-			// NO! we can't ignore it in this case ... it is possible that
-			// the number of weights did not change, but the weight values did.
-
-			// if( bw.size() == bone->mNumWeights)
-			//	continue;
-
-			// FIX (Aramis, 07|21|08)
-			// It is possible that all weights of a bone have been removed.
-			// This would naturally cause an exception in &bw[0].
 			if ( bw.empty() )
 			{
 				abNoNeed[a] = bChanged = true;
@@ -177,7 +170,7 @@ void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
 
 			// copy the weight list. should always be less weights than before, so we don't need a new allocation
 			ai_assert( bw.size() <= bone->mNumWeights);
-			bone->mNumWeights = (unsigned int) bw.size();
+			bone->mNumWeights = static_cast<unsigned int>( bw.size() );
 			::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight));
 		}
 

+ 3 - 3
code/MD3FileData.h

@@ -263,9 +263,9 @@ inline void LatLngNormalToVec3(uint16_t p_iNormal, float* p_afOut)
 	lat *= 3.141926f/128.0f;
 	lng *= 3.141926f/128.0f;
 
-	p_afOut[0] = cosf(lat) * sinf(lng);
-	p_afOut[1] = sinf(lat) * sinf(lng);
-	p_afOut[2] = cosf(lng);
+	p_afOut[0] = std::cos(lat) * std::sin(lng);
+	p_afOut[1] = std::sin(lat) * std::sin(lng);
+	p_afOut[2] = std::cos(lng);
 	return;
 }
 

+ 1 - 1
code/MD5Parser.h

@@ -259,7 +259,7 @@ inline void ConvertQuaternion (const aiVector3D& in, aiQuaternion& out) {
 
 	if (t < 0.0f)
 		out.w = 0.0f;
-	else out.w = sqrt (t);
+	else out.w = std::sqrt (t);
 }
 
 // ---------------------------------------------------------------------------

+ 36 - 32
code/ObjExporter.cpp

@@ -75,6 +75,7 @@ void ExportSceneObj(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene
 
 } // end of namespace Assimp
 
+static const std::string MaterialExt = ".mtl";
 
 // ------------------------------------------------------------------------------------------------
 ObjExporter :: ObjExporter(const char* _filename, const aiScene* pScene)
@@ -107,7 +108,7 @@ std::string ObjExporter :: GetMaterialLibName()
 // ------------------------------------------------------------------------------------------------
 std::string ObjExporter :: GetMaterialLibFileName()
 {	
-	return filename + ".mtl";
+    return filename + MaterialExt;
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -132,7 +133,7 @@ std::string ObjExporter :: GetMaterialName(unsigned int index)
 }
 
 // ------------------------------------------------------------------------------------------------
-void ObjExporter :: WriteMaterialFile()
+void ObjExporter::WriteMaterialFile()
 {
 	WriteHeader(mOutputMat);
 
@@ -144,16 +145,16 @@ void ObjExporter :: WriteMaterialFile()
 
 		aiColor4D c;
 		if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_DIFFUSE,c)) {
-			mOutputMat << "kd " << c.r << " " << c.g << " " << c.b << endl;
+			mOutputMat << "Kd " << c.r << " " << c.g << " " << c.b << endl;
 		}
 		if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_AMBIENT,c)) {
-			mOutputMat << "ka " << c.r << " " << c.g << " " << c.b << endl;
+			mOutputMat << "Ka " << c.r << " " << c.g << " " << c.b << endl;
 		}
 		if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_SPECULAR,c)) {
-			mOutputMat << "ks " << c.r << " " << c.g << " " << c.b << endl;
+			mOutputMat << "Ks " << c.r << " " << c.g << " " << c.b << endl;
 		}
 		if(AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_EMISSIVE,c)) {
-			mOutputMat << "ke " << c.r << " " << c.g << " " << c.b << endl;
+			mOutputMat << "Ke " << c.r << " " << c.g << " " << c.b << endl;
 		}
 
 		float o;
@@ -170,16 +171,19 @@ void ObjExporter :: WriteMaterialFile()
 
 		aiString s;
 		if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_DIFFUSE(0),s)) {
-			mOutputMat << "map_kd " << s.data << endl;
+			mOutputMat << "map_Kd " << s.data << endl;
 		}
 		if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_AMBIENT(0),s)) {
-			mOutputMat << "map_ka " << s.data << endl;
+			mOutputMat << "map_Ka " << s.data << endl;
 		}
 		if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_SPECULAR(0),s)) {
-			mOutputMat << "map_ks " << s.data << endl;
+			mOutputMat << "map_Ks " << s.data << endl;
 		}
 		if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_SHININESS(0),s)) {
-			mOutputMat << "map_ns " << s.data << endl;
+			mOutputMat << "map_Ns " << s.data << endl;
+		}
+		if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_OPACITY(0),s)) {
+			mOutputMat << "map_d " << s.data << endl;
 		}
 		if(AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_HEIGHT(0),s) || AI_SUCCESS == mat->Get(AI_MATKEY_TEXTURE_NORMALS(0),s)) {
 			// implementations seem to vary here, so write both variants
@@ -199,7 +203,7 @@ void ObjExporter :: WriteGeometryFile()
 
 	// collect mesh geometry
 	aiMatrix4x4 mBase;
-	AddNode(pScene->mRootNode,mBase);
+	AddNode(pScene->mRootNode, mBase);
 
 	// write vertex positions
 	vpMap.getVectors(vp);
@@ -228,7 +232,9 @@ void ObjExporter :: WriteGeometryFile()
 	// now write all mesh instances
 	BOOST_FOREACH(const MeshInstance& m, meshes) {
 		mOutput << "# Mesh \'" << m.name << "\' with " << m.faces.size() << " faces" << endl;
-		mOutput << "g " << m.name << endl;
+		if (!m.name.empty()) {
+			mOutput << "g " << m.name << endl;
+		}
 		mOutput << "usemtl " << m.matname << endl;
 
 		BOOST_FOREACH(const Face& f, m.faces) {
@@ -243,11 +249,8 @@ void ObjExporter :: WriteGeometryFile()
 					if (fv.vt) {
 						mOutput << fv.vt;
 					}
-					if (f.kind == 'f') {
-						mOutput << '/';
-						if (fv.vn) {
-							mOutput << fv.vn;
-						}
+					if (f.kind == 'f' && fv.vn) {
+						mOutput << '/' << fv.vn;
 					}
 				}
 			}
@@ -258,14 +261,12 @@ void ObjExporter :: WriteGeometryFile()
 	}
 }
 
-
-
-
-
+// ------------------------------------------------------------------------------------------------
 int ObjExporter::vecIndexMap::getIndex(const aiVector3D& vec)
 {
-	vecIndexMap::dataType::iterator vertIt = vecMap.find(vec); 
-	if(vertIt != vecMap.end()){// vertex already exists, so reference it
+	vecIndexMap::dataType::iterator vertIt = vecMap.find(vec);
+	// vertex already exists, so reference it
+	if(vertIt != vecMap.end()){
 		return vertIt->second;
 	}
 	vecMap[vec] = mNextIndex;
@@ -274,6 +275,7 @@ int ObjExporter::vecIndexMap::getIndex(const aiVector3D& vec)
 	return ret;
 }
 
+// ------------------------------------------------------------------------------------------------
 void ObjExporter::vecIndexMap::getVectors( std::vector<aiVector3D>& vecs )
 {
 	vecs.resize(vecMap.size());
@@ -282,14 +284,13 @@ void ObjExporter::vecIndexMap::getVectors( std::vector<aiVector3D>& vecs )
 	}
 }
 
-
 // ------------------------------------------------------------------------------------------------
-void ObjExporter :: AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat)
+void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat)
 {
 	meshes.push_back(MeshInstance());
 	MeshInstance& mesh = meshes.back();
 
-	mesh.name = std::string(name.data,name.length) + (m->mName.length ? "_"+std::string(m->mName.data,m->mName.length) : "");
+	mesh.name = std::string(name.data,name.length) + (m->mName.length ? "_" + std::string(m->mName.data,m->mName.length) : "");
 	mesh.matname = GetMaterialName(m->mMaterialIndex);
 
 	mesh.faces.resize(m->mNumFaces);
@@ -317,7 +318,8 @@ void ObjExporter :: AddMesh(const aiString& name, const aiMesh* m, const aiMatri
 			face.indices[a].vp = vpMap.getIndex(vert);
 
 			if (m->mNormals) {
-				face.indices[a].vn = vnMap.getIndex(m->mNormals[idx]);
+				aiVector3D norm = aiMatrix3x3(mat) * m->mNormals[idx];
+				face.indices[a].vn = vnMap.getIndex(norm);
 			}
 			else{
 				face.indices[a].vn = 0;
@@ -334,18 +336,20 @@ void ObjExporter :: AddMesh(const aiString& name, const aiMesh* m, const aiMatri
 }
 
 // ------------------------------------------------------------------------------------------------
-void ObjExporter :: AddNode(const aiNode* nd, const aiMatrix4x4& mParent)
+void ObjExporter::AddNode(const aiNode* nd, const aiMatrix4x4& mParent)
 {
 	const aiMatrix4x4& mAbs = mParent * nd->mTransformation;
 
 	for(unsigned int i = 0; i < nd->mNumMeshes; ++i) {
-		AddMesh(nd->mName, pScene->mMeshes[nd->mMeshes[i]],mAbs);
+		AddMesh(nd->mName, pScene->mMeshes[nd->mMeshes[i]], mAbs);
 	}
 
 	for(unsigned int i = 0; i < nd->mNumChildren; ++i) {
-		AddNode(nd->mChildren[i],mAbs);
+		AddNode(nd->mChildren[i], mAbs);
 	}
 }
 
-#endif
-#endif
+// ------------------------------------------------------------------------------------------------
+
+#endif // ASSIMP_BUILD_NO_OBJ_EXPORTER
+#endif // ASSIMP_BUILD_NO_EXPORT

+ 38 - 29
code/ObjFileImporter.cpp

@@ -62,7 +62,7 @@ static const aiImporterDesc desc = {
 
 static const unsigned int ObjMinSize = 16;
 
-namespace Assimp	{
+namespace Assimp {
 
 using namespace std;
 
@@ -109,9 +109,7 @@ const aiImporterDesc* ObjFileImporter::GetInfo () const
 // ------------------------------------------------------------------------------------------------
 //	Obj-file import implementation
 void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
-{
-    DefaultIOSystem io;
-    
+{    
 	// Read file into memory
 	const std::string mode = "rb";
 	boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, mode));
@@ -139,7 +137,23 @@ void ObjFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
 	{
 		strModelName = pFile;
 	}
-	
+
+	// process all '\'
+	std::vector<char> ::iterator iter = m_Buffer.begin();
+	while (iter != m_Buffer.end())
+	{
+		if (*iter == '\\')
+		{
+			// remove '\'
+			iter = m_Buffer.erase(iter);
+			// remove next character
+			while (*iter == '\r' || *iter == '\n')
+				iter = m_Buffer.erase(iter);
+		}
+		else
+			++iter;
+	}
+
 	// parse the file into a temporary representation
 	ObjFileParser parser(m_Buffer, strModelName, pIOHandler);
 
@@ -216,16 +230,10 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile
 	for ( unsigned int i=0; i< pObject->m_Meshes.size(); i++ )
 	{
 		unsigned int meshId = pObject->m_Meshes[ i ];
-		aiMesh *pMesh = new aiMesh;
-		createTopology( pModel, pObject, meshId, pMesh );	
-		if ( pMesh->mNumVertices > 0 ) 
-		{
+		aiMesh *pMesh = createTopology( pModel, pObject, meshId );	
+        if( pMesh && pMesh->mNumFaces > 0 ) {
 			MeshArray.push_back( pMesh );
 		}
-		else
-		{
-			delete pMesh;
-		}
 	}
 
 	// Create all nodes from the sub-objects stored in the current object
@@ -258,45 +266,44 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile
 
 // ------------------------------------------------------------------------------------------------
 //	Create topology data
-void ObjFileImporter::createTopology(const ObjFile::Model* pModel, 
-									 const ObjFile::Object* pData, 
-									 unsigned int uiMeshIndex,
-									 aiMesh* pMesh )
+aiMesh *ObjFileImporter::createTopology( const ObjFile::Model* pModel, const ObjFile::Object* pData, 
+                                         unsigned int uiMeshIndex )
 {
 	// Checking preconditions
 	ai_assert( NULL != pModel );
     if( NULL == pData ) {
-        return;
+        return NULL;
     }
 
 	// Create faces
 	ObjFile::Mesh *pObjMesh = pModel->m_Meshes[ uiMeshIndex ];
-	ai_assert( NULL != pObjMesh );
-
-	pMesh->mNumFaces = 0;
+    if( !pObjMesh ) {
+        return NULL;
+    }
+    ai_assert( NULL != pObjMesh );
+    aiMesh* pMesh = new aiMesh;
 	for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++)
 	{
-		ObjFile::Face* const inp = pObjMesh->m_Faces[ index ];
-	
+		ObjFile::Face *const inp = pObjMesh->m_Faces[ index ];
+        ai_assert( NULL != inp  );
+
 		if (inp->m_PrimitiveType == aiPrimitiveType_LINE) {
 			pMesh->mNumFaces += inp->m_pVertices->size() - 1;
 			pMesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
-		}
-		else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
+		} else if (inp->m_PrimitiveType == aiPrimitiveType_POINT) {
 			pMesh->mNumFaces += inp->m_pVertices->size();
 			pMesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
 		} else {
 			++pMesh->mNumFaces;
 			if (inp->m_pVertices->size() > 3) {
 				pMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
-			}
-			else {
+			} else {
 				pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
 			}
 		}
 	}
 
-	unsigned int uiIdxCount = 0u;
+	unsigned int uiIdxCount( 0u );
 	if ( pMesh->mNumFaces > 0 )
 	{
 		pMesh->mFaces = new aiFace[ pMesh->mNumFaces ];
@@ -305,7 +312,7 @@ void ObjFileImporter::createTopology(const ObjFile::Model* pModel,
 			pMesh->mMaterialIndex = pObjMesh->m_uiMaterialIndex;
 		}
 
-		unsigned int outIndex = 0;
+		unsigned int outIndex( 0 );
 
 		// Copy all data from all stored meshes
 		for (size_t index = 0; index < pObjMesh->m_Faces.size(); index++)
@@ -339,6 +346,8 @@ void ObjFileImporter::createTopology(const ObjFile::Model* pModel,
 
 	// Create mesh vertices
 	createVertexArray(pModel, pData, uiMeshIndex, pMesh, uiIdxCount);
+
+    return pMesh;
 }
 
 // ------------------------------------------------------------------------------------------------

+ 2 - 2
code/ObjFileImporter.h

@@ -91,8 +91,8 @@ private:
 		aiNode *pParent, aiScene* pScene, std::vector<aiMesh*> &MeshArray);
 
 	//!	\brief	Creates topology data like faces and meshes for the geometry.
-	void createTopology(const ObjFile::Model* pModel, const ObjFile::Object* pData,
-		unsigned int uiMeshIndex, aiMesh* pMesh);	
+    aiMesh *createTopology( const ObjFile::Model* pModel, const ObjFile::Object* pData,
+		unsigned int uiMeshIndex );	
 	
 	//!	\brief	Creates vertices from model.
 	void createVertexArray(const ObjFile::Model* pModel, const ObjFile::Object* pCurrentObject,

+ 24 - 28
code/ObjFileMtlImporter.cpp

@@ -46,14 +46,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "ObjTools.h"
 #include "ObjFileData.h"
 #include "fast_atof.h"
+#include "ParsingUtils.h"
 
 namespace Assimp	{
 
 // Material specific token
-static const std::string DiffuseTexture      = "map_kd";
-static const std::string AmbientTexture      = "map_ka";
-static const std::string SpecularTexture     = "map_ks";
+static const std::string DiffuseTexture      = "map_Kd";
+static const std::string AmbientTexture      = "map_Ka";
+static const std::string SpecularTexture     = "map_Ks";
 static const std::string OpacityTexture      = "map_d";
+static const std::string EmmissiveTexture    = "map_emissive";
 static const std::string BumpTexture1        = "map_bump";
 static const std::string BumpTexture2        = "map_Bump";
 static const std::string BumpTexture3        = "bump";
@@ -128,6 +130,7 @@ void ObjFileMtlImporter::load()
 	{
 		switch (*m_DataIt)
 		{
+		case 'k':
 		case 'K':
 			{
 				++m_DataIt;
@@ -163,25 +166,27 @@ void ObjFileMtlImporter::load()
 			}
 			break;
 
-		case 'N':	// Shineness
+		case 'N':
+		case 'n':
 			{
 				++m_DataIt;
-				switch(*m_DataIt) 
+				switch(*m_DataIt)
 				{
-				case 's':
+				case 's':	// Specular exponent
 					++m_DataIt;
 					getFloatValue(m_pModel->m_pCurrentMaterial->shineness);
 					break;
-				case 'i': //Index Of refraction 
+				case 'i':	// Index Of refraction
 					++m_DataIt;
 					getFloatValue(m_pModel->m_pCurrentMaterial->ior);
 					break;
+				case 'e':	// New material
+					createMaterial();
+					break;
 				}
 				m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
-				break;
 			}
 			break;
-		
 
 		case 'm':	// Texture
 		case 'b':   // quick'n'dirty - for 'bump' sections
@@ -191,13 +196,6 @@ void ObjFileMtlImporter::load()
 			}
 			break;
 
-		case 'n':	// New material name
-			{
-				createMaterial();
-				m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
-			}
-			break;
-
 		case 'i':	// Illumination model
 			{
 				m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
@@ -221,15 +219,17 @@ void ObjFileMtlImporter::getColorRGBA( aiColor3D *pColor )
 {
 	ai_assert( NULL != pColor );
 	
-	float r, g, b;
+	float r( 0.0f ), g( 0.0f ), b( 0.0f );
 	m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, r );
 	pColor->r = r;
 	
-	m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, g );
-	pColor->g = g;
-
-	m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, b );
-	pColor->b = b;
+    // we have to check if color is default 0 with only one token
+    if( !IsLineEnd( *m_DataIt ) ) {
+        m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, g );
+        m_DataIt = getFloat<DataArrayIt>( m_DataIt, m_DataItEnd, b );
+    }
+    pColor->g = g;
+    pColor->b = b;
 }
 
 // -------------------------------------------------------------------
@@ -253,7 +253,7 @@ void ObjFileMtlImporter::getFloatValue( float &value )
 void ObjFileMtlImporter::createMaterial()
 {	
 	std::string line( "" );
-	while ( !isNewLine( *m_DataIt ) ) {
+    while( !IsLineEnd( *m_DataIt ) ) {
 		line += *m_DataIt;
 		++m_DataIt;
 	}
@@ -303,11 +303,7 @@ void ObjFileMtlImporter::getTexture() {
 		// Opacity texture
 		out = & m_pModel->m_pCurrentMaterial->textureOpacity;
 		clampIndex = ObjFile::Material::TextureOpacityType;
-	} else if (!ASSIMP_strincmp( pPtr,"map_ka",6)) {
-		// Ambient texture
-		out = & m_pModel->m_pCurrentMaterial->textureAmbient;
-		clampIndex = ObjFile::Material::TextureAmbientType;
-	} else if (!ASSIMP_strincmp(&(*m_DataIt),"map_emissive",6)) {
+	} else if (!ASSIMP_strincmp( pPtr, EmmissiveTexture.c_str(), EmmissiveTexture.size())) {
 		// Emissive texture
 		out = & m_pModel->m_pCurrentMaterial->textureEmissive;
 		clampIndex = ObjFile::Material::TextureEmissiveType;

+ 42 - 32
code/ObjFileParser.cpp

@@ -113,8 +113,8 @@ void ObjFileParser::parseFile()
 					getVector3(m_pModel->m_Vertices);
 				} else if (*m_DataIt == 't') {
 					// read in texture coordinate ( 2D or 3D )
-                    ++m_DataIt;
-                    getVector( m_pModel->m_TextureCoord );
+                                        ++m_DataIt;
+                                        getVector( m_pModel->m_TextureCoord );
 				} else if (*m_DataIt == 'n') {
 					// Read in normal vector definition
 					++m_DataIt;
@@ -186,12 +186,12 @@ void ObjFileParser::copyNextWord(char *pBuffer, size_t length)
 {
 	size_t index = 0;
 	m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
-	while ( m_DataIt != m_DataItEnd && !isSeparator(*m_DataIt) )
-	{
+    while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) {
 		pBuffer[index] = *m_DataIt;
 		index++;
-		if (index == length-1)
-			break;
+        if( index == length - 1 ) {
+            break;
+        }
 		++m_DataIt;
 	}
 
@@ -233,12 +233,13 @@ void ObjFileParser::copyNextLine(char *pBuffer, size_t length)
 // -------------------------------------------------------------------
 void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
     size_t numComponents( 0 );
-    DataArrayIt tmp( m_DataIt );
+    const char* tmp( &m_DataIt[0] );
     while( !IsLineEnd( *tmp ) ) {
-        if( *tmp == ' ' ) {
-            ++numComponents;
+        if ( !SkipSpaces( &tmp ) ) {
+            break;
         }
-        tmp++;
+        SkipToken( tmp );
+        ++numComponents;
     }
     float x, y, z;
     if( 2 == numComponents ) {
@@ -344,7 +345,7 @@ void ObjFileParser::getFace(aiPrimitiveType type)
 			}
 			iPos++;
 		}
-		else if ( isSeparator(*pPtr) )
+        else if( IsSpaceOrNewLine( *pPtr ) )
 		{
 			iPos = 0;
 		}
@@ -462,8 +463,9 @@ void ObjFileParser::getMaterialDesc()
 		return;
 
 	char *pStart = &(*m_DataIt);
-	while ( m_DataIt != m_DataItEnd && !isSeparator(*m_DataIt) )
-		++m_DataIt;
+    while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) {
+        ++m_DataIt;
+    }
 
 	// Get name
 	std::string strName(pStart, &(*m_DataIt));
@@ -517,12 +519,14 @@ void ObjFileParser::getMaterialLib()
 {
 	// Translate tuple
 	m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
-	if (m_DataIt ==  m_DataItEnd)
-		return;
+    if( m_DataIt == m_DataItEnd ) {
+        return;
+    }
 	
 	char *pStart = &(*m_DataIt);
-	while (m_DataIt != m_DataItEnd && !isNewLine(*m_DataIt))
-		m_DataIt++;
+    while( m_DataIt != m_DataItEnd && !IsLineEnd( *m_DataIt ) ) {
+        ++m_DataIt;
+    }
 
 	// Check for existence
 	const std::string strMatName(pStart, &(*m_DataIt));
@@ -550,13 +554,15 @@ void ObjFileParser::getNewMaterial()
 {
 	m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
 	m_DataIt = getNextWord<DataArrayIt>(m_DataIt, m_DataItEnd);
-	if ( m_DataIt == m_DataItEnd )
-		return;
+    if( m_DataIt == m_DataItEnd ) {
+        return;
+    }
 
 	char *pStart = &(*m_DataIt);
 	std::string strMat( pStart, *m_DataIt );
-	while ( m_DataIt != m_DataItEnd && isSeparator( *m_DataIt ) )
-		m_DataIt++;
+    while( m_DataIt != m_DataItEnd && IsSpaceOrNewLine( *m_DataIt ) ) {
+        ++m_DataIt;
+    }
 	std::map<std::string, ObjFile::Material*>::iterator it = m_pModel->m_MaterialMap.find( strMat );
 	if ( it == m_pModel->m_MaterialMap.end() )
 	{
@@ -581,8 +587,9 @@ void ObjFileParser::getNewMaterial()
 int ObjFileParser::getMaterialIndex( const std::string &strMaterialName )
 {
 	int mat_index = -1;
-	if ( strMaterialName.empty() )
-		return mat_index;
+    if( strMaterialName.empty() ) {
+        return mat_index;
+    }
 	for (size_t index = 0; index < m_pModel->m_MaterialLib.size(); ++index)
 	{
 		if ( strMaterialName == m_pModel->m_MaterialLib[ index ])
@@ -601,8 +608,9 @@ void ObjFileParser::getGroupName()
 	std::string strGroupName;
    
 	m_DataIt = getName<DataArrayIt>(m_DataIt, m_DataItEnd, strGroupName);
-	if ( isEndOfBuffer( m_DataIt, m_DataItEnd ) )
-		return;
+    if( isEndOfBuffer( m_DataIt, m_DataItEnd ) ) {
+        return;
+    }
 
 	// Change active group, if necessary
 	if ( m_pModel->m_strActiveGroup != strGroupName )
@@ -653,11 +661,13 @@ void ObjFileParser::getGroupNumberAndResolution()
 void ObjFileParser::getObjectName()
 {
 	m_DataIt = getNextToken<DataArrayIt>(m_DataIt, m_DataItEnd);
-	if (m_DataIt == m_DataItEnd)
-		return;
+    if( m_DataIt == m_DataItEnd ) {
+        return;
+    }
 	char *pStart = &(*m_DataIt);
-	while ( m_DataIt != m_DataItEnd && !isSeparator( *m_DataIt ) )
-		++m_DataIt;
+    while( m_DataIt != m_DataItEnd && !IsSpaceOrNewLine( *m_DataIt ) ) {
+        ++m_DataIt;
+    }
 
 	std::string strObjectName(pStart, &(*m_DataIt));
 	if (!strObjectName.empty()) 
@@ -678,8 +688,9 @@ void ObjFileParser::getObjectName()
 		}
 
 		// Allocate a new object, if current one was not found before
-		if ( NULL == m_pModel->m_pCurrent )
-			createObject(strObjectName);
+        if( NULL == m_pModel->m_pCurrent ) {
+            createObject( strObjectName );
+        }
 	}
 	m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
 }
@@ -694,7 +705,6 @@ void ObjFileParser::createObject(const std::string &strObjectName)
 	m_pModel->m_pCurrent->m_strObjName = strObjectName;
 	m_pModel->m_Objects.push_back( m_pModel->m_pCurrent );
 	
-
 	createMesh();
 
 	if( m_pModel->m_pCurrentMaterial )

+ 15 - 35
code/ObjTools.h

@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define OBJ_TOOLS_H_INC
 
 #include "fast_atof.h"
+#include "ParsingUtils.h"
 
 namespace Assimp
 {
@@ -68,28 +69,6 @@ inline bool isEndOfBuffer(  char_t it, char_t end )
 	return ( it == end );	
 }
 
-/** @brief	Returns true, if token is a space on any supported platform
-*	@param	token	Token to search in
-*	@return	true, if token is a space			
-*/
-inline bool isSeparator( char token )
-{
-	return ( token == ' ' || 
-			token == '\n' || 
-			token == '\f' || 
-			token == '\r' ||
-			token == '\t' );
-}
-
-/**	@brief	Returns true, fi token id a new line marking token.
- *	@param	token	Token to search in
- *	@return	true, if token is a newline token.
- */
-inline bool isNewLine( char token )
-{
-	return ( token == '\n' || token == '\f' || token == '\r' );
-}
-
 /**	@brief	Returns next word separated by a space
  *	@param	pBuffer	Pointer to data buffer
  *	@param	pEnd	Pointer to end of buffer
@@ -100,7 +79,7 @@ inline Char_T getNextWord( Char_T pBuffer, Char_T pEnd )
 {
 	while ( !isEndOfBuffer( pBuffer, pEnd ) )
 	{
-		if ( !isSeparator( *pBuffer ) || isNewLine( *pBuffer ) )
+        if( !IsSpaceOrNewLine( *pBuffer ) || IsLineEnd( *pBuffer ) )
 			break;
 		pBuffer++;
 	}
@@ -117,7 +96,7 @@ inline Char_T getNextToken( Char_T pBuffer, Char_T pEnd )
 {
 	while ( !isEndOfBuffer( pBuffer, pEnd ) )
 	{
-		if ( isSeparator( *pBuffer ) )
+        if( IsSpaceOrNewLine( *pBuffer ) )
 			break;
 		pBuffer++;
 	}
@@ -127,14 +106,14 @@ inline Char_T getNextToken( Char_T pBuffer, Char_T pEnd )
 /**	@brief	Skips a line
  *	@param	it		Iterator set to current position
  *	@param	end		Iterator set to end of scratch buffer for readout
- *	@param	uiLine	Current linenumber in format
+ *	@param	uiLine	Current line number in format
  *	@return	Current-iterator with new position
  */
 template<class char_t>
-inline char_t skipLine( char_t it, char_t end, unsigned int &uiLine )
-{
-	while ( !isEndOfBuffer( it, end ) && !isNewLine( *it ) )
-		++it;
+inline char_t skipLine( char_t it, char_t end, unsigned int &uiLine ) {
+    while( !isEndOfBuffer( it, end ) && !IsLineEnd( *it ) ) {
+        ++it;
+    }
 	if ( it != end )
 	{
 		++it;
@@ -157,15 +136,16 @@ template<class char_t>
 inline char_t getName( char_t it, char_t end, std::string &name )
 {
 	name = "";
-	if ( isEndOfBuffer( it, end ) )
-		return end;
+    if( isEndOfBuffer( it, end ) ) {
+        return end;
+    }
 	
 	char *pStart = &( *it );
-	while ( !isEndOfBuffer( it, end ) && !isNewLine( *it ) ) {
+    while( !isEndOfBuffer( it, end ) && !IsLineEnd( *it ) ) {
 		++it;
 	}
 
-	while(isEndOfBuffer( it, end ) || isNewLine( *it ) || isSeparator(*it)) {
+    while( isEndOfBuffer( it, end ) || IsLineEnd( *it ) || IsSpaceOrNewLine( *it ) ) {
 		--it;
 	}
 	++it;
@@ -196,7 +176,7 @@ inline char_t CopyNextWord( char_t it, char_t end, char *pBuffer, size_t length
 {
 	size_t index = 0;
 	it = getNextWord<char_t>( it, end );
-	while ( !isSeparator( *it ) && !isEndOfBuffer( it, end ) )
+    while( !IsSpaceOrNewLine( *it ) && !isEndOfBuffer( it, end ) )
 	{
 		pBuffer[index] = *it ;
 		index++;
@@ -259,4 +239,4 @@ unsigned int tokenize( const string_type& str, std::vector<string_type>& tokens,
 
 } // Namespace Assimp
 
-#endif
+#endif // OBJ_TOOLS_H_INC

+ 5 - 6
code/OgreBinarySerializer.cpp

@@ -376,14 +376,14 @@ void OgreBinarySerializer::ReadMeshSkeletonLink(Mesh *mesh)
 	mesh->skeletonRef = ReadLine();
 }
 
-void OgreBinarySerializer::ReadMeshBounds(Mesh *mesh)
+void OgreBinarySerializer::ReadMeshBounds(Mesh * /*mesh*/)
 {
 	// Skip bounds, not compatible with Assimp.
 	// 2x float vec3 + 1x float sphere radius
 	SkipBytes(sizeof(float) * 7);
 }
 
-void OgreBinarySerializer::ReadMeshExtremes(Mesh *mesh)
+void OgreBinarySerializer::ReadMeshExtremes(Mesh * /*mesh*/)
 {
 	// Skip extremes, not compatible with Assimp.
 	size_t numBytes = m_currentLen - MSTREAM_OVERHEAD_SIZE; 
@@ -534,7 +534,6 @@ void OgreBinarySerializer::ReadSubMeshTextureAlias(SubMesh *submesh)
 void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh)
 {
 	uint16_t id = 0;
-	uint16_t submeshIndex = 0;
 
 	if (!AtEnd())
 	{
@@ -644,7 +643,7 @@ void OgreBinarySerializer::ReadGeometryVertexBuffer(VertexData *dest)
 	DefaultLogger::get()->debug(Formatter::format() << "    - Read vertex buffer for source " << bindIndex << " of " << numBytes << " bytes");
 }
 
-void OgreBinarySerializer::ReadEdgeList(Mesh *mesh)
+void OgreBinarySerializer::ReadEdgeList(Mesh * /*mesh*/)
 {
 	// Assimp does not acknowledge LOD levels as far as I can see it. This info is just skipped.
 
@@ -1055,7 +1054,7 @@ void OgreBinarySerializer::ReadSkeletonAnimation(Skeleton *skeleton)
 	DefaultLogger::get()->debug(Formatter::format() << "    " << anim->name << " (" << anim->length << " sec, " << anim->tracks.size() << " tracks)");	
 }
 
-void OgreBinarySerializer::ReadSkeletonAnimationTrack(Skeleton *skeleton, Animation *dest)
+void OgreBinarySerializer::ReadSkeletonAnimationTrack(Skeleton * /*skeleton*/, Animation *dest)
 {
 	uint16_t boneId = Read<uint16_t>();
 	Bone *bone = dest->parentSkeleton->BoneById(boneId);
@@ -1097,7 +1096,7 @@ void OgreBinarySerializer::ReadSkeletonAnimationKeyFrame(VertexAnimationTrack *d
 	dest->transformKeyFrames.push_back(keyframe);
 }
 
-void OgreBinarySerializer::ReadSkeletonAnimationLink(Skeleton *skeleton)
+void OgreBinarySerializer::ReadSkeletonAnimationLink(Skeleton * /*skeleton*/)
 {
 	// Skip bounds, not compatible with Assimp.
 	ReadLine(); // skeleton name

+ 6 - 2
code/OgreBinarySerializer.h

@@ -75,8 +75,8 @@ private:
 	};
 	
 	OgreBinarySerializer(MemoryStreamReader *reader, AssetMode mode) :
-		m_reader(reader),
 		m_currentLen(0),
+		m_reader(reader),
 		assetMode(mode)
 	{
 	}
@@ -301,11 +301,12 @@ enum MeshChunkId
 						// unsigned short poseIndex 
 						// float influence
 		// Optional submesh extreme vertex list chink
-		M_TABLE_EXTREMES = 0xE000,
+		M_TABLE_EXTREMES = 0xE000
 		// unsigned short submesh_index;
 		// float extremes [n_extremes][3];
 };
 
+/*
 static std::string MeshHeaderToString(MeshChunkId id)
 {
 	switch(id)
@@ -347,6 +348,7 @@ static std::string MeshHeaderToString(MeshChunkId id)
 	}
 	return "Unknown_MeshChunkId";
 }
+*/
 
 enum SkeletonChunkId
 {
@@ -393,6 +395,7 @@ enum SkeletonChunkId
 		// float scale							: scale to apply to trans/scale keys
 };
 
+/*
 static std::string SkeletonHeaderToString(SkeletonChunkId id)
 {
 	switch(id)
@@ -409,6 +412,7 @@ static std::string SkeletonHeaderToString(SkeletonChunkId id)
 	}
 	return "Unknown_SkeletonChunkId";
 }
+*/
 } // Ogre
 } // Assimp
 

+ 4 - 4
code/OgreImporter.cpp

@@ -108,10 +108,10 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
 		MemoryStreamReader reader(f);
 
 		// Import mesh
-		boost::scoped_ptr<Mesh> mesh = OgreBinarySerializer::ImportMesh(&reader);
+		boost::scoped_ptr<Mesh> mesh(OgreBinarySerializer::ImportMesh(&reader));
 
 		// Import skeleton
-		OgreBinarySerializer::ImportSkeleton(pIOHandler, mesh);
+		OgreBinarySerializer::ImportSkeleton(pIOHandler, mesh.get());
 
 		// Import mesh referenced materials
 		ReadMaterials(pFile, pIOHandler, pScene, mesh.get());
@@ -128,10 +128,10 @@ void OgreImporter::InternReadFile(const std::string &pFile, aiScene *pScene, Ass
 		boost::scoped_ptr<XmlReader> reader(irr::io::createIrrXMLReader(xmlStream.get()));
 
 		// Import mesh
-		boost::scoped_ptr<MeshXml> mesh = OgreXmlSerializer::ImportMesh(reader.get());
+		boost::scoped_ptr<MeshXml> mesh(OgreXmlSerializer::ImportMesh(reader.get()));
 		
 		// Import skeleton
-		OgreXmlSerializer::ImportSkeleton(pIOHandler, mesh);
+		OgreXmlSerializer::ImportSkeleton(pIOHandler, mesh.get());
 
 		// Import mesh referenced materials
 		ReadMaterials(pFile, pIOHandler, pScene, mesh.get());

+ 14 - 12
code/OgreStructs.cpp

@@ -325,7 +325,7 @@ uint32_t VertexData::VertexSize(uint16_t source) const
 MemoryStream *VertexData::VertexBuffer(uint16_t source)
 {
 	if (vertexBindings.find(source) != vertexBindings.end())
-		return vertexBindings[source];
+		return vertexBindings[source].get();
 	return 0;
 }
 
@@ -404,9 +404,9 @@ size_t IndexData::FaceSize() const
 // Mesh
 
 Mesh::Mesh() :
-	sharedVertexData(0),
-	skeleton(0),
-	hasSkeletalAnimations(false)
+	hasSkeletalAnimations(false),
+	skeleton(NULL),
+	sharedVertexData(NULL)
 {
 }
 
@@ -712,8 +712,8 @@ aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent)
 // MeshXml
 
 MeshXml::MeshXml() :
-	sharedVertexData(0),
-	skeleton(0)
+	skeleton(0),
+	sharedVertexData(0)
 {
 }
 
@@ -797,8 +797,8 @@ void MeshXml::ConvertToAssimpScene(aiScene* dest)
 // SubMeshXml
 
 SubMeshXml::SubMeshXml() :
-	vertexData(0),
-	indexData(new IndexDataXml())
+	indexData(new IndexDataXml()),
+	vertexData(0)
 {
 }
 
@@ -912,8 +912,8 @@ aiMesh *SubMeshXml::ConvertToAssimpMesh(MeshXml *parent)
 // Animation
 
 Animation::Animation(Skeleton *parent) :
+	parentMesh(NULL),
 	parentSkeleton(parent),
-	parentMesh(0),
 	length(0.0f),
 	baseTime(-1.0f)
 {
@@ -963,6 +963,8 @@ aiAnimation *Animation::ConvertToAssimpAnimation()
 // Skeleton
 
 Skeleton::Skeleton() :
+	bones(),
+	animations(),
 	blendMode(ANIMBLEND_AVERAGE)
 {
 }
@@ -1103,7 +1105,7 @@ aiNode *Bone::ConvertToAssimpNode(Skeleton *skeleton, aiNode *parentNode)
 	return node;
 }
 
-aiBone *Bone::ConvertToAssimpBone(Skeleton *parent, const std::vector<aiVertexWeight> &boneWeights)
+aiBone *Bone::ConvertToAssimpBone(Skeleton * /*parent*/, const std::vector<aiVertexWeight> &boneWeights)
 {
 	aiBone *bone = new aiBone();
 	bone->mName = name;
@@ -1122,8 +1124,8 @@ aiBone *Bone::ConvertToAssimpBone(Skeleton *parent, const std::vector<aiVertexWe
 // VertexAnimationTrack
 
 VertexAnimationTrack::VertexAnimationTrack() :
-	target(0),
-	type(VAT_NONE)
+	type(VAT_NONE),
+	target(0)
 {
 }
 

+ 45 - 32
code/OptimizeMeshes.cpp

@@ -47,22 +47,28 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS
 
 using namespace Assimp;
+
 #include "OptimizeMeshes.h"
 #include "ProcessHelper.h"
 #include "SceneCombiner.h"
 
+static const unsigned int NotSet   = 0xffffffff;
+static const unsigned int DeadBeef = 0xdeadbeef;
+
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 OptimizeMeshesProcess::OptimizeMeshesProcess()
 : pts (false)
-, max_verts (0xffffffff)
-, max_faces (0xffffffff)
-{}
+, max_verts( NotSet )
+, max_faces( NotSet ) {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
-OptimizeMeshesProcess::~OptimizeMeshesProcess()
-{}
+OptimizeMeshesProcess::~OptimizeMeshesProcess() {
+    // empty
+}
 
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
@@ -74,17 +80,17 @@ bool OptimizeMeshesProcess::IsActive( unsigned int pFlags) const
 	// That's a serious design flaw, consider redesign.
 	if( 0 != (pFlags & aiProcess_OptimizeMeshes) ) {
 		pts = (0 != (pFlags & aiProcess_SortByPType));
-		max_verts = (0 != (pFlags & aiProcess_SplitLargeMeshes)) ? 0xdeadbeef : max_verts;
+        max_verts = ( 0 != ( pFlags & aiProcess_SplitLargeMeshes ) ) ? DeadBeef : max_verts;
 		return true;
 	}
 	return false;
 }
 
 // ------------------------------------------------------------------------------------------------
-// Setup properties for the postprocessing step
+// Setup properties for the post-processing step
 void OptimizeMeshesProcess::SetupProperties(const Importer* pImp)
 {
-	if (max_verts == 0xdeadbeef /* magic hack */) {
+    if( max_verts == DeadBeef /* magic hack */ ) {
 		max_faces = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES);
 		max_verts = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES);
 	}
@@ -104,35 +110,36 @@ void OptimizeMeshesProcess::Execute( aiScene* pScene)
 	mScene = pScene;
 
 	// need to clear persistent members from previous runs
-	merge_list.clear();
-	output.clear();
+	merge_list.resize( 0 );
+	output.resize( 0 );
 
+    // ensure we have the right sizes
 	merge_list.reserve(pScene->mNumMeshes);
 	output.reserve(pScene->mNumMeshes);
 
 	// Prepare lookup tables
 	meshes.resize(pScene->mNumMeshes);
 	FindInstancedMeshes(pScene->mRootNode);
-	if (max_verts == 0xdeadbeef) /* undo the magic hack */
-		max_verts = 0xffffffff;
+    if( max_verts == DeadBeef ) /* undo the magic hack */
+		max_verts = NotSet;
 
 	// ... instanced meshes are immediately processed and added to the output list
 	for (unsigned int i = 0, n = 0; i < pScene->mNumMeshes;++i) {
 		meshes[i].vertex_format = GetMeshVFormatUnique(pScene->mMeshes[i]);
 
-		if (meshes[i].instance_cnt > 1 && meshes[i].output_id == 0xffffffff) {
+		if (meshes[i].instance_cnt > 1 && meshes[i].output_id == NotSet ) {
 			meshes[i].output_id = n++;
 			output.push_back(mScene->mMeshes[i]);
 		}
 	}
 
-	// and process all nodes in the scenegraoh recursively
+	// and process all nodes in the scenegraph recursively
 	ProcessNode(pScene->mRootNode);
 	if (!output.size()) {
 		throw DeadlyImportError("OptimizeMeshes: No meshes remaining; there's definitely something wrong");
 	}
 
-	meshes.clear();
+	meshes.resize( 0 );
 	ai_assert(output.size() <= num_old);
 
 	mScene->mNumMeshes = output.size();
@@ -142,8 +149,9 @@ void OptimizeMeshesProcess::Execute( aiScene* pScene)
 		char tmp[512];
 		::sprintf(tmp,"OptimizeMeshesProcess finished. Input meshes: %i, Output meshes: %i",num_old,pScene->mNumMeshes);
 		DefaultLogger::get()->info(tmp);
-	}
-	else DefaultLogger::get()->debug("OptimizeMeshesProcess finished");
+    } else {
+        DefaultLogger::get()->debug( "OptimizeMeshesProcess finished" );
+    }
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -157,7 +165,7 @@ void OptimizeMeshesProcess::ProcessNode( aiNode* pNode)
 			im = meshes[im].output_id;
 		}
 		else  {
-			merge_list.clear();
+			merge_list.resize( 0 );
 			unsigned int verts = 0, faces = 0;
 
 			// Find meshes to merge with us
@@ -170,8 +178,9 @@ void OptimizeMeshesProcess::ProcessNode( aiNode* pNode)
 					faces += mScene->mMeshes[am]->mNumFaces;
 
 					--pNode->mNumMeshes;
-					for (unsigned int n = a; n < pNode->mNumMeshes; ++n)
-						pNode->mMeshes[n] = pNode->mMeshes[n+1];
+                    for( unsigned int n = a; n < pNode->mNumMeshes; ++n ) {
+                        pNode->mMeshes[ n ] = pNode->mMeshes[ n + 1 ];
+                    }
 
 					--a;
 				}
@@ -184,8 +193,7 @@ void OptimizeMeshesProcess::ProcessNode( aiNode* pNode)
 				aiMesh* out;
 				SceneCombiner::MergeMeshes(&out,0,merge_list.begin(),merge_list.end());
 				output.push_back(out);
-			}
-			else {
+			} else {
 				output.push_back(mScene->mMeshes[im]);
 			}
 			im = output.size()-1;
@@ -193,8 +201,9 @@ void OptimizeMeshesProcess::ProcessNode( aiNode* pNode)
 	}
 
 
-	for (unsigned int i = 0; i < pNode->mNumChildren; ++i)
-		ProcessNode(pNode->mChildren[i]);
+    for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) {
+        ProcessNode( pNode->mChildren[ i ] );
+    }
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -206,8 +215,8 @@ bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned i
 
 	aiMesh* ma = mScene->mMeshes[a], *mb = mScene->mMeshes[b];
 
-	if ((0xffffffff != max_verts && verts+mb->mNumVertices > max_verts) ||
-		(0xffffffff != max_faces && faces+mb->mNumFaces    > max_faces)) {
+	if ((NotSet != max_verts && verts+mb->mNumVertices > max_verts) ||
+		(NotSet != max_faces && faces+mb->mNumFaces    > max_faces)) {
 		return false;
 	}
 
@@ -221,7 +230,7 @@ bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned i
 		return false;
 
 	// If both meshes are skinned, check whether we have many bones defined in both meshes. 
-	// If yes, we can savely join them. 
+	// If yes, we can join them. 
 	if (ma->HasBones()) {
 		// TODO
 		return false;
@@ -230,14 +239,18 @@ bool OptimizeMeshesProcess::CanJoin ( unsigned int a, unsigned int b, unsigned i
 }
 
 // ------------------------------------------------------------------------------------------------
-// Buidl a LUT of all instanced meshes
+// Build a LUT of all instanced meshes
 void OptimizeMeshesProcess::FindInstancedMeshes (aiNode* pNode)
 {
-	for (unsigned int i = 0; i < pNode->mNumMeshes;++i)
-		++meshes[pNode->mMeshes[i]].instance_cnt; 
+    for( unsigned int i = 0; i < pNode->mNumMeshes; ++i ) {
+        ++meshes[ pNode->mMeshes[ i ] ].instance_cnt;
+    }
 
-	for (unsigned int i = 0; i < pNode->mNumChildren; ++i)
-		FindInstancedMeshes(pNode->mChildren[i]);
+    for( unsigned int i = 0; i < pNode->mNumChildren; ++i ) {
+        FindInstancedMeshes( pNode->mChildren[ i ] );
+    }
 }
 
+// ------------------------------------------------------------------------------------------------
+
 #endif // !! ASSIMP_BUILD_NO_OPTIMIZEMESHES_PROCESS

+ 60 - 27
code/ParsingUtils.h

@@ -46,16 +46,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define AI_PARSING_UTILS_H_INC
 
 #include "StringComparison.h"
+
 namespace Assimp {
 
-	// NOTE: the functions below are mostly intended as replacement for
-	// std::upper, std::lower, std::isupper, std::islower, std::isspace.
-	// we don't bother of locales. We don't want them. We want reliable
-	// (i.e. identical) results across all locales.
+// NOTE: the functions below are mostly intended as replacement for
+// std::upper, std::lower, std::isupper, std::islower, std::isspace.
+// we don't bother of locales. We don't want them. We want reliable
+// (i.e. identical) results across all locales.
+
+// The functions below accept any character type, but know only
+// about ASCII. However, UTF-32 is the only safe ASCII superset to
+// use since it doesn't have multi-byte sequences.
 
-	// The functions below accept any character type, but know only
-	// about ASCII. However, UTF-32 is the only safe ASCII superset to
-	// use since it doesn't have multibyte sequences.
+static const unsigned int BufferSize = 4096;
 
 // ---------------------------------------------------------------------------------
 template <class char_t>
@@ -63,118 +66,145 @@ AI_FORCE_INLINE char_t ToLower( char_t in)
 {
 	return (in >= (char_t)'A' && in <= (char_t)'Z') ? (char_t)(in+0x20) : in;
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
-AI_FORCE_INLINE char_t ToUpper( char_t in)
-{
-	return (in >= (char_t)'a' && in <= (char_t)'z') ? (char_t)(in-0x20) : in;
+AI_FORCE_INLINE char_t ToUpper( char_t in) {
+    return (in >= (char_t)'a' && in <= (char_t)'z') ? (char_t)(in-0x20) : in;
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool IsUpper( char_t in)
 {
 	return (in >= (char_t)'A' && in <= (char_t)'Z');
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool IsLower( char_t in)
 {
 	return (in >= (char_t)'a' && in <= (char_t)'z');
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool IsSpace( char_t in)
 {
 	return (in == (char_t)' ' || in == (char_t)'\t');
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool IsLineEnd( char_t in)
 {
-	return (in == (char_t)'\r' || in == (char_t)'\n' || in == (char_t)'\0');
+	return (in==(char_t)'\r'||in==(char_t)'\n'||in==(char_t)'\0'||in==(char_t)'\f');
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool IsSpaceOrNewLine( char_t in)
 {
 	return IsSpace<char_t>(in) || IsLineEnd<char_t>(in);
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool SkipSpaces( const char_t* in, const char_t** out)
 {
-	while (*in == (char_t)' ' || *in == (char_t)'\t')in++;
+    while( *in == ( char_t )' ' || *in == ( char_t )'\t' ) {
+        ++in;
+    }
 	*out = in;
 	return !IsLineEnd<char_t>(*in);
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool SkipSpaces( const char_t** inout)
 {
 	return SkipSpaces<char_t>(*inout,inout);
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool SkipLine( const char_t* in, const char_t** out)
 {
-	while (*in != (char_t)'\r' && *in != (char_t)'\n' && *in != (char_t)'\0')in++;
+    while( *in != ( char_t )'\r' && *in != ( char_t )'\n' && *in != ( char_t )'\0' ) {
+        ++in;
+    }
 
 	// files are opened in binary mode. Ergo there are both NL and CR
-	while (*in == (char_t)'\r' || *in == (char_t)'\n')in++;
+    while( *in == ( char_t )'\r' || *in == ( char_t )'\n' ) {
+        ++in;
+    }
 	*out = in;
 	return *in != (char_t)'\0';
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool SkipLine( const char_t** inout)
 {
 	return SkipLine<char_t>(*inout,inout);
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out)
 {
-	while (*in == (char_t)' ' || *in == (char_t)'\t' ||
-		*in == (char_t)'\r' || *in == (char_t)'\n')in++;
+    while( *in == ( char_t )' ' || *in == ( char_t )'\t' || *in == ( char_t )'\r' || *in == ( char_t )'\n' ) {
+        ++in;
+    }
 	*out = in;
 	return *in != '\0';
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t** inout)
 {
 	return SkipSpacesAndLineEnd<char_t>(*inout,inout);
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
-AI_FORCE_INLINE bool GetNextLine(const char_t*& buffer, char_t out[4096])
+AI_FORCE_INLINE bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize ] )
 {
-	if ((char_t)'\0' == *buffer)return false;
+    if( ( char_t )'\0' == *buffer ) {
+        return false;
+    }
 
 	char* _out = out;
-	char* const end = _out+4096;
-	while (!IsLineEnd( *buffer ) && _out < end)
-		*_out++ = *buffer++;
+    char* const end = _out + BufferSize;
+    while( !IsLineEnd( *buffer ) && _out < end ) {
+        *_out++ = *buffer++;
+    }
 	*_out = (char_t)'\0';
 
-	while (IsLineEnd( *buffer ) && '\0' != *buffer)++buffer;
-	return true;
+    while( IsLineEnd( *buffer ) && '\0' != *buffer ) {
+        ++buffer;
+    }
+
+    return true;
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool IsNumeric( char_t in)
 {
 	return ( in >= '0' && in <= '9' ) || '-' == in || '+' == in;
 }
+
 // ---------------------------------------------------------------------------------
 template <class char_t>
 AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len)
 {
-	if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len]))
-	{
+	if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) {
 		in += len+1;
 		return true;
 	}
+
 	return false;
 }
 // ---------------------------------------------------------------------------------
@@ -185,8 +215,7 @@ AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len
  */
 AI_FORCE_INLINE bool TokenMatchI(const char*& in, const char* token, unsigned int len)
 {
-	if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len]))
-	{
+	if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len])) {
 		in += len+1;
 		return true;
 	}
@@ -206,5 +235,9 @@ AI_FORCE_INLINE std::string GetNextToken(const char*& in)
 	while (!IsSpaceOrNewLine(*in))++in;
 	return std::string(cur,(size_t)(in-cur));
 }
+
+// ---------------------------------------------------------------------------------
+
 } // ! namespace Assimp
+
 #endif // ! AI_PARSING_UTILS_H_INC

+ 103 - 6
code/PlyExporter.cpp

@@ -64,6 +64,20 @@ void ExportScenePly(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene
 	outfile->Write( exporter.mOutput.str().c_str(), static_cast<size_t>(exporter.mOutput.tellp()),1);
 }
 
+void ExportScenePlyBinary(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene)
+{
+	// invoke the exporter 
+	PlyExporter exporter(pFile, pScene, true);
+
+	// we're still here - export successfully completed. Write the file.
+	boost::scoped_ptr<IOStream> outfile(pIOSystem->Open(pFile, "wb"));
+	if (outfile == NULL) {
+		throw DeadlyExportError("could not open output .ply file: " + std::string(pFile));
+	}
+
+	outfile->Write(exporter.mOutput.str().c_str(), static_cast<size_t>(exporter.mOutput.tellp()), 1);
+}
+
 } // end of namespace Assimp
 
 #define PLY_EXPORT_HAS_NORMALS 0x1
@@ -72,7 +86,7 @@ void ExportScenePly(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene
 #define PLY_EXPORT_HAS_COLORS (PLY_EXPORT_HAS_TEXCOORDS << AI_MAX_NUMBER_OF_TEXTURECOORDS)
 
 // ------------------------------------------------------------------------------------------------
-PlyExporter :: PlyExporter(const char* _filename, const aiScene* pScene)
+PlyExporter::PlyExporter(const char* _filename, const aiScene* pScene, bool binary)
 : filename(_filename)
 , pScene(pScene)
 , endl("\n") 
@@ -102,7 +116,16 @@ PlyExporter :: PlyExporter(const char* _filename, const aiScene* pScene)
 	}
 
 	mOutput << "ply" << endl;
-	mOutput << "format ascii 1.0" << endl;
+	if (binary) {
+#if (defined AI_BUILD_BIG_ENDIAN)
+		mOutput << "format binary_big_endian 1.0" << endl;
+#else
+		mOutput << "format binary_little_endian 1.0" << endl;
+#endif
+	}
+	else {
+		mOutput << "format ascii 1.0" << endl;
+	}
 	mOutput << "comment Created by Open Asset Import Library - http://assimp.sf.net (v"
 		<< aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' 
 		<< aiGetVersionRevision() << ")" << endl;
@@ -163,17 +186,29 @@ PlyExporter :: PlyExporter(const char* _filename, const aiScene* pScene)
 	mOutput << "end_header" << endl;
 
 	for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
-		WriteMeshVerts(pScene->mMeshes[i],components);
+		if (binary) {
+			WriteMeshVertsBinary(pScene->mMeshes[i], components);
+		}
+		else {
+			WriteMeshVerts(pScene->mMeshes[i], components);
+		}
 	}
 	for (unsigned int i = 0, ofs = 0; i < pScene->mNumMeshes; ++i) {
-		WriteMeshIndices(pScene->mMeshes[i],ofs);
+		if (binary) {
+			WriteMeshIndicesBinary(pScene->mMeshes[i], ofs);
+		}
+		else {
+			WriteMeshIndices(pScene->mMeshes[i], ofs);
+		}
 		ofs += pScene->mMeshes[i]->mNumVertices;
 	}
 }
 
 // ------------------------------------------------------------------------------------------------
-void PlyExporter :: WriteMeshVerts(const aiMesh* m, unsigned int components)
+void PlyExporter::WriteMeshVerts(const aiMesh* m, unsigned int components)
 {
+	// If a component (for instance normal vectors) is present in at least one mesh in the scene,
+	// then default values are written for meshes that do not contain this component.
 	for (unsigned int i = 0; i < m->mNumVertices; ++i) {
 		mOutput << 
 			m->mVertices[i].x << " " << 
@@ -237,7 +272,57 @@ void PlyExporter :: WriteMeshVerts(const aiMesh* m, unsigned int components)
 }
 
 // ------------------------------------------------------------------------------------------------
-void PlyExporter :: WriteMeshIndices(const aiMesh* m, unsigned int offset)
+void PlyExporter::WriteMeshVertsBinary(const aiMesh* m, unsigned int components)
+{
+	// If a component (for instance normal vectors) is present in at least one mesh in the scene,
+	// then default values are written for meshes that do not contain this component.
+	aiVector3D defaultNormal(0, 0, 0);
+	aiVector2D defaultUV(-1, -1);
+	aiColor4D defaultColor(-1, -1, -1, -1);
+	for (unsigned int i = 0; i < m->mNumVertices; ++i) {
+		mOutput.write(reinterpret_cast<const char*>(&m->mVertices[i].x), 12);
+		if (components & PLY_EXPORT_HAS_NORMALS) {
+			if (m->HasNormals()) {
+				mOutput.write(reinterpret_cast<const char*>(&m->mNormals[i].x), 12);
+			}
+			else {
+				mOutput.write(reinterpret_cast<const char*>(&defaultNormal.x), 12);
+			}
+		}
+
+		for (unsigned int n = PLY_EXPORT_HAS_TEXCOORDS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_TEXTURECOORDS; n <<= 1, ++c) {
+			if (m->HasTextureCoords(c)) {
+				mOutput.write(reinterpret_cast<const char*>(&m->mTextureCoords[c][i].x), 6);
+			}
+			else {
+				mOutput.write(reinterpret_cast<const char*>(&defaultUV.x), 6);
+			}
+		}
+
+		for (unsigned int n = PLY_EXPORT_HAS_COLORS, c = 0; (components & n) && c != AI_MAX_NUMBER_OF_COLOR_SETS; n <<= 1, ++c) {
+			if (m->HasVertexColors(c)) {
+				mOutput.write(reinterpret_cast<const char*>(&m->mColors[c][i].r), 16);
+			}
+			else {
+				mOutput.write(reinterpret_cast<const char*>(&defaultColor.r), 16);
+			}
+		}
+
+		if (components & PLY_EXPORT_HAS_TANGENTS_BITANGENTS) {
+			if (m->HasTangentsAndBitangents()) {
+				mOutput.write(reinterpret_cast<const char*>(&m->mTangents[i].x), 12);
+				mOutput.write(reinterpret_cast<const char*>(&m->mBitangents[i].x), 12);
+			}
+			else {
+				mOutput.write(reinterpret_cast<const char*>(&defaultNormal.x), 12);
+				mOutput.write(reinterpret_cast<const char*>(&defaultNormal.x), 12);
+			}
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void PlyExporter::WriteMeshIndices(const aiMesh* m, unsigned int offset)
 {
 	for (unsigned int i = 0; i < m->mNumFaces; ++i) {
 		const aiFace& f = m->mFaces[i];
@@ -248,4 +333,16 @@ void PlyExporter :: WriteMeshIndices(const aiMesh* m, unsigned int offset)
 	}
 }
 
+void PlyExporter::WriteMeshIndicesBinary(const aiMesh* m, unsigned int offset)
+{
+	for (unsigned int i = 0; i < m->mNumFaces; ++i) {
+		const aiFace& f = m->mFaces[i];
+		mOutput.write(reinterpret_cast<const char*>(&f.mNumIndices), 4);
+		for (unsigned int c = 0; c < f.mNumIndices; ++c) {
+			unsigned int index = f.mIndices[c] + offset;
+			mOutput.write(reinterpret_cast<const char*>(&index), 4);
+		}
+	}
+}
+
 #endif

+ 4 - 1
code/PlyExporter.h

@@ -59,7 +59,7 @@ class PlyExporter
 {
 public:
 	/// Constructor for a specific scene to export
-	PlyExporter(const char* filename, const aiScene* pScene);
+	PlyExporter(const char* filename, const aiScene* pScene, bool binary = false);
 
 public:
 
@@ -71,6 +71,9 @@ private:
 	void WriteMeshVerts(const aiMesh* m, unsigned int components);
 	void WriteMeshIndices(const aiMesh* m, unsigned int ofs);
 
+	void WriteMeshVertsBinary(const aiMesh* m, unsigned int components);
+	void WriteMeshIndicesBinary(const aiMesh* m, unsigned int offset);
+
 private:
 
 	const std::string filename;

+ 1 - 1
code/PlyParser.cpp

@@ -182,7 +182,7 @@ PLY::ESemantic PLY::Property::ParseSemantic(const char* pCur,const char** pCurOu
 	{
 		eOut = PLY::EST_Opacity;
 	}
-	else if (TokenMatch(pCur,"specular_power",6))
+	else if (TokenMatch(pCur,"specular_power",14))
 	{
 		eOut = PLY::EST_PhongPower;
 	}

+ 6 - 6
code/PolyTools.h

@@ -118,9 +118,9 @@ inline bool IsCCW(T* in, size_t npoints) {
 			((-in[i+2].y + in[i+1].y) *
 			(-in[i+2].y + in[i+1].y));
 
-		b = sqrt(bb);
-		c = sqrt(cc);
-		theta = acos((bb + cc - aa) / (2 * b * c));
+		b = std::sqrt(bb);
+		c = std::sqrt(cc);
+		theta = std::acos((bb + cc - aa) / (2 * b * c));
 
 		if (OnLeftSideOfLine2D(in[i],in[i+2],in[i+1])) {
 			//	if (convex(in[i].x, in[i].y,
@@ -146,9 +146,9 @@ inline bool IsCCW(T* in, size_t npoints) {
 	cc = ((in[1].x - in[0].x) * (in[1].x - in[0].x)) +
 		((-in[1].y + in[0].y) * (-in[1].y + in[0].y));
 
-	b = sqrt(bb);
-	c = sqrt(cc);
-	theta = acos((bb + cc - aa) / (2 * b * c));
+	b = std::sqrt(bb);
+	c = std::sqrt(cc);
+	theta = std::acos((bb + cc - aa) / (2 * b * c));
 
 	//if (convex(in[npoints-2].x, in[npoints-2].y,
 	//	in[0].x, in[0].y,

+ 10 - 8
code/Q3BSPFileImporter.cpp

@@ -73,11 +73,13 @@ static const aiImporterDesc desc = {
 
 namespace Assimp {
 
+/*
 static void getSupportedExtensions(std::vector<std::string> &supportedExtensions) {
     supportedExtensions.push_back( ".jpg" );
     supportedExtensions.push_back( ".png" );
     supportedExtensions.push_back( ".tga" );
 }
+*/
     
 using namespace Q3BSP;
 
@@ -628,7 +630,7 @@ aiFace *Q3BSPFileImporter::getNextFace( aiMesh *pMesh, unsigned int &rFaceIdx )
 bool Q3BSPFileImporter::importTextureFromArchive( const Q3BSP::Q3BSPModel *pModel,
 												 Q3BSP::Q3BSPZipArchive *pArchive, aiScene*,
 												 aiMaterial *pMatHelper, int textureId ) {
-	if ( NULL == pArchive || NULL == pArchive || NULL == pMatHelper ) {
+	if ( NULL == pArchive || NULL == pMatHelper ) {
 		return false;
 	}
 
@@ -639,17 +641,17 @@ bool Q3BSPFileImporter::importTextureFromArchive( const Q3BSP::Q3BSPModel *pMode
 	bool res = true;
 	sQ3BSPTexture *pTexture = pModel->m_Textures[ textureId ];
 	if ( !pTexture ) {
-        return false;
-    }
+		return false;
+	}
 
-    std::vector<std::string> supportedExtensions;
-    supportedExtensions.push_back( ".jpg" );
-    supportedExtensions.push_back( ".png" );
-    supportedExtensions.push_back( ".tga" );
+	std::vector<std::string> supportedExtensions;
+	supportedExtensions.push_back( ".jpg" );
+	supportedExtensions.push_back( ".png" );
+	supportedExtensions.push_back( ".tga" );
 	std::string textureName, ext;
 	if ( expandFile( pArchive, pTexture->strName, supportedExtensions, textureName, ext ) ) {
 		IOStream *pTextureStream = pArchive->Open( textureName.c_str() );
-		if ( !pTextureStream ) {
+		if ( pTextureStream ) {
 			size_t texSize = pTextureStream->FileSize();
 			aiTexture *pTexture = new aiTexture;
 			pTexture->mHeight = 0;

+ 5 - 5
code/Q3BSPZipArchive.cpp

@@ -68,25 +68,25 @@ voidpf IOSystem2Unzip::open(voidpf opaque, const char* filename, int mode) {
 	return (voidpf) io_system->Open(filename, mode_fopen);
 }
 
-uLong IOSystem2Unzip::read(voidpf opaque, voidpf stream, void* buf, uLong size) {
+uLong IOSystem2Unzip::read(voidpf /*opaque*/, voidpf stream, void* buf, uLong size) {
 	IOStream* io_stream = (IOStream*) stream;
 
 	return io_stream->Read(buf, 1, size);
 }
 
-uLong IOSystem2Unzip::write(voidpf opaque, voidpf stream, const void* buf, uLong size) {
+uLong IOSystem2Unzip::write(voidpf /*opaque*/, voidpf stream, const void* buf, uLong size) {
 	IOStream* io_stream = (IOStream*) stream;
 
 	return io_stream->Write(buf, 1, size);
 }
 
-long IOSystem2Unzip::tell(voidpf opaque, voidpf stream) {
+long IOSystem2Unzip::tell(voidpf /*opaque*/, voidpf stream) {
 	IOStream* io_stream = (IOStream*) stream;
 
 	return io_stream->Tell();
 }
 
-long IOSystem2Unzip::seek(voidpf opaque, voidpf stream, uLong offset, int origin) {
+long IOSystem2Unzip::seek(voidpf /*opaque*/, voidpf stream, uLong offset, int origin) {
 	IOStream* io_stream = (IOStream*) stream;
 
 	aiOrigin assimp_origin;
@@ -115,7 +115,7 @@ int IOSystem2Unzip::close(voidpf opaque, voidpf stream) {
 	return 0;
 }
 
-int IOSystem2Unzip::testerror(voidpf opaque, voidpf stream) {
+int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) {
 	return 0;
 }
 

+ 2 - 2
code/STLLoader.cpp

@@ -348,8 +348,8 @@ bool STLImporter::LoadBinaryFile()
 	bool bIsMaterialise = false;
 
 	// search for an occurence of "COLOR=" in the header
-	const char* sz2 = (const char*)mBuffer;
-	const char* const szEnd = sz2+80;
+	const unsigned char* sz2 = (const unsigned char*)mBuffer;
+	const unsigned char* const szEnd = sz2+80;
 	while (sz2 < szEnd)	{
 
 		if ('C' == *sz2++ && 'O' == *sz2++ && 'L' == *sz2++ &&

+ 46 - 0
code/SceneCombiner.cpp

@@ -38,6 +38,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ----------------------------------------------------------------------
 */
 
+// TODO: refactor entire file to get rid of the "flat-copy" first approach
+// to copying structures. This easily breaks in the most unintuitive way
+// possible as new fields are added to assimp structures.
 
 // ----------------------------------------------------------------------------
 /** @file Implements Assimp::SceneCombiner. This is a smart utility
@@ -1194,10 +1197,53 @@ void SceneCombiner::Copy     (aiNode** _dest, const aiNode* src)
 	// get a flat copy
 	::memcpy(dest,src,sizeof(aiNode));
 
+	if (src->mMetaData) {
+		Copy(&dest->mMetaData, src->mMetaData);
+	}
+
 	// and reallocate all arrays
 	GetArrayCopy( dest->mMeshes, dest->mNumMeshes );
 	CopyPtrArray( dest->mChildren, src->mChildren,dest->mNumChildren);
 }
 
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy (aiMetadata** _dest, const aiMetadata* src)
+{
+	ai_assert(NULL != _dest && NULL != src);
+
+	aiMetadata* dest = *_dest = new aiMetadata();
+	dest->mNumProperties = src->mNumProperties;
+	dest->mKeys = new aiString[src->mNumProperties];
+	std::copy(src->mKeys, src->mKeys + src->mNumProperties, dest->mKeys);
+
+	dest->mValues = new aiMetadataEntry[src->mNumProperties];
+	for (unsigned int i = 0; i < src->mNumProperties; ++i) {
+		aiMetadataEntry& in = src->mValues[i];
+		aiMetadataEntry& out = dest->mValues[i];
+		out.mType = in.mType;
+		switch (dest->mValues[i].mType) {
+		case AI_BOOL:
+			out.mData = new bool(*static_cast<bool*>(in.mData));
+			break;
+		case AI_INT:
+			out.mData = new int(*static_cast<int*>(in.mData));
+			break;
+		case AI_UINT64:
+			out.mData = new uint64_t(*static_cast<uint64_t*>(in.mData));
+			break;
+		case AI_FLOAT:
+			out.mData = new float(*static_cast<float*>(in.mData));
+			break;
+		case AI_AISTRING:
+			out.mData = new aiString(*static_cast<aiString*>(in.mData));
+			break;
+		case AI_AIVECTOR3D:
+			out.mData = new aiVector3D(*static_cast<aiVector3D*>(in.mData));
+			break;
+		default:
+			ai_assert(false);
+		}
+	}
+}
 
 }

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно