diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..0b8dc48bb --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# See for details + +root = true + +[CMakeLists.txt,*.cmake{,.in}] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_size = 2 +indent_style = space + +[*.h.in] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_size = 4 +indent_style = space diff --git a/.gitignore b/.gitignore index 833e56ef1..89fe66c1f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea build .project *.kdev4* diff --git a/AssimpConfig.cmake.in b/AssimpConfig.cmake.in index d46fa8315..5bb705a41 100644 --- a/AssimpConfig.cmake.in +++ b/AssimpConfig.cmake.in @@ -7,10 +7,10 @@ # Compute paths get_filename_component(FOOBAR_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) if(EXISTS "${FOOBAR_CMAKE_DIR}/CMakeCache.txt") - # In build tree - include("${FOOBAR_CMAKE_DIR}/FooBarBuildTreeSettings.cmake") + # In build tree + include("${FOOBAR_CMAKE_DIR}/FooBarBuildTreeSettings.cmake") else() - set(FOOBAR_INCLUDE_DIRS "${FOOBAR_CMAKE_DIR}/@CONF_REL_INCLUDE_DIR@") + set(FOOBAR_INCLUDE_DIRS "${FOOBAR_CMAKE_DIR}/@CONF_REL_INCLUDE_DIR@") endif() # Our library dependencies (contains definitions for IMPORTED targets) diff --git a/CMakeLists.txt b/CMakeLists.txt index d8bace045..a79c0c371 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,8 @@ PROJECT( Assimp ) option(BUILD_SHARED_LIBS "Build package with shared libraries." ON) if(NOT BUILD_SHARED_LIBS) - #set(CMAKE_EXE_LINKER_FLAGS "-static") - set(LINK_SEARCH_START_STATIC TRUE) + #set(CMAKE_EXE_LINKER_FLAGS "-static") + set(LINK_SEARCH_START_STATIC TRUE) endif(NOT BUILD_SHARED_LIBS) # Define here the needed parameters @@ -23,27 +23,27 @@ add_definitions( -DOPENDDL_NO_USE_CPP11 ) # Get the current working branch execute_process( - COMMAND git rev-parse --abbrev-ref HEAD - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE GIT_BRANCH - OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND git rev-parse --abbrev-ref HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_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_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE GIT_COMMIT_HASH - OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND git log -1 --format=%h + WORKING_DIRECTORY ${CMAKE_CURRENT_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_CURRENT_SOURCE_DIR}/revision.h.in - ${CMAKE_CURRENT_BINARY_DIR}/revision.h + ${CMAKE_CURRENT_SOURCE_DIR}/revision.h.in + ${CMAKE_CURRENT_BINARY_DIR}/revision.h ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) @@ -59,16 +59,16 @@ option(ASSIMP_ANDROID_JNIIOSYSTEM "Android JNI IOSystem support is active" OFF) # Workaround to be able to deal with compiler bug "Too many sections" with mingw. if( CMAKE_COMPILER_IS_MINGW ) - ADD_DEFINITIONS(-DASSIMP_BUILD_NO_IFC_IMPORTER ) + ADD_DEFINITIONS(-DASSIMP_BUILD_NO_IFC_IMPORTER ) endif() if((CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) AND NOT CMAKE_COMPILER_IS_MINGW) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # this is a very important switch and some libraries seem now to have it.... - # hide all not-exported symbols - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wall" ) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # this is a very important switch and some libraries seem now to have it.... + # hide all not-exported symbols + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wall" ) elseif(MSVC) - # enable multi-core compilation with MSVC - add_definitions(/MP) + # enable multi-core compilation with MSVC + add_definitions(/MP) endif() INCLUDE (FindPkgConfig) @@ -81,51 +81,51 @@ 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(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Debug Postfitx for lib, samples and tools") # 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. 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 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" "1.55" "1.55.0" "1.56" "1.56.0" "1.57" "1.57.0" "1.58" "1.58.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" "1.58" "1.58.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 @@ -134,8 +134,8 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config-version.cmake.in" "${C 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}) option ( ASSIMP_NO_EXPORT - "Disable Assimp's export functionality." - OFF + "Disable Assimp's export functionality." + OFF ) if( CMAKE_COMPILER_IS_GNUCXX ) @@ -146,134 +146,134 @@ endif( CMAKE_COMPILER_IS_GNUCXX ) # 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) - set(ZLIB_LIBRARIES_LINKED -lz) + 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 ) 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 ) SET ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER OFF CACHE BOOL - "Build the C4D importer, which relies on the non-free Melange SDK." + "Build the C4D importer, which relies on the non-free Melange SDK." ) IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) - IF ( MSVC ) - SET(C4D_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/_melange/includes") - - # pick the correct prebuilt library - IF(MSVC11) - SET(C4D_LIB_POSTFIX "_2012md") - ELSEIF(MSVC10) - SET(C4D_LIB_POSTFIX "_2010md") - ELSEIF(MSVC90) - SET(C4D_LIB_POSTFIX "_2008md") - ELSE() - MESSAGE( FATAL_ERROR - "C4D is currently only supported with MSVC 9, 10, 11" - ) - ENDIF() - - IF(CMAKE_CL_64) - SET(C4D_LIB_ARCH_POSTFIX "_x64") - ELSE() - SET(C4D_LIB_ARCH_POSTFIX "") - ENDIF() - - SET(C4D_LIB_BASE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/_melange/lib/WIN") - - SET(C4D_DEBUG_LIBRARY "${C4D_LIB_BASE_PATH}/debug/_melange_lib${C4D_LIB_ARCH_POSTFIX}${C4D_LIB_POSTFIX}.lib") - SET(C4D_RELEASE_LIBRARY "${C4D_LIB_BASE_PATH}/release/_melange_lib${C4D_LIB_ARCH_POSTFIX}${C4D_LIB_POSTFIX}.lib") - - # winsock and winmm are necessary dependencies of melange (this is undocumented, but true.) - SET(C4D_EXTRA_LIBRARIES WSock32.lib Winmm.lib) - ELSE () - MESSAGE( FATAL_ERROR - "C4D is currently only available on Windows with melange SDK installed in contrib/Melange" - ) - ENDIF ( MSVC ) + IF ( MSVC ) + SET(C4D_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/_melange/includes") + + # pick the correct prebuilt library + IF(MSVC11) + SET(C4D_LIB_POSTFIX "_2012md") + ELSEIF(MSVC10) + SET(C4D_LIB_POSTFIX "_2010md") + ELSEIF(MSVC90) + SET(C4D_LIB_POSTFIX "_2008md") + ELSE() + MESSAGE( FATAL_ERROR + "C4D is currently only supported with MSVC 9, 10, 11" + ) + ENDIF() + + IF(CMAKE_CL_64) + SET(C4D_LIB_ARCH_POSTFIX "_x64") + ELSE() + SET(C4D_LIB_ARCH_POSTFIX "") + ENDIF() + + SET(C4D_LIB_BASE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/_melange/lib/WIN") + + SET(C4D_DEBUG_LIBRARY "${C4D_LIB_BASE_PATH}/debug/_melange_lib${C4D_LIB_ARCH_POSTFIX}${C4D_LIB_POSTFIX}.lib") + SET(C4D_RELEASE_LIBRARY "${C4D_LIB_BASE_PATH}/release/_melange_lib${C4D_LIB_ARCH_POSTFIX}${C4D_LIB_POSTFIX}.lib") + + # winsock and winmm are necessary dependencies of melange (this is undocumented, but true.) + SET(C4D_EXTRA_LIBRARIES WSock32.lib Winmm.lib) + ELSE () + MESSAGE( FATAL_ERROR + "C4D is currently only available on Windows with melange SDK installed in contrib/Melange" + ) + ENDIF ( MSVC ) else (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) - ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER ) + ADD_DEFINITIONS( -DASSIMP_BUILD_NO_C4D_IMPORTER ) ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) ADD_SUBDIRECTORY( code/ ) option ( ASSIMP_BUILD_ASSIMP_TOOLS - "If the supplementary tools for Assimp are built in addition to the library." - ON + "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 ) option ( ASSIMP_BUILD_SAMPLES - "If the official samples are built as well (needs Glut)." - OFF + "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 ) option ( ASSIMP_BUILD_TESTS - "If the test suite for Assimp is built in addition to the library." - ON + "If the test suite for Assimp is built in addition to the library." + ON ) IF ( ASSIMP_BUILD_TESTS ) - ADD_SUBDIRECTORY( test/ ) + ADD_SUBDIRECTORY( test/ ) ENDIF ( ASSIMP_BUILD_TESTS ) IF(MSVC) - option ( ASSIMP_INSTALL_PDB - "Install MSVC debug files." - ON - ) + option ( ASSIMP_INSTALL_PDB + "Install MSVC debug files." + ON + ) ENDIF(MSVC) # Generate a pkg-config .pc for the Assimp library. @@ -281,50 +281,50 @@ CONFIGURE_FILE( "${PROJECT_SOURCE_DIR}/assimp.pc.in" "${PROJECT_BINARY_DIR}/assi 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_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") + # 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") + 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) + 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) + # 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() diff --git a/cmake-modules/AddGTest.cmake b/cmake-modules/AddGTest.cmake index 8dd77415d..1ffd36a10 100644 --- a/cmake-modules/AddGTest.cmake +++ b/cmake-modules/AddGTest.cmake @@ -2,30 +2,30 @@ find_package(Threads REQUIRED) include(ExternalProject) if(MSYS OR MINGW) - set(DISABLE_PTHREADS ON) + set(DISABLE_PTHREADS ON) else() - set(DISABLE_PTHREADS OFF) + set(DISABLE_PTHREADS OFF) endif() if (MSVC) - set(RELEASE_LIB_DIR ReleaseLibs) - set(DEBUG_LIB_DIR DebugLibs) + set(RELEASE_LIB_DIR ReleaseLibs) + set(DEBUG_LIB_DIR DebugLibs) else() - set(RELEASE_LIB_DIR "") - set(DEBUG_LIB_DIR "") + 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}") + "-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) + 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") @@ -33,40 +33,40 @@ set(GTEST_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/gtest") # try to find git - if found, setup gtest find_package(Git) if(NOT GIT_FOUND) - set(AddGTest_FOUND false CACHE BOOL "Was gtest setup correctly?") + set(AddGTest_FOUND false CACHE BOOL "Was gtest setup correctly?") else(NOT GIT_FOUND) - set(AddGTest_FOUND true CACHE BOOL "Was gtest setup correctly?") - - 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(AddGTest_FOUND true CACHE BOOL "Was gtest setup correctly?") - 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}") + 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 "" + ) - if(MSVC_VERSION EQUAL 1700) - add_definitions(-D_VARIADIC_MAX=10) - endif() + 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}") - ExternalProject_Get_Property(gtest source_dir) - include_directories(${source_dir}/include) - include_directories(${source_dir}/gtest/include) + if(MSVC_VERSION EQUAL 1700) + add_definitions(-D_VARIADIC_MAX=10) + endif() - ExternalProject_Get_Property(gtest binary_dir) - link_directories(${binary_dir}) + 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}) endif(NOT GIT_FOUND) diff --git a/cmake-modules/DebSourcePPA.cmake b/cmake-modules/DebSourcePPA.cmake index 62c645251..d8a786fb2 100644 --- a/cmake-modules/DebSourcePPA.cmake +++ b/cmake-modules/DebSourcePPA.cmake @@ -1,347 +1,347 @@ -## Debian Source Package Generator -# -# Copyright (c) 2010 Daniel Pfeifer -# Many modifications by Rosen Diankov -# -# Creates source debian files and manages library dependencies -# -# Features: -# -# - Automatically generates symbols and run-time dependencies from the build dependencies -# - Custom copy of source directory via CPACK_DEBIAN_PACKAGE_SOURCE_COPY -# - Simultaneous output of multiple debian source packages for each distribution -# - Can specificy distribution-specific dependencies by suffixing DEPENDS with _${DISTRO_NAME}, for example: CPACK_DEBIAN_PACKAGE_DEPENDS_LUCID, CPACK_COMPONENT_MYCOMP0_DEPENDS_LUCID -# -# Usage: -# -# set(CPACK_DEBIAN_BUILD_DEPENDS debhelper cmake) -# set(CPACK_DEBIAN_PACKAGE_PRIORITY optional) -# set(CPACK_DEBIAN_PACKAGE_SECTION devel) -# set(CPACK_DEBIAN_CMAKE_OPTIONS "-DMYOPTION=myvalue") -# set(CPACK_DEBIAN_PACKAGE_DEPENDS mycomp0 mycomp1 some_ubuntu_package) -# set(CPACK_DEBIAN_PACKAGE_DEPENDS_UBUNTU_LUCID mycomp0 mycomp1 lucid_specific_package) -# set(CPACK_DEBIAN_PACKAGE_NAME mypackage) -# set(CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES unnecessary_file unnecessary_dir/file0) -# set(CPACK_DEBIAN_PACKAGE_SOURCE_COPY svn export --force) # if using subversion -# set(CPACK_DEBIAN_DISTRIBUTION_NAME ubuntu) -# set(CPACK_DEBIAN_DISTRIBUTION_RELEASES karmic lucid maverick natty) -# set(CPACK_DEBIAN_CHANGELOG " * Extra change log lines") -# set(CPACK_DEBIAN_PACKAGE_SUGGESTS "ipython") -# set(CPACK_COMPONENT_X_RECOMMENDS "recommended-package") -## - -find_program(DEBUILD_EXECUTABLE debuild) -find_program(DPUT_EXECUTABLE dput) - -if(NOT DEBUILD_EXECUTABLE OR NOT DPUT_EXECUTABLE) - return() -endif(NOT DEBUILD_EXECUTABLE OR NOT DPUT_EXECUTABLE) - -# DEBIAN/control -# debian policy enforce lower case for package name -# Package: (mandatory) -IF(NOT CPACK_DEBIAN_PACKAGE_NAME) - STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_DEBIAN_PACKAGE_NAME) -ENDIF(NOT CPACK_DEBIAN_PACKAGE_NAME) - -# Section: (recommended) -IF(NOT CPACK_DEBIAN_PACKAGE_SECTION) - SET(CPACK_DEBIAN_PACKAGE_SECTION "devel") -ENDIF(NOT CPACK_DEBIAN_PACKAGE_SECTION) - -# Priority: (recommended) -IF(NOT CPACK_DEBIAN_PACKAGE_PRIORITY) - SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") -ENDIF(NOT CPACK_DEBIAN_PACKAGE_PRIORITY) - -file(STRINGS ${CPACK_PACKAGE_DESCRIPTION_FILE} DESC_LINES) -foreach(LINE ${DESC_LINES}) - set(DEB_LONG_DESCRIPTION "${DEB_LONG_DESCRIPTION} ${LINE}\n") -endforeach(LINE ${DESC_LINES}) - -file(REMOVE_RECURSE "${CMAKE_BINARY_DIR}/Debian") -file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/Debian") -set(DEBIAN_SOURCE_ORIG_DIR "${CMAKE_BINARY_DIR}/Debian/${CPACK_DEBIAN_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") - -if( CPACK_DEBIAN_PACKAGE_SOURCE_COPY ) - execute_process(COMMAND ${CPACK_DEBIAN_PACKAGE_SOURCE_COPY} "${CMAKE_SOURCE_DIR}" "${DEBIAN_SOURCE_ORIG_DIR}.orig") -else() - execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR} "${DEBIAN_SOURCE_ORIG_DIR}.orig") - execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory "${DEBIAN_SOURCE_ORIG_DIR}.orig/.git") - execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory "${DEBIAN_SOURCE_ORIG_DIR}.orig/.svn") -endif() - -# remove unnecessary folders -foreach(REMOVE_DIR ${CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES}) - file(REMOVE_RECURSE ${DEBIAN_SOURCE_ORIG_DIR}.orig/${REMOVE_DIR}) -endforeach() - -# create the original source tar -execute_process(COMMAND ${CMAKE_COMMAND} -E tar czf "${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}.orig.tar.gz" "${CPACK_DEBIAN_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.orig" WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Debian) - -set(DEB_SOURCE_CHANGES) -foreach(RELEASE ${CPACK_DEBIAN_DISTRIBUTION_RELEASES}) - set(DEBIAN_SOURCE_DIR "${DEBIAN_SOURCE_ORIG_DIR}-${CPACK_DEBIAN_DISTRIBUTION_NAME}1~${RELEASE}1") - set(RELEASE_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}-${CPACK_DEBIAN_DISTRIBUTION_NAME}1~${RELEASE}1") - string(TOUPPER ${RELEASE} RELEASE_UPPER) - string(TOUPPER ${CPACK_DEBIAN_DISTRIBUTION_NAME} DISTRIBUTION_NAME_UPPER) - file(MAKE_DIRECTORY ${DEBIAN_SOURCE_DIR}/debian) - ############################################################################## - # debian/control - set(DEBIAN_CONTROL ${DEBIAN_SOURCE_DIR}/debian/control) - file(WRITE ${DEBIAN_CONTROL} - "Source: ${CPACK_DEBIAN_PACKAGE_NAME}\n" - "Section: ${CPACK_DEBIAN_PACKAGE_SECTION}\n" - "Priority: ${CPACK_DEBIAN_PACKAGE_PRIORITY}\n" - "DM-Upload-Allowed: yes\n" - "Maintainer: ${CPACK_PACKAGE_CONTACT}\n" - "Build-Depends: " - ) - - if( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - else( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - if( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) - else( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS}) - endif( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) - endif( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - - - file(APPEND ${DEBIAN_CONTROL} "\n" - "Standards-Version: 3.8.4\n" - "Homepage: ${CPACK_PACKAGE_VENDOR}\n" - "\n" - "Package: ${CPACK_DEBIAN_PACKAGE_NAME}\n" - "Architecture: any\n" - "Depends: " - ) - - if( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - else( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - if( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) - else( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS}) - endif( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) - endif( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - - file(APPEND ${DEBIAN_CONTROL} "\nRecommends: ") - if( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - else( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - if( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) - else( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS}) - endif( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) - endif( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - - file(APPEND ${DEBIAN_CONTROL} "\nSuggests: ") - if( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - else( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - if( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) - else( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS}) - file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") - endforeach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS}) - endif( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) - endif( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - - file(APPEND ${DEBIAN_CONTROL} "\n" - "Description: ${CPACK_PACKAGE_DISPLAY_NAME} ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}\n" - "${DEB_LONG_DESCRIPTION}" - ) - - foreach(COMPONENT ${CPACK_COMPONENTS_ALL}) - string(TOUPPER ${COMPONENT} UPPER_COMPONENT) - set(DEPENDS "\${shlibs:Depends}") - if( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - set(DEPENDS "${DEPENDS}, ${DEP}") - endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - else( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - if( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) - set(DEPENDS "${DEPENDS}, ${DEP}") - endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) - else( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS}) - set(DEPENDS "${DEPENDS}, ${DEP}") - endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS}) - endif( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) - endif( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - - set(RECOMMENDS) - if( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - set(RECOMMENDS "${RECOMMENDS} ${DEP}, ") - endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - else( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - if( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) - set(RECOMMENDS "${RECOMMENDS} ${DEP}, ") - endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) - else( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS}) - set(RECOMMENDS "${RECOMMENDS} ${DEP}, ") - endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS}) - endif( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) - endif( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - - set(SUGGESTS) - if( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - set(SUGGESTS "${SUGGESTS} ${DEP}, ") - endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) - else( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - if( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) - set(SUGGESTS "${SUGGESTS} ${DEP}, ") - endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) - else( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) - foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS}) - set(SUGGESTS "${SUGGESTS} ${DEP}, ") - endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS}) - endif( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) - endif( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) - - file(APPEND ${DEBIAN_CONTROL} "\n" - "Package: ${COMPONENT}\n" - "Architecture: any\n" - "Depends: ${DEPENDS}\n" - "Recommends: ${RECOMMENDS}\n" - "Suggests: ${SUGGESTS}\n" - "Description: ${CPACK_PACKAGE_DISPLAY_NAME} ${CPACK_COMPONENT_${UPPER_COMPONENT}_DISPLAY_NAME}\n" - "${DEB_LONG_DESCRIPTION}" - " .\n" - " ${CPACK_COMPONENT_${UPPER_COMPONENT}_DESCRIPTION}\n" - ) - endforeach(COMPONENT ${CPACK_COMPONENTS_ALL}) - - ############################################################################## - # debian/copyright - set(DEBIAN_COPYRIGHT ${DEBIAN_SOURCE_DIR}/debian/copyright) - execute_process(COMMAND ${CMAKE_COMMAND} -E - copy ${CPACK_RESOURCE_FILE_LICENSE} ${DEBIAN_COPYRIGHT} - ) - - ############################################################################## - # debian/rules - set(DEBIAN_RULES ${DEBIAN_SOURCE_DIR}/debian/rules) - file(WRITE ${DEBIAN_RULES} - "#!/usr/bin/make -f\n" - "\n" - "BUILDDIR = build_dir\n" - "\n" - "build:\n" - " mkdir $(BUILDDIR)\n" - " cd $(BUILDDIR); cmake -DCMAKE_BUILD_TYPE=Release ${CPACK_DEBIAN_CMAKE_OPTIONS} -DCMAKE_INSTALL_PREFIX=/usr ..\n" - " $(MAKE) -C $(BUILDDIR) preinstall\n" - " touch build\n" - "\n" - "binary: binary-indep binary-arch\n" - "\n" - "binary-indep: build\n" - "\n" - "binary-arch: build\n" - " cd $(BUILDDIR); cmake -DCOMPONENT=Unspecified -DCMAKE_INSTALL_PREFIX=../debian/tmp/usr -P cmake_install.cmake\n" - " mkdir -p debian/tmp/DEBIAN\n" - " dpkg-gensymbols -p${CPACK_DEBIAN_PACKAGE_NAME}\n" - ) - - foreach(COMPONENT ${CPACK_COMPONENTS_ALL}) - set(PATH debian/${COMPONENT}) - file(APPEND ${DEBIAN_RULES} - " cd $(BUILDDIR); cmake -DCOMPONENT=${COMPONENT} -DCMAKE_INSTALL_PREFIX=../${PATH}/usr -P cmake_install.cmake\n" - " mkdir -p ${PATH}/DEBIAN\n" - " dpkg-gensymbols -p${COMPONENT} -P${PATH}\n" - ) - endforeach(COMPONENT ${CPACK_COMPONENTS_ALL}) - - file(APPEND ${DEBIAN_RULES} - " dh_shlibdeps\n" - " dh_strip\n" # for reducing size - " dpkg-gencontrol -p${CPACK_DEBIAN_PACKAGE_NAME}\n" - " dpkg --build debian/tmp ..\n" - ) - - foreach(COMPONENT ${CPACK_COMPONENTS_ALL}) - set(PATH debian/${COMPONENT}) - file(APPEND ${DEBIAN_RULES} - " dpkg-gencontrol -p${COMPONENT} -P${PATH} -Tdebian/${COMPONENT}.substvars\n" - " dpkg --build ${PATH} ..\n" - ) - endforeach(COMPONENT ${CPACK_COMPONENTS_ALL}) - - file(APPEND ${DEBIAN_RULES} - "\n" - "clean:\n" - " rm -f build\n" - " rm -rf $(BUILDDIR)\n" - "\n" - ".PHONY: binary binary-arch binary-indep clean\n" - ) - - execute_process(COMMAND chmod +x ${DEBIAN_RULES}) - - ############################################################################## - # debian/compat - file(WRITE ${DEBIAN_SOURCE_DIR}/debian/compat "7") - - ############################################################################## - # debian/source/format - file(WRITE ${DEBIAN_SOURCE_DIR}/debian/source/format "3.0 (quilt)") - - ############################################################################## - # debian/changelog - set(DEBIAN_CHANGELOG ${DEBIAN_SOURCE_DIR}/debian/changelog) - execute_process(COMMAND date -R OUTPUT_VARIABLE DATE_TIME) - file(WRITE ${DEBIAN_CHANGELOG} - "${CPACK_DEBIAN_PACKAGE_NAME} (${RELEASE_PACKAGE_VERSION}) ${RELEASE}; urgency=medium\n\n" - " * Package built with CMake\n\n" - "${CPACK_DEBIAN_CHANGELOG}" - " -- ${CPACK_PACKAGE_CONTACT} ${DATE_TIME}" - ) - - ############################################################################## - # debuild -S - if( DEB_SOURCE_CHANGES ) - set(DEBUILD_OPTIONS "-sd") - else() - set(DEBUILD_OPTIONS "-sa") - endif() - set(SOURCE_CHANGES_FILE "${CPACK_DEBIAN_PACKAGE_NAME}_${RELEASE_PACKAGE_VERSION}_source.changes") - set(DEB_SOURCE_CHANGES ${DEB_SOURCE_CHANGES} "${SOURCE_CHANGES_FILE}") - add_custom_command(OUTPUT "${SOURCE_CHANGES_FILE}" COMMAND ${DEBUILD_EXECUTABLE} -S ${DEBUILD_OPTIONS} WORKING_DIRECTORY ${DEBIAN_SOURCE_DIR}) -endforeach(RELEASE ${CPACK_DEBIAN_DISTRIBUTION_RELEASES}) - -############################################################################## -# dput ppa:your-lp-id/ppa -add_custom_target(dput ${DPUT_EXECUTABLE} ${DPUT_HOST} ${DEB_SOURCE_CHANGES} DEPENDS ${DEB_SOURCE_CHANGES} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Debian) +## Debian Source Package Generator +# +# Copyright (c) 2010 Daniel Pfeifer +# Many modifications by Rosen Diankov +# +# Creates source debian files and manages library dependencies +# +# Features: +# +# - Automatically generates symbols and run-time dependencies from the build dependencies +# - Custom copy of source directory via CPACK_DEBIAN_PACKAGE_SOURCE_COPY +# - Simultaneous output of multiple debian source packages for each distribution +# - Can specificy distribution-specific dependencies by suffixing DEPENDS with _${DISTRO_NAME}, for example: CPACK_DEBIAN_PACKAGE_DEPENDS_LUCID, CPACK_COMPONENT_MYCOMP0_DEPENDS_LUCID +# +# Usage: +# +# set(CPACK_DEBIAN_BUILD_DEPENDS debhelper cmake) +# set(CPACK_DEBIAN_PACKAGE_PRIORITY optional) +# set(CPACK_DEBIAN_PACKAGE_SECTION devel) +# set(CPACK_DEBIAN_CMAKE_OPTIONS "-DMYOPTION=myvalue") +# set(CPACK_DEBIAN_PACKAGE_DEPENDS mycomp0 mycomp1 some_ubuntu_package) +# set(CPACK_DEBIAN_PACKAGE_DEPENDS_UBUNTU_LUCID mycomp0 mycomp1 lucid_specific_package) +# set(CPACK_DEBIAN_PACKAGE_NAME mypackage) +# set(CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES unnecessary_file unnecessary_dir/file0) +# set(CPACK_DEBIAN_PACKAGE_SOURCE_COPY svn export --force) # if using subversion +# set(CPACK_DEBIAN_DISTRIBUTION_NAME ubuntu) +# set(CPACK_DEBIAN_DISTRIBUTION_RELEASES karmic lucid maverick natty) +# set(CPACK_DEBIAN_CHANGELOG " * Extra change log lines") +# set(CPACK_DEBIAN_PACKAGE_SUGGESTS "ipython") +# set(CPACK_COMPONENT_X_RECOMMENDS "recommended-package") +## + +find_program(DEBUILD_EXECUTABLE debuild) +find_program(DPUT_EXECUTABLE dput) + +if(NOT DEBUILD_EXECUTABLE OR NOT DPUT_EXECUTABLE) + return() +endif(NOT DEBUILD_EXECUTABLE OR NOT DPUT_EXECUTABLE) + +# DEBIAN/control +# debian policy enforce lower case for package name +# Package: (mandatory) +IF(NOT CPACK_DEBIAN_PACKAGE_NAME) + STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_DEBIAN_PACKAGE_NAME) +ENDIF(NOT CPACK_DEBIAN_PACKAGE_NAME) + +# Section: (recommended) +IF(NOT CPACK_DEBIAN_PACKAGE_SECTION) + SET(CPACK_DEBIAN_PACKAGE_SECTION "devel") +ENDIF(NOT CPACK_DEBIAN_PACKAGE_SECTION) + +# Priority: (recommended) +IF(NOT CPACK_DEBIAN_PACKAGE_PRIORITY) + SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") +ENDIF(NOT CPACK_DEBIAN_PACKAGE_PRIORITY) + +file(STRINGS ${CPACK_PACKAGE_DESCRIPTION_FILE} DESC_LINES) +foreach(LINE ${DESC_LINES}) + set(DEB_LONG_DESCRIPTION "${DEB_LONG_DESCRIPTION} ${LINE}\n") +endforeach(LINE ${DESC_LINES}) + +file(REMOVE_RECURSE "${CMAKE_BINARY_DIR}/Debian") +file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/Debian") +set(DEBIAN_SOURCE_ORIG_DIR "${CMAKE_BINARY_DIR}/Debian/${CPACK_DEBIAN_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") + +if( CPACK_DEBIAN_PACKAGE_SOURCE_COPY ) + execute_process(COMMAND ${CPACK_DEBIAN_PACKAGE_SOURCE_COPY} "${CMAKE_SOURCE_DIR}" "${DEBIAN_SOURCE_ORIG_DIR}.orig") +else() + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR} "${DEBIAN_SOURCE_ORIG_DIR}.orig") + execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory "${DEBIAN_SOURCE_ORIG_DIR}.orig/.git") + execute_process(COMMAND ${CMAKE_COMMAND} -E remove_directory "${DEBIAN_SOURCE_ORIG_DIR}.orig/.svn") +endif() + +# remove unnecessary folders +foreach(REMOVE_DIR ${CPACK_DEBIAN_PACKAGE_REMOVE_SOURCE_FILES}) + file(REMOVE_RECURSE ${DEBIAN_SOURCE_ORIG_DIR}.orig/${REMOVE_DIR}) +endforeach() + +# create the original source tar +execute_process(COMMAND ${CMAKE_COMMAND} -E tar czf "${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}.orig.tar.gz" "${CPACK_DEBIAN_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.orig" WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Debian) + +set(DEB_SOURCE_CHANGES) +foreach(RELEASE ${CPACK_DEBIAN_DISTRIBUTION_RELEASES}) + set(DEBIAN_SOURCE_DIR "${DEBIAN_SOURCE_ORIG_DIR}-${CPACK_DEBIAN_DISTRIBUTION_NAME}1~${RELEASE}1") + set(RELEASE_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}-${CPACK_DEBIAN_DISTRIBUTION_NAME}1~${RELEASE}1") + string(TOUPPER ${RELEASE} RELEASE_UPPER) + string(TOUPPER ${CPACK_DEBIAN_DISTRIBUTION_NAME} DISTRIBUTION_NAME_UPPER) + file(MAKE_DIRECTORY ${DEBIAN_SOURCE_DIR}/debian) + ############################################################################## + # debian/control + set(DEBIAN_CONTROL ${DEBIAN_SOURCE_DIR}/debian/control) + file(WRITE ${DEBIAN_CONTROL} + "Source: ${CPACK_DEBIAN_PACKAGE_NAME}\n" + "Section: ${CPACK_DEBIAN_PACKAGE_SECTION}\n" + "Priority: ${CPACK_DEBIAN_PACKAGE_PRIORITY}\n" + "DM-Upload-Allowed: yes\n" + "Maintainer: ${CPACK_PACKAGE_CONTACT}\n" + "Build-Depends: " + ) + + if( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_BUILD_DEPENDS}) + endif( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_DEBIAN_BUILD_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + + file(APPEND ${DEBIAN_CONTROL} "\n" + "Standards-Version: 3.8.4\n" + "Homepage: ${CPACK_PACKAGE_VENDOR}\n" + "\n" + "Package: ${CPACK_DEBIAN_PACKAGE_NAME}\n" + "Architecture: any\n" + "Depends: " + ) + + if( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_DEPENDS}) + endif( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_DEBIAN_PACKAGE_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + file(APPEND ${DEBIAN_CONTROL} "\nRecommends: ") + if( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_RECOMMENDS}) + endif( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_DEBIAN_PACKAGE_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + file(APPEND ${DEBIAN_CONTROL} "\nSuggests: ") + if( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS}) + file(APPEND ${DEBIAN_CONTROL} "${DEP}, ") + endforeach(DEP ${CPACK_DEBIAN_PACKAGE_SUGGESTS}) + endif( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_DEBIAN_PACKAGE_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + file(APPEND ${DEBIAN_CONTROL} "\n" + "Description: ${CPACK_PACKAGE_DISPLAY_NAME} ${CPACK_PACKAGE_DESCRIPTION_SUMMARY}\n" + "${DEB_LONG_DESCRIPTION}" + ) + + foreach(COMPONENT ${CPACK_COMPONENTS_ALL}) + string(TOUPPER ${COMPONENT} UPPER_COMPONENT) + set(DEPENDS "\${shlibs:Depends}") + if( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + set(DEPENDS "${DEPENDS}, ${DEP}") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + set(DEPENDS "${DEPENDS}, ${DEP}") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS}) + set(DEPENDS "${DEPENDS}, ${DEP}") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS}) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_DEPENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + set(RECOMMENDS) + if( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + set(RECOMMENDS "${RECOMMENDS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) + set(RECOMMENDS "${RECOMMENDS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS}) + set(RECOMMENDS "${RECOMMENDS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS}) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_RECOMMENDS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + set(SUGGESTS) + if( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + set(SUGGESTS "${SUGGESTS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + if( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) + set(SUGGESTS "${SUGGESTS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}}) + else( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + foreach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS}) + set(SUGGESTS "${SUGGESTS} ${DEP}, ") + endforeach(DEP ${CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS}) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER} ) + endif( CPACK_COMPONENT_${UPPER_COMPONENT}_SUGGESTS_${DISTRIBUTION_NAME_UPPER}_${RELEASE_UPPER} ) + + file(APPEND ${DEBIAN_CONTROL} "\n" + "Package: ${COMPONENT}\n" + "Architecture: any\n" + "Depends: ${DEPENDS}\n" + "Recommends: ${RECOMMENDS}\n" + "Suggests: ${SUGGESTS}\n" + "Description: ${CPACK_PACKAGE_DISPLAY_NAME} ${CPACK_COMPONENT_${UPPER_COMPONENT}_DISPLAY_NAME}\n" + "${DEB_LONG_DESCRIPTION}" + " .\n" + " ${CPACK_COMPONENT_${UPPER_COMPONENT}_DESCRIPTION}\n" + ) + endforeach(COMPONENT ${CPACK_COMPONENTS_ALL}) + + ############################################################################## + # debian/copyright + set(DEBIAN_COPYRIGHT ${DEBIAN_SOURCE_DIR}/debian/copyright) + execute_process(COMMAND ${CMAKE_COMMAND} -E + copy ${CPACK_RESOURCE_FILE_LICENSE} ${DEBIAN_COPYRIGHT} + ) + + ############################################################################## + # debian/rules + set(DEBIAN_RULES ${DEBIAN_SOURCE_DIR}/debian/rules) + file(WRITE ${DEBIAN_RULES} + "#!/usr/bin/make -f\n" + "\n" + "BUILDDIR = build_dir\n" + "\n" + "build:\n" + " mkdir $(BUILDDIR)\n" + " cd $(BUILDDIR); cmake -DCMAKE_BUILD_TYPE=Release ${CPACK_DEBIAN_CMAKE_OPTIONS} -DCMAKE_INSTALL_PREFIX=/usr ..\n" + " $(MAKE) -C $(BUILDDIR) preinstall\n" + " touch build\n" + "\n" + "binary: binary-indep binary-arch\n" + "\n" + "binary-indep: build\n" + "\n" + "binary-arch: build\n" + " cd $(BUILDDIR); cmake -DCOMPONENT=Unspecified -DCMAKE_INSTALL_PREFIX=../debian/tmp/usr -P cmake_install.cmake\n" + " mkdir -p debian/tmp/DEBIAN\n" + " dpkg-gensymbols -p${CPACK_DEBIAN_PACKAGE_NAME}\n" + ) + + foreach(COMPONENT ${CPACK_COMPONENTS_ALL}) + set(PATH debian/${COMPONENT}) + file(APPEND ${DEBIAN_RULES} + " cd $(BUILDDIR); cmake -DCOMPONENT=${COMPONENT} -DCMAKE_INSTALL_PREFIX=../${PATH}/usr -P cmake_install.cmake\n" + " mkdir -p ${PATH}/DEBIAN\n" + " dpkg-gensymbols -p${COMPONENT} -P${PATH}\n" + ) + endforeach(COMPONENT ${CPACK_COMPONENTS_ALL}) + + file(APPEND ${DEBIAN_RULES} + " dh_shlibdeps\n" + " dh_strip\n" # for reducing size + " dpkg-gencontrol -p${CPACK_DEBIAN_PACKAGE_NAME}\n" + " dpkg --build debian/tmp ..\n" + ) + + foreach(COMPONENT ${CPACK_COMPONENTS_ALL}) + set(PATH debian/${COMPONENT}) + file(APPEND ${DEBIAN_RULES} + " dpkg-gencontrol -p${COMPONENT} -P${PATH} -Tdebian/${COMPONENT}.substvars\n" + " dpkg --build ${PATH} ..\n" + ) + endforeach(COMPONENT ${CPACK_COMPONENTS_ALL}) + + file(APPEND ${DEBIAN_RULES} + "\n" + "clean:\n" + " rm -f build\n" + " rm -rf $(BUILDDIR)\n" + "\n" + ".PHONY: binary binary-arch binary-indep clean\n" + ) + + execute_process(COMMAND chmod +x ${DEBIAN_RULES}) + + ############################################################################## + # debian/compat + file(WRITE ${DEBIAN_SOURCE_DIR}/debian/compat "7") + + ############################################################################## + # debian/source/format + file(WRITE ${DEBIAN_SOURCE_DIR}/debian/source/format "3.0 (quilt)") + + ############################################################################## + # debian/changelog + set(DEBIAN_CHANGELOG ${DEBIAN_SOURCE_DIR}/debian/changelog) + execute_process(COMMAND date -R OUTPUT_VARIABLE DATE_TIME) + file(WRITE ${DEBIAN_CHANGELOG} + "${CPACK_DEBIAN_PACKAGE_NAME} (${RELEASE_PACKAGE_VERSION}) ${RELEASE}; urgency=medium\n\n" + " * Package built with CMake\n\n" + "${CPACK_DEBIAN_CHANGELOG}" + " -- ${CPACK_PACKAGE_CONTACT} ${DATE_TIME}" + ) + + ############################################################################## + # debuild -S + if( DEB_SOURCE_CHANGES ) + set(DEBUILD_OPTIONS "-sd") + else() + set(DEBUILD_OPTIONS "-sa") + endif() + set(SOURCE_CHANGES_FILE "${CPACK_DEBIAN_PACKAGE_NAME}_${RELEASE_PACKAGE_VERSION}_source.changes") + set(DEB_SOURCE_CHANGES ${DEB_SOURCE_CHANGES} "${SOURCE_CHANGES_FILE}") + add_custom_command(OUTPUT "${SOURCE_CHANGES_FILE}" COMMAND ${DEBUILD_EXECUTABLE} -S ${DEBUILD_OPTIONS} WORKING_DIRECTORY ${DEBIAN_SOURCE_DIR}) +endforeach(RELEASE ${CPACK_DEBIAN_DISTRIBUTION_RELEASES}) + +############################################################################## +# dput ppa:your-lp-id/ppa +add_custom_target(dput ${DPUT_EXECUTABLE} ${DPUT_HOST} ${DEB_SOURCE_CHANGES} DEPENDS ${DEB_SOURCE_CHANGES} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/Debian) diff --git a/cmake-modules/FindDirectX.cmake b/cmake-modules/FindDirectX.cmake index c8eac1a7d..38a861a83 100644 --- a/cmake-modules/FindDirectX.cmake +++ b/cmake-modules/FindDirectX.cmake @@ -1,100 +1,100 @@ -#------------------------------------------------------------------- -# This file is part of the CMake build system for OGRE -# (Object-oriented Graphics Rendering Engine) -# For the latest info, see http://www.ogre3d.org/ -# -# The contents of this file are placed in the public domain. Feel -# free to make use of it in any way you like. -#------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# Find DirectX SDK -# Define: -# DirectX_FOUND -# DirectX_INCLUDE_DIR -# DirectX_LIBRARY -# DirectX_ROOT_DIR - -if(WIN32) # The only platform it makes sense to check for DirectX SDK - include(FindPkgMacros) - findpkg_begin(DirectX) - - # Get path, convert backslashes as ${ENV_DXSDK_DIR} - getenv_path(DXSDK_DIR) - getenv_path(DIRECTX_HOME) - getenv_path(DIRECTX_ROOT) - getenv_path(DIRECTX_BASE) - - # construct search paths - set(DirectX_PREFIX_PATH - "${DXSDK_DIR}" "${ENV_DXSDK_DIR}" - "${DIRECTX_HOME}" "${ENV_DIRECTX_HOME}" - "${DIRECTX_ROOT}" "${ENV_DIRECTX_ROOT}" - "${DIRECTX_BASE}" "${ENV_DIRECTX_BASE}" - "C:/apps_x86/Microsoft DirectX SDK*" - "C:/Program Files (x86)/Microsoft DirectX SDK*" - "C:/apps/Microsoft DirectX SDK*" - "C:/Program Files/Microsoft DirectX SDK*" - "$ENV{ProgramFiles}/Microsoft DirectX SDK*" - ) - create_search_paths(DirectX) - # redo search if prefix path changed - clear_if_changed(DirectX_PREFIX_PATH - DirectX_LIBRARY - DirectX_INCLUDE_DIR - ) - - find_path(DirectX_INCLUDE_DIR NAMES d3d9.h HINTS ${DirectX_INC_SEARCH_PATH}) - # dlls are in DirectX_ROOT_DIR/Developer Runtime/x64|x86 - # lib files are in DirectX_ROOT_DIR/Lib/x64|x86 - if(CMAKE_CL_64) - set(DirectX_LIBPATH_SUFFIX "x64") - else(CMAKE_CL_64) - set(DirectX_LIBPATH_SUFFIX "x86") - endif(CMAKE_CL_64) - find_library(DirectX_LIBRARY NAMES d3d9 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) - find_library(DirectX_D3DX9_LIBRARY NAMES d3dx9 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) - find_library(DirectX_DXERR_LIBRARY NAMES DxErr HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) - find_library(DirectX_DXGUID_LIBRARY NAMES dxguid HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) - - - # look for dxgi (needed by both 10 and 11) - find_library(DirectX_DXGI_LIBRARY NAMES dxgi HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) - - # look for d3dcompiler (needed by 11) - find_library(DirectX_D3DCOMPILER_LIBRARY NAMES d3dcompiler HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) - - findpkg_finish(DirectX) - set(DirectX_LIBRARIES ${DirectX_LIBRARIES} - ${DirectX_D3DX9_LIBRARY} - ${DirectX_DXERR_LIBRARY} - ${DirectX_DXGUID_LIBRARY} - ) - - mark_as_advanced(DirectX_D3DX9_LIBRARY DirectX_DXERR_LIBRARY DirectX_DXGUID_LIBRARY - DirectX_DXGI_LIBRARY DirectX_D3DCOMPILER_LIBRARY) - - - # look for D3D11 components - if (DirectX_FOUND) - find_path(DirectX_D3D11_INCLUDE_DIR NAMES D3D11Shader.h HINTS ${DirectX_INC_SEARCH_PATH}) - get_filename_component(DirectX_LIBRARY_DIR "${DirectX_LIBRARY}" PATH) - message(STATUS "DX lib dir: ${DirectX_LIBRARY_DIR}") - find_library(DirectX_D3D11_LIBRARY NAMES d3d11 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) - find_library(DirectX_D3DX11_LIBRARY NAMES d3dx11 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) - if (DirectX_D3D11_INCLUDE_DIR AND DirectX_D3D11_LIBRARY) - set(DirectX_D3D11_FOUND TRUE) - set(DirectX_D3D11_INCLUDE_DIR ${DirectX_D3D11_INCLUDE_DIR}) - set(DirectX_D3D11_LIBRARIES ${DirectX_D3D11_LIBRARIES} - ${DirectX_D3D11_LIBRARY} - ${DirectX_D3DX11_LIBRARY} - ${DirectX_DXGI_LIBRARY} - ${DirectX_DXERR_LIBRARY} - ${DirectX_DXGUID_LIBRARY} - ${DirectX_D3DCOMPILER_LIBRARY} - ) - endif () - mark_as_advanced(DirectX_D3D11_INCLUDE_DIR DirectX_D3D11_LIBRARY DirectX_D3DX11_LIBRARY) - endif () - -endif(WIN32) +#------------------------------------------------------------------- +# This file is part of the CMake build system for OGRE +# (Object-oriented Graphics Rendering Engine) +# For the latest info, see http://www.ogre3d.org/ +# +# The contents of this file are placed in the public domain. Feel +# free to make use of it in any way you like. +#------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# Find DirectX SDK +# Define: +# DirectX_FOUND +# DirectX_INCLUDE_DIR +# DirectX_LIBRARY +# DirectX_ROOT_DIR + +if(WIN32) # The only platform it makes sense to check for DirectX SDK + include(FindPkgMacros) + findpkg_begin(DirectX) + + # Get path, convert backslashes as ${ENV_DXSDK_DIR} + getenv_path(DXSDK_DIR) + getenv_path(DIRECTX_HOME) + getenv_path(DIRECTX_ROOT) + getenv_path(DIRECTX_BASE) + + # construct search paths + set(DirectX_PREFIX_PATH + "${DXSDK_DIR}" "${ENV_DXSDK_DIR}" + "${DIRECTX_HOME}" "${ENV_DIRECTX_HOME}" + "${DIRECTX_ROOT}" "${ENV_DIRECTX_ROOT}" + "${DIRECTX_BASE}" "${ENV_DIRECTX_BASE}" + "C:/apps_x86/Microsoft DirectX SDK*" + "C:/Program Files (x86)/Microsoft DirectX SDK*" + "C:/apps/Microsoft DirectX SDK*" + "C:/Program Files/Microsoft DirectX SDK*" + "$ENV{ProgramFiles}/Microsoft DirectX SDK*" + ) + create_search_paths(DirectX) + # redo search if prefix path changed + clear_if_changed(DirectX_PREFIX_PATH + DirectX_LIBRARY + DirectX_INCLUDE_DIR + ) + + find_path(DirectX_INCLUDE_DIR NAMES d3d9.h HINTS ${DirectX_INC_SEARCH_PATH}) + # dlls are in DirectX_ROOT_DIR/Developer Runtime/x64|x86 + # lib files are in DirectX_ROOT_DIR/Lib/x64|x86 + if(CMAKE_CL_64) + set(DirectX_LIBPATH_SUFFIX "x64") + else(CMAKE_CL_64) + set(DirectX_LIBPATH_SUFFIX "x86") + endif(CMAKE_CL_64) + find_library(DirectX_LIBRARY NAMES d3d9 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) + find_library(DirectX_D3DX9_LIBRARY NAMES d3dx9 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) + find_library(DirectX_DXERR_LIBRARY NAMES DxErr HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) + find_library(DirectX_DXGUID_LIBRARY NAMES dxguid HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) + + + # look for dxgi (needed by both 10 and 11) + find_library(DirectX_DXGI_LIBRARY NAMES dxgi HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) + + # look for d3dcompiler (needed by 11) + find_library(DirectX_D3DCOMPILER_LIBRARY NAMES d3dcompiler HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) + + findpkg_finish(DirectX) + set(DirectX_LIBRARIES ${DirectX_LIBRARIES} + ${DirectX_D3DX9_LIBRARY} + ${DirectX_DXERR_LIBRARY} + ${DirectX_DXGUID_LIBRARY} + ) + + mark_as_advanced(DirectX_D3DX9_LIBRARY DirectX_DXERR_LIBRARY DirectX_DXGUID_LIBRARY + DirectX_DXGI_LIBRARY DirectX_D3DCOMPILER_LIBRARY) + + + # look for D3D11 components + if (DirectX_FOUND) + find_path(DirectX_D3D11_INCLUDE_DIR NAMES D3D11Shader.h HINTS ${DirectX_INC_SEARCH_PATH}) + get_filename_component(DirectX_LIBRARY_DIR "${DirectX_LIBRARY}" PATH) + message(STATUS "DX lib dir: ${DirectX_LIBRARY_DIR}") + find_library(DirectX_D3D11_LIBRARY NAMES d3d11 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) + find_library(DirectX_D3DX11_LIBRARY NAMES d3dx11 HINTS ${DirectX_LIB_SEARCH_PATH} PATH_SUFFIXES ${DirectX_LIBPATH_SUFFIX}) + if (DirectX_D3D11_INCLUDE_DIR AND DirectX_D3D11_LIBRARY) + set(DirectX_D3D11_FOUND TRUE) + set(DirectX_D3D11_INCLUDE_DIR ${DirectX_D3D11_INCLUDE_DIR}) + set(DirectX_D3D11_LIBRARIES ${DirectX_D3D11_LIBRARIES} + ${DirectX_D3D11_LIBRARY} + ${DirectX_D3DX11_LIBRARY} + ${DirectX_DXGI_LIBRARY} + ${DirectX_DXERR_LIBRARY} + ${DirectX_DXGUID_LIBRARY} + ${DirectX_D3DCOMPILER_LIBRARY} + ) + endif () + mark_as_advanced(DirectX_D3D11_INCLUDE_DIR DirectX_D3D11_LIBRARY DirectX_D3DX11_LIBRARY) + endif () + +endif(WIN32) diff --git a/cmake-modules/FindPkgMacros.cmake b/cmake-modules/FindPkgMacros.cmake index bc3b145ba..facd5946b 100644 --- a/cmake-modules/FindPkgMacros.cmake +++ b/cmake-modules/FindPkgMacros.cmake @@ -1,142 +1,142 @@ -#------------------------------------------------------------------- -# This file is part of the CMake build system for OGRE -# (Object-oriented Graphics Rendering Engine) -# For the latest info, see http://www.ogre3d.org/ -# -# The contents of this file are placed in the public domain. Feel -# free to make use of it in any way you like. -#------------------------------------------------------------------- - -################################################################## -# Provides some common functionality for the FindPackage modules -################################################################## - -# Begin processing of package -macro(findpkg_begin PREFIX) - if (NOT ${PREFIX}_FIND_QUIETLY) - message(STATUS "Looking for ${PREFIX}...") - endif () -endmacro(findpkg_begin) - -# Display a status message unless FIND_QUIETLY is set -macro(pkg_message PREFIX) - if (NOT ${PREFIX}_FIND_QUIETLY) - message(STATUS ${ARGN}) - endif () -endmacro(pkg_message) - -# Get environment variable, define it as ENV_$var and make sure backslashes are converted to forward slashes -macro(getenv_path VAR) - set(ENV_${VAR} $ENV{${VAR}}) - # replace won't work if var is blank - if (ENV_${VAR}) - string( REGEX REPLACE "\\\\" "/" ENV_${VAR} ${ENV_${VAR}} ) - endif () -endmacro(getenv_path) - -# Construct search paths for includes and libraries from a PREFIX_PATH -macro(create_search_paths PREFIX) - foreach(dir ${${PREFIX}_PREFIX_PATH}) - set(${PREFIX}_INC_SEARCH_PATH ${${PREFIX}_INC_SEARCH_PATH} - ${dir}/include ${dir}/include/${PREFIX} ${dir}/Headers) - set(${PREFIX}_LIB_SEARCH_PATH ${${PREFIX}_LIB_SEARCH_PATH} - ${dir}/lib ${dir}/lib/${PREFIX} ${dir}/Libs) - endforeach(dir) - set(${PREFIX}_FRAMEWORK_SEARCH_PATH ${${PREFIX}_PREFIX_PATH}) -endmacro(create_search_paths) - -# clear cache variables if a certain variable changed -macro(clear_if_changed TESTVAR) - # test against internal check variable - if (NOT "${${TESTVAR}}" STREQUAL "${${TESTVAR}_INT_CHECK}") - message(STATUS "${TESTVAR} changed.") - foreach(var ${ARGN}) - set(${var} "NOTFOUND" CACHE STRING "x" FORCE) - endforeach(var) - endif () - set(${TESTVAR}_INT_CHECK ${${TESTVAR}} CACHE INTERNAL "x" FORCE) -endmacro(clear_if_changed) - -# Try to get some hints from pkg-config, if available -macro(use_pkgconfig PREFIX PKGNAME) - find_package(PkgConfig) - if (PKG_CONFIG_FOUND) - pkg_check_modules(${PREFIX} ${PKGNAME}) - endif () -endmacro (use_pkgconfig) - -# Couple a set of release AND debug libraries (or frameworks) -macro(make_library_set PREFIX) - if (${PREFIX}_FWK) - set(${PREFIX} ${${PREFIX}_FWK}) - elseif (${PREFIX}_REL AND ${PREFIX}_DBG) - set(${PREFIX} optimized ${${PREFIX}_REL} debug ${${PREFIX}_DBG}) - elseif (${PREFIX}_REL) - set(${PREFIX} ${${PREFIX}_REL}) - elseif (${PREFIX}_DBG) - set(${PREFIX} ${${PREFIX}_DBG}) - endif () -endmacro(make_library_set) - -# Generate debug names from given release names -macro(get_debug_names PREFIX) - foreach(i ${${PREFIX}}) - set(${PREFIX}_DBG ${${PREFIX}_DBG} ${i}d ${i}D ${i}_d ${i}_D ${i}_debug ${i}) - endforeach(i) -endmacro(get_debug_names) - -# Add the parent dir from DIR to VAR -macro(add_parent_dir VAR DIR) - get_filename_component(${DIR}_TEMP "${${DIR}}/.." ABSOLUTE) - set(${VAR} ${${VAR}} ${${DIR}_TEMP}) -endmacro(add_parent_dir) - -# Do the final processing for the package find. -macro(findpkg_finish PREFIX) - # skip if already processed during this run - if (NOT ${PREFIX}_FOUND) - if (${PREFIX}_INCLUDE_DIR AND ${PREFIX}_LIBRARY) - set(${PREFIX}_FOUND TRUE) - set(${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIR}) - set(${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARY}) - if (NOT ${PREFIX}_FIND_QUIETLY) - message(STATUS "Found ${PREFIX}: ${${PREFIX}_LIBRARIES}") - endif () - else () - if (NOT ${PREFIX}_FIND_QUIETLY) - message(STATUS "Could not locate ${PREFIX}") - endif () - if (${PREFIX}_FIND_REQUIRED) - message(FATAL_ERROR "Required library ${PREFIX} not found! Install the library (including dev packages) and try again. If the library is already installed, set the missing variables manually in cmake.") - endif () - endif () - - mark_as_advanced(${PREFIX}_INCLUDE_DIR ${PREFIX}_LIBRARY ${PREFIX}_LIBRARY_REL ${PREFIX}_LIBRARY_DBG ${PREFIX}_LIBRARY_FWK) - endif () -endmacro(findpkg_finish) - - -# Slightly customised framework finder -MACRO(findpkg_framework fwk) - IF(APPLE) - SET(${fwk}_FRAMEWORK_PATH - ${${fwk}_FRAMEWORK_SEARCH_PATH} - ${CMAKE_FRAMEWORK_PATH} - ~/Library/Frameworks - /Library/Frameworks - /System/Library/Frameworks - /Network/Library/Frameworks - /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk/System/Library/Frameworks/ - ) - FOREACH(dir ${${fwk}_FRAMEWORK_PATH}) - SET(fwkpath ${dir}/${fwk}.framework) - IF(EXISTS ${fwkpath}) - SET(${fwk}_FRAMEWORK_INCLUDES ${${fwk}_FRAMEWORK_INCLUDES} - ${fwkpath}/Headers ${fwkpath}/PrivateHeaders) - if (NOT ${fwk}_LIBRARY_FWK) - SET(${fwk}_LIBRARY_FWK "-framework ${fwk}") - endif () - ENDIF(EXISTS ${fwkpath}) - ENDFOREACH(dir) - ENDIF(APPLE) -ENDMACRO(findpkg_framework) +#------------------------------------------------------------------- +# This file is part of the CMake build system for OGRE +# (Object-oriented Graphics Rendering Engine) +# For the latest info, see http://www.ogre3d.org/ +# +# The contents of this file are placed in the public domain. Feel +# free to make use of it in any way you like. +#------------------------------------------------------------------- + +################################################################## +# Provides some common functionality for the FindPackage modules +################################################################## + +# Begin processing of package +macro(findpkg_begin PREFIX) + if (NOT ${PREFIX}_FIND_QUIETLY) + message(STATUS "Looking for ${PREFIX}...") + endif () +endmacro(findpkg_begin) + +# Display a status message unless FIND_QUIETLY is set +macro(pkg_message PREFIX) + if (NOT ${PREFIX}_FIND_QUIETLY) + message(STATUS ${ARGN}) + endif () +endmacro(pkg_message) + +# Get environment variable, define it as ENV_$var and make sure backslashes are converted to forward slashes +macro(getenv_path VAR) + set(ENV_${VAR} $ENV{${VAR}}) + # replace won't work if var is blank + if (ENV_${VAR}) + string( REGEX REPLACE "\\\\" "/" ENV_${VAR} ${ENV_${VAR}} ) + endif () +endmacro(getenv_path) + +# Construct search paths for includes and libraries from a PREFIX_PATH +macro(create_search_paths PREFIX) + foreach(dir ${${PREFIX}_PREFIX_PATH}) + set(${PREFIX}_INC_SEARCH_PATH ${${PREFIX}_INC_SEARCH_PATH} + ${dir}/include ${dir}/include/${PREFIX} ${dir}/Headers) + set(${PREFIX}_LIB_SEARCH_PATH ${${PREFIX}_LIB_SEARCH_PATH} + ${dir}/lib ${dir}/lib/${PREFIX} ${dir}/Libs) + endforeach(dir) + set(${PREFIX}_FRAMEWORK_SEARCH_PATH ${${PREFIX}_PREFIX_PATH}) +endmacro(create_search_paths) + +# clear cache variables if a certain variable changed +macro(clear_if_changed TESTVAR) + # test against internal check variable + if (NOT "${${TESTVAR}}" STREQUAL "${${TESTVAR}_INT_CHECK}") + message(STATUS "${TESTVAR} changed.") + foreach(var ${ARGN}) + set(${var} "NOTFOUND" CACHE STRING "x" FORCE) + endforeach(var) + endif () + set(${TESTVAR}_INT_CHECK ${${TESTVAR}} CACHE INTERNAL "x" FORCE) +endmacro(clear_if_changed) + +# Try to get some hints from pkg-config, if available +macro(use_pkgconfig PREFIX PKGNAME) + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(${PREFIX} ${PKGNAME}) + endif () +endmacro (use_pkgconfig) + +# Couple a set of release AND debug libraries (or frameworks) +macro(make_library_set PREFIX) + if (${PREFIX}_FWK) + set(${PREFIX} ${${PREFIX}_FWK}) + elseif (${PREFIX}_REL AND ${PREFIX}_DBG) + set(${PREFIX} optimized ${${PREFIX}_REL} debug ${${PREFIX}_DBG}) + elseif (${PREFIX}_REL) + set(${PREFIX} ${${PREFIX}_REL}) + elseif (${PREFIX}_DBG) + set(${PREFIX} ${${PREFIX}_DBG}) + endif () +endmacro(make_library_set) + +# Generate debug names from given release names +macro(get_debug_names PREFIX) + foreach(i ${${PREFIX}}) + set(${PREFIX}_DBG ${${PREFIX}_DBG} ${i}d ${i}D ${i}_d ${i}_D ${i}_debug ${i}) + endforeach(i) +endmacro(get_debug_names) + +# Add the parent dir from DIR to VAR +macro(add_parent_dir VAR DIR) + get_filename_component(${DIR}_TEMP "${${DIR}}/.." ABSOLUTE) + set(${VAR} ${${VAR}} ${${DIR}_TEMP}) +endmacro(add_parent_dir) + +# Do the final processing for the package find. +macro(findpkg_finish PREFIX) + # skip if already processed during this run + if (NOT ${PREFIX}_FOUND) + if (${PREFIX}_INCLUDE_DIR AND ${PREFIX}_LIBRARY) + set(${PREFIX}_FOUND TRUE) + set(${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIR}) + set(${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARY}) + if (NOT ${PREFIX}_FIND_QUIETLY) + message(STATUS "Found ${PREFIX}: ${${PREFIX}_LIBRARIES}") + endif () + else () + if (NOT ${PREFIX}_FIND_QUIETLY) + message(STATUS "Could not locate ${PREFIX}") + endif () + if (${PREFIX}_FIND_REQUIRED) + message(FATAL_ERROR "Required library ${PREFIX} not found! Install the library (including dev packages) and try again. If the library is already installed, set the missing variables manually in cmake.") + endif () + endif () + + mark_as_advanced(${PREFIX}_INCLUDE_DIR ${PREFIX}_LIBRARY ${PREFIX}_LIBRARY_REL ${PREFIX}_LIBRARY_DBG ${PREFIX}_LIBRARY_FWK) + endif () +endmacro(findpkg_finish) + + +# Slightly customised framework finder +MACRO(findpkg_framework fwk) + IF(APPLE) + SET(${fwk}_FRAMEWORK_PATH + ${${fwk}_FRAMEWORK_SEARCH_PATH} + ${CMAKE_FRAMEWORK_PATH} + ~/Library/Frameworks + /Library/Frameworks + /System/Library/Frameworks + /Network/Library/Frameworks + /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk/System/Library/Frameworks/ + ) + FOREACH(dir ${${fwk}_FRAMEWORK_PATH}) + SET(fwkpath ${dir}/${fwk}.framework) + IF(EXISTS ${fwkpath}) + SET(${fwk}_FRAMEWORK_INCLUDES ${${fwk}_FRAMEWORK_INCLUDES} + ${fwkpath}/Headers ${fwkpath}/PrivateHeaders) + if (NOT ${fwk}_LIBRARY_FWK) + SET(${fwk}_LIBRARY_FWK "-framework ${fwk}") + endif () + ENDIF(EXISTS ${fwkpath}) + ENDFOREACH(dir) + ENDIF(APPLE) +ENDMACRO(findpkg_framework) diff --git a/cmake-modules/FindZLIB.cmake b/cmake-modules/FindZLIB.cmake index c1e20016b..f6806b4ee 100644 --- a/cmake-modules/FindZLIB.cmake +++ b/cmake-modules/FindZLIB.cmake @@ -1,48 +1,48 @@ -#------------------------------------------------------------------- -# This file is part of the CMake build system for OGRE -# (Object-oriented Graphics Rendering Engine) -# For the latest info, see http://www.ogre3d.org/ -# -# The contents of this file are placed in the public domain. Feel -# free to make use of it in any way you like. -#------------------------------------------------------------------- - -# - Try to find ZLIB -# Once done, this will define -# -# ZLIB_FOUND - system has ZLIB -# ZLIB_INCLUDE_DIRS - the ZLIB include directories -# ZLIB_LIBRARIES - link these to use ZLIB - -include(FindPkgMacros) -findpkg_begin(ZLIB) - -# Get path, convert backslashes as ${ENV_${var}} -getenv_path(ZLIB_HOME) - -# construct search paths -set(ZLIB_PREFIX_PATH ${ZLIB_HOME} ${ENV_ZLIB_HOME}) -create_search_paths(ZLIB) -# redo search if prefix path changed -clear_if_changed(ZLIB_PREFIX_PATH - ZLIB_LIBRARY_FWK - ZLIB_LIBRARY_REL - ZLIB_LIBRARY_DBG - ZLIB_INCLUDE_DIR -) - -set(ZLIB_LIBRARY_NAMES z zlib zdll) -get_debug_names(ZLIB_LIBRARY_NAMES) - -use_pkgconfig(ZLIB_PKGC zzip-zlib-config) - -findpkg_framework(ZLIB) - -find_path(ZLIB_INCLUDE_DIR NAMES zlib.h HINTS ${ZLIB_INC_SEARCH_PATH} ${ZLIB_PKGC_INCLUDE_DIRS}) -find_library(ZLIB_LIBRARY_REL NAMES ${ZLIB_LIBRARY_NAMES} HINTS ${ZLIB_LIB_SEARCH_PATH} ${ZLIB_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" release relwithdebinfo minsizerel) -find_library(ZLIB_LIBRARY_DBG NAMES ${ZLIB_LIBRARY_NAMES_DBG} HINTS ${ZLIB_LIB_SEARCH_PATH} ${ZLIB_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" debug) - -make_library_set(ZLIB_LIBRARY) - -findpkg_finish(ZLIB) - +#------------------------------------------------------------------- +# This file is part of the CMake build system for OGRE +# (Object-oriented Graphics Rendering Engine) +# For the latest info, see http://www.ogre3d.org/ +# +# The contents of this file are placed in the public domain. Feel +# free to make use of it in any way you like. +#------------------------------------------------------------------- + +# - Try to find ZLIB +# Once done, this will define +# +# ZLIB_FOUND - system has ZLIB +# ZLIB_INCLUDE_DIRS - the ZLIB include directories +# ZLIB_LIBRARIES - link these to use ZLIB + +include(FindPkgMacros) +findpkg_begin(ZLIB) + +# Get path, convert backslashes as ${ENV_${var}} +getenv_path(ZLIB_HOME) + +# construct search paths +set(ZLIB_PREFIX_PATH ${ZLIB_HOME} ${ENV_ZLIB_HOME}) +create_search_paths(ZLIB) +# redo search if prefix path changed +clear_if_changed(ZLIB_PREFIX_PATH + ZLIB_LIBRARY_FWK + ZLIB_LIBRARY_REL + ZLIB_LIBRARY_DBG + ZLIB_INCLUDE_DIR +) + +set(ZLIB_LIBRARY_NAMES z zlib zdll) +get_debug_names(ZLIB_LIBRARY_NAMES) + +use_pkgconfig(ZLIB_PKGC zzip-zlib-config) + +findpkg_framework(ZLIB) + +find_path(ZLIB_INCLUDE_DIR NAMES zlib.h HINTS ${ZLIB_INC_SEARCH_PATH} ${ZLIB_PKGC_INCLUDE_DIRS}) +find_library(ZLIB_LIBRARY_REL NAMES ${ZLIB_LIBRARY_NAMES} HINTS ${ZLIB_LIB_SEARCH_PATH} ${ZLIB_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" release relwithdebinfo minsizerel) +find_library(ZLIB_LIBRARY_DBG NAMES ${ZLIB_LIBRARY_NAMES_DBG} HINTS ${ZLIB_LIB_SEARCH_PATH} ${ZLIB_PKGC_LIBRARY_DIRS} PATH_SUFFIXES "" debug) + +make_library_set(ZLIB_LIBRARY) + +findpkg_finish(ZLIB) + diff --git a/cmake-modules/Findassimp.cmake b/cmake-modules/Findassimp.cmake index 300c631e7..971b49d5e 100644 --- a/cmake-modules/Findassimp.cmake +++ b/cmake-modules/Findassimp.cmake @@ -1,25 +1,25 @@ FIND_PATH( - assimp_INCLUDE_DIRS - NAMES postprocess.h scene.h version.h config.h cimport.h - PATHS /usr/local/include/ + assimp_INCLUDE_DIRS + NAMES postprocess.h scene.h version.h config.h cimport.h + PATHS /usr/local/include/ ) FIND_LIBRARY( - assimp_LIBRARIES - NAMES assimp - PATHS /usr/local/lib/ + assimp_LIBRARIES + NAMES assimp + PATHS /usr/local/lib/ ) IF (assimp_INCLUDE_DIRS AND assimp_LIBRARIES) - SET(assimp_FOUND TRUE) + SET(assimp_FOUND TRUE) ENDIF (assimp_INCLUDE_DIRS AND assimp_LIBRARIES) IF (assimp_FOUND) - IF (NOT assimp_FIND_QUIETLY) - MESSAGE(STATUS "Found asset importer library: ${assimp_LIBRARIES}") - ENDIF (NOT assimp_FIND_QUIETLY) + IF (NOT assimp_FIND_QUIETLY) + MESSAGE(STATUS "Found asset importer library: ${assimp_LIBRARIES}") + ENDIF (NOT assimp_FIND_QUIETLY) ELSE (assimp_FOUND) - IF (assimp_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could not find asset importer library") - ENDIF (assimp_FIND_REQUIRED) + IF (assimp_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find asset importer library") + ENDIF (assimp_FIND_REQUIRED) ENDIF (assimp_FOUND) diff --git a/cmake-modules/PrecompiledHeader.cmake b/cmake-modules/PrecompiledHeader.cmake index 99c43a3cb..6af7866f5 100644 --- a/cmake-modules/PrecompiledHeader.cmake +++ b/cmake-modules/PrecompiledHeader.cmake @@ -5,21 +5,21 @@ MACRO(ADD_MSVC_PRECOMPILED_HEADER PrecompiledHeader PrecompiledSource SourcesVar SET(Sources ${${SourcesVar}}) SET_SOURCE_FILES_PROPERTIES(${PrecompiledSource} - PROPERTIES COMPILE_FLAGS "/Yc\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\"" - OBJECT_OUTPUTS "${PrecompiledBinary}") - - # Do not consider .c files - foreach(fname ${Sources}) - GET_FILENAME_COMPONENT(fext ${fname} EXT) - if(fext STREQUAL ".cpp") - SET_SOURCE_FILES_PROPERTIES(${fname} - PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledBinary}\" /FI\"${PrecompiledBinary}\" /Fp\"${PrecompiledBinary}\"" - OBJECT_DEPENDS "${PrecompiledBinary}") - endif(fext STREQUAL ".cpp") - endforeach(fname) - + PROPERTIES COMPILE_FLAGS "/Yc\"${PrecompiledHeader}\" /Fp\"${PrecompiledBinary}\"" + OBJECT_OUTPUTS "${PrecompiledBinary}") + + # Do not consider .c files + foreach(fname ${Sources}) + GET_FILENAME_COMPONENT(fext ${fname} EXT) + if(fext STREQUAL ".cpp") + SET_SOURCE_FILES_PROPERTIES(${fname} + PROPERTIES COMPILE_FLAGS "/Yu\"${PrecompiledBinary}\" /FI\"${PrecompiledBinary}\" /Fp\"${PrecompiledBinary}\"" + OBJECT_DEPENDS "${PrecompiledBinary}") + endif(fext STREQUAL ".cpp") + endforeach(fname) + ENDIF(MSVC) # Add precompiled header to SourcesVar LIST(APPEND ${SourcesVar} ${PrecompiledSource}) - -ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER) \ No newline at end of file + +ENDMACRO(ADD_MSVC_PRECOMPILED_HEADER) diff --git a/code/.editorconfig b/code/.editorconfig new file mode 100644 index 000000000..4a194a317 --- /dev/null +++ b/code/.editorconfig @@ -0,0 +1,8 @@ +# See for details + +[*.{h,hpp,c,cpp}] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_size = 4 +indent_style = space diff --git a/code/3DSConverter.cpp b/code/3DSConverter.cpp index 6f8b24e63..08dd5ce49 100644 --- a/code/3DSConverter.cpp +++ b/code/3DSConverter.cpp @@ -1,866 +1,866 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the 3ds importer class */ - - -#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER - -// internal headers -#include "3DSLoader.h" -#include "TargetAnimation.h" -#include "../include/assimp/scene.h" -#include "../include/assimp/DefaultLogger.hpp" -#include "StringComparison.h" -#include -#include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Setup final material indices, generae a default material if necessary -void Discreet3DSImporter::ReplaceDefaultMaterial() -{ - - // Try to find an existing material that matches the - // typical default material setting: - // - no textures - // - diffuse color (in grey!) - // NOTE: This is here to workaround the fact that some - // exporters are writing a default material, too. - unsigned int idx = 0xcdcdcdcd; - for (unsigned int i = 0; i < mScene->mMaterials.size();++i) - { - std::string s = mScene->mMaterials[i].mName; - for (std::string::iterator it = s.begin(); it != s.end(); ++it) - *it = ::tolower(*it); - - if (std::string::npos == s.find("default"))continue; - - if (mScene->mMaterials[i].mDiffuse.r != - mScene->mMaterials[i].mDiffuse.g || - mScene->mMaterials[i].mDiffuse.r != - mScene->mMaterials[i].mDiffuse.b)continue; - - if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || - mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || - mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || - mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || - mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || - mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 ) - { - continue; - } - idx = i; - } - if (0xcdcdcdcd == idx)idx = (unsigned int)mScene->mMaterials.size(); - - // now iterate through all meshes and through all faces and - // find all faces that are using the default material - unsigned int cnt = 0; - for (std::vector::iterator - i = mScene->mMeshes.begin(); - i != mScene->mMeshes.end();++i) - { - for (std::vector::iterator - a = (*i).mFaceMaterials.begin(); - a != (*i).mFaceMaterials.end();++a) - { - // NOTE: The additional check seems to be necessary, - // some exporters seem to generate invalid data here - if (0xcdcdcdcd == (*a)) - { - (*a) = idx; - ++cnt; - } - else if ( (*a) >= mScene->mMaterials.size()) - { - (*a) = idx; - DefaultLogger::get()->warn("Material index overflow in 3DS file. Using default material"); - ++cnt; - } - } - } - if (cnt && idx == mScene->mMaterials.size()) - { - // We need to create our own default material - D3DS::Material sMat; - sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f); - sMat.mName = "%%%DEFAULT"; - mScene->mMaterials.push_back(sMat); - - DefaultLogger::get()->info("3DS: Generating default material"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Check whether all indices are valid. Otherwise we'd crash before the validation step is reached -void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh) -{ - for (std::vector< D3DS::Face >::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i) - { - // check whether all indices are in range - for (unsigned int a = 0; a < 3;++a) - { - if ((*i).mIndices[a] >= sMesh.mPositions.size()) - { - DefaultLogger::get()->warn("3DS: Vertex index overflow)"); - (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1; - } - if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size()) - { - DefaultLogger::get()->warn("3DS: Texture coordinate index overflow)"); - (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1; - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Generate out unique verbose format representation -void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh) -{ - // TODO: really necessary? I don't think. Just a waste of memory and time - // to do it now in a separate buffer. - - // Allocate output storage - std::vector vNew (sMesh.mFaces.size() * 3); - std::vector vNew2; - if (sMesh.mTexCoords.size()) - vNew2.resize(sMesh.mFaces.size() * 3); - - for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i) - { - D3DS::Face& face = sMesh.mFaces[i]; - - // Positions - for (unsigned int a = 0; a < 3;++a,++base) - { - vNew[base] = sMesh.mPositions[face.mIndices[a]]; - if (sMesh.mTexCoords.size()) - vNew2[base] = sMesh.mTexCoords[face.mIndices[a]]; - - face.mIndices[a] = base; - } - } - sMesh.mPositions = vNew; - sMesh.mTexCoords = vNew2; -} - -// ------------------------------------------------------------------------------------------------ -// Convert a 3DS texture to texture keys in an aiMaterial -void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type) -{ - // Setup the texture name - aiString tex; - tex.Set( texture.mMapName); - mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0)); - - // Setup the texture blend factor - if (is_not_qnan(texture.mTextureBlend)) - mat.AddProperty( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0)); - - // Setup the texture mapping mode - mat.AddProperty((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0)); - mat.AddProperty((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0)); - - // Mirroring - double the scaling values - // FIXME: this is not really correct ... - if (texture.mMapMode == aiTextureMapMode_Mirror) - { - texture.mScaleU *= 2.f; - texture.mScaleV *= 2.f; - texture.mOffsetU /= 2.f; - texture.mOffsetV /= 2.f; - } - - // Setup texture UV transformations - mat.AddProperty(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0)); -} - -// ------------------------------------------------------------------------------------------------ -// Convert a 3DS material to an aiMaterial -void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat, - aiMaterial& mat) -{ - // NOTE: Pass the background image to the viewer by bypassing the - // material system. This is an evil hack, never do it again! - if (0 != mBackgroundImage.length() && bHasBG) - { - aiString tex; - tex.Set( mBackgroundImage); - mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE); - - // Be sure this is only done for the first material - mBackgroundImage = std::string(""); - } - - // At first add the base ambient color of the scene to the material - oldMat.mAmbient.r += mClrAmbient.r; - oldMat.mAmbient.g += mClrAmbient.g; - oldMat.mAmbient.b += mClrAmbient.b; - - aiString name; - name.Set( oldMat.mName); - mat.AddProperty( &name, AI_MATKEY_NAME); - - // Material colors - mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); - mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); - mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); - - // Phong shininess and shininess strength - if (D3DS::Discreet3DS::Phong == oldMat.mShading || - D3DS::Discreet3DS::Metal == oldMat.mShading) - { - if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength) - { - oldMat.mShading = D3DS::Discreet3DS::Gouraud; - } - else - { - mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS); - mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); - } - } - - // Opacity - mat.AddProperty( &oldMat.mTransparency,1,AI_MATKEY_OPACITY); - - // Bump height scaling - mat.AddProperty( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING); - - // Two sided rendering? - if (oldMat.mTwoSided) - { - int i = 1; - mat.AddProperty(&i,1,AI_MATKEY_TWOSIDED); - } - - // Shading mode - aiShadingMode eShading = aiShadingMode_NoShading; - switch (oldMat.mShading) - { - case D3DS::Discreet3DS::Flat: - eShading = aiShadingMode_Flat; break; - - // I don't know what "Wire" shading should be, - // assume it is simple lambertian diffuse shading - case D3DS::Discreet3DS::Wire: - { - // Set the wireframe flag - unsigned int iWire = 1; - mat.AddProperty( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME); - } - - case D3DS::Discreet3DS::Gouraud: - eShading = aiShadingMode_Gouraud; break; - - // assume cook-torrance shading for metals. - case D3DS::Discreet3DS::Phong : - eShading = aiShadingMode_Phong; break; - - case D3DS::Discreet3DS::Metal : - eShading = aiShadingMode_CookTorrance; break; - - // FIX to workaround a warning with GCC 4 who complained - // about a missing case Blinn: here - Blinn isn't a valid - // value in the 3DS Loader, it is just needed for ASE - case D3DS::Discreet3DS::Blinn : - eShading = aiShadingMode_Blinn; break; - } - mat.AddProperty( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); - - // DIFFUSE texture - if( oldMat.sTexDiffuse.mMapName.length() > 0) - CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE); - - // SPECULAR texture - if( oldMat.sTexSpecular.mMapName.length() > 0) - CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR); - - // OPACITY texture - if( oldMat.sTexOpacity.mMapName.length() > 0) - CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY); - - // EMISSIVE texture - if( oldMat.sTexEmissive.mMapName.length() > 0) - CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE); - - // BUMP texture - if( oldMat.sTexBump.mMapName.length() > 0) - CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT); - - // SHININESS texture - if( oldMat.sTexShininess.mMapName.length() > 0) - CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS); - - // REFLECTION texture - if( oldMat.sTexReflective.mMapName.length() > 0) - CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION); - - // Store the name of the material itself, too - if( oldMat.mName.length()) { - aiString tex; - tex.Set( oldMat.mName); - mat.AddProperty( &tex, AI_MATKEY_NAME); - } -} - -// ------------------------------------------------------------------------------------------------ -// Split meshes by their materials and generate output aiMesh'es -void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut) -{ - std::vector avOutMeshes; - avOutMeshes.reserve(mScene->mMeshes.size() * 2); - - unsigned int iFaceCnt = 0,num = 0; - aiString name; - - // we need to split all meshes by their materials - for (std::vector::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i) { - boost::scoped_array< std::vector > aiSplit(new std::vector[mScene->mMaterials.size()]); - - name.length = ASSIMP_itoa10(name.data,num++); - - unsigned int iNum = 0; - for (std::vector::const_iterator a = (*i).mFaceMaterials.begin(); - a != (*i).mFaceMaterials.end();++a,++iNum) - { - aiSplit[*a].push_back(iNum); - } - // now generate submeshes - for (unsigned int p = 0; p < mScene->mMaterials.size();++p) - { - if (aiSplit[p].empty()) { - continue; - } - aiMesh* meshOut = new aiMesh(); - meshOut->mName = name; - meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - // be sure to setup the correct material index - meshOut->mMaterialIndex = p; - - // use the color data as temporary storage - meshOut->mColors[0] = (aiColor4D*)(&*i); - avOutMeshes.push_back(meshOut); - - // convert vertices - meshOut->mNumFaces = (unsigned int)aiSplit[p].size(); - meshOut->mNumVertices = meshOut->mNumFaces*3; - - // allocate enough storage for faces - meshOut->mFaces = new aiFace[meshOut->mNumFaces]; - iFaceCnt += meshOut->mNumFaces; - - meshOut->mVertices = new aiVector3D[meshOut->mNumVertices]; - meshOut->mNormals = new aiVector3D[meshOut->mNumVertices]; - if ((*i).mTexCoords.size()) - { - meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices]; - } - for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q) - { - unsigned int index = aiSplit[p][q]; - aiFace& face = meshOut->mFaces[q]; - - face.mIndices = new unsigned int[3]; - face.mNumIndices = 3; - - for (unsigned int a = 0; a < 3;++a,++base) - { - unsigned int idx = (*i).mFaces[index].mIndices[a]; - meshOut->mVertices[base] = (*i).mPositions[idx]; - meshOut->mNormals [base] = (*i).mNormals[idx]; - - if ((*i).mTexCoords.size()) - meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx]; - - face.mIndices[a] = base; - } - } - } - } - - // Copy them to the output array - pcOut->mNumMeshes = (unsigned int)avOutMeshes.size(); - pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes](); - for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) { - pcOut->mMeshes[a] = avOutMeshes[a]; - } - - // We should have at least one face here - if (!iFaceCnt) { - throw DeadlyImportError("No faces loaded. The mesh is empty"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Add a node to the scenegraph and setup its final transformation -void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut, - D3DS::Node* pcIn, aiMatrix4x4& /*absTrafo*/) -{ - std::vector iArray; - iArray.reserve(3); - - aiMatrix4x4 abs; - - // Find all meshes with the same name as the node - for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a) - { - const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0]; - ai_assert(NULL != pcMesh); - - if (pcIn->mName == pcMesh->mName) - iArray.push_back(a); - } - if (!iArray.empty()) - { - // The matrix should be identical for all meshes with the - // same name. It HAS to be identical for all meshes ..... - D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]); - - // Compute the inverse of the transformation matrix to move the - // vertices back to their relative and local space - aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat; - mInv.Inverse();mInvTransposed.Transpose(); - aiVector3D pivot = pcIn->vPivot; - - pcOut->mNumMeshes = (unsigned int)iArray.size(); - pcOut->mMeshes = new unsigned int[iArray.size()]; - for (unsigned int i = 0;i < iArray.size();++i) { - const unsigned int iIndex = iArray[i]; - aiMesh* const mesh = pcSOut->mMeshes[iIndex]; - - if (mesh->mColors[1] == NULL) - { - // Transform the vertices back into their local space - // fixme: consider computing normals after this, so we don't need to transform them - const aiVector3D* const pvEnd = mesh->mVertices + mesh->mNumVertices; - aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals; - - for (; pvCurrent != pvEnd; ++pvCurrent, ++t2) { - *pvCurrent = mInv * (*pvCurrent); - *t2 = mInvTransposed * (*t2); - } - - // Handle negative transformation matrix determinant -> invert vertex x - if (imesh->mMat.Determinant() < 0.0f) - { - /* we *must* have normals */ - for (pvCurrent = mesh->mVertices, t2 = mesh->mNormals; pvCurrent != pvEnd; ++pvCurrent, ++t2) { - pvCurrent->x *= -1.f; - t2->x *= -1.f; - } - DefaultLogger::get()->info("3DS: Flipping mesh X-Axis"); - } - - // Handle pivot point - if (pivot.x || pivot.y || pivot.z) - { - for (pvCurrent = mesh->mVertices; pvCurrent != pvEnd; ++pvCurrent) { - *pvCurrent -= pivot; - } - } - - mesh->mColors[1] = (aiColor4D*)1; - } - else - mesh->mColors[1] = (aiColor4D*)1; - - // Setup the mesh index - pcOut->mMeshes[i] = iIndex; - } - } - - // Setup the name of the node - // First instance keeps its name otherwise something might break, all others will be postfixed with their instance number - if (pcIn->mInstanceNumber > 1) - { - char tmp[12]; - ASSIMP_itoa10(tmp, pcIn->mInstanceNumber); - std::string tempStr = pcIn->mName + "_inst_"; - tempStr += tmp; - pcOut->mName.Set(tempStr); - } - else - pcOut->mName.Set(pcIn->mName); - - // Now build the transformation matrix of the node - // ROTATION - if (pcIn->aRotationKeys.size()){ - - // FIX to get to Assimp's quaternion conventions - for (std::vector::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) { - (*it).mValue.w *= -1.f; - } - - pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() ); - } - else if (pcIn->aCameraRollKeys.size()) - { - aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue), - pcOut->mTransformation); - } - - // SCALING - aiMatrix4x4& m = pcOut->mTransformation; - if (pcIn->aScalingKeys.size()) - { - const aiVector3D& v = pcIn->aScalingKeys[0].mValue; - m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x; - m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y; - m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z; - } - - // TRANSLATION - if (pcIn->aPositionKeys.size()) - { - const aiVector3D& v = pcIn->aPositionKeys[0].mValue; - m.a4 += v.x; - m.b4 += v.y; - m.c4 += v.z; - } - - // Generate animation channels for the node - if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 || - pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 || - pcIn->aTargetPositionKeys.size() > 1) - { - aiAnimation* anim = pcSOut->mAnimations[0]; - ai_assert(NULL != anim); - - if (pcIn->aCameraRollKeys.size() > 1) - { - DefaultLogger::get()->debug("3DS: Converting camera roll track ..."); - - // Camera roll keys - in fact they're just rotations - // around the camera's z axis. The angles are given - // in degrees (and they're clockwise). - pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size()); - for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i) - { - aiQuatKey& q = pcIn->aRotationKeys[i]; - aiFloatKey& f = pcIn->aCameraRollKeys[i]; - - q.mTime = f.mTime; - - // FIX to get to Assimp quaternion conventions - q.mValue = aiQuaternion(0.f,0.f,AI_DEG_TO_RAD( /*-*/ f.mValue)); - } - } -#if 0 - if (pcIn->aTargetPositionKeys.size() > 1) - { - DefaultLogger::get()->debug("3DS: Converting target track ..."); - - // Camera or spot light - need to convert the separate - // target position channel to our representation - TargetAnimationHelper helper; - - if (pcIn->aPositionKeys.empty()) - { - // We can just pass zero here ... - helper.SetFixedMainAnimationChannel(aiVector3D()); - } - else helper.SetMainAnimationChannel(&pcIn->aPositionKeys); - helper.SetTargetAnimationChannel(&pcIn->aTargetPositionKeys); - - // Do the conversion - std::vector distanceTrack; - helper.Process(&distanceTrack); - - // Now add a new node as child, name it .Target - // and assign the distance track to it. This is that the - // information where the target is and how it moves is - // not lost - D3DS::Node* nd = new D3DS::Node(); - pcIn->push_back(nd); - - nd->mName = pcIn->mName + ".Target"; - - aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim(); - nda->mNodeName.Set(nd->mName); - - nda->mNumPositionKeys = (unsigned int)distanceTrack.size(); - nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys]; - ::memcpy(nda->mPositionKeys,&distanceTrack[0], - sizeof(aiVectorKey)*nda->mNumPositionKeys); - } -#endif - - // Cameras or lights define their transformation in their parent node and in the - // corresponding light or camera chunks. However, we read and process the latter - // to to be able to return valid cameras/lights even if no scenegraph is given. - for (unsigned int n = 0; n < pcSOut->mNumCameras;++n) { - if (pcSOut->mCameras[n]->mName == pcOut->mName) { - pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f,0.f,1.f); - } - } - for (unsigned int n = 0; n < pcSOut->mNumLights;++n) { - if (pcSOut->mLights[n]->mName == pcOut->mName) { - pcSOut->mLights[n]->mDirection = aiVector3D(0.f,0.f,1.f); - } - } - - // Allocate a new node anim and setup its name - aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim(); - nda->mNodeName.Set(pcIn->mName); - - // POSITION keys - if (pcIn->aPositionKeys.size() > 0) - { - nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size(); - nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys]; - ::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0], - sizeof(aiVectorKey)*nda->mNumPositionKeys); - } - - // ROTATION keys - if (pcIn->aRotationKeys.size() > 0) - { - nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size(); - nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys]; - - // Rotations are quaternion offsets - aiQuaternion abs; - for (unsigned int n = 0; n < nda->mNumRotationKeys;++n) - { - const aiQuatKey& q = pcIn->aRotationKeys[n]; - - abs = (n ? abs * q.mValue : q.mValue); - nda->mRotationKeys[n].mTime = q.mTime; - nda->mRotationKeys[n].mValue = abs.Normalize(); - } - } - - // SCALING keys - if (pcIn->aScalingKeys.size() > 0) - { - nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size(); - nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys]; - ::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0], - sizeof(aiVectorKey)*nda->mNumScalingKeys); - } - } - - // Allocate storage for children - pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size(); - pcOut->mChildren = new aiNode*[pcIn->mChildren.size()]; - - // Recursively process all children - const unsigned int size = pcIn->mChildren.size(); - for (unsigned int i = 0; i < size;++i) - { - pcOut->mChildren[i] = new aiNode(); - pcOut->mChildren[i]->mParent = pcOut; - AddNodeToGraph(pcSOut,pcOut->mChildren[i],pcIn->mChildren[i],abs); - } -} - -// ------------------------------------------------------------------------------------------------ -// Find out how many node animation channels we'll have finally -void CountTracks(D3DS::Node* node, unsigned int& cnt) -{ - ////////////////////////////////////////////////////////////////////////////// - // We will never generate more than one channel for a node, so - // this is rather easy here. - - if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 || - node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 || - node->aTargetPositionKeys.size() > 1) - { - ++cnt; - - // account for the additional channel for the camera/spotlight target position - if (node->aTargetPositionKeys.size() > 1)++cnt; - } - - // Recursively process all children - for (unsigned int i = 0; i < node->mChildren.size();++i) - CountTracks(node->mChildren[i],cnt); -} - -// ------------------------------------------------------------------------------------------------ -// Generate the output node graph -void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut) -{ - pcOut->mRootNode = new aiNode(); - if (0 == mRootNode->mChildren.size()) - { - ////////////////////////////////////////////////////////////////////////////// - // It seems the file is so messed up that it has not even a hierarchy. - // generate a flat hiearachy which looks like this: - // - // ROOT_NODE - // | - // ---------------------------------------- - // | | | | | - // MESH_0 MESH_1 MESH_2 ... MESH_N CAMERA_0 .... - // - DefaultLogger::get()->warn("No hierarchy information has been found in the file. "); - - pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes + - mScene->mCameras.size() + mScene->mLights.size(); - - pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mRootNode->mNumChildren ]; - pcOut->mRootNode->mName.Set("<3DSDummyRoot>"); - - // Build dummy nodes for all meshes - unsigned int a = 0; - for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a) - { - aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); - pcNode->mParent = pcOut->mRootNode; - pcNode->mMeshes = new unsigned int[1]; - pcNode->mMeshes[0] = i; - pcNode->mNumMeshes = 1; - - // Build a name for the node - pcNode->mName.length = sprintf(pcNode->mName.data,"3DSMesh_%u",i); - } - - // Build dummy nodes for all cameras - for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a) - { - aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); - pcNode->mParent = pcOut->mRootNode; - - // Build a name for the node - pcNode->mName = mScene->mCameras[i]->mName; - } - - // Build dummy nodes for all lights - for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a) - { - aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); - pcNode->mParent = pcOut->mRootNode; - - // Build a name for the node - pcNode->mName = mScene->mLights[i]->mName; - } - } - else - { - // First of all: find out how many scaling, rotation and translation - // animation tracks we'll have afterwards - unsigned int numChannel = 0; - CountTracks(mRootNode,numChannel); - - if (numChannel) - { - // Allocate a primary animation channel - pcOut->mNumAnimations = 1; - pcOut->mAnimations = new aiAnimation*[1]; - aiAnimation* anim = pcOut->mAnimations[0] = new aiAnimation(); - - anim->mName.Set("3DSMasterAnim"); - - // Allocate enough storage for all node animation channels, - // but don't set the mNumChannels member - we'll use it to - // index into the array - anim->mChannels = new aiNodeAnim*[numChannel]; - } - - aiMatrix4x4 m; - AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode,m); - } - - // We used the first and second vertex color set to store some temporary values so we need to cleanup here - for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a) - { - pcOut->mMeshes[a]->mColors[0] = NULL; - pcOut->mMeshes[a]->mColors[1] = NULL; - } - - pcOut->mRootNode->mTransformation = aiMatrix4x4( - 1.f,0.f,0.f,0.f, - 0.f,0.f,1.f,0.f, - 0.f,-1.f,0.f,0.f, - 0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation; - - // If the root node is unnamed name it "<3DSRoot>" - if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) || - (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') ) - { - pcOut->mRootNode->mName.Set("<3DSRoot>"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Convert all meshes in the scene and generate the final output scene. -void Discreet3DSImporter::ConvertScene(aiScene* pcOut) -{ - // Allocate enough storage for all output materials - pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size(); - pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials]; - - // ... and convert the 3DS materials to aiMaterial's - for (unsigned int i = 0; i < pcOut->mNumMaterials;++i) - { - aiMaterial* pcNew = new aiMaterial(); - ConvertMaterial(mScene->mMaterials[i],*pcNew); - pcOut->mMaterials[i] = pcNew; - } - - // Generate the output mesh list - ConvertMeshes(pcOut); - - // Now copy all light sources to the output scene - pcOut->mNumLights = (unsigned int)mScene->mLights.size(); - if (pcOut->mNumLights) - { - pcOut->mLights = new aiLight*[pcOut->mNumLights]; - ::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights); - } - - // Now copy all cameras to the output scene - pcOut->mNumCameras = (unsigned int)mScene->mCameras.size(); - if (pcOut->mNumCameras) - { - pcOut->mCameras = new aiCamera*[pcOut->mNumCameras]; - ::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras); - } -} - -#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the 3ds importer class */ + + +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER + +// internal headers +#include "3DSLoader.h" +#include "TargetAnimation.h" +#include "../include/assimp/scene.h" +#include "../include/assimp/DefaultLogger.hpp" +#include "StringComparison.h" +#include +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Setup final material indices, generae a default material if necessary +void Discreet3DSImporter::ReplaceDefaultMaterial() +{ + + // Try to find an existing material that matches the + // typical default material setting: + // - no textures + // - diffuse color (in grey!) + // NOTE: This is here to workaround the fact that some + // exporters are writing a default material, too. + unsigned int idx = 0xcdcdcdcd; + for (unsigned int i = 0; i < mScene->mMaterials.size();++i) + { + std::string s = mScene->mMaterials[i].mName; + for (std::string::iterator it = s.begin(); it != s.end(); ++it) + *it = ::tolower(*it); + + if (std::string::npos == s.find("default"))continue; + + if (mScene->mMaterials[i].mDiffuse.r != + mScene->mMaterials[i].mDiffuse.g || + mScene->mMaterials[i].mDiffuse.r != + mScene->mMaterials[i].mDiffuse.b)continue; + + if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || + mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || + mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || + mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || + mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || + mScene->mMaterials[i].sTexShininess.mMapName.length() != 0 ) + { + continue; + } + idx = i; + } + if (0xcdcdcdcd == idx)idx = (unsigned int)mScene->mMaterials.size(); + + // now iterate through all meshes and through all faces and + // find all faces that are using the default material + unsigned int cnt = 0; + for (std::vector::iterator + i = mScene->mMeshes.begin(); + i != mScene->mMeshes.end();++i) + { + for (std::vector::iterator + a = (*i).mFaceMaterials.begin(); + a != (*i).mFaceMaterials.end();++a) + { + // NOTE: The additional check seems to be necessary, + // some exporters seem to generate invalid data here + if (0xcdcdcdcd == (*a)) + { + (*a) = idx; + ++cnt; + } + else if ( (*a) >= mScene->mMaterials.size()) + { + (*a) = idx; + DefaultLogger::get()->warn("Material index overflow in 3DS file. Using default material"); + ++cnt; + } + } + } + if (cnt && idx == mScene->mMaterials.size()) + { + // We need to create our own default material + D3DS::Material sMat; + sMat.mDiffuse = aiColor3D(0.3f,0.3f,0.3f); + sMat.mName = "%%%DEFAULT"; + mScene->mMaterials.push_back(sMat); + + DefaultLogger::get()->info("3DS: Generating default material"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Check whether all indices are valid. Otherwise we'd crash before the validation step is reached +void Discreet3DSImporter::CheckIndices(D3DS::Mesh& sMesh) +{ + for (std::vector< D3DS::Face >::iterator i = sMesh.mFaces.begin(); i != sMesh.mFaces.end();++i) + { + // check whether all indices are in range + for (unsigned int a = 0; a < 3;++a) + { + if ((*i).mIndices[a] >= sMesh.mPositions.size()) + { + DefaultLogger::get()->warn("3DS: Vertex index overflow)"); + (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1; + } + if ( !sMesh.mTexCoords.empty() && (*i).mIndices[a] >= sMesh.mTexCoords.size()) + { + DefaultLogger::get()->warn("3DS: Texture coordinate index overflow)"); + (*i).mIndices[a] = (uint32_t)sMesh.mTexCoords.size()-1; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Generate out unique verbose format representation +void Discreet3DSImporter::MakeUnique(D3DS::Mesh& sMesh) +{ + // TODO: really necessary? I don't think. Just a waste of memory and time + // to do it now in a separate buffer. + + // Allocate output storage + std::vector vNew (sMesh.mFaces.size() * 3); + std::vector vNew2; + if (sMesh.mTexCoords.size()) + vNew2.resize(sMesh.mFaces.size() * 3); + + for (unsigned int i = 0, base = 0; i < sMesh.mFaces.size();++i) + { + D3DS::Face& face = sMesh.mFaces[i]; + + // Positions + for (unsigned int a = 0; a < 3;++a,++base) + { + vNew[base] = sMesh.mPositions[face.mIndices[a]]; + if (sMesh.mTexCoords.size()) + vNew2[base] = sMesh.mTexCoords[face.mIndices[a]]; + + face.mIndices[a] = base; + } + } + sMesh.mPositions = vNew; + sMesh.mTexCoords = vNew2; +} + +// ------------------------------------------------------------------------------------------------ +// Convert a 3DS texture to texture keys in an aiMaterial +void CopyTexture(aiMaterial& mat, D3DS::Texture& texture, aiTextureType type) +{ + // Setup the texture name + aiString tex; + tex.Set( texture.mMapName); + mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0)); + + // Setup the texture blend factor + if (is_not_qnan(texture.mTextureBlend)) + mat.AddProperty( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0)); + + // Setup the texture mapping mode + mat.AddProperty((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_U(type,0)); + mat.AddProperty((int*)&texture.mMapMode,1,AI_MATKEY_MAPPINGMODE_V(type,0)); + + // Mirroring - double the scaling values + // FIXME: this is not really correct ... + if (texture.mMapMode == aiTextureMapMode_Mirror) + { + texture.mScaleU *= 2.f; + texture.mScaleV *= 2.f; + texture.mOffsetU /= 2.f; + texture.mOffsetV /= 2.f; + } + + // Setup texture UV transformations + mat.AddProperty(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0)); +} + +// ------------------------------------------------------------------------------------------------ +// Convert a 3DS material to an aiMaterial +void Discreet3DSImporter::ConvertMaterial(D3DS::Material& oldMat, + aiMaterial& mat) +{ + // NOTE: Pass the background image to the viewer by bypassing the + // material system. This is an evil hack, never do it again! + if (0 != mBackgroundImage.length() && bHasBG) + { + aiString tex; + tex.Set( mBackgroundImage); + mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE); + + // Be sure this is only done for the first material + mBackgroundImage = std::string(""); + } + + // At first add the base ambient color of the scene to the material + oldMat.mAmbient.r += mClrAmbient.r; + oldMat.mAmbient.g += mClrAmbient.g; + oldMat.mAmbient.b += mClrAmbient.b; + + aiString name; + name.Set( oldMat.mName); + mat.AddProperty( &name, AI_MATKEY_NAME); + + // Material colors + mat.AddProperty( &oldMat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); + mat.AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + mat.AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); + mat.AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); + + // Phong shininess and shininess strength + if (D3DS::Discreet3DS::Phong == oldMat.mShading || + D3DS::Discreet3DS::Metal == oldMat.mShading) + { + if (!oldMat.mSpecularExponent || !oldMat.mShininessStrength) + { + oldMat.mShading = D3DS::Discreet3DS::Gouraud; + } + else + { + mat.AddProperty( &oldMat.mSpecularExponent, 1, AI_MATKEY_SHININESS); + mat.AddProperty( &oldMat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); + } + } + + // Opacity + mat.AddProperty( &oldMat.mTransparency,1,AI_MATKEY_OPACITY); + + // Bump height scaling + mat.AddProperty( &oldMat.mBumpHeight,1,AI_MATKEY_BUMPSCALING); + + // Two sided rendering? + if (oldMat.mTwoSided) + { + int i = 1; + mat.AddProperty(&i,1,AI_MATKEY_TWOSIDED); + } + + // Shading mode + aiShadingMode eShading = aiShadingMode_NoShading; + switch (oldMat.mShading) + { + case D3DS::Discreet3DS::Flat: + eShading = aiShadingMode_Flat; break; + + // I don't know what "Wire" shading should be, + // assume it is simple lambertian diffuse shading + case D3DS::Discreet3DS::Wire: + { + // Set the wireframe flag + unsigned int iWire = 1; + mat.AddProperty( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME); + } + + case D3DS::Discreet3DS::Gouraud: + eShading = aiShadingMode_Gouraud; break; + + // assume cook-torrance shading for metals. + case D3DS::Discreet3DS::Phong : + eShading = aiShadingMode_Phong; break; + + case D3DS::Discreet3DS::Metal : + eShading = aiShadingMode_CookTorrance; break; + + // FIX to workaround a warning with GCC 4 who complained + // about a missing case Blinn: here - Blinn isn't a valid + // value in the 3DS Loader, it is just needed for ASE + case D3DS::Discreet3DS::Blinn : + eShading = aiShadingMode_Blinn; break; + } + mat.AddProperty( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); + + // DIFFUSE texture + if( oldMat.sTexDiffuse.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexDiffuse, aiTextureType_DIFFUSE); + + // SPECULAR texture + if( oldMat.sTexSpecular.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexSpecular, aiTextureType_SPECULAR); + + // OPACITY texture + if( oldMat.sTexOpacity.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexOpacity, aiTextureType_OPACITY); + + // EMISSIVE texture + if( oldMat.sTexEmissive.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexEmissive, aiTextureType_EMISSIVE); + + // BUMP texture + if( oldMat.sTexBump.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexBump, aiTextureType_HEIGHT); + + // SHININESS texture + if( oldMat.sTexShininess.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexShininess, aiTextureType_SHININESS); + + // REFLECTION texture + if( oldMat.sTexReflective.mMapName.length() > 0) + CopyTexture(mat,oldMat.sTexReflective, aiTextureType_REFLECTION); + + // Store the name of the material itself, too + if( oldMat.mName.length()) { + aiString tex; + tex.Set( oldMat.mName); + mat.AddProperty( &tex, AI_MATKEY_NAME); + } +} + +// ------------------------------------------------------------------------------------------------ +// Split meshes by their materials and generate output aiMesh'es +void Discreet3DSImporter::ConvertMeshes(aiScene* pcOut) +{ + std::vector avOutMeshes; + avOutMeshes.reserve(mScene->mMeshes.size() * 2); + + unsigned int iFaceCnt = 0,num = 0; + aiString name; + + // we need to split all meshes by their materials + for (std::vector::iterator i = mScene->mMeshes.begin(); i != mScene->mMeshes.end();++i) { + boost::scoped_array< std::vector > aiSplit(new std::vector[mScene->mMaterials.size()]); + + name.length = ASSIMP_itoa10(name.data,num++); + + unsigned int iNum = 0; + for (std::vector::const_iterator a = (*i).mFaceMaterials.begin(); + a != (*i).mFaceMaterials.end();++a,++iNum) + { + aiSplit[*a].push_back(iNum); + } + // now generate submeshes + for (unsigned int p = 0; p < mScene->mMaterials.size();++p) + { + if (aiSplit[p].empty()) { + continue; + } + aiMesh* meshOut = new aiMesh(); + meshOut->mName = name; + meshOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + // be sure to setup the correct material index + meshOut->mMaterialIndex = p; + + // use the color data as temporary storage + meshOut->mColors[0] = (aiColor4D*)(&*i); + avOutMeshes.push_back(meshOut); + + // convert vertices + meshOut->mNumFaces = (unsigned int)aiSplit[p].size(); + meshOut->mNumVertices = meshOut->mNumFaces*3; + + // allocate enough storage for faces + meshOut->mFaces = new aiFace[meshOut->mNumFaces]; + iFaceCnt += meshOut->mNumFaces; + + meshOut->mVertices = new aiVector3D[meshOut->mNumVertices]; + meshOut->mNormals = new aiVector3D[meshOut->mNumVertices]; + if ((*i).mTexCoords.size()) + { + meshOut->mTextureCoords[0] = new aiVector3D[meshOut->mNumVertices]; + } + for (unsigned int q = 0, base = 0; q < aiSplit[p].size();++q) + { + unsigned int index = aiSplit[p][q]; + aiFace& face = meshOut->mFaces[q]; + + face.mIndices = new unsigned int[3]; + face.mNumIndices = 3; + + for (unsigned int a = 0; a < 3;++a,++base) + { + unsigned int idx = (*i).mFaces[index].mIndices[a]; + meshOut->mVertices[base] = (*i).mPositions[idx]; + meshOut->mNormals [base] = (*i).mNormals[idx]; + + if ((*i).mTexCoords.size()) + meshOut->mTextureCoords[0][base] = (*i).mTexCoords[idx]; + + face.mIndices[a] = base; + } + } + } + } + + // Copy them to the output array + pcOut->mNumMeshes = (unsigned int)avOutMeshes.size(); + pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes](); + for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) { + pcOut->mMeshes[a] = avOutMeshes[a]; + } + + // We should have at least one face here + if (!iFaceCnt) { + throw DeadlyImportError("No faces loaded. The mesh is empty"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Add a node to the scenegraph and setup its final transformation +void Discreet3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut, + D3DS::Node* pcIn, aiMatrix4x4& /*absTrafo*/) +{ + std::vector iArray; + iArray.reserve(3); + + aiMatrix4x4 abs; + + // Find all meshes with the same name as the node + for (unsigned int a = 0; a < pcSOut->mNumMeshes;++a) + { + const D3DS::Mesh* pcMesh = (const D3DS::Mesh*)pcSOut->mMeshes[a]->mColors[0]; + ai_assert(NULL != pcMesh); + + if (pcIn->mName == pcMesh->mName) + iArray.push_back(a); + } + if (!iArray.empty()) + { + // The matrix should be identical for all meshes with the + // same name. It HAS to be identical for all meshes ..... + D3DS::Mesh* imesh = ((D3DS::Mesh*)pcSOut->mMeshes[iArray[0]]->mColors[0]); + + // Compute the inverse of the transformation matrix to move the + // vertices back to their relative and local space + aiMatrix4x4 mInv = imesh->mMat, mInvTransposed = imesh->mMat; + mInv.Inverse();mInvTransposed.Transpose(); + aiVector3D pivot = pcIn->vPivot; + + pcOut->mNumMeshes = (unsigned int)iArray.size(); + pcOut->mMeshes = new unsigned int[iArray.size()]; + for (unsigned int i = 0;i < iArray.size();++i) { + const unsigned int iIndex = iArray[i]; + aiMesh* const mesh = pcSOut->mMeshes[iIndex]; + + if (mesh->mColors[1] == NULL) + { + // Transform the vertices back into their local space + // fixme: consider computing normals after this, so we don't need to transform them + const aiVector3D* const pvEnd = mesh->mVertices + mesh->mNumVertices; + aiVector3D* pvCurrent = mesh->mVertices, *t2 = mesh->mNormals; + + for (; pvCurrent != pvEnd; ++pvCurrent, ++t2) { + *pvCurrent = mInv * (*pvCurrent); + *t2 = mInvTransposed * (*t2); + } + + // Handle negative transformation matrix determinant -> invert vertex x + if (imesh->mMat.Determinant() < 0.0f) + { + /* we *must* have normals */ + for (pvCurrent = mesh->mVertices, t2 = mesh->mNormals; pvCurrent != pvEnd; ++pvCurrent, ++t2) { + pvCurrent->x *= -1.f; + t2->x *= -1.f; + } + DefaultLogger::get()->info("3DS: Flipping mesh X-Axis"); + } + + // Handle pivot point + if (pivot.x || pivot.y || pivot.z) + { + for (pvCurrent = mesh->mVertices; pvCurrent != pvEnd; ++pvCurrent) { + *pvCurrent -= pivot; + } + } + + mesh->mColors[1] = (aiColor4D*)1; + } + else + mesh->mColors[1] = (aiColor4D*)1; + + // Setup the mesh index + pcOut->mMeshes[i] = iIndex; + } + } + + // Setup the name of the node + // First instance keeps its name otherwise something might break, all others will be postfixed with their instance number + if (pcIn->mInstanceNumber > 1) + { + char tmp[12]; + ASSIMP_itoa10(tmp, pcIn->mInstanceNumber); + std::string tempStr = pcIn->mName + "_inst_"; + tempStr += tmp; + pcOut->mName.Set(tempStr); + } + else + pcOut->mName.Set(pcIn->mName); + + // Now build the transformation matrix of the node + // ROTATION + if (pcIn->aRotationKeys.size()){ + + // FIX to get to Assimp's quaternion conventions + for (std::vector::iterator it = pcIn->aRotationKeys.begin(); it != pcIn->aRotationKeys.end(); ++it) { + (*it).mValue.w *= -1.f; + } + + pcOut->mTransformation = aiMatrix4x4( pcIn->aRotationKeys[0].mValue.GetMatrix() ); + } + else if (pcIn->aCameraRollKeys.size()) + { + aiMatrix4x4::RotationZ(AI_DEG_TO_RAD(- pcIn->aCameraRollKeys[0].mValue), + pcOut->mTransformation); + } + + // SCALING + aiMatrix4x4& m = pcOut->mTransformation; + if (pcIn->aScalingKeys.size()) + { + const aiVector3D& v = pcIn->aScalingKeys[0].mValue; + m.a1 *= v.x; m.b1 *= v.x; m.c1 *= v.x; + m.a2 *= v.y; m.b2 *= v.y; m.c2 *= v.y; + m.a3 *= v.z; m.b3 *= v.z; m.c3 *= v.z; + } + + // TRANSLATION + if (pcIn->aPositionKeys.size()) + { + const aiVector3D& v = pcIn->aPositionKeys[0].mValue; + m.a4 += v.x; + m.b4 += v.y; + m.c4 += v.z; + } + + // Generate animation channels for the node + if (pcIn->aPositionKeys.size() > 1 || pcIn->aRotationKeys.size() > 1 || + pcIn->aScalingKeys.size() > 1 || pcIn->aCameraRollKeys.size() > 1 || + pcIn->aTargetPositionKeys.size() > 1) + { + aiAnimation* anim = pcSOut->mAnimations[0]; + ai_assert(NULL != anim); + + if (pcIn->aCameraRollKeys.size() > 1) + { + DefaultLogger::get()->debug("3DS: Converting camera roll track ..."); + + // Camera roll keys - in fact they're just rotations + // around the camera's z axis. The angles are given + // in degrees (and they're clockwise). + pcIn->aRotationKeys.resize(pcIn->aCameraRollKeys.size()); + for (unsigned int i = 0; i < pcIn->aCameraRollKeys.size();++i) + { + aiQuatKey& q = pcIn->aRotationKeys[i]; + aiFloatKey& f = pcIn->aCameraRollKeys[i]; + + q.mTime = f.mTime; + + // FIX to get to Assimp quaternion conventions + q.mValue = aiQuaternion(0.f,0.f,AI_DEG_TO_RAD( /*-*/ f.mValue)); + } + } +#if 0 + if (pcIn->aTargetPositionKeys.size() > 1) + { + DefaultLogger::get()->debug("3DS: Converting target track ..."); + + // Camera or spot light - need to convert the separate + // target position channel to our representation + TargetAnimationHelper helper; + + if (pcIn->aPositionKeys.empty()) + { + // We can just pass zero here ... + helper.SetFixedMainAnimationChannel(aiVector3D()); + } + else helper.SetMainAnimationChannel(&pcIn->aPositionKeys); + helper.SetTargetAnimationChannel(&pcIn->aTargetPositionKeys); + + // Do the conversion + std::vector distanceTrack; + helper.Process(&distanceTrack); + + // Now add a new node as child, name it .Target + // and assign the distance track to it. This is that the + // information where the target is and how it moves is + // not lost + D3DS::Node* nd = new D3DS::Node(); + pcIn->push_back(nd); + + nd->mName = pcIn->mName + ".Target"; + + aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim(); + nda->mNodeName.Set(nd->mName); + + nda->mNumPositionKeys = (unsigned int)distanceTrack.size(); + nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys]; + ::memcpy(nda->mPositionKeys,&distanceTrack[0], + sizeof(aiVectorKey)*nda->mNumPositionKeys); + } +#endif + + // Cameras or lights define their transformation in their parent node and in the + // corresponding light or camera chunks. However, we read and process the latter + // to to be able to return valid cameras/lights even if no scenegraph is given. + for (unsigned int n = 0; n < pcSOut->mNumCameras;++n) { + if (pcSOut->mCameras[n]->mName == pcOut->mName) { + pcSOut->mCameras[n]->mLookAt = aiVector3D(0.f,0.f,1.f); + } + } + for (unsigned int n = 0; n < pcSOut->mNumLights;++n) { + if (pcSOut->mLights[n]->mName == pcOut->mName) { + pcSOut->mLights[n]->mDirection = aiVector3D(0.f,0.f,1.f); + } + } + + // Allocate a new node anim and setup its name + aiNodeAnim* nda = anim->mChannels[anim->mNumChannels++] = new aiNodeAnim(); + nda->mNodeName.Set(pcIn->mName); + + // POSITION keys + if (pcIn->aPositionKeys.size() > 0) + { + nda->mNumPositionKeys = (unsigned int)pcIn->aPositionKeys.size(); + nda->mPositionKeys = new aiVectorKey[nda->mNumPositionKeys]; + ::memcpy(nda->mPositionKeys,&pcIn->aPositionKeys[0], + sizeof(aiVectorKey)*nda->mNumPositionKeys); + } + + // ROTATION keys + if (pcIn->aRotationKeys.size() > 0) + { + nda->mNumRotationKeys = (unsigned int)pcIn->aRotationKeys.size(); + nda->mRotationKeys = new aiQuatKey[nda->mNumRotationKeys]; + + // Rotations are quaternion offsets + aiQuaternion abs; + for (unsigned int n = 0; n < nda->mNumRotationKeys;++n) + { + const aiQuatKey& q = pcIn->aRotationKeys[n]; + + abs = (n ? abs * q.mValue : q.mValue); + nda->mRotationKeys[n].mTime = q.mTime; + nda->mRotationKeys[n].mValue = abs.Normalize(); + } + } + + // SCALING keys + if (pcIn->aScalingKeys.size() > 0) + { + nda->mNumScalingKeys = (unsigned int)pcIn->aScalingKeys.size(); + nda->mScalingKeys = new aiVectorKey[nda->mNumScalingKeys]; + ::memcpy(nda->mScalingKeys,&pcIn->aScalingKeys[0], + sizeof(aiVectorKey)*nda->mNumScalingKeys); + } + } + + // Allocate storage for children + pcOut->mNumChildren = (unsigned int)pcIn->mChildren.size(); + pcOut->mChildren = new aiNode*[pcIn->mChildren.size()]; + + // Recursively process all children + const unsigned int size = pcIn->mChildren.size(); + for (unsigned int i = 0; i < size;++i) + { + pcOut->mChildren[i] = new aiNode(); + pcOut->mChildren[i]->mParent = pcOut; + AddNodeToGraph(pcSOut,pcOut->mChildren[i],pcIn->mChildren[i],abs); + } +} + +// ------------------------------------------------------------------------------------------------ +// Find out how many node animation channels we'll have finally +void CountTracks(D3DS::Node* node, unsigned int& cnt) +{ + ////////////////////////////////////////////////////////////////////////////// + // We will never generate more than one channel for a node, so + // this is rather easy here. + + if (node->aPositionKeys.size() > 1 || node->aRotationKeys.size() > 1 || + node->aScalingKeys.size() > 1 || node->aCameraRollKeys.size() > 1 || + node->aTargetPositionKeys.size() > 1) + { + ++cnt; + + // account for the additional channel for the camera/spotlight target position + if (node->aTargetPositionKeys.size() > 1)++cnt; + } + + // Recursively process all children + for (unsigned int i = 0; i < node->mChildren.size();++i) + CountTracks(node->mChildren[i],cnt); +} + +// ------------------------------------------------------------------------------------------------ +// Generate the output node graph +void Discreet3DSImporter::GenerateNodeGraph(aiScene* pcOut) +{ + pcOut->mRootNode = new aiNode(); + if (0 == mRootNode->mChildren.size()) + { + ////////////////////////////////////////////////////////////////////////////// + // It seems the file is so messed up that it has not even a hierarchy. + // generate a flat hiearachy which looks like this: + // + // ROOT_NODE + // | + // ---------------------------------------- + // | | | | | + // MESH_0 MESH_1 MESH_2 ... MESH_N CAMERA_0 .... + // + DefaultLogger::get()->warn("No hierarchy information has been found in the file. "); + + pcOut->mRootNode->mNumChildren = pcOut->mNumMeshes + + mScene->mCameras.size() + mScene->mLights.size(); + + pcOut->mRootNode->mChildren = new aiNode* [ pcOut->mRootNode->mNumChildren ]; + pcOut->mRootNode->mName.Set("<3DSDummyRoot>"); + + // Build dummy nodes for all meshes + unsigned int a = 0; + for (unsigned int i = 0; i < pcOut->mNumMeshes;++i,++a) + { + aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); + pcNode->mParent = pcOut->mRootNode; + pcNode->mMeshes = new unsigned int[1]; + pcNode->mMeshes[0] = i; + pcNode->mNumMeshes = 1; + + // Build a name for the node + pcNode->mName.length = sprintf(pcNode->mName.data,"3DSMesh_%u",i); + } + + // Build dummy nodes for all cameras + for (unsigned int i = 0; i < (unsigned int )mScene->mCameras.size();++i,++a) + { + aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); + pcNode->mParent = pcOut->mRootNode; + + // Build a name for the node + pcNode->mName = mScene->mCameras[i]->mName; + } + + // Build dummy nodes for all lights + for (unsigned int i = 0; i < (unsigned int )mScene->mLights.size();++i,++a) + { + aiNode* pcNode = pcOut->mRootNode->mChildren[a] = new aiNode(); + pcNode->mParent = pcOut->mRootNode; + + // Build a name for the node + pcNode->mName = mScene->mLights[i]->mName; + } + } + else + { + // First of all: find out how many scaling, rotation and translation + // animation tracks we'll have afterwards + unsigned int numChannel = 0; + CountTracks(mRootNode,numChannel); + + if (numChannel) + { + // Allocate a primary animation channel + pcOut->mNumAnimations = 1; + pcOut->mAnimations = new aiAnimation*[1]; + aiAnimation* anim = pcOut->mAnimations[0] = new aiAnimation(); + + anim->mName.Set("3DSMasterAnim"); + + // Allocate enough storage for all node animation channels, + // but don't set the mNumChannels member - we'll use it to + // index into the array + anim->mChannels = new aiNodeAnim*[numChannel]; + } + + aiMatrix4x4 m; + AddNodeToGraph(pcOut, pcOut->mRootNode, mRootNode,m); + } + + // We used the first and second vertex color set to store some temporary values so we need to cleanup here + for (unsigned int a = 0; a < pcOut->mNumMeshes; ++a) + { + pcOut->mMeshes[a]->mColors[0] = NULL; + pcOut->mMeshes[a]->mColors[1] = NULL; + } + + pcOut->mRootNode->mTransformation = aiMatrix4x4( + 1.f,0.f,0.f,0.f, + 0.f,0.f,1.f,0.f, + 0.f,-1.f,0.f,0.f, + 0.f,0.f,0.f,1.f) * pcOut->mRootNode->mTransformation; + + // If the root node is unnamed name it "<3DSRoot>" + if (::strstr( pcOut->mRootNode->mName.data, "UNNAMED" ) || + (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') ) + { + pcOut->mRootNode->mName.Set("<3DSRoot>"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Convert all meshes in the scene and generate the final output scene. +void Discreet3DSImporter::ConvertScene(aiScene* pcOut) +{ + // Allocate enough storage for all output materials + pcOut->mNumMaterials = (unsigned int)mScene->mMaterials.size(); + pcOut->mMaterials = new aiMaterial*[pcOut->mNumMaterials]; + + // ... and convert the 3DS materials to aiMaterial's + for (unsigned int i = 0; i < pcOut->mNumMaterials;++i) + { + aiMaterial* pcNew = new aiMaterial(); + ConvertMaterial(mScene->mMaterials[i],*pcNew); + pcOut->mMaterials[i] = pcNew; + } + + // Generate the output mesh list + ConvertMeshes(pcOut); + + // Now copy all light sources to the output scene + pcOut->mNumLights = (unsigned int)mScene->mLights.size(); + if (pcOut->mNumLights) + { + pcOut->mLights = new aiLight*[pcOut->mNumLights]; + ::memcpy(pcOut->mLights,&mScene->mLights[0],sizeof(void*)*pcOut->mNumLights); + } + + // Now copy all cameras to the output scene + pcOut->mNumCameras = (unsigned int)mScene->mCameras.size(); + if (pcOut->mNumCameras) + { + pcOut->mCameras = new aiCamera*[pcOut->mNumCameras]; + ::memcpy(pcOut->mCameras,&mScene->mCameras[0],sizeof(void*)*pcOut->mNumCameras); + } +} + +#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER diff --git a/code/3DSExporter.cpp b/code/3DSExporter.cpp index 8620e5544..324dc2235 100644 --- a/code/3DSExporter.cpp +++ b/code/3DSExporter.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2015, 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 +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -23,16 +23,16 @@ following conditions are met: 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 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- @@ -53,129 +53,129 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include using namespace Assimp; -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: + ////////////////////////////////////////////////////////////////////////////////////// + // 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(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(); + ~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; + 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; - }; + 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); + // 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; - } + 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); + // 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; - } + aiString mat_name; + if (AI_SUCCESS == mat.Get(AI_MATKEY_NAME, mat_name)) { + return mat_name.C_Str() + underscore + postfix; + } - return "Material" + underscore + postfix; - } + return "Material" + underscore + postfix; + } - // Collect world transformations for each node - void CollectTrafos(const aiNode* node, std::map& 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); - } - } + // Collect world transformations for each node + void CollectTrafos(const aiNode* node, std::map& 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& 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); - } - } + // Generate a flat list of the meshes (by index) assigned to each node + void CollectMeshes(const aiNode* node, std::multimap& 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, const ExportProperties* pProperties) { - boost::shared_ptr outfile (pIOSystem->Open(pFile, "wb")); - if(!outfile) { - throw DeadlyExportError("Could not open output .3ds file: " + std::string(pFile)); - } + boost::shared_ptr 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 scenecopy(scenecopy_tmp); + // 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 scenecopy(scenecopy_tmp); - SplitLargeMeshesProcess_Triangle tri_splitter; - tri_splitter.SetLimit(0xffff); - tri_splitter.Execute(scenecopy.get()); + 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()); + SplitLargeMeshesProcess_Vertex vert_splitter; + vert_splitter.SetLimit(0xffff); + vert_splitter.Execute(scenecopy.get()); - // Invoke the actual exporter - Discreet3DSExporter exporter(outfile, scenecopy.get()); + // Invoke the actual exporter + Discreet3DSExporter exporter(outfile, scenecopy.get()); } } // end of namespace Assimp @@ -185,379 +185,379 @@ Discreet3DSExporter:: Discreet3DSExporter(boost::shared_ptr outfile, c : scene(scene) , writer(outfile) { - CollectTrafos(scene->mRootNode, trafos); - CollectMeshes(scene->mRootNode, meshes); + CollectTrafos(scene->mRootNode, trafos); + CollectMeshes(scene->mRootNode, meshes); - ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAIN); + ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAIN); - { - ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJMESH); - WriteMaterials(); - WriteMeshes(); + { + ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJMESH); + WriteMaterials(); + WriteMeshes(); - { - ChunkWriter chunk(writer, Discreet3DS::CHUNK_MASTER_SCALE); - writer.PutF4(1.0f); - } - } + { + ChunkWriter chunk(writer, Discreet3DS::CHUNK_MASTER_SCALE); + writer.PutF4(1.0f); + } + } - { - ChunkWriter chunk(writer, Discreet3DS::CHUNK_KEYFRAMER); - WriteHierarchy(*scene->mRootNode, -1, -1); - } + { + 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); + // 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); + // 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); + // 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(seq); - if (sibling_level != -1) { - hierarchy_pos = sibling_level; - } + int16_t hierarchy_pos = static_cast(seq); + if (sibling_level != -1) { + hierarchy_pos = sibling_level; + } - // Write the hierarchy position - writer.PutI2(hierarchy_pos); - } - } + // Write the hierarchy position + writer.PutI2(hierarchy_pos); + } + } - // TODO: write transformation chunks + // TODO: write transformation chunks - ++seq; - sibling_level = seq; + ++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 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; + // 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]; + 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)); + 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(first_child ? seq : sibling_level)); - ++seq; - } - } - return seq; + writer.PutI4(0); + writer.PutI2(static_cast(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]; + 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); - } + { + 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); - } + 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_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_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); - } + if (mat.Get(AI_MATKEY_COLOR_EMISSIVE, color) == AI_SUCCESS) { + ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SELF_ILLUM); + WriteColor(color); + } - aiShadingMode shading_mode = aiShadingMode_Flat; - if (mat.Get(AI_MATKEY_SHADING_MODEL, shading_mode) == AI_SUCCESS) { - ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHADING); + aiShadingMode shading_mode = aiShadingMode_Flat; + 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; + 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_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; + case aiShadingMode_Phong: + case aiShadingMode_Blinn: + case aiShadingMode_CookTorrance: + case aiShadingMode_Fresnel: + shading_mode_out = Discreet3DS::Phong; + break; - default: - shading_mode_out = Discreet3DS::Flat; - ai_assert(false); - }; - writer.PutU2(static_cast(shading_mode_out)); - } + default: + shading_mode_out = Discreet3DS::Flat; + ai_assert(false); + }; + writer.PutU2(static_cast(shading_mode_out)); + } - float f; - if (mat.Get(AI_MATKEY_SHININESS, f) == AI_SUCCESS) { - ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAT_SHININESS); - WritePercentChunk(f); - } + 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); - } + 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); - } + 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) +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; - } + 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; - } + // 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); - } + ChunkWriter chunk(writer, chunk_flags); + { + ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAPFILE); + WriteString(path); + } - WritePercentChunk(blend); + 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) + { + 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; + // 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]; + 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); + // 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]; + const aiMatrix4x4& trafo = trafos[&node]; - ChunkWriter chunk(writer, Discreet3DS::CHUNK_OBJBLOCK); + 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); + // 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); + // TRIMESH chunk + ChunkWriter chunk2(writer, Discreet3DS::CHUNK_TRIMESH); - // Vertices in world space - { - ChunkWriter chunk(writer, Discreet3DS::CHUNK_VERTLIST); + // Vertices in world space + { + ChunkWriter chunk(writer, Discreet3DS::CHUNK_VERTLIST); - const uint16_t count = static_cast(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); - } - } + const uint16_t count = static_cast(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(mesh.mNumVertices); - writer.PutU2(count); + // UV coordinates + if (mesh.HasTextureCoords(0)) { + ChunkWriter chunk(writer, Discreet3DS::CHUNK_MAPLIST); + const uint16_t count = static_cast(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); - } - } + 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); + // Faces (indices) + { + ChunkWriter chunk(writer, Discreet3DS::CHUNK_FACELIST); - ai_assert(mesh.mNumFaces <= 0xffff); + 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; - } + // 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; - } + 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(f.mIndices[j])); - } + for (unsigned int j = 0; j < 3; ++j) { + ai_assert(f.mIndices[j] <= 0xffff); + writer.PutI2(static_cast(f.mIndices[j])); + } - // Edge visibility flag - writer.PutI2(0x0); - } + // Edge visibility flag + writer.PutI2(0x0); + } - // TODO: write smoothing groups (CHUNK_SMOOLIST) + // TODO: write smoothing groups (CHUNK_SMOOLIST) - WriteFaceMaterialChunk(mesh); - } + 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]); - } - } - } - } + // 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); + 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(mesh.mNumFaces); - writer.PutU2(count); + // 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(mesh.mNumFaces); + writer.PutU2(count); - for (unsigned int i = 0; i < mesh.mNumFaces; ++i) { - writer.PutU2(static_cast(i)); - } + for (unsigned int i = 0; i < mesh.mNumFaces; ++i) { + writer.PutU2(static_cast(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'); + 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'); + 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); + 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); + ChunkWriter chunk(writer, Discreet3DS::CHUNK_PERCENTF); + writer.PutF4(f); } diff --git a/code/3DSExporter.h b/code/3DSExporter.h index 706a685cd..97ebf003e 100644 --- a/code/3DSExporter.h +++ b/code/3DSExporter.h @@ -5,8 +5,8 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2015, 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 +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -23,16 +23,16 @@ following conditions are met: 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 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- @@ -55,7 +55,7 @@ struct aiNode; struct aiMaterial; struct aiMesh; -namespace Assimp +namespace Assimp { // ------------------------------------------------------------------------------------------------ @@ -64,32 +64,32 @@ namespace Assimp class Discreet3DSExporter { public: - Discreet3DSExporter(boost::shared_ptr outfile, const aiScene* pScene); + Discreet3DSExporter(boost::shared_ptr outfile, const aiScene* pScene); private: - void WriteMeshes(); - void WriteMaterials(); - void WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags); + void WriteMeshes(); + void WriteMaterials(); + void WriteTexture(const aiMaterial& mat, aiTextureType type, uint16_t chunk_flags); - void WriteFaceMaterialChunk(const aiMesh& mesh); + void WriteFaceMaterialChunk(const aiMesh& mesh); - int WriteHierarchy(const aiNode& node, int level, int sibling_level); + 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); + 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; + const aiScene* const scene; + StreamWriterLE writer; - std::map trafos; + std::map trafos; - typedef std::multimap MeshesByNodeMap; - MeshesByNodeMap meshes; + typedef std::multimap MeshesByNodeMap; + MeshesByNodeMap meshes; }; diff --git a/code/3DSHelper.h b/code/3DSHelper.h index b68b13ced..26f72a66c 100644 --- a/code/3DSHelper.h +++ b/code/3DSHelper.h @@ -1,590 +1,590 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Defines helper data structures for the import of 3DS files */ - -#ifndef AI_3DSFILEHELPER_H_INC -#define AI_3DSFILEHELPER_H_INC - - -#include "SpatialSort.h" -#include "SmoothingGroups.h" -#include "qnan.h" -#include "./../include/assimp/material.h" -#include "./../include/assimp/camera.h" -#include "./../include/assimp/light.h" -#include "./../include/assimp/anim.h" -#include //sprintf - -namespace Assimp { -namespace D3DS { - -#include "./../include/assimp/Compiler/pushpack1.h" - -// --------------------------------------------------------------------------- -/** Discreet3DS class: Helper class for loading 3ds files. Defines chunks -* and data structures. -*/ -class Discreet3DS -{ -private: - inline Discreet3DS() {} - -public: - - //! data structure for a single chunk in a .3ds file - struct Chunk - { - uint16_t Flag; - uint32_t Size; - } PACK_STRUCT; - - - //! Used for shading field in material3ds structure - //! From AutoDesk 3ds SDK - typedef enum - { - // translated to gouraud shading with wireframe active - Wire = 0x0, - - // if this material is set, no vertex normals will - // be calculated for the model. Face normals + gouraud - Flat = 0x1, - - // standard gouraud shading - Gouraud = 0x2, - - // phong shading - Phong = 0x3, - - // cooktorrance or anistropic phong shading ... - // the exact meaning is unknown, if you know it - // feel free to tell me ;-) - Metal = 0x4, - - // required by the ASE loader - Blinn = 0x5 - } shadetype3ds; - - // Flags for animated keys - enum - { - KEY_USE_TENS = 0x1, - KEY_USE_CONT = 0x2, - KEY_USE_BIAS = 0x4, - KEY_USE_EASE_TO = 0x8, - KEY_USE_EASE_FROM = 0x10 - } ; - - enum - { - - // ******************************************************************** - // Basic chunks which can be found everywhere in the file - CHUNK_VERSION = 0x0002, - CHUNK_RGBF = 0x0010, // float4 R; float4 G; float4 B - CHUNK_RGBB = 0x0011, // int1 R; int1 G; int B - - // Linear color values (gamma = 2.2?) - CHUNK_LINRGBF = 0x0013, // float4 R; float4 G; float4 B - CHUNK_LINRGBB = 0x0012, // int1 R; int1 G; int B - - CHUNK_PERCENTW = 0x0030, // int2 percentage - CHUNK_PERCENTF = 0x0031, // float4 percentage - // ******************************************************************** - - // Prj master chunk - CHUNK_PRJ = 0xC23D, - - // MDLI master chunk - CHUNK_MLI = 0x3DAA, - - // Primary main chunk of the .3ds file - CHUNK_MAIN = 0x4D4D, - - // Mesh main chunk - CHUNK_OBJMESH = 0x3D3D, - - // Specifies the background color of the .3ds file - // This is passed through the material system for - // viewing purposes. - CHUNK_BKGCOLOR = 0x1200, - - // Specifies the ambient base color of the scene. - // This is added to all materials in the file - CHUNK_AMBCOLOR = 0x2100, - - // Specifies the background image for the whole scene - // This value is passed through the material system - // to the viewer - CHUNK_BIT_MAP = 0x1100, - CHUNK_BIT_MAP_EXISTS = 0x1101, - - // ******************************************************************** - // Viewport related stuff. Ignored - CHUNK_DEFAULT_VIEW = 0x3000, - CHUNK_VIEW_TOP = 0x3010, - CHUNK_VIEW_BOTTOM = 0x3020, - CHUNK_VIEW_LEFT = 0x3030, - CHUNK_VIEW_RIGHT = 0x3040, - CHUNK_VIEW_FRONT = 0x3050, - CHUNK_VIEW_BACK = 0x3060, - CHUNK_VIEW_USER = 0x3070, - CHUNK_VIEW_CAMERA = 0x3080, - // ******************************************************************** - - // Mesh chunks - CHUNK_OBJBLOCK = 0x4000, - CHUNK_TRIMESH = 0x4100, - CHUNK_VERTLIST = 0x4110, - CHUNK_VERTFLAGS = 0x4111, - CHUNK_FACELIST = 0x4120, - CHUNK_FACEMAT = 0x4130, - CHUNK_MAPLIST = 0x4140, - CHUNK_SMOOLIST = 0x4150, - CHUNK_TRMATRIX = 0x4160, - CHUNK_MESHCOLOR = 0x4165, - CHUNK_TXTINFO = 0x4170, - CHUNK_LIGHT = 0x4600, - CHUNK_CAMERA = 0x4700, - CHUNK_HIERARCHY = 0x4F00, - - // Specifies the global scaling factor. This is applied - // to the root node's transformation matrix - CHUNK_MASTER_SCALE = 0x0100, - - // ******************************************************************** - // Material chunks - CHUNK_MAT_MATERIAL = 0xAFFF, - - // asciiz containing the name of the material - CHUNK_MAT_MATNAME = 0xA000, - CHUNK_MAT_AMBIENT = 0xA010, // followed by color chunk - CHUNK_MAT_DIFFUSE = 0xA020, // followed by color chunk - CHUNK_MAT_SPECULAR = 0xA030, // followed by color chunk - - // Specifies the shininess of the material - // followed by percentage chunk - CHUNK_MAT_SHININESS = 0xA040, - CHUNK_MAT_SHININESS_PERCENT = 0xA041 , - - // Specifies the shading mode to be used - // followed by a short - CHUNK_MAT_SHADING = 0xA100, - - // NOTE: Emissive color (self illumination) seems not - // to be a color but a single value, type is unknown. - // Make the parser accept both of them. - // followed by percentage chunk (?) - CHUNK_MAT_SELF_ILLUM = 0xA080, - - // Always followed by percentage chunk (?) - CHUNK_MAT_SELF_ILPCT = 0xA084, - - // Always followed by percentage chunk - CHUNK_MAT_TRANSPARENCY = 0xA050, - - // Diffuse texture channel 0 - CHUNK_MAT_TEXTURE = 0xA200, - - // Contains opacity information for each texel - CHUNK_MAT_OPACMAP = 0xA210, - - // Contains a reflection map to be used to reflect - // the environment. This is partially supported. - CHUNK_MAT_REFLMAP = 0xA220, - - // Self Illumination map (emissive colors) - CHUNK_MAT_SELFIMAP = 0xA33d, - - // Bumpmap. Not specified whether it is a heightmap - // or a normal map. Assme it is a heightmap since - // artist normally prefer this format. - CHUNK_MAT_BUMPMAP = 0xA230, - - // Specular map. Seems to influence the specular color - CHUNK_MAT_SPECMAP = 0xA204, - - // Holds shininess data. - CHUNK_MAT_MAT_SHINMAP = 0xA33C, - - // Scaling in U/V direction. - // (need to gen separate UV coordinate set - // and do this by hand) - CHUNK_MAT_MAP_USCALE = 0xA354, - CHUNK_MAT_MAP_VSCALE = 0xA356, - - // Translation in U/V direction. - // (need to gen separate UV coordinate set - // and do this by hand) - CHUNK_MAT_MAP_UOFFSET = 0xA358, - CHUNK_MAT_MAP_VOFFSET = 0xA35a, - - // UV-coordinates rotation around the z-axis - // Assumed to be in radians. - CHUNK_MAT_MAP_ANG = 0xA35C, - - // Tiling flags for 3DS files - CHUNK_MAT_MAP_TILING = 0xa351, - - // Specifies the file name of a texture - CHUNK_MAPFILE = 0xA300, - - // Specifies whether a materail requires two-sided rendering - CHUNK_MAT_TWO_SIDE = 0xA081, - // ******************************************************************** - - // Main keyframer chunk. Contains translation/rotation/scaling data - CHUNK_KEYFRAMER = 0xB000, - - // Supported sub chunks - CHUNK_TRACKINFO = 0xB002, - CHUNK_TRACKOBJNAME = 0xB010, - CHUNK_TRACKDUMMYOBJNAME = 0xB011, - CHUNK_TRACKPIVOT = 0xB013, - CHUNK_TRACKPOS = 0xB020, - CHUNK_TRACKROTATE = 0xB021, - CHUNK_TRACKSCALE = 0xB022, - - // ******************************************************************** - // Keyframes for various other stuff in the file - // Partially ignored - CHUNK_AMBIENTKEY = 0xB001, - CHUNK_TRACKMORPH = 0xB026, - CHUNK_TRACKHIDE = 0xB029, - CHUNK_OBJNUMBER = 0xB030, - CHUNK_TRACKCAMERA = 0xB003, - CHUNK_TRACKFOV = 0xB023, - CHUNK_TRACKROLL = 0xB024, - CHUNK_TRACKCAMTGT = 0xB004, - CHUNK_TRACKLIGHT = 0xB005, - CHUNK_TRACKLIGTGT = 0xB006, - CHUNK_TRACKSPOTL = 0xB007, - CHUNK_FRAMES = 0xB008, - // ******************************************************************** - - // light sub-chunks - CHUNK_DL_OFF = 0x4620, - CHUNK_DL_OUTER_RANGE = 0x465A, - CHUNK_DL_INNER_RANGE = 0x4659, - CHUNK_DL_MULTIPLIER = 0x465B, - CHUNK_DL_EXCLUDE = 0x4654, - CHUNK_DL_ATTENUATE = 0x4625, - CHUNK_DL_SPOTLIGHT = 0x4610, - - // camera sub-chunks - CHUNK_CAM_RANGES = 0x4720 - }; -}; - -// --------------------------------------------------------------------------- -/** Helper structure representing a 3ds mesh face */ -struct Face : public FaceWithSmoothingGroup -{ -}; - -// --------------------------------------------------------------------------- -/** Helper structure representing a texture */ -struct Texture -{ - //! Default constructor - Texture() - : mOffsetU (0.0f) - , mOffsetV (0.0f) - , mScaleU (1.0f) - , mScaleV (1.0f) - , mRotation (0.0f) - , mMapMode (aiTextureMapMode_Wrap) - , iUVSrc (0) - { - mTextureBlend = get_qnan(); - } - - //! Specifies the blend factor for the texture - float mTextureBlend; - - //! Specifies the filename of the texture - std::string mMapName; - - //! Specifies texture coordinate offsets/scaling/rotations - float mOffsetU; - float mOffsetV; - float mScaleU; - float mScaleV; - float mRotation; - - //! Specifies the mapping mode to be used for the texture - aiTextureMapMode mMapMode; - - //! Used internally - bool bPrivate; - int iUVSrc; -}; - -#include "./../include/assimp/Compiler/poppack1.h" - -// --------------------------------------------------------------------------- -/** Helper structure representing a 3ds material */ -struct Material -{ - //! Default constructor. Builds a default name for the material - Material() - : - mDiffuse (0.6f,0.6f,0.6f), // FIX ... we won't want object to be black - mSpecularExponent (0.0f), - mShininessStrength (1.0f), - mShading(Discreet3DS::Gouraud), - mTransparency (1.0f), - mBumpHeight (1.0f), - mTwoSided (false) - { - static int iCnt = 0; - - char szTemp[128]; - sprintf(szTemp,"UNNAMED_%i",iCnt++); - mName = szTemp; - } - - //! Name of the material - std::string mName; - //! Diffuse color of the material - aiColor3D mDiffuse; - //! Specular exponent - float mSpecularExponent; - //! Shininess strength, in percent - float mShininessStrength; - //! Specular color of the material - aiColor3D mSpecular; - //! Ambient color of the material - aiColor3D mAmbient; - //! Shading type to be used - Discreet3DS::shadetype3ds mShading; - //! Opacity of the material - float mTransparency; - //! Diffuse texture channel - Texture sTexDiffuse; - //! Opacity texture channel - Texture sTexOpacity; - //! Specular texture channel - Texture sTexSpecular; - //! Reflective texture channel - Texture sTexReflective; - //! Bump texture channel - Texture sTexBump; - //! Emissive texture channel - Texture sTexEmissive; - //! Shininess texture channel - Texture sTexShininess; - //! Scaling factor for the bump values - float mBumpHeight; - //! Emissive color - aiColor3D mEmissive; - //! Ambient texture channel - //! (used by the ASE format) - Texture sTexAmbient; - //! True if the material must be rendered from two sides - bool mTwoSided; -}; - -// --------------------------------------------------------------------------- -/** Helper structure to represent a 3ds file mesh */ -struct Mesh : public MeshWithSmoothingGroups -{ - //! Default constructor - Mesh() - { - static int iCnt = 0; - - // Generate a default name for the mesh - char szTemp[128]; - ::sprintf(szTemp,"UNNAMED_%i",iCnt++); - mName = szTemp; - } - - //! Name of the mesh - std::string mName; - - //! Texture coordinates - std::vector mTexCoords; - - //! Face materials - std::vector mFaceMaterials; - - //! Local transformation matrix - aiMatrix4x4 mMat; -}; - -// --------------------------------------------------------------------------- -/** Float key - quite similar to aiVectorKey and aiQuatKey. Both are in the - C-API, so it would be difficult to make them a template. */ -struct aiFloatKey -{ - double mTime; ///< The time of this key - float mValue; ///< The value of this key - -#ifdef __cplusplus - - // time is not compared - bool operator == (const aiFloatKey& o) const - {return o.mValue == this->mValue;} - - bool operator != (const aiFloatKey& o) const - {return o.mValue != this->mValue;} - - // Only time is compared. This operator is defined - // for use with std::sort - bool operator < (const aiFloatKey& o) const - {return mTime < o.mTime;} - - bool operator > (const aiFloatKey& o) const - {return mTime > o.mTime;} - -#endif -}; - -// --------------------------------------------------------------------------- -/** Helper structure to represent a 3ds file node */ -struct Node -{ - Node() - - : mHierarchyPos (0) - , mHierarchyIndex (0) - , mInstanceCount (1) - - { - static int iCnt = 0; - - // Generate a default name for the node - char szTemp[128]; - ::sprintf(szTemp,"UNNAMED_%i",iCnt++); - mName = szTemp; - - aRotationKeys.reserve (20); - aPositionKeys.reserve (20); - aScalingKeys.reserve (20); - } - - ~Node() - { - for (unsigned int i = 0; i < mChildren.size();++i) - delete mChildren[i]; - } - - //! Pointer to the parent node - Node* mParent; - - //! Holds all child nodes - std::vector mChildren; - - //! Name of the node - std::string mName; - - //! InstanceNumber of the node - int32_t mInstanceNumber; - - //! Dummy nodes: real name to be combined with the $$$DUMMY - std::string mDummyName; - - //! Position of the node in the hierarchy (tree depth) - int16_t mHierarchyPos; - - //! Index of the node - int16_t mHierarchyIndex; - - //! Rotation keys loaded from the file - std::vector aRotationKeys; - - //! Position keys loaded from the file - std::vector aPositionKeys; - - //! Scaling keys loaded from the file - std::vector aScalingKeys; - - - // For target lights (spot lights and directional lights): - // The position of the target - std::vector< aiVectorKey > aTargetPositionKeys; - - // For cameras: the camera roll angle - std::vector< aiFloatKey > aCameraRollKeys; - - //! Pivot position loaded from the file - aiVector3D vPivot; - - //instance count, will be kept only for the first node - int32_t mInstanceCount; - - //! Add a child node, setup the right parent node for it - //! \param pc Node to be 'adopted' - inline Node& push_back(Node* pc) - { - mChildren.push_back(pc); - pc->mParent = this; - return *this; - } -}; -// --------------------------------------------------------------------------- -/** Helper structure analogue to aiScene */ -struct Scene -{ - //! List of all materials loaded - //! NOTE: 3ds references materials globally - std::vector mMaterials; - - //! List of all meshes loaded - std::vector mMeshes; - - //! List of all cameras loaded - std::vector mCameras; - - //! List of all lights loaded - std::vector mLights; - - //! Pointer to the root node of the scene - // --- moved to main class - // Node* pcRootNode; -}; - - -} // end of namespace D3DS -} // end of namespace Assimp - -#endif // AI_XFILEHELPER_H_INC +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Defines helper data structures for the import of 3DS files */ + +#ifndef AI_3DSFILEHELPER_H_INC +#define AI_3DSFILEHELPER_H_INC + + +#include "SpatialSort.h" +#include "SmoothingGroups.h" +#include "qnan.h" +#include "./../include/assimp/material.h" +#include "./../include/assimp/camera.h" +#include "./../include/assimp/light.h" +#include "./../include/assimp/anim.h" +#include //sprintf + +namespace Assimp { +namespace D3DS { + +#include "./../include/assimp/Compiler/pushpack1.h" + +// --------------------------------------------------------------------------- +/** Discreet3DS class: Helper class for loading 3ds files. Defines chunks +* and data structures. +*/ +class Discreet3DS +{ +private: + inline Discreet3DS() {} + +public: + + //! data structure for a single chunk in a .3ds file + struct Chunk + { + uint16_t Flag; + uint32_t Size; + } PACK_STRUCT; + + + //! Used for shading field in material3ds structure + //! From AutoDesk 3ds SDK + typedef enum + { + // translated to gouraud shading with wireframe active + Wire = 0x0, + + // if this material is set, no vertex normals will + // be calculated for the model. Face normals + gouraud + Flat = 0x1, + + // standard gouraud shading + Gouraud = 0x2, + + // phong shading + Phong = 0x3, + + // cooktorrance or anistropic phong shading ... + // the exact meaning is unknown, if you know it + // feel free to tell me ;-) + Metal = 0x4, + + // required by the ASE loader + Blinn = 0x5 + } shadetype3ds; + + // Flags for animated keys + enum + { + KEY_USE_TENS = 0x1, + KEY_USE_CONT = 0x2, + KEY_USE_BIAS = 0x4, + KEY_USE_EASE_TO = 0x8, + KEY_USE_EASE_FROM = 0x10 + } ; + + enum + { + + // ******************************************************************** + // Basic chunks which can be found everywhere in the file + CHUNK_VERSION = 0x0002, + CHUNK_RGBF = 0x0010, // float4 R; float4 G; float4 B + CHUNK_RGBB = 0x0011, // int1 R; int1 G; int B + + // Linear color values (gamma = 2.2?) + CHUNK_LINRGBF = 0x0013, // float4 R; float4 G; float4 B + CHUNK_LINRGBB = 0x0012, // int1 R; int1 G; int B + + CHUNK_PERCENTW = 0x0030, // int2 percentage + CHUNK_PERCENTF = 0x0031, // float4 percentage + // ******************************************************************** + + // Prj master chunk + CHUNK_PRJ = 0xC23D, + + // MDLI master chunk + CHUNK_MLI = 0x3DAA, + + // Primary main chunk of the .3ds file + CHUNK_MAIN = 0x4D4D, + + // Mesh main chunk + CHUNK_OBJMESH = 0x3D3D, + + // Specifies the background color of the .3ds file + // This is passed through the material system for + // viewing purposes. + CHUNK_BKGCOLOR = 0x1200, + + // Specifies the ambient base color of the scene. + // This is added to all materials in the file + CHUNK_AMBCOLOR = 0x2100, + + // Specifies the background image for the whole scene + // This value is passed through the material system + // to the viewer + CHUNK_BIT_MAP = 0x1100, + CHUNK_BIT_MAP_EXISTS = 0x1101, + + // ******************************************************************** + // Viewport related stuff. Ignored + CHUNK_DEFAULT_VIEW = 0x3000, + CHUNK_VIEW_TOP = 0x3010, + CHUNK_VIEW_BOTTOM = 0x3020, + CHUNK_VIEW_LEFT = 0x3030, + CHUNK_VIEW_RIGHT = 0x3040, + CHUNK_VIEW_FRONT = 0x3050, + CHUNK_VIEW_BACK = 0x3060, + CHUNK_VIEW_USER = 0x3070, + CHUNK_VIEW_CAMERA = 0x3080, + // ******************************************************************** + + // Mesh chunks + CHUNK_OBJBLOCK = 0x4000, + CHUNK_TRIMESH = 0x4100, + CHUNK_VERTLIST = 0x4110, + CHUNK_VERTFLAGS = 0x4111, + CHUNK_FACELIST = 0x4120, + CHUNK_FACEMAT = 0x4130, + CHUNK_MAPLIST = 0x4140, + CHUNK_SMOOLIST = 0x4150, + CHUNK_TRMATRIX = 0x4160, + CHUNK_MESHCOLOR = 0x4165, + CHUNK_TXTINFO = 0x4170, + CHUNK_LIGHT = 0x4600, + CHUNK_CAMERA = 0x4700, + CHUNK_HIERARCHY = 0x4F00, + + // Specifies the global scaling factor. This is applied + // to the root node's transformation matrix + CHUNK_MASTER_SCALE = 0x0100, + + // ******************************************************************** + // Material chunks + CHUNK_MAT_MATERIAL = 0xAFFF, + + // asciiz containing the name of the material + CHUNK_MAT_MATNAME = 0xA000, + CHUNK_MAT_AMBIENT = 0xA010, // followed by color chunk + CHUNK_MAT_DIFFUSE = 0xA020, // followed by color chunk + CHUNK_MAT_SPECULAR = 0xA030, // followed by color chunk + + // Specifies the shininess of the material + // followed by percentage chunk + CHUNK_MAT_SHININESS = 0xA040, + CHUNK_MAT_SHININESS_PERCENT = 0xA041 , + + // Specifies the shading mode to be used + // followed by a short + CHUNK_MAT_SHADING = 0xA100, + + // NOTE: Emissive color (self illumination) seems not + // to be a color but a single value, type is unknown. + // Make the parser accept both of them. + // followed by percentage chunk (?) + CHUNK_MAT_SELF_ILLUM = 0xA080, + + // Always followed by percentage chunk (?) + CHUNK_MAT_SELF_ILPCT = 0xA084, + + // Always followed by percentage chunk + CHUNK_MAT_TRANSPARENCY = 0xA050, + + // Diffuse texture channel 0 + CHUNK_MAT_TEXTURE = 0xA200, + + // Contains opacity information for each texel + CHUNK_MAT_OPACMAP = 0xA210, + + // Contains a reflection map to be used to reflect + // the environment. This is partially supported. + CHUNK_MAT_REFLMAP = 0xA220, + + // Self Illumination map (emissive colors) + CHUNK_MAT_SELFIMAP = 0xA33d, + + // Bumpmap. Not specified whether it is a heightmap + // or a normal map. Assme it is a heightmap since + // artist normally prefer this format. + CHUNK_MAT_BUMPMAP = 0xA230, + + // Specular map. Seems to influence the specular color + CHUNK_MAT_SPECMAP = 0xA204, + + // Holds shininess data. + CHUNK_MAT_MAT_SHINMAP = 0xA33C, + + // Scaling in U/V direction. + // (need to gen separate UV coordinate set + // and do this by hand) + CHUNK_MAT_MAP_USCALE = 0xA354, + CHUNK_MAT_MAP_VSCALE = 0xA356, + + // Translation in U/V direction. + // (need to gen separate UV coordinate set + // and do this by hand) + CHUNK_MAT_MAP_UOFFSET = 0xA358, + CHUNK_MAT_MAP_VOFFSET = 0xA35a, + + // UV-coordinates rotation around the z-axis + // Assumed to be in radians. + CHUNK_MAT_MAP_ANG = 0xA35C, + + // Tiling flags for 3DS files + CHUNK_MAT_MAP_TILING = 0xa351, + + // Specifies the file name of a texture + CHUNK_MAPFILE = 0xA300, + + // Specifies whether a materail requires two-sided rendering + CHUNK_MAT_TWO_SIDE = 0xA081, + // ******************************************************************** + + // Main keyframer chunk. Contains translation/rotation/scaling data + CHUNK_KEYFRAMER = 0xB000, + + // Supported sub chunks + CHUNK_TRACKINFO = 0xB002, + CHUNK_TRACKOBJNAME = 0xB010, + CHUNK_TRACKDUMMYOBJNAME = 0xB011, + CHUNK_TRACKPIVOT = 0xB013, + CHUNK_TRACKPOS = 0xB020, + CHUNK_TRACKROTATE = 0xB021, + CHUNK_TRACKSCALE = 0xB022, + + // ******************************************************************** + // Keyframes for various other stuff in the file + // Partially ignored + CHUNK_AMBIENTKEY = 0xB001, + CHUNK_TRACKMORPH = 0xB026, + CHUNK_TRACKHIDE = 0xB029, + CHUNK_OBJNUMBER = 0xB030, + CHUNK_TRACKCAMERA = 0xB003, + CHUNK_TRACKFOV = 0xB023, + CHUNK_TRACKROLL = 0xB024, + CHUNK_TRACKCAMTGT = 0xB004, + CHUNK_TRACKLIGHT = 0xB005, + CHUNK_TRACKLIGTGT = 0xB006, + CHUNK_TRACKSPOTL = 0xB007, + CHUNK_FRAMES = 0xB008, + // ******************************************************************** + + // light sub-chunks + CHUNK_DL_OFF = 0x4620, + CHUNK_DL_OUTER_RANGE = 0x465A, + CHUNK_DL_INNER_RANGE = 0x4659, + CHUNK_DL_MULTIPLIER = 0x465B, + CHUNK_DL_EXCLUDE = 0x4654, + CHUNK_DL_ATTENUATE = 0x4625, + CHUNK_DL_SPOTLIGHT = 0x4610, + + // camera sub-chunks + CHUNK_CAM_RANGES = 0x4720 + }; +}; + +// --------------------------------------------------------------------------- +/** Helper structure representing a 3ds mesh face */ +struct Face : public FaceWithSmoothingGroup +{ +}; + +// --------------------------------------------------------------------------- +/** Helper structure representing a texture */ +struct Texture +{ + //! Default constructor + Texture() + : mOffsetU (0.0f) + , mOffsetV (0.0f) + , mScaleU (1.0f) + , mScaleV (1.0f) + , mRotation (0.0f) + , mMapMode (aiTextureMapMode_Wrap) + , iUVSrc (0) + { + mTextureBlend = get_qnan(); + } + + //! Specifies the blend factor for the texture + float mTextureBlend; + + //! Specifies the filename of the texture + std::string mMapName; + + //! Specifies texture coordinate offsets/scaling/rotations + float mOffsetU; + float mOffsetV; + float mScaleU; + float mScaleV; + float mRotation; + + //! Specifies the mapping mode to be used for the texture + aiTextureMapMode mMapMode; + + //! Used internally + bool bPrivate; + int iUVSrc; +}; + +#include "./../include/assimp/Compiler/poppack1.h" + +// --------------------------------------------------------------------------- +/** Helper structure representing a 3ds material */ +struct Material +{ + //! Default constructor. Builds a default name for the material + Material() + : + mDiffuse (0.6f,0.6f,0.6f), // FIX ... we won't want object to be black + mSpecularExponent (0.0f), + mShininessStrength (1.0f), + mShading(Discreet3DS::Gouraud), + mTransparency (1.0f), + mBumpHeight (1.0f), + mTwoSided (false) + { + static int iCnt = 0; + + char szTemp[128]; + sprintf(szTemp,"UNNAMED_%i",iCnt++); + mName = szTemp; + } + + //! Name of the material + std::string mName; + //! Diffuse color of the material + aiColor3D mDiffuse; + //! Specular exponent + float mSpecularExponent; + //! Shininess strength, in percent + float mShininessStrength; + //! Specular color of the material + aiColor3D mSpecular; + //! Ambient color of the material + aiColor3D mAmbient; + //! Shading type to be used + Discreet3DS::shadetype3ds mShading; + //! Opacity of the material + float mTransparency; + //! Diffuse texture channel + Texture sTexDiffuse; + //! Opacity texture channel + Texture sTexOpacity; + //! Specular texture channel + Texture sTexSpecular; + //! Reflective texture channel + Texture sTexReflective; + //! Bump texture channel + Texture sTexBump; + //! Emissive texture channel + Texture sTexEmissive; + //! Shininess texture channel + Texture sTexShininess; + //! Scaling factor for the bump values + float mBumpHeight; + //! Emissive color + aiColor3D mEmissive; + //! Ambient texture channel + //! (used by the ASE format) + Texture sTexAmbient; + //! True if the material must be rendered from two sides + bool mTwoSided; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent a 3ds file mesh */ +struct Mesh : public MeshWithSmoothingGroups +{ + //! Default constructor + Mesh() + { + static int iCnt = 0; + + // Generate a default name for the mesh + char szTemp[128]; + ::sprintf(szTemp,"UNNAMED_%i",iCnt++); + mName = szTemp; + } + + //! Name of the mesh + std::string mName; + + //! Texture coordinates + std::vector mTexCoords; + + //! Face materials + std::vector mFaceMaterials; + + //! Local transformation matrix + aiMatrix4x4 mMat; +}; + +// --------------------------------------------------------------------------- +/** Float key - quite similar to aiVectorKey and aiQuatKey. Both are in the + C-API, so it would be difficult to make them a template. */ +struct aiFloatKey +{ + double mTime; ///< The time of this key + float mValue; ///< The value of this key + +#ifdef __cplusplus + + // time is not compared + bool operator == (const aiFloatKey& o) const + {return o.mValue == this->mValue;} + + bool operator != (const aiFloatKey& o) const + {return o.mValue != this->mValue;} + + // Only time is compared. This operator is defined + // for use with std::sort + bool operator < (const aiFloatKey& o) const + {return mTime < o.mTime;} + + bool operator > (const aiFloatKey& o) const + {return mTime > o.mTime;} + +#endif +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent a 3ds file node */ +struct Node +{ + Node() + + : mHierarchyPos (0) + , mHierarchyIndex (0) + , mInstanceCount (1) + + { + static int iCnt = 0; + + // Generate a default name for the node + char szTemp[128]; + ::sprintf(szTemp,"UNNAMED_%i",iCnt++); + mName = szTemp; + + aRotationKeys.reserve (20); + aPositionKeys.reserve (20); + aScalingKeys.reserve (20); + } + + ~Node() + { + for (unsigned int i = 0; i < mChildren.size();++i) + delete mChildren[i]; + } + + //! Pointer to the parent node + Node* mParent; + + //! Holds all child nodes + std::vector mChildren; + + //! Name of the node + std::string mName; + + //! InstanceNumber of the node + int32_t mInstanceNumber; + + //! Dummy nodes: real name to be combined with the $$$DUMMY + std::string mDummyName; + + //! Position of the node in the hierarchy (tree depth) + int16_t mHierarchyPos; + + //! Index of the node + int16_t mHierarchyIndex; + + //! Rotation keys loaded from the file + std::vector aRotationKeys; + + //! Position keys loaded from the file + std::vector aPositionKeys; + + //! Scaling keys loaded from the file + std::vector aScalingKeys; + + + // For target lights (spot lights and directional lights): + // The position of the target + std::vector< aiVectorKey > aTargetPositionKeys; + + // For cameras: the camera roll angle + std::vector< aiFloatKey > aCameraRollKeys; + + //! Pivot position loaded from the file + aiVector3D vPivot; + + //instance count, will be kept only for the first node + int32_t mInstanceCount; + + //! Add a child node, setup the right parent node for it + //! \param pc Node to be 'adopted' + inline Node& push_back(Node* pc) + { + mChildren.push_back(pc); + pc->mParent = this; + return *this; + } +}; +// --------------------------------------------------------------------------- +/** Helper structure analogue to aiScene */ +struct Scene +{ + //! List of all materials loaded + //! NOTE: 3ds references materials globally + std::vector mMaterials; + + //! List of all meshes loaded + std::vector mMeshes; + + //! List of all cameras loaded + std::vector mCameras; + + //! List of all lights loaded + std::vector mLights; + + //! Pointer to the root node of the scene + // --- moved to main class + // Node* pcRootNode; +}; + + +} // end of namespace D3DS +} // end of namespace Assimp + +#endif // AI_XFILEHELPER_H_INC diff --git a/code/3DSLoader.cpp b/code/3DSLoader.cpp index a9b979f6e..3be5f291c 100644 --- a/code/3DSLoader.cpp +++ b/code/3DSLoader.cpp @@ -1,1414 +1,1414 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file 3DSLoader.cpp - * @brief Implementation of the 3ds importer class - * - * http://www.the-labs.com/Blender/3DS-details.html - */ - - -#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER - -// internal headers -#include "3DSLoader.h" -#include "Macros.h" -#include "../include/assimp/IOSystem.hpp" -#include "../include/assimp/scene.h" -#include "../include/assimp/DefaultLogger.hpp" -#include "StringComparison.h" - -using namespace Assimp; - -static const aiImporterDesc desc = { - "Discreet 3DS Importer", - "", - "", - "Limited animation support", - aiImporterFlags_SupportBinaryFlavour, - 0, - 0, - 0, - 0, - "3ds prj" -}; - - -// ------------------------------------------------------------------------------------------------ -// Begins a new parsing block -// - Reads the current chunk and validates it -// - computes its length -#define ASSIMP_3DS_BEGIN_CHUNK() \ - while (true) { \ - if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)){ \ - return; \ - } \ - Discreet3DS::Chunk chunk; \ - ReadChunk(&chunk); \ - int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \ - if(chunkSize <= 0) \ - continue; \ - const int oldReadLimit = stream->GetReadLimit(); \ - stream->SetReadLimit(stream->GetCurrentPos() + chunkSize); \ - - -// ------------------------------------------------------------------------------------------------ -// End a parsing block -// Must follow at the end of each parsing block, reset chunk end marker to previous value -#define ASSIMP_3DS_END_CHUNK() \ - stream->SkipToReadLimit(); \ - stream->SetReadLimit(oldReadLimit); \ - if (stream->GetRemainingSizeToLimit() == 0) \ - return; \ - } - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -Discreet3DSImporter::Discreet3DSImporter() -{} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -Discreet3DSImporter::~Discreet3DSImporter() -{} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. -bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ - std::string extension = GetExtension(pFile); - if(extension == "3ds" || extension == "prj" ) { - return true; - } - if (!extension.length() || checkSig) { - uint16_t token[3]; - token[0] = 0x4d4d; - token[1] = 0x3dc2; - //token[2] = 0x3daa; - return CheckMagicToken(pIOHandler,pFile,token,2,0,2); - } - return false; -} - -// ------------------------------------------------------------------------------------------------ -// Loader registry entry -const aiImporterDesc* Discreet3DSImporter::GetInfo () const -{ - return &desc; -} - -// ------------------------------------------------------------------------------------------------ -// Setup configuration properties -void Discreet3DSImporter::SetupProperties(const Importer* /*pImp*/) -{ - // nothing to be done for the moment -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void Discreet3DSImporter::InternReadFile( const std::string& pFile, - aiScene* pScene, IOSystem* pIOHandler) -{ - StreamReaderLE stream(pIOHandler->Open(pFile,"rb")); - this->stream = &stream; - - // We should have at least one chunk - if (stream.GetRemainingSize() < 16) { - throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile); - } - - // Allocate our temporary 3DS representation - mScene = new D3DS::Scene(); - - // Initialize members - mLastNodeIndex = -1; - mCurrentNode = new D3DS::Node(); - mRootNode = mCurrentNode; - mRootNode->mHierarchyPos = -1; - mRootNode->mHierarchyIndex = -1; - mRootNode->mParent = NULL; - mMasterScale = 1.0f; - mBackgroundImage = ""; - bHasBG = false; - bIsPrj = false; - - // Parse the file - ParseMainChunk(); - - // Process all meshes in the file. First check whether all - // face indices haev valid values. The generate our - // internal verbose representation. Finally compute normal - // vectors from the smoothing groups we read from the - // file. - for (std::vector::iterator i = mScene->mMeshes.begin(), - end = mScene->mMeshes.end(); i != end;++i) { - if ((*i).mFaces.size() > 0 && (*i).mPositions.size() == 0) { - delete mScene; - throw DeadlyImportError("3DS file contains faces but no vertices: " + pFile); - } - CheckIndices(*i); - MakeUnique (*i); - ComputeNormalsWithSmoothingsGroups(*i); - } - - // Replace all occurences of the default material with a - // valid material. Generate it if no material containing - // DEFAULT in its name has been found in the file - ReplaceDefaultMaterial(); - - // Convert the scene from our internal representation to an - // aiScene object. This involves copying all meshes, lights - // and cameras to the scene - ConvertScene(pScene); - - // Generate the node graph for the scene. This is a little bit - // tricky since we'll need to split some meshes into submeshes - GenerateNodeGraph(pScene); - - // Now apply the master scaling factor to the scene - ApplyMasterScale(pScene); - - // Delete our internal scene representation and the root - // node, so the whole hierarchy will follow - delete mRootNode; - delete mScene; - - AI_DEBUG_INVALIDATE_PTR(mRootNode); - AI_DEBUG_INVALIDATE_PTR(mScene); - AI_DEBUG_INVALIDATE_PTR(this->stream); -} - -// ------------------------------------------------------------------------------------------------ -// Applies a master-scaling factor to the imported scene -void Discreet3DSImporter::ApplyMasterScale(aiScene* pScene) -{ - // There are some 3DS files with a zero scaling factor - if (!mMasterScale)mMasterScale = 1.0f; - else mMasterScale = 1.0f / mMasterScale; - - // Construct an uniform scaling matrix and multiply with it - pScene->mRootNode->mTransformation *= aiMatrix4x4( - mMasterScale,0.0f, 0.0f, 0.0f, - 0.0f, mMasterScale,0.0f, 0.0f, - 0.0f, 0.0f, mMasterScale,0.0f, - 0.0f, 0.0f, 0.0f, 1.0f); - - // Check whether a scaling track is assigned to the root node. -} - -// ------------------------------------------------------------------------------------------------ -// Reads a new chunk from the file -void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk* pcOut) -{ - ai_assert(pcOut != NULL); - - pcOut->Flag = stream->GetI2(); - pcOut->Size = stream->GetI4(); - - if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize()) - throw DeadlyImportError("Chunk is too large"); - - if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit()) - DefaultLogger::get()->error("3DS: Chunk overflow"); -} - -// ------------------------------------------------------------------------------------------------ -// Skip a chunk -void Discreet3DSImporter::SkipChunk() -{ - Discreet3DS::Chunk psChunk; - ReadChunk(&psChunk); - - stream->IncPtr(psChunk.Size-sizeof(Discreet3DS::Chunk)); - return; -} - -// ------------------------------------------------------------------------------------------------ -// Process the primary chunk of the file -void Discreet3DSImporter::ParseMainChunk() -{ - ASSIMP_3DS_BEGIN_CHUNK(); - - // get chunk type - switch (chunk.Flag) - { - - case Discreet3DS::CHUNK_PRJ: - bIsPrj = true; - case Discreet3DS::CHUNK_MAIN: - ParseEditorChunk(); - break; - }; - - ASSIMP_3DS_END_CHUNK(); - // recursively continue processing this hierarchy level - return ParseMainChunk(); -} - -// ------------------------------------------------------------------------------------------------ -void Discreet3DSImporter::ParseEditorChunk() -{ - ASSIMP_3DS_BEGIN_CHUNK(); - - // get chunk type - switch (chunk.Flag) - { - case Discreet3DS::CHUNK_OBJMESH: - - ParseObjectChunk(); - break; - - // NOTE: In several documentations in the internet this - // chunk appears at different locations - case Discreet3DS::CHUNK_KEYFRAMER: - - ParseKeyframeChunk(); - break; - - case Discreet3DS::CHUNK_VERSION: - { - // print the version number - char buff[10]; - ASSIMP_itoa10(buff,stream->GetI2()); - DefaultLogger::get()->info(std::string("3DS file format version: ") + buff); - } - break; - }; - ASSIMP_3DS_END_CHUNK(); -} - -// ------------------------------------------------------------------------------------------------ -void Discreet3DSImporter::ParseObjectChunk() -{ - ASSIMP_3DS_BEGIN_CHUNK(); - - // get chunk type - switch (chunk.Flag) - { - case Discreet3DS::CHUNK_OBJBLOCK: - { - unsigned int cnt = 0; - const char* sz = (const char*)stream->GetPtr(); - - // Get the name of the geometry object - while (stream->GetI1())++cnt; - ParseChunk(sz,cnt); - } - break; - - case Discreet3DS::CHUNK_MAT_MATERIAL: - - // Add a new material to the list - mScene->mMaterials.push_back(D3DS::Material()); - ParseMaterialChunk(); - break; - - case Discreet3DS::CHUNK_AMBCOLOR: - - // This is the ambient base color of the scene. - // We add it to the ambient color of all materials - ParseColorChunk(&mClrAmbient,true); - if (is_qnan(mClrAmbient.r)) - { - // We failed to read the ambient base color. - DefaultLogger::get()->error("3DS: Failed to read ambient base color"); - mClrAmbient.r = mClrAmbient.g = mClrAmbient.b = 0.0f; - } - break; - - case Discreet3DS::CHUNK_BIT_MAP: - { - // Specifies the background image. The string should already be - // properly 0 terminated but we need to be sure - unsigned int cnt = 0; - const char* sz = (const char*)stream->GetPtr(); - while (stream->GetI1())++cnt; - mBackgroundImage = std::string(sz,cnt); - } - break; - - case Discreet3DS::CHUNK_BIT_MAP_EXISTS: - bHasBG = true; - break; - - case Discreet3DS::CHUNK_MASTER_SCALE: - // Scene master scaling factor - mMasterScale = stream->GetF4(); - break; - }; - ASSIMP_3DS_END_CHUNK(); -} - -// ------------------------------------------------------------------------------------------------ -void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num) -{ - ASSIMP_3DS_BEGIN_CHUNK(); - - // IMPLEMENTATION NOTE; - // Cameras or lights define their transformation in their parent node and in the - // corresponding light or camera chunks. However, we read and process the latter - // to to be able to return valid cameras/lights even if no scenegraph is given. - - // get chunk type - switch (chunk.Flag) - { - case Discreet3DS::CHUNK_TRIMESH: - { - // this starts a new triangle mesh - mScene->mMeshes.push_back(D3DS::Mesh()); - D3DS::Mesh& m = mScene->mMeshes.back(); - - // Setup the name of the mesh - m.mName = std::string(name, num); - - // Read mesh chunks - ParseMeshChunk(); - } - break; - - case Discreet3DS::CHUNK_LIGHT: - { - // This starts a new light - aiLight* light = new aiLight(); - mScene->mLights.push_back(light); - - light->mName.Set(std::string(name, num)); - - // First read the position of the light - light->mPosition.x = stream->GetF4(); - light->mPosition.y = stream->GetF4(); - light->mPosition.z = stream->GetF4(); - - light->mColorDiffuse = aiColor3D(1.f,1.f,1.f); - - // Now check for further subchunks - if (!bIsPrj) /* fixme */ - ParseLightChunk(); - - // The specular light color is identical the the diffuse light color. The ambient light color - // is equal to the ambient base color of the whole scene. - light->mColorSpecular = light->mColorDiffuse; - light->mColorAmbient = mClrAmbient; - - if (light->mType == aiLightSource_UNDEFINED) - { - // It must be a point light - light->mType = aiLightSource_POINT; - }} - break; - - case Discreet3DS::CHUNK_CAMERA: - { - // This starts a new camera - aiCamera* camera = new aiCamera(); - mScene->mCameras.push_back(camera); - camera->mName.Set(std::string(name, num)); - - // First read the position of the camera - camera->mPosition.x = stream->GetF4(); - camera->mPosition.y = stream->GetF4(); - camera->mPosition.z = stream->GetF4(); - - // Then the camera target - camera->mLookAt.x = stream->GetF4() - camera->mPosition.x; - camera->mLookAt.y = stream->GetF4() - camera->mPosition.y; - camera->mLookAt.z = stream->GetF4() - camera->mPosition.z; - float len = camera->mLookAt.Length(); - if (len < 1e-5f) { - - // There are some files with lookat == position. Don't know why or whether it's ok or not. - DefaultLogger::get()->error("3DS: Unable to read proper camera look-at vector"); - camera->mLookAt = aiVector3D(0.f,1.f,0.f); - - } - else camera->mLookAt /= len; - - // And finally - the camera rotation angle, in counter clockwise direction - const float angle = AI_DEG_TO_RAD( stream->GetF4() ); - aiQuaternion quat(camera->mLookAt,angle); - camera->mUp = quat.GetMatrix() * aiVector3D(0.f,1.f,0.f); - - // Read the lense angle - camera->mHorizontalFOV = AI_DEG_TO_RAD ( stream->GetF4() ); - if (camera->mHorizontalFOV < 0.001f) { - camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f); - } - - // Now check for further subchunks - if (!bIsPrj) /* fixme */ { - ParseCameraChunk(); - }} - break; - }; - ASSIMP_3DS_END_CHUNK(); -} - -// ------------------------------------------------------------------------------------------------ -void Discreet3DSImporter::ParseLightChunk() -{ - ASSIMP_3DS_BEGIN_CHUNK(); - aiLight* light = mScene->mLights.back(); - - // get chunk type - switch (chunk.Flag) - { - case Discreet3DS::CHUNK_DL_SPOTLIGHT: - // Now we can be sure that the light is a spot light - light->mType = aiLightSource_SPOT; - - // We wouldn't need to normalize here, but we do it - light->mDirection.x = stream->GetF4() - light->mPosition.x; - light->mDirection.y = stream->GetF4() - light->mPosition.y; - light->mDirection.z = stream->GetF4() - light->mPosition.z; - light->mDirection.Normalize(); - - // Now the hotspot and falloff angles - in degrees - light->mAngleInnerCone = AI_DEG_TO_RAD( stream->GetF4() ); - - // FIX: the falloff angle is just an offset - light->mAngleOuterCone = light->mAngleInnerCone+AI_DEG_TO_RAD( stream->GetF4() ); - break; - - // intensity multiplier - case Discreet3DS::CHUNK_DL_MULTIPLIER: - light->mColorDiffuse = light->mColorDiffuse * stream->GetF4(); - break; - - // light color - case Discreet3DS::CHUNK_RGBF: - case Discreet3DS::CHUNK_LINRGBF: - light->mColorDiffuse.r *= stream->GetF4(); - light->mColorDiffuse.g *= stream->GetF4(); - light->mColorDiffuse.b *= stream->GetF4(); - break; - - // light attenuation - case Discreet3DS::CHUNK_DL_ATTENUATE: - light->mAttenuationLinear = stream->GetF4(); - break; - }; - - ASSIMP_3DS_END_CHUNK(); -} - -// ------------------------------------------------------------------------------------------------ -void Discreet3DSImporter::ParseCameraChunk() -{ - ASSIMP_3DS_BEGIN_CHUNK(); - aiCamera* camera = mScene->mCameras.back(); - - // get chunk type - switch (chunk.Flag) - { - // near and far clip plane - case Discreet3DS::CHUNK_CAM_RANGES: - camera->mClipPlaneNear = stream->GetF4(); - camera->mClipPlaneFar = stream->GetF4(); - break; - } - - ASSIMP_3DS_END_CHUNK(); -} - -// ------------------------------------------------------------------------------------------------ -void Discreet3DSImporter::ParseKeyframeChunk() -{ - ASSIMP_3DS_BEGIN_CHUNK(); - - // get chunk type - switch (chunk.Flag) - { - case Discreet3DS::CHUNK_TRACKCAMTGT: - case Discreet3DS::CHUNK_TRACKSPOTL: - case Discreet3DS::CHUNK_TRACKCAMERA: - case Discreet3DS::CHUNK_TRACKINFO: - case Discreet3DS::CHUNK_TRACKLIGHT: - case Discreet3DS::CHUNK_TRACKLIGTGT: - - // this starts a new mesh hierarchy chunk - ParseHierarchyChunk(chunk.Flag); - break; - }; - - ASSIMP_3DS_END_CHUNK(); -} - -// ------------------------------------------------------------------------------------------------ -// Little helper function for ParseHierarchyChunk -void Discreet3DSImporter::InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent) -{ - if (!pcCurrent) { - mRootNode->push_back(pcNode); - return; - } - - if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos) { - if(pcCurrent->mParent) { - pcCurrent->mParent->push_back(pcNode); - } - else pcCurrent->push_back(pcNode); - return; - } - return InverseNodeSearch(pcNode,pcCurrent->mParent); -} - -// ------------------------------------------------------------------------------------------------ -// Find a node with a specific name in the import hierarchy -D3DS::Node* FindNode(D3DS::Node* root, const std::string& name) -{ - if (root->mName == name) - return root; - for (std::vector::iterator it = root->mChildren.begin();it != root->mChildren.end(); ++it) { - D3DS::Node* nd; - if (( nd = FindNode(*it,name))) - return nd; - } - return NULL; -} - -// ------------------------------------------------------------------------------------------------ -// Binary predicate for std::unique() -template -bool KeyUniqueCompare(const T& first, const T& second) -{ - return first.mTime == second.mTime; -} - -// ------------------------------------------------------------------------------------------------ -// Skip some additional import data. -void Discreet3DSImporter::SkipTCBInfo() -{ - unsigned int flags = stream->GetI2(); - - if (!flags) { - // Currently we can't do anything with these values. They occur - // quite rare, so it wouldn't be worth the effort implementing - // them. 3DS ist not really suitable for complex animations, - // so full support is not required. - DefaultLogger::get()->warn("3DS: Skipping TCB animation info"); - } - - if (flags & Discreet3DS::KEY_USE_TENS) { - stream->IncPtr(4); - } - if (flags & Discreet3DS::KEY_USE_BIAS) { - stream->IncPtr(4); - } - if (flags & Discreet3DS::KEY_USE_CONT) { - stream->IncPtr(4); - } - if (flags & Discreet3DS::KEY_USE_EASE_FROM) { - stream->IncPtr(4); - } - if (flags & Discreet3DS::KEY_USE_EASE_TO) { - stream->IncPtr(4); - } -} - -// ------------------------------------------------------------------------------------------------ -// Read hierarchy and keyframe info -void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent) -{ - ASSIMP_3DS_BEGIN_CHUNK(); - - // get chunk type - switch (chunk.Flag) - { - case Discreet3DS::CHUNK_TRACKOBJNAME: - - // This is the name of the object to which the track applies. The chunk also - // defines the position of this object in the hierarchy. - { - - // First of all: get the name of the object - unsigned int cnt = 0; - const char* sz = (const char*)stream->GetPtr(); - - while (stream->GetI1())++cnt; - std::string name = std::string(sz,cnt); - - // Now find out whether we have this node already (target animation channels - // are stored with a separate object ID) - D3DS::Node* pcNode = FindNode(mRootNode,name); - int instanceNumber = 1; - - if ( pcNode) - { - // if the source is not a CHUNK_TRACKINFO block it wont be an object instance - if (parent != Discreet3DS::CHUNK_TRACKINFO) - { - mCurrentNode = pcNode; - break; - } - pcNode->mInstanceCount++; - instanceNumber = pcNode->mInstanceCount; - } - pcNode = new D3DS::Node(); - pcNode->mName = name; - pcNode->mInstanceNumber = instanceNumber; - - // There are two unknown values which we can safely ignore - stream->IncPtr(4); - - // Now read the hierarchy position of the object - uint16_t hierarchy = stream->GetI2() + 1; - pcNode->mHierarchyPos = hierarchy; - pcNode->mHierarchyIndex = mLastNodeIndex; - - // And find a proper position in the graph for it - if (mCurrentNode && mCurrentNode->mHierarchyPos == hierarchy) { - - // add to the parent of the last touched node - mCurrentNode->mParent->push_back(pcNode); - mLastNodeIndex++; - } - else if(hierarchy >= mLastNodeIndex) { - - // place it at the current position in the hierarchy - mCurrentNode->push_back(pcNode); - mLastNodeIndex = hierarchy; - } - else { - // need to go back to the specified position in the hierarchy. - InverseNodeSearch(pcNode,mCurrentNode); - mLastNodeIndex++; - } - // Make this node the current node - mCurrentNode = pcNode; - } - break; - - case Discreet3DS::CHUNK_TRACKDUMMYOBJNAME: - - // This is the "real" name of a $$$DUMMY object - { - const char* sz = (const char*) stream->GetPtr(); - while (stream->GetI1()); - - // If object name is DUMMY, take this one instead - if (mCurrentNode->mName == "$$$DUMMY") { - //DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object"); - mCurrentNode->mName = std::string(sz); - break; - } - } - break; - - case Discreet3DS::CHUNK_TRACKPIVOT: - - if ( Discreet3DS::CHUNK_TRACKINFO != parent) - { - DefaultLogger::get()->warn("3DS: Skipping pivot subchunk for non usual object"); - break; - } - - // Pivot = origin of rotation and scaling - mCurrentNode->vPivot.x = stream->GetF4(); - mCurrentNode->vPivot.y = stream->GetF4(); - mCurrentNode->vPivot.z = stream->GetF4(); - break; - - - // //////////////////////////////////////////////////////////////////// - // POSITION KEYFRAME - case Discreet3DS::CHUNK_TRACKPOS: - { - stream->IncPtr(10); - const unsigned int numFrames = stream->GetI4(); - bool sortKeys = false; - - // This could also be meant as the target position for - // (targeted) lights and cameras - std::vector* l; - if ( Discreet3DS::CHUNK_TRACKCAMTGT == parent || Discreet3DS::CHUNK_TRACKLIGTGT == parent) { - l = & mCurrentNode->aTargetPositionKeys; - } - else l = & mCurrentNode->aPositionKeys; - - l->reserve(numFrames); - for (unsigned int i = 0; i < numFrames;++i) { - const unsigned int fidx = stream->GetI4(); - - // Setup a new position key - aiVectorKey v; - v.mTime = (double)fidx; - - SkipTCBInfo(); - v.mValue.x = stream->GetF4(); - v.mValue.y = stream->GetF4(); - v.mValue.z = stream->GetF4(); - - // check whether we'll need to sort the keys - if (!l->empty() && v.mTime <= l->back().mTime) - sortKeys = true; - - // Add the new keyframe to the list - l->push_back(v); - } - - // Sort all keys with ascending time values and remove duplicates? - if (sortKeys) { - std::stable_sort(l->begin(),l->end()); - l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); - }} - - break; - - // //////////////////////////////////////////////////////////////////// - // CAMERA ROLL KEYFRAME - case Discreet3DS::CHUNK_TRACKROLL: - { - // roll keys are accepted for cameras only - if (parent != Discreet3DS::CHUNK_TRACKCAMERA) { - DefaultLogger::get()->warn("3DS: Ignoring roll track for non-camera object"); - break; - } - bool sortKeys = false; - std::vector* l = &mCurrentNode->aCameraRollKeys; - - stream->IncPtr(10); - const unsigned int numFrames = stream->GetI4(); - l->reserve(numFrames); - for (unsigned int i = 0; i < numFrames;++i) { - const unsigned int fidx = stream->GetI4(); - - // Setup a new position key - aiFloatKey v; - v.mTime = (double)fidx; - - // This is just a single float - SkipTCBInfo(); - v.mValue = stream->GetF4(); - - // Check whether we'll need to sort the keys - if (!l->empty() && v.mTime <= l->back().mTime) - sortKeys = true; - - // Add the new keyframe to the list - l->push_back(v); - } - - // Sort all keys with ascending time values and remove duplicates? - if (sortKeys) { - std::stable_sort(l->begin(),l->end()); - l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); - }} - break; - - - // //////////////////////////////////////////////////////////////////// - // CAMERA FOV KEYFRAME - case Discreet3DS::CHUNK_TRACKFOV: - { - DefaultLogger::get()->error("3DS: Skipping FOV animation track. " - "This is not supported"); - } - break; - - - // //////////////////////////////////////////////////////////////////// - // ROTATION KEYFRAME - case Discreet3DS::CHUNK_TRACKROTATE: - { - stream->IncPtr(10); - const unsigned int numFrames = stream->GetI4(); - - bool sortKeys = false; - std::vector* l = &mCurrentNode->aRotationKeys; - l->reserve(numFrames); - - for (unsigned int i = 0; i < numFrames;++i) { - const unsigned int fidx = stream->GetI4(); - SkipTCBInfo(); - - aiQuatKey v; - v.mTime = (double)fidx; - - // The rotation keyframe is given as an axis-angle pair - const float rad = stream->GetF4(); - aiVector3D axis; - axis.x = stream->GetF4(); - axis.y = stream->GetF4(); - axis.z = stream->GetF4(); - - if (!axis.x && !axis.y && !axis.z) - axis.y = 1.f; - - // Construct a rotation quaternion from the axis-angle pair - v.mValue = aiQuaternion(axis,rad); - - // Check whether we'll need to sort the keys - if (!l->empty() && v.mTime <= l->back().mTime) - sortKeys = true; - - // add the new keyframe to the list - l->push_back(v); - } - // Sort all keys with ascending time values and remove duplicates? - if (sortKeys) { - std::stable_sort(l->begin(),l->end()); - l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); - }} - break; - - // //////////////////////////////////////////////////////////////////// - // SCALING KEYFRAME - case Discreet3DS::CHUNK_TRACKSCALE: - { - stream->IncPtr(10); - const unsigned int numFrames = stream->GetI2(); - stream->IncPtr(2); - - bool sortKeys = false; - std::vector* l = &mCurrentNode->aScalingKeys; - l->reserve(numFrames); - - for (unsigned int i = 0; i < numFrames;++i) { - const unsigned int fidx = stream->GetI4(); - SkipTCBInfo(); - - // Setup a new key - aiVectorKey v; - v.mTime = (double)fidx; - - // ... and read its value - v.mValue.x = stream->GetF4(); - v.mValue.y = stream->GetF4(); - v.mValue.z = stream->GetF4(); - - // check whether we'll need to sort the keys - if (!l->empty() && v.mTime <= l->back().mTime) - sortKeys = true; - - // Remove zero-scalings on singular axes - they've been reported to be there erroneously in some strange files - if (!v.mValue.x) v.mValue.x = 1.f; - if (!v.mValue.y) v.mValue.y = 1.f; - if (!v.mValue.z) v.mValue.z = 1.f; - - l->push_back(v); - } - // Sort all keys with ascending time values and remove duplicates? - if (sortKeys) { - std::stable_sort(l->begin(),l->end()); - l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); - }} - break; - }; - - ASSIMP_3DS_END_CHUNK(); -} - -// ------------------------------------------------------------------------------------------------ -// Read a face chunk - it contains smoothing groups and material assignments -void Discreet3DSImporter::ParseFaceChunk() -{ - ASSIMP_3DS_BEGIN_CHUNK(); - - // Get the mesh we're currently working on - D3DS::Mesh& mMesh = mScene->mMeshes.back(); - - // Get chunk type - switch (chunk.Flag) - { - case Discreet3DS::CHUNK_SMOOLIST: - { - // This is the list of smoothing groups - a bitfield for every face. - // Up to 32 smoothing groups assigned to a single face. - unsigned int num = chunkSize/4, m = 0; - if (num > mMesh.mFaces.size()) { - throw DeadlyImportError("3DS: More smoothing groups than faces"); - } - for (std::vector::iterator i = mMesh.mFaces.begin(); m != num;++i, ++m) { - // nth bit is set for nth smoothing group - (*i).iSmoothGroup = stream->GetI4(); - }} - break; - - case Discreet3DS::CHUNK_FACEMAT: - { - // at fist an asciiz with the material name - const char* sz = (const char*)stream->GetPtr(); - while (stream->GetI1()); - - // find the index of the material - unsigned int idx = 0xcdcdcdcd, cnt = 0; - for (std::vector::const_iterator i = mScene->mMaterials.begin();i != mScene->mMaterials.end();++i,++cnt) { - // use case independent comparisons. hopefully it will work. - if ((*i).mName.length() && !ASSIMP_stricmp(sz, (*i).mName.c_str())) { - idx = cnt; - break; - } - } - if (0xcdcdcdcd == idx) { - DefaultLogger::get()->error(std::string("3DS: Unknown material: ") + sz); - } - - // Now continue and read all material indices - cnt = (uint16_t)stream->GetI2(); - for (unsigned int i = 0; i < cnt;++i) { - unsigned int fidx = (uint16_t)stream->GetI2(); - - // check range - if (fidx >= mMesh.mFaceMaterials.size()) { - DefaultLogger::get()->error("3DS: Invalid face index in face material list"); - } - else mMesh.mFaceMaterials[fidx] = idx; - }} - break; - }; - ASSIMP_3DS_END_CHUNK(); -} - -// ------------------------------------------------------------------------------------------------ -// Read a mesh chunk. Here's the actual mesh data -void Discreet3DSImporter::ParseMeshChunk() -{ - ASSIMP_3DS_BEGIN_CHUNK(); - - // Get the mesh we're currently working on - D3DS::Mesh& mMesh = mScene->mMeshes.back(); - - // get chunk type - switch (chunk.Flag) - { - case Discreet3DS::CHUNK_VERTLIST: - { - // This is the list of all vertices in the current mesh - int num = (int)(uint16_t)stream->GetI2(); - mMesh.mPositions.reserve(num); - while (num-- > 0) { - aiVector3D v; - v.x = stream->GetF4(); - v.y = stream->GetF4(); - v.z = stream->GetF4(); - mMesh.mPositions.push_back(v); - }} - break; - case Discreet3DS::CHUNK_TRMATRIX: - { - // This is the RLEATIVE transformation matrix of the current mesh. Vertices are - // pretransformed by this matrix wonder. - mMesh.mMat.a1 = stream->GetF4(); - mMesh.mMat.b1 = stream->GetF4(); - mMesh.mMat.c1 = stream->GetF4(); - mMesh.mMat.a2 = stream->GetF4(); - mMesh.mMat.b2 = stream->GetF4(); - mMesh.mMat.c2 = stream->GetF4(); - mMesh.mMat.a3 = stream->GetF4(); - mMesh.mMat.b3 = stream->GetF4(); - mMesh.mMat.c3 = stream->GetF4(); - mMesh.mMat.a4 = stream->GetF4(); - mMesh.mMat.b4 = stream->GetF4(); - mMesh.mMat.c4 = stream->GetF4(); - } - break; - - case Discreet3DS::CHUNK_MAPLIST: - { - // This is the list of all UV coords in the current mesh - int num = (int)(uint16_t)stream->GetI2(); - mMesh.mTexCoords.reserve(num); - while (num-- > 0) { - aiVector3D v; - v.x = stream->GetF4(); - v.y = stream->GetF4(); - mMesh.mTexCoords.push_back(v); - }} - break; - - case Discreet3DS::CHUNK_FACELIST: - { - // This is the list of all faces in the current mesh - int num = (int)(uint16_t)stream->GetI2(); - mMesh.mFaces.reserve(num); - while (num-- > 0) { - // 3DS faces are ALWAYS triangles - mMesh.mFaces.push_back(D3DS::Face()); - D3DS::Face& sFace = mMesh.mFaces.back(); - - sFace.mIndices[0] = (uint16_t)stream->GetI2(); - sFace.mIndices[1] = (uint16_t)stream->GetI2(); - sFace.mIndices[2] = (uint16_t)stream->GetI2(); - - stream->IncPtr(2); // skip edge visibility flag - } - - // Resize the material array (0xcdcdcdcd marks the default material; so if a face is - // not referenced by a material, $$DEFAULT will be assigned to it) - mMesh.mFaceMaterials.resize(mMesh.mFaces.size(),0xcdcdcdcd); - - // Larger 3DS files could have multiple FACE chunks here - chunkSize = stream->GetRemainingSizeToLimit(); - if ( chunkSize > (int) sizeof(Discreet3DS::Chunk ) ) - ParseFaceChunk(); - } - break; - }; - ASSIMP_3DS_END_CHUNK(); -} - -// ------------------------------------------------------------------------------------------------ -// Read a 3DS material chunk -void Discreet3DSImporter::ParseMaterialChunk() -{ - ASSIMP_3DS_BEGIN_CHUNK(); - switch (chunk.Flag) - { - case Discreet3DS::CHUNK_MAT_MATNAME: - - { - // The material name string is already zero-terminated, but we need to be sure ... - const char* sz = (const char*)stream->GetPtr(); - unsigned int cnt = 0; - while (stream->GetI1()) - ++cnt; - - if (!cnt) { - // This may not be, we use the default name instead - DefaultLogger::get()->error("3DS: Empty material name"); - } - else mScene->mMaterials.back().mName = std::string(sz,cnt); - } - break; - - case Discreet3DS::CHUNK_MAT_DIFFUSE: - { - // This is the diffuse material color - aiColor3D* pc = &mScene->mMaterials.back().mDiffuse; - ParseColorChunk(pc); - if (is_qnan(pc->r)) { - // color chunk is invalid. Simply ignore it - DefaultLogger::get()->error("3DS: Unable to read DIFFUSE chunk"); - pc->r = pc->g = pc->b = 1.0f; - }} - break; - - case Discreet3DS::CHUNK_MAT_SPECULAR: - { - // This is the specular material color - aiColor3D* pc = &mScene->mMaterials.back().mSpecular; - ParseColorChunk(pc); - if (is_qnan(pc->r)) { - // color chunk is invalid. Simply ignore it - DefaultLogger::get()->error("3DS: Unable to read SPECULAR chunk"); - pc->r = pc->g = pc->b = 1.0f; - }} - break; - - case Discreet3DS::CHUNK_MAT_AMBIENT: - { - // This is the ambient material color - aiColor3D* pc = &mScene->mMaterials.back().mAmbient; - ParseColorChunk(pc); - if (is_qnan(pc->r)) { - // color chunk is invalid. Simply ignore it - DefaultLogger::get()->error("3DS: Unable to read AMBIENT chunk"); - pc->r = pc->g = pc->b = 0.0f; - }} - break; - - case Discreet3DS::CHUNK_MAT_SELF_ILLUM: - { - // This is the emissive material color - aiColor3D* pc = &mScene->mMaterials.back().mEmissive; - ParseColorChunk(pc); - if (is_qnan(pc->r)) { - // color chunk is invalid. Simply ignore it - DefaultLogger::get()->error("3DS: Unable to read EMISSIVE chunk"); - pc->r = pc->g = pc->b = 0.0f; - }} - break; - - case Discreet3DS::CHUNK_MAT_TRANSPARENCY: - { - // This is the material's transparency - float* pcf = &mScene->mMaterials.back().mTransparency; - *pcf = ParsePercentageChunk(); - - // NOTE: transparency, not opacity - if (is_qnan(*pcf)) - *pcf = 1.0f; - else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f; - } - break; - - case Discreet3DS::CHUNK_MAT_SHADING: - // This is the material shading mode - mScene->mMaterials.back().mShading = (D3DS::Discreet3DS::shadetype3ds)stream->GetI2(); - break; - - case Discreet3DS::CHUNK_MAT_TWO_SIDE: - // This is the two-sided flag - mScene->mMaterials.back().mTwoSided = true; - break; - - case Discreet3DS::CHUNK_MAT_SHININESS: - { // This is the shininess of the material - float* pcf = &mScene->mMaterials.back().mSpecularExponent; - *pcf = ParsePercentageChunk(); - if (is_qnan(*pcf)) - *pcf = 0.0f; - else *pcf *= (float)0xFFFF; - } - break; - - case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT: - { // This is the shininess strength of the material - float* pcf = &mScene->mMaterials.back().mShininessStrength; - *pcf = ParsePercentageChunk(); - if (is_qnan(*pcf)) - *pcf = 0.0f; - else *pcf *= (float)0xffff / 100.0f; - } - break; - - case Discreet3DS::CHUNK_MAT_SELF_ILPCT: - { // This is the self illumination strength of the material - float f = ParsePercentageChunk(); - if (is_qnan(f)) - f = 0.0f; - else f *= (float)0xFFFF / 100.0f; - mScene->mMaterials.back().mEmissive = aiColor3D(f,f,f); - } - break; - - // Parse texture chunks - case Discreet3DS::CHUNK_MAT_TEXTURE: - // Diffuse texture - ParseTextureChunk(&mScene->mMaterials.back().sTexDiffuse); - break; - case Discreet3DS::CHUNK_MAT_BUMPMAP: - // Height map - ParseTextureChunk(&mScene->mMaterials.back().sTexBump); - break; - case Discreet3DS::CHUNK_MAT_OPACMAP: - // Opacity texture - ParseTextureChunk(&mScene->mMaterials.back().sTexOpacity); - break; - case Discreet3DS::CHUNK_MAT_MAT_SHINMAP: - // Shininess map - ParseTextureChunk(&mScene->mMaterials.back().sTexShininess); - break; - case Discreet3DS::CHUNK_MAT_SPECMAP: - // Specular map - ParseTextureChunk(&mScene->mMaterials.back().sTexSpecular); - break; - case Discreet3DS::CHUNK_MAT_SELFIMAP: - // Self-illumination (emissive) map - ParseTextureChunk(&mScene->mMaterials.back().sTexEmissive); - break; - case Discreet3DS::CHUNK_MAT_REFLMAP: - // Reflection map - ParseTextureChunk(&mScene->mMaterials.back().sTexReflective); - break; - }; - ASSIMP_3DS_END_CHUNK(); -} - -// ------------------------------------------------------------------------------------------------ -void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut) -{ - ASSIMP_3DS_BEGIN_CHUNK(); - - // get chunk type - switch (chunk.Flag) - { - case Discreet3DS::CHUNK_MAPFILE: - { - // The material name string is already zero-terminated, but we need to be sure ... - const char* sz = (const char*)stream->GetPtr(); - unsigned int cnt = 0; - while (stream->GetI1()) - ++cnt; - pcOut->mMapName = std::string(sz,cnt); - } - break; - - - case Discreet3DS::CHUNK_PERCENTF: - // Manually parse the blend factor - pcOut->mTextureBlend = stream->GetF4(); - break; - - case Discreet3DS::CHUNK_PERCENTW: - // Manually parse the blend factor - pcOut->mTextureBlend = (float)((uint16_t)stream->GetI2()) / 100.0f; - break; - - case Discreet3DS::CHUNK_MAT_MAP_USCALE: - // Texture coordinate scaling in the U direction - pcOut->mScaleU = stream->GetF4(); - if (0.0f == pcOut->mScaleU) - { - DefaultLogger::get()->warn("Texture coordinate scaling in the x direction is zero. Assuming 1."); - pcOut->mScaleU = 1.0f; - } - break; - case Discreet3DS::CHUNK_MAT_MAP_VSCALE: - // Texture coordinate scaling in the V direction - pcOut->mScaleV = stream->GetF4(); - if (0.0f == pcOut->mScaleV) - { - DefaultLogger::get()->warn("Texture coordinate scaling in the y direction is zero. Assuming 1."); - pcOut->mScaleV = 1.0f; - } - break; - - case Discreet3DS::CHUNK_MAT_MAP_UOFFSET: - // Texture coordinate offset in the U direction - pcOut->mOffsetU = -stream->GetF4(); - break; - - case Discreet3DS::CHUNK_MAT_MAP_VOFFSET: - // Texture coordinate offset in the V direction - pcOut->mOffsetV = stream->GetF4(); - break; - - case Discreet3DS::CHUNK_MAT_MAP_ANG: - // Texture coordinate rotation, CCW in DEGREES - pcOut->mRotation = -AI_DEG_TO_RAD( stream->GetF4() ); - break; - - case Discreet3DS::CHUNK_MAT_MAP_TILING: - { - const uint16_t iFlags = stream->GetI2(); - - // Get the mapping mode (for both axes) - if (iFlags & 0x2u) - pcOut->mMapMode = aiTextureMapMode_Mirror; - - else if (iFlags & 0x10u) - pcOut->mMapMode = aiTextureMapMode_Decal; - - // wrapping in all remaining cases - else pcOut->mMapMode = aiTextureMapMode_Wrap; - } - break; - }; - - ASSIMP_3DS_END_CHUNK(); -} - -// ------------------------------------------------------------------------------------------------ -// Read a percentage chunk -float Discreet3DSImporter::ParsePercentageChunk() -{ - Discreet3DS::Chunk chunk; - ReadChunk(&chunk); - - if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag) - return stream->GetF4(); - else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag) - return (float)((uint16_t)stream->GetI2()) / (float)0xFFFF; - return get_qnan(); -} - -// ------------------------------------------------------------------------------------------------ -// Read a color chunk. If a percentage chunk is found instead it is read as a grayscale color -void Discreet3DSImporter::ParseColorChunk(aiColor3D* out, - bool acceptPercent) -{ - ai_assert(out != NULL); - - // error return value - const float qnan = get_qnan(); - static const aiColor3D clrError = aiColor3D(qnan,qnan,qnan); - - Discreet3DS::Chunk chunk; - ReadChunk(&chunk); - const unsigned int diff = chunk.Size - sizeof(Discreet3DS::Chunk); - - bool bGamma = false; - - // Get the type of the chunk - switch(chunk.Flag) - { - case Discreet3DS::CHUNK_LINRGBF: - bGamma = true; - - case Discreet3DS::CHUNK_RGBF: - if (sizeof(float) * 3 > diff) { - *out = clrError; - return; - } - out->r = stream->GetF4(); - out->g = stream->GetF4(); - out->b = stream->GetF4(); - break; - - case Discreet3DS::CHUNK_LINRGBB: - bGamma = true; - case Discreet3DS::CHUNK_RGBB: - if (sizeof(char) * 3 > diff) { - *out = clrError; - return; - } - out->r = (float)(uint8_t)stream->GetI1() / 255.0f; - out->g = (float)(uint8_t)stream->GetI1() / 255.0f; - out->b = (float)(uint8_t)stream->GetI1() / 255.0f; - break; - - // Percentage chunks are accepted, too. - case Discreet3DS::CHUNK_PERCENTF: - if (acceptPercent && 4 <= diff) { - out->g = out->b = out->r = stream->GetF4(); - break; - } - *out = clrError; - return; - - case Discreet3DS::CHUNK_PERCENTW: - if (acceptPercent && 1 <= diff) { - out->g = out->b = out->r = (float)(uint8_t)stream->GetI1() / 255.0f; - break; - } - *out = clrError; - return; - - default: - stream->IncPtr(diff); - // Skip unknown chunks, hope this won't cause any problems. - return ParseColorChunk(out,acceptPercent); - }; - (void)bGamma; -} - -#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file 3DSLoader.cpp + * @brief Implementation of the 3ds importer class + * + * http://www.the-labs.com/Blender/3DS-details.html + */ + + +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER + +// internal headers +#include "3DSLoader.h" +#include "Macros.h" +#include "../include/assimp/IOSystem.hpp" +#include "../include/assimp/scene.h" +#include "../include/assimp/DefaultLogger.hpp" +#include "StringComparison.h" + +using namespace Assimp; + +static const aiImporterDesc desc = { + "Discreet 3DS Importer", + "", + "", + "Limited animation support", + aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "3ds prj" +}; + + +// ------------------------------------------------------------------------------------------------ +// Begins a new parsing block +// - Reads the current chunk and validates it +// - computes its length +#define ASSIMP_3DS_BEGIN_CHUNK() \ + while (true) { \ + if (stream->GetRemainingSizeToLimit() < sizeof(Discreet3DS::Chunk)){ \ + return; \ + } \ + Discreet3DS::Chunk chunk; \ + ReadChunk(&chunk); \ + int chunkSize = chunk.Size-sizeof(Discreet3DS::Chunk); \ + if(chunkSize <= 0) \ + continue; \ + const int oldReadLimit = stream->GetReadLimit(); \ + stream->SetReadLimit(stream->GetCurrentPos() + chunkSize); \ + + +// ------------------------------------------------------------------------------------------------ +// End a parsing block +// Must follow at the end of each parsing block, reset chunk end marker to previous value +#define ASSIMP_3DS_END_CHUNK() \ + stream->SkipToReadLimit(); \ + stream->SetReadLimit(oldReadLimit); \ + if (stream->GetRemainingSizeToLimit() == 0) \ + return; \ + } + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +Discreet3DSImporter::Discreet3DSImporter() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +Discreet3DSImporter::~Discreet3DSImporter() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +{ + std::string extension = GetExtension(pFile); + if(extension == "3ds" || extension == "prj" ) { + return true; + } + if (!extension.length() || checkSig) { + uint16_t token[3]; + token[0] = 0x4d4d; + token[1] = 0x3dc2; + //token[2] = 0x3daa; + return CheckMagicToken(pIOHandler,pFile,token,2,0,2); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Loader registry entry +const aiImporterDesc* Discreet3DSImporter::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration properties +void Discreet3DSImporter::SetupProperties(const Importer* /*pImp*/) +{ + // nothing to be done for the moment +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void Discreet3DSImporter::InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) +{ + StreamReaderLE stream(pIOHandler->Open(pFile,"rb")); + this->stream = &stream; + + // We should have at least one chunk + if (stream.GetRemainingSize() < 16) { + throw DeadlyImportError("3DS file is either empty or corrupt: " + pFile); + } + + // Allocate our temporary 3DS representation + mScene = new D3DS::Scene(); + + // Initialize members + mLastNodeIndex = -1; + mCurrentNode = new D3DS::Node(); + mRootNode = mCurrentNode; + mRootNode->mHierarchyPos = -1; + mRootNode->mHierarchyIndex = -1; + mRootNode->mParent = NULL; + mMasterScale = 1.0f; + mBackgroundImage = ""; + bHasBG = false; + bIsPrj = false; + + // Parse the file + ParseMainChunk(); + + // Process all meshes in the file. First check whether all + // face indices haev valid values. The generate our + // internal verbose representation. Finally compute normal + // vectors from the smoothing groups we read from the + // file. + for (std::vector::iterator i = mScene->mMeshes.begin(), + end = mScene->mMeshes.end(); i != end;++i) { + if ((*i).mFaces.size() > 0 && (*i).mPositions.size() == 0) { + delete mScene; + throw DeadlyImportError("3DS file contains faces but no vertices: " + pFile); + } + CheckIndices(*i); + MakeUnique (*i); + ComputeNormalsWithSmoothingsGroups(*i); + } + + // Replace all occurences of the default material with a + // valid material. Generate it if no material containing + // DEFAULT in its name has been found in the file + ReplaceDefaultMaterial(); + + // Convert the scene from our internal representation to an + // aiScene object. This involves copying all meshes, lights + // and cameras to the scene + ConvertScene(pScene); + + // Generate the node graph for the scene. This is a little bit + // tricky since we'll need to split some meshes into submeshes + GenerateNodeGraph(pScene); + + // Now apply the master scaling factor to the scene + ApplyMasterScale(pScene); + + // Delete our internal scene representation and the root + // node, so the whole hierarchy will follow + delete mRootNode; + delete mScene; + + AI_DEBUG_INVALIDATE_PTR(mRootNode); + AI_DEBUG_INVALIDATE_PTR(mScene); + AI_DEBUG_INVALIDATE_PTR(this->stream); +} + +// ------------------------------------------------------------------------------------------------ +// Applies a master-scaling factor to the imported scene +void Discreet3DSImporter::ApplyMasterScale(aiScene* pScene) +{ + // There are some 3DS files with a zero scaling factor + if (!mMasterScale)mMasterScale = 1.0f; + else mMasterScale = 1.0f / mMasterScale; + + // Construct an uniform scaling matrix and multiply with it + pScene->mRootNode->mTransformation *= aiMatrix4x4( + mMasterScale,0.0f, 0.0f, 0.0f, + 0.0f, mMasterScale,0.0f, 0.0f, + 0.0f, 0.0f, mMasterScale,0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + + // Check whether a scaling track is assigned to the root node. +} + +// ------------------------------------------------------------------------------------------------ +// Reads a new chunk from the file +void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk* pcOut) +{ + ai_assert(pcOut != NULL); + + pcOut->Flag = stream->GetI2(); + pcOut->Size = stream->GetI4(); + + if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize()) + throw DeadlyImportError("Chunk is too large"); + + if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit()) + DefaultLogger::get()->error("3DS: Chunk overflow"); +} + +// ------------------------------------------------------------------------------------------------ +// Skip a chunk +void Discreet3DSImporter::SkipChunk() +{ + Discreet3DS::Chunk psChunk; + ReadChunk(&psChunk); + + stream->IncPtr(psChunk.Size-sizeof(Discreet3DS::Chunk)); + return; +} + +// ------------------------------------------------------------------------------------------------ +// Process the primary chunk of the file +void Discreet3DSImporter::ParseMainChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + + case Discreet3DS::CHUNK_PRJ: + bIsPrj = true; + case Discreet3DS::CHUNK_MAIN: + ParseEditorChunk(); + break; + }; + + ASSIMP_3DS_END_CHUNK(); + // recursively continue processing this hierarchy level + return ParseMainChunk(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseEditorChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_OBJMESH: + + ParseObjectChunk(); + break; + + // NOTE: In several documentations in the internet this + // chunk appears at different locations + case Discreet3DS::CHUNK_KEYFRAMER: + + ParseKeyframeChunk(); + break; + + case Discreet3DS::CHUNK_VERSION: + { + // print the version number + char buff[10]; + ASSIMP_itoa10(buff,stream->GetI2()); + DefaultLogger::get()->info(std::string("3DS file format version: ") + buff); + } + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseObjectChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_OBJBLOCK: + { + unsigned int cnt = 0; + const char* sz = (const char*)stream->GetPtr(); + + // Get the name of the geometry object + while (stream->GetI1())++cnt; + ParseChunk(sz,cnt); + } + break; + + case Discreet3DS::CHUNK_MAT_MATERIAL: + + // Add a new material to the list + mScene->mMaterials.push_back(D3DS::Material()); + ParseMaterialChunk(); + break; + + case Discreet3DS::CHUNK_AMBCOLOR: + + // This is the ambient base color of the scene. + // We add it to the ambient color of all materials + ParseColorChunk(&mClrAmbient,true); + if (is_qnan(mClrAmbient.r)) + { + // We failed to read the ambient base color. + DefaultLogger::get()->error("3DS: Failed to read ambient base color"); + mClrAmbient.r = mClrAmbient.g = mClrAmbient.b = 0.0f; + } + break; + + case Discreet3DS::CHUNK_BIT_MAP: + { + // Specifies the background image. The string should already be + // properly 0 terminated but we need to be sure + unsigned int cnt = 0; + const char* sz = (const char*)stream->GetPtr(); + while (stream->GetI1())++cnt; + mBackgroundImage = std::string(sz,cnt); + } + break; + + case Discreet3DS::CHUNK_BIT_MAP_EXISTS: + bHasBG = true; + break; + + case Discreet3DS::CHUNK_MASTER_SCALE: + // Scene master scaling factor + mMasterScale = stream->GetF4(); + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseChunk(const char* name, unsigned int num) +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // IMPLEMENTATION NOTE; + // Cameras or lights define their transformation in their parent node and in the + // corresponding light or camera chunks. However, we read and process the latter + // to to be able to return valid cameras/lights even if no scenegraph is given. + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_TRIMESH: + { + // this starts a new triangle mesh + mScene->mMeshes.push_back(D3DS::Mesh()); + D3DS::Mesh& m = mScene->mMeshes.back(); + + // Setup the name of the mesh + m.mName = std::string(name, num); + + // Read mesh chunks + ParseMeshChunk(); + } + break; + + case Discreet3DS::CHUNK_LIGHT: + { + // This starts a new light + aiLight* light = new aiLight(); + mScene->mLights.push_back(light); + + light->mName.Set(std::string(name, num)); + + // First read the position of the light + light->mPosition.x = stream->GetF4(); + light->mPosition.y = stream->GetF4(); + light->mPosition.z = stream->GetF4(); + + light->mColorDiffuse = aiColor3D(1.f,1.f,1.f); + + // Now check for further subchunks + if (!bIsPrj) /* fixme */ + ParseLightChunk(); + + // The specular light color is identical the the diffuse light color. The ambient light color + // is equal to the ambient base color of the whole scene. + light->mColorSpecular = light->mColorDiffuse; + light->mColorAmbient = mClrAmbient; + + if (light->mType == aiLightSource_UNDEFINED) + { + // It must be a point light + light->mType = aiLightSource_POINT; + }} + break; + + case Discreet3DS::CHUNK_CAMERA: + { + // This starts a new camera + aiCamera* camera = new aiCamera(); + mScene->mCameras.push_back(camera); + camera->mName.Set(std::string(name, num)); + + // First read the position of the camera + camera->mPosition.x = stream->GetF4(); + camera->mPosition.y = stream->GetF4(); + camera->mPosition.z = stream->GetF4(); + + // Then the camera target + camera->mLookAt.x = stream->GetF4() - camera->mPosition.x; + camera->mLookAt.y = stream->GetF4() - camera->mPosition.y; + camera->mLookAt.z = stream->GetF4() - camera->mPosition.z; + float len = camera->mLookAt.Length(); + if (len < 1e-5f) { + + // There are some files with lookat == position. Don't know why or whether it's ok or not. + DefaultLogger::get()->error("3DS: Unable to read proper camera look-at vector"); + camera->mLookAt = aiVector3D(0.f,1.f,0.f); + + } + else camera->mLookAt /= len; + + // And finally - the camera rotation angle, in counter clockwise direction + const float angle = AI_DEG_TO_RAD( stream->GetF4() ); + aiQuaternion quat(camera->mLookAt,angle); + camera->mUp = quat.GetMatrix() * aiVector3D(0.f,1.f,0.f); + + // Read the lense angle + camera->mHorizontalFOV = AI_DEG_TO_RAD ( stream->GetF4() ); + if (camera->mHorizontalFOV < 0.001f) { + camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f); + } + + // Now check for further subchunks + if (!bIsPrj) /* fixme */ { + ParseCameraChunk(); + }} + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseLightChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + aiLight* light = mScene->mLights.back(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_DL_SPOTLIGHT: + // Now we can be sure that the light is a spot light + light->mType = aiLightSource_SPOT; + + // We wouldn't need to normalize here, but we do it + light->mDirection.x = stream->GetF4() - light->mPosition.x; + light->mDirection.y = stream->GetF4() - light->mPosition.y; + light->mDirection.z = stream->GetF4() - light->mPosition.z; + light->mDirection.Normalize(); + + // Now the hotspot and falloff angles - in degrees + light->mAngleInnerCone = AI_DEG_TO_RAD( stream->GetF4() ); + + // FIX: the falloff angle is just an offset + light->mAngleOuterCone = light->mAngleInnerCone+AI_DEG_TO_RAD( stream->GetF4() ); + break; + + // intensity multiplier + case Discreet3DS::CHUNK_DL_MULTIPLIER: + light->mColorDiffuse = light->mColorDiffuse * stream->GetF4(); + break; + + // light color + case Discreet3DS::CHUNK_RGBF: + case Discreet3DS::CHUNK_LINRGBF: + light->mColorDiffuse.r *= stream->GetF4(); + light->mColorDiffuse.g *= stream->GetF4(); + light->mColorDiffuse.b *= stream->GetF4(); + break; + + // light attenuation + case Discreet3DS::CHUNK_DL_ATTENUATE: + light->mAttenuationLinear = stream->GetF4(); + break; + }; + + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseCameraChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + aiCamera* camera = mScene->mCameras.back(); + + // get chunk type + switch (chunk.Flag) + { + // near and far clip plane + case Discreet3DS::CHUNK_CAM_RANGES: + camera->mClipPlaneNear = stream->GetF4(); + camera->mClipPlaneFar = stream->GetF4(); + break; + } + + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseKeyframeChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_TRACKCAMTGT: + case Discreet3DS::CHUNK_TRACKSPOTL: + case Discreet3DS::CHUNK_TRACKCAMERA: + case Discreet3DS::CHUNK_TRACKINFO: + case Discreet3DS::CHUNK_TRACKLIGHT: + case Discreet3DS::CHUNK_TRACKLIGTGT: + + // this starts a new mesh hierarchy chunk + ParseHierarchyChunk(chunk.Flag); + break; + }; + + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +// Little helper function for ParseHierarchyChunk +void Discreet3DSImporter::InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent) +{ + if (!pcCurrent) { + mRootNode->push_back(pcNode); + return; + } + + if (pcCurrent->mHierarchyPos == pcNode->mHierarchyPos) { + if(pcCurrent->mParent) { + pcCurrent->mParent->push_back(pcNode); + } + else pcCurrent->push_back(pcNode); + return; + } + return InverseNodeSearch(pcNode,pcCurrent->mParent); +} + +// ------------------------------------------------------------------------------------------------ +// Find a node with a specific name in the import hierarchy +D3DS::Node* FindNode(D3DS::Node* root, const std::string& name) +{ + if (root->mName == name) + return root; + for (std::vector::iterator it = root->mChildren.begin();it != root->mChildren.end(); ++it) { + D3DS::Node* nd; + if (( nd = FindNode(*it,name))) + return nd; + } + return NULL; +} + +// ------------------------------------------------------------------------------------------------ +// Binary predicate for std::unique() +template +bool KeyUniqueCompare(const T& first, const T& second) +{ + return first.mTime == second.mTime; +} + +// ------------------------------------------------------------------------------------------------ +// Skip some additional import data. +void Discreet3DSImporter::SkipTCBInfo() +{ + unsigned int flags = stream->GetI2(); + + if (!flags) { + // Currently we can't do anything with these values. They occur + // quite rare, so it wouldn't be worth the effort implementing + // them. 3DS ist not really suitable for complex animations, + // so full support is not required. + DefaultLogger::get()->warn("3DS: Skipping TCB animation info"); + } + + if (flags & Discreet3DS::KEY_USE_TENS) { + stream->IncPtr(4); + } + if (flags & Discreet3DS::KEY_USE_BIAS) { + stream->IncPtr(4); + } + if (flags & Discreet3DS::KEY_USE_CONT) { + stream->IncPtr(4); + } + if (flags & Discreet3DS::KEY_USE_EASE_FROM) { + stream->IncPtr(4); + } + if (flags & Discreet3DS::KEY_USE_EASE_TO) { + stream->IncPtr(4); + } +} + +// ------------------------------------------------------------------------------------------------ +// Read hierarchy and keyframe info +void Discreet3DSImporter::ParseHierarchyChunk(uint16_t parent) +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_TRACKOBJNAME: + + // This is the name of the object to which the track applies. The chunk also + // defines the position of this object in the hierarchy. + { + + // First of all: get the name of the object + unsigned int cnt = 0; + const char* sz = (const char*)stream->GetPtr(); + + while (stream->GetI1())++cnt; + std::string name = std::string(sz,cnt); + + // Now find out whether we have this node already (target animation channels + // are stored with a separate object ID) + D3DS::Node* pcNode = FindNode(mRootNode,name); + int instanceNumber = 1; + + if ( pcNode) + { + // if the source is not a CHUNK_TRACKINFO block it wont be an object instance + if (parent != Discreet3DS::CHUNK_TRACKINFO) + { + mCurrentNode = pcNode; + break; + } + pcNode->mInstanceCount++; + instanceNumber = pcNode->mInstanceCount; + } + pcNode = new D3DS::Node(); + pcNode->mName = name; + pcNode->mInstanceNumber = instanceNumber; + + // There are two unknown values which we can safely ignore + stream->IncPtr(4); + + // Now read the hierarchy position of the object + uint16_t hierarchy = stream->GetI2() + 1; + pcNode->mHierarchyPos = hierarchy; + pcNode->mHierarchyIndex = mLastNodeIndex; + + // And find a proper position in the graph for it + if (mCurrentNode && mCurrentNode->mHierarchyPos == hierarchy) { + + // add to the parent of the last touched node + mCurrentNode->mParent->push_back(pcNode); + mLastNodeIndex++; + } + else if(hierarchy >= mLastNodeIndex) { + + // place it at the current position in the hierarchy + mCurrentNode->push_back(pcNode); + mLastNodeIndex = hierarchy; + } + else { + // need to go back to the specified position in the hierarchy. + InverseNodeSearch(pcNode,mCurrentNode); + mLastNodeIndex++; + } + // Make this node the current node + mCurrentNode = pcNode; + } + break; + + case Discreet3DS::CHUNK_TRACKDUMMYOBJNAME: + + // This is the "real" name of a $$$DUMMY object + { + const char* sz = (const char*) stream->GetPtr(); + while (stream->GetI1()); + + // If object name is DUMMY, take this one instead + if (mCurrentNode->mName == "$$$DUMMY") { + //DefaultLogger::get()->warn("3DS: Skipping dummy object name for non-dummy object"); + mCurrentNode->mName = std::string(sz); + break; + } + } + break; + + case Discreet3DS::CHUNK_TRACKPIVOT: + + if ( Discreet3DS::CHUNK_TRACKINFO != parent) + { + DefaultLogger::get()->warn("3DS: Skipping pivot subchunk for non usual object"); + break; + } + + // Pivot = origin of rotation and scaling + mCurrentNode->vPivot.x = stream->GetF4(); + mCurrentNode->vPivot.y = stream->GetF4(); + mCurrentNode->vPivot.z = stream->GetF4(); + break; + + + // //////////////////////////////////////////////////////////////////// + // POSITION KEYFRAME + case Discreet3DS::CHUNK_TRACKPOS: + { + stream->IncPtr(10); + const unsigned int numFrames = stream->GetI4(); + bool sortKeys = false; + + // This could also be meant as the target position for + // (targeted) lights and cameras + std::vector* l; + if ( Discreet3DS::CHUNK_TRACKCAMTGT == parent || Discreet3DS::CHUNK_TRACKLIGTGT == parent) { + l = & mCurrentNode->aTargetPositionKeys; + } + else l = & mCurrentNode->aPositionKeys; + + l->reserve(numFrames); + for (unsigned int i = 0; i < numFrames;++i) { + const unsigned int fidx = stream->GetI4(); + + // Setup a new position key + aiVectorKey v; + v.mTime = (double)fidx; + + SkipTCBInfo(); + v.mValue.x = stream->GetF4(); + v.mValue.y = stream->GetF4(); + v.mValue.z = stream->GetF4(); + + // check whether we'll need to sort the keys + if (!l->empty() && v.mTime <= l->back().mTime) + sortKeys = true; + + // Add the new keyframe to the list + l->push_back(v); + } + + // Sort all keys with ascending time values and remove duplicates? + if (sortKeys) { + std::stable_sort(l->begin(),l->end()); + l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); + }} + + break; + + // //////////////////////////////////////////////////////////////////// + // CAMERA ROLL KEYFRAME + case Discreet3DS::CHUNK_TRACKROLL: + { + // roll keys are accepted for cameras only + if (parent != Discreet3DS::CHUNK_TRACKCAMERA) { + DefaultLogger::get()->warn("3DS: Ignoring roll track for non-camera object"); + break; + } + bool sortKeys = false; + std::vector* l = &mCurrentNode->aCameraRollKeys; + + stream->IncPtr(10); + const unsigned int numFrames = stream->GetI4(); + l->reserve(numFrames); + for (unsigned int i = 0; i < numFrames;++i) { + const unsigned int fidx = stream->GetI4(); + + // Setup a new position key + aiFloatKey v; + v.mTime = (double)fidx; + + // This is just a single float + SkipTCBInfo(); + v.mValue = stream->GetF4(); + + // Check whether we'll need to sort the keys + if (!l->empty() && v.mTime <= l->back().mTime) + sortKeys = true; + + // Add the new keyframe to the list + l->push_back(v); + } + + // Sort all keys with ascending time values and remove duplicates? + if (sortKeys) { + std::stable_sort(l->begin(),l->end()); + l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); + }} + break; + + + // //////////////////////////////////////////////////////////////////// + // CAMERA FOV KEYFRAME + case Discreet3DS::CHUNK_TRACKFOV: + { + DefaultLogger::get()->error("3DS: Skipping FOV animation track. " + "This is not supported"); + } + break; + + + // //////////////////////////////////////////////////////////////////// + // ROTATION KEYFRAME + case Discreet3DS::CHUNK_TRACKROTATE: + { + stream->IncPtr(10); + const unsigned int numFrames = stream->GetI4(); + + bool sortKeys = false; + std::vector* l = &mCurrentNode->aRotationKeys; + l->reserve(numFrames); + + for (unsigned int i = 0; i < numFrames;++i) { + const unsigned int fidx = stream->GetI4(); + SkipTCBInfo(); + + aiQuatKey v; + v.mTime = (double)fidx; + + // The rotation keyframe is given as an axis-angle pair + const float rad = stream->GetF4(); + aiVector3D axis; + axis.x = stream->GetF4(); + axis.y = stream->GetF4(); + axis.z = stream->GetF4(); + + if (!axis.x && !axis.y && !axis.z) + axis.y = 1.f; + + // Construct a rotation quaternion from the axis-angle pair + v.mValue = aiQuaternion(axis,rad); + + // Check whether we'll need to sort the keys + if (!l->empty() && v.mTime <= l->back().mTime) + sortKeys = true; + + // add the new keyframe to the list + l->push_back(v); + } + // Sort all keys with ascending time values and remove duplicates? + if (sortKeys) { + std::stable_sort(l->begin(),l->end()); + l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); + }} + break; + + // //////////////////////////////////////////////////////////////////// + // SCALING KEYFRAME + case Discreet3DS::CHUNK_TRACKSCALE: + { + stream->IncPtr(10); + const unsigned int numFrames = stream->GetI2(); + stream->IncPtr(2); + + bool sortKeys = false; + std::vector* l = &mCurrentNode->aScalingKeys; + l->reserve(numFrames); + + for (unsigned int i = 0; i < numFrames;++i) { + const unsigned int fidx = stream->GetI4(); + SkipTCBInfo(); + + // Setup a new key + aiVectorKey v; + v.mTime = (double)fidx; + + // ... and read its value + v.mValue.x = stream->GetF4(); + v.mValue.y = stream->GetF4(); + v.mValue.z = stream->GetF4(); + + // check whether we'll need to sort the keys + if (!l->empty() && v.mTime <= l->back().mTime) + sortKeys = true; + + // Remove zero-scalings on singular axes - they've been reported to be there erroneously in some strange files + if (!v.mValue.x) v.mValue.x = 1.f; + if (!v.mValue.y) v.mValue.y = 1.f; + if (!v.mValue.z) v.mValue.z = 1.f; + + l->push_back(v); + } + // Sort all keys with ascending time values and remove duplicates? + if (sortKeys) { + std::stable_sort(l->begin(),l->end()); + l->erase ( std::unique (l->begin(),l->end(),&KeyUniqueCompare), l->end() ); + }} + break; + }; + + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +// Read a face chunk - it contains smoothing groups and material assignments +void Discreet3DSImporter::ParseFaceChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // Get the mesh we're currently working on + D3DS::Mesh& mMesh = mScene->mMeshes.back(); + + // Get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_SMOOLIST: + { + // This is the list of smoothing groups - a bitfield for every face. + // Up to 32 smoothing groups assigned to a single face. + unsigned int num = chunkSize/4, m = 0; + if (num > mMesh.mFaces.size()) { + throw DeadlyImportError("3DS: More smoothing groups than faces"); + } + for (std::vector::iterator i = mMesh.mFaces.begin(); m != num;++i, ++m) { + // nth bit is set for nth smoothing group + (*i).iSmoothGroup = stream->GetI4(); + }} + break; + + case Discreet3DS::CHUNK_FACEMAT: + { + // at fist an asciiz with the material name + const char* sz = (const char*)stream->GetPtr(); + while (stream->GetI1()); + + // find the index of the material + unsigned int idx = 0xcdcdcdcd, cnt = 0; + for (std::vector::const_iterator i = mScene->mMaterials.begin();i != mScene->mMaterials.end();++i,++cnt) { + // use case independent comparisons. hopefully it will work. + if ((*i).mName.length() && !ASSIMP_stricmp(sz, (*i).mName.c_str())) { + idx = cnt; + break; + } + } + if (0xcdcdcdcd == idx) { + DefaultLogger::get()->error(std::string("3DS: Unknown material: ") + sz); + } + + // Now continue and read all material indices + cnt = (uint16_t)stream->GetI2(); + for (unsigned int i = 0; i < cnt;++i) { + unsigned int fidx = (uint16_t)stream->GetI2(); + + // check range + if (fidx >= mMesh.mFaceMaterials.size()) { + DefaultLogger::get()->error("3DS: Invalid face index in face material list"); + } + else mMesh.mFaceMaterials[fidx] = idx; + }} + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +// Read a mesh chunk. Here's the actual mesh data +void Discreet3DSImporter::ParseMeshChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // Get the mesh we're currently working on + D3DS::Mesh& mMesh = mScene->mMeshes.back(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_VERTLIST: + { + // This is the list of all vertices in the current mesh + int num = (int)(uint16_t)stream->GetI2(); + mMesh.mPositions.reserve(num); + while (num-- > 0) { + aiVector3D v; + v.x = stream->GetF4(); + v.y = stream->GetF4(); + v.z = stream->GetF4(); + mMesh.mPositions.push_back(v); + }} + break; + case Discreet3DS::CHUNK_TRMATRIX: + { + // This is the RLEATIVE transformation matrix of the current mesh. Vertices are + // pretransformed by this matrix wonder. + mMesh.mMat.a1 = stream->GetF4(); + mMesh.mMat.b1 = stream->GetF4(); + mMesh.mMat.c1 = stream->GetF4(); + mMesh.mMat.a2 = stream->GetF4(); + mMesh.mMat.b2 = stream->GetF4(); + mMesh.mMat.c2 = stream->GetF4(); + mMesh.mMat.a3 = stream->GetF4(); + mMesh.mMat.b3 = stream->GetF4(); + mMesh.mMat.c3 = stream->GetF4(); + mMesh.mMat.a4 = stream->GetF4(); + mMesh.mMat.b4 = stream->GetF4(); + mMesh.mMat.c4 = stream->GetF4(); + } + break; + + case Discreet3DS::CHUNK_MAPLIST: + { + // This is the list of all UV coords in the current mesh + int num = (int)(uint16_t)stream->GetI2(); + mMesh.mTexCoords.reserve(num); + while (num-- > 0) { + aiVector3D v; + v.x = stream->GetF4(); + v.y = stream->GetF4(); + mMesh.mTexCoords.push_back(v); + }} + break; + + case Discreet3DS::CHUNK_FACELIST: + { + // This is the list of all faces in the current mesh + int num = (int)(uint16_t)stream->GetI2(); + mMesh.mFaces.reserve(num); + while (num-- > 0) { + // 3DS faces are ALWAYS triangles + mMesh.mFaces.push_back(D3DS::Face()); + D3DS::Face& sFace = mMesh.mFaces.back(); + + sFace.mIndices[0] = (uint16_t)stream->GetI2(); + sFace.mIndices[1] = (uint16_t)stream->GetI2(); + sFace.mIndices[2] = (uint16_t)stream->GetI2(); + + stream->IncPtr(2); // skip edge visibility flag + } + + // Resize the material array (0xcdcdcdcd marks the default material; so if a face is + // not referenced by a material, $$DEFAULT will be assigned to it) + mMesh.mFaceMaterials.resize(mMesh.mFaces.size(),0xcdcdcdcd); + + // Larger 3DS files could have multiple FACE chunks here + chunkSize = stream->GetRemainingSizeToLimit(); + if ( chunkSize > (int) sizeof(Discreet3DS::Chunk ) ) + ParseFaceChunk(); + } + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +// Read a 3DS material chunk +void Discreet3DSImporter::ParseMaterialChunk() +{ + ASSIMP_3DS_BEGIN_CHUNK(); + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_MAT_MATNAME: + + { + // The material name string is already zero-terminated, but we need to be sure ... + const char* sz = (const char*)stream->GetPtr(); + unsigned int cnt = 0; + while (stream->GetI1()) + ++cnt; + + if (!cnt) { + // This may not be, we use the default name instead + DefaultLogger::get()->error("3DS: Empty material name"); + } + else mScene->mMaterials.back().mName = std::string(sz,cnt); + } + break; + + case Discreet3DS::CHUNK_MAT_DIFFUSE: + { + // This is the diffuse material color + aiColor3D* pc = &mScene->mMaterials.back().mDiffuse; + ParseColorChunk(pc); + if (is_qnan(pc->r)) { + // color chunk is invalid. Simply ignore it + DefaultLogger::get()->error("3DS: Unable to read DIFFUSE chunk"); + pc->r = pc->g = pc->b = 1.0f; + }} + break; + + case Discreet3DS::CHUNK_MAT_SPECULAR: + { + // This is the specular material color + aiColor3D* pc = &mScene->mMaterials.back().mSpecular; + ParseColorChunk(pc); + if (is_qnan(pc->r)) { + // color chunk is invalid. Simply ignore it + DefaultLogger::get()->error("3DS: Unable to read SPECULAR chunk"); + pc->r = pc->g = pc->b = 1.0f; + }} + break; + + case Discreet3DS::CHUNK_MAT_AMBIENT: + { + // This is the ambient material color + aiColor3D* pc = &mScene->mMaterials.back().mAmbient; + ParseColorChunk(pc); + if (is_qnan(pc->r)) { + // color chunk is invalid. Simply ignore it + DefaultLogger::get()->error("3DS: Unable to read AMBIENT chunk"); + pc->r = pc->g = pc->b = 0.0f; + }} + break; + + case Discreet3DS::CHUNK_MAT_SELF_ILLUM: + { + // This is the emissive material color + aiColor3D* pc = &mScene->mMaterials.back().mEmissive; + ParseColorChunk(pc); + if (is_qnan(pc->r)) { + // color chunk is invalid. Simply ignore it + DefaultLogger::get()->error("3DS: Unable to read EMISSIVE chunk"); + pc->r = pc->g = pc->b = 0.0f; + }} + break; + + case Discreet3DS::CHUNK_MAT_TRANSPARENCY: + { + // This is the material's transparency + float* pcf = &mScene->mMaterials.back().mTransparency; + *pcf = ParsePercentageChunk(); + + // NOTE: transparency, not opacity + if (is_qnan(*pcf)) + *pcf = 1.0f; + else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f; + } + break; + + case Discreet3DS::CHUNK_MAT_SHADING: + // This is the material shading mode + mScene->mMaterials.back().mShading = (D3DS::Discreet3DS::shadetype3ds)stream->GetI2(); + break; + + case Discreet3DS::CHUNK_MAT_TWO_SIDE: + // This is the two-sided flag + mScene->mMaterials.back().mTwoSided = true; + break; + + case Discreet3DS::CHUNK_MAT_SHININESS: + { // This is the shininess of the material + float* pcf = &mScene->mMaterials.back().mSpecularExponent; + *pcf = ParsePercentageChunk(); + if (is_qnan(*pcf)) + *pcf = 0.0f; + else *pcf *= (float)0xFFFF; + } + break; + + case Discreet3DS::CHUNK_MAT_SHININESS_PERCENT: + { // This is the shininess strength of the material + float* pcf = &mScene->mMaterials.back().mShininessStrength; + *pcf = ParsePercentageChunk(); + if (is_qnan(*pcf)) + *pcf = 0.0f; + else *pcf *= (float)0xffff / 100.0f; + } + break; + + case Discreet3DS::CHUNK_MAT_SELF_ILPCT: + { // This is the self illumination strength of the material + float f = ParsePercentageChunk(); + if (is_qnan(f)) + f = 0.0f; + else f *= (float)0xFFFF / 100.0f; + mScene->mMaterials.back().mEmissive = aiColor3D(f,f,f); + } + break; + + // Parse texture chunks + case Discreet3DS::CHUNK_MAT_TEXTURE: + // Diffuse texture + ParseTextureChunk(&mScene->mMaterials.back().sTexDiffuse); + break; + case Discreet3DS::CHUNK_MAT_BUMPMAP: + // Height map + ParseTextureChunk(&mScene->mMaterials.back().sTexBump); + break; + case Discreet3DS::CHUNK_MAT_OPACMAP: + // Opacity texture + ParseTextureChunk(&mScene->mMaterials.back().sTexOpacity); + break; + case Discreet3DS::CHUNK_MAT_MAT_SHINMAP: + // Shininess map + ParseTextureChunk(&mScene->mMaterials.back().sTexShininess); + break; + case Discreet3DS::CHUNK_MAT_SPECMAP: + // Specular map + ParseTextureChunk(&mScene->mMaterials.back().sTexSpecular); + break; + case Discreet3DS::CHUNK_MAT_SELFIMAP: + // Self-illumination (emissive) map + ParseTextureChunk(&mScene->mMaterials.back().sTexEmissive); + break; + case Discreet3DS::CHUNK_MAT_REFLMAP: + // Reflection map + ParseTextureChunk(&mScene->mMaterials.back().sTexReflective); + break; + }; + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut) +{ + ASSIMP_3DS_BEGIN_CHUNK(); + + // get chunk type + switch (chunk.Flag) + { + case Discreet3DS::CHUNK_MAPFILE: + { + // The material name string is already zero-terminated, but we need to be sure ... + const char* sz = (const char*)stream->GetPtr(); + unsigned int cnt = 0; + while (stream->GetI1()) + ++cnt; + pcOut->mMapName = std::string(sz,cnt); + } + break; + + + case Discreet3DS::CHUNK_PERCENTF: + // Manually parse the blend factor + pcOut->mTextureBlend = stream->GetF4(); + break; + + case Discreet3DS::CHUNK_PERCENTW: + // Manually parse the blend factor + pcOut->mTextureBlend = (float)((uint16_t)stream->GetI2()) / 100.0f; + break; + + case Discreet3DS::CHUNK_MAT_MAP_USCALE: + // Texture coordinate scaling in the U direction + pcOut->mScaleU = stream->GetF4(); + if (0.0f == pcOut->mScaleU) + { + DefaultLogger::get()->warn("Texture coordinate scaling in the x direction is zero. Assuming 1."); + pcOut->mScaleU = 1.0f; + } + break; + case Discreet3DS::CHUNK_MAT_MAP_VSCALE: + // Texture coordinate scaling in the V direction + pcOut->mScaleV = stream->GetF4(); + if (0.0f == pcOut->mScaleV) + { + DefaultLogger::get()->warn("Texture coordinate scaling in the y direction is zero. Assuming 1."); + pcOut->mScaleV = 1.0f; + } + break; + + case Discreet3DS::CHUNK_MAT_MAP_UOFFSET: + // Texture coordinate offset in the U direction + pcOut->mOffsetU = -stream->GetF4(); + break; + + case Discreet3DS::CHUNK_MAT_MAP_VOFFSET: + // Texture coordinate offset in the V direction + pcOut->mOffsetV = stream->GetF4(); + break; + + case Discreet3DS::CHUNK_MAT_MAP_ANG: + // Texture coordinate rotation, CCW in DEGREES + pcOut->mRotation = -AI_DEG_TO_RAD( stream->GetF4() ); + break; + + case Discreet3DS::CHUNK_MAT_MAP_TILING: + { + const uint16_t iFlags = stream->GetI2(); + + // Get the mapping mode (for both axes) + if (iFlags & 0x2u) + pcOut->mMapMode = aiTextureMapMode_Mirror; + + else if (iFlags & 0x10u) + pcOut->mMapMode = aiTextureMapMode_Decal; + + // wrapping in all remaining cases + else pcOut->mMapMode = aiTextureMapMode_Wrap; + } + break; + }; + + ASSIMP_3DS_END_CHUNK(); +} + +// ------------------------------------------------------------------------------------------------ +// Read a percentage chunk +float Discreet3DSImporter::ParsePercentageChunk() +{ + Discreet3DS::Chunk chunk; + ReadChunk(&chunk); + + if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag) + return stream->GetF4(); + else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag) + return (float)((uint16_t)stream->GetI2()) / (float)0xFFFF; + return get_qnan(); +} + +// ------------------------------------------------------------------------------------------------ +// Read a color chunk. If a percentage chunk is found instead it is read as a grayscale color +void Discreet3DSImporter::ParseColorChunk(aiColor3D* out, + bool acceptPercent) +{ + ai_assert(out != NULL); + + // error return value + const float qnan = get_qnan(); + static const aiColor3D clrError = aiColor3D(qnan,qnan,qnan); + + Discreet3DS::Chunk chunk; + ReadChunk(&chunk); + const unsigned int diff = chunk.Size - sizeof(Discreet3DS::Chunk); + + bool bGamma = false; + + // Get the type of the chunk + switch(chunk.Flag) + { + case Discreet3DS::CHUNK_LINRGBF: + bGamma = true; + + case Discreet3DS::CHUNK_RGBF: + if (sizeof(float) * 3 > diff) { + *out = clrError; + return; + } + out->r = stream->GetF4(); + out->g = stream->GetF4(); + out->b = stream->GetF4(); + break; + + case Discreet3DS::CHUNK_LINRGBB: + bGamma = true; + case Discreet3DS::CHUNK_RGBB: + if (sizeof(char) * 3 > diff) { + *out = clrError; + return; + } + out->r = (float)(uint8_t)stream->GetI1() / 255.0f; + out->g = (float)(uint8_t)stream->GetI1() / 255.0f; + out->b = (float)(uint8_t)stream->GetI1() / 255.0f; + break; + + // Percentage chunks are accepted, too. + case Discreet3DS::CHUNK_PERCENTF: + if (acceptPercent && 4 <= diff) { + out->g = out->b = out->r = stream->GetF4(); + break; + } + *out = clrError; + return; + + case Discreet3DS::CHUNK_PERCENTW: + if (acceptPercent && 1 <= diff) { + out->g = out->b = out->r = (float)(uint8_t)stream->GetI1() / 255.0f; + break; + } + *out = clrError; + return; + + default: + stream->IncPtr(diff); + // Skip unknown chunks, hope this won't cause any problems. + return ParseColorChunk(out,acceptPercent); + }; + (void)bGamma; +} + +#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER diff --git a/code/3DSLoader.h b/code/3DSLoader.h index e857910e1..5d30a9106 100644 --- a/code/3DSLoader.h +++ b/code/3DSLoader.h @@ -1,282 +1,282 @@ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file 3DSLoader.h - * @brief 3DS File format loader - */ -#ifndef AI_3DSIMPORTER_H_INC -#define AI_3DSIMPORTER_H_INC - -#include "BaseImporter.h" -#include "../include/assimp/types.h" - -#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER - -#include "3DSHelper.h" -#include "StreamReader.h" - -struct aiNode; - -namespace Assimp { - - -using namespace D3DS; - -// --------------------------------------------------------------------------------- -/** Importer class for 3D Studio r3 and r4 3DS files - */ -class Discreet3DSImporter : public BaseImporter -{ -public: - - Discreet3DSImporter(); - ~Discreet3DSImporter(); - -public: - - // ------------------------------------------------------------------- - /** Returns whether the class can handle the format of the given file. - * See BaseImporter::CanRead() for details. - */ - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, - bool checkSig) const; - - // ------------------------------------------------------------------- - /** Called prior to ReadFile(). - * The function is a request to the importer to update its configuration - * basing on the Importer's configuration property list. - */ - void SetupProperties(const Importer* pImp); - -protected: - - // ------------------------------------------------------------------- - /** Return importer meta information. - * See #BaseImporter::GetInfo for the details - */ - const aiImporterDesc* GetInfo () const; - - // ------------------------------------------------------------------- - /** Imports the given file into the given scene structure. - * See BaseImporter::InternReadFile() for details - */ - void InternReadFile( const std::string& pFile, aiScene* pScene, - IOSystem* pIOHandler); - - // ------------------------------------------------------------------- - /** Converts a temporary material to the outer representation - */ - void ConvertMaterial(D3DS::Material& p_cMat, - aiMaterial& p_pcOut); - - // ------------------------------------------------------------------- - /** Read a chunk - * - * @param pcOut Receives the current chunk - */ - void ReadChunk(Discreet3DS::Chunk* pcOut); - - // ------------------------------------------------------------------- - /** Parse a percentage chunk. mCurrent will point to the next - * chunk behind afterwards. If no percentage chunk is found - * QNAN is returned. - */ - float ParsePercentageChunk(); - - // ------------------------------------------------------------------- - /** Parse a color chunk. mCurrent will point to the next - * chunk behind afterwards. If no color chunk is found - * QNAN is returned in all members. - */ - void ParseColorChunk(aiColor3D* p_pcOut, - bool p_bAcceptPercent = true); - - - // ------------------------------------------------------------------- - /** Skip a chunk in the file - */ - void SkipChunk(); - - // ------------------------------------------------------------------- - /** Generate the nodegraph - */ - void GenerateNodeGraph(aiScene* pcOut); - - // ------------------------------------------------------------------- - /** Parse a main top-level chunk in the file - */ - void ParseMainChunk(); - - // ------------------------------------------------------------------- - /** Parse a top-level chunk in the file - */ - void ParseChunk(const char* name, unsigned int num); - - // ------------------------------------------------------------------- - /** Parse a top-level editor chunk in the file - */ - void ParseEditorChunk(); - - // ------------------------------------------------------------------- - /** Parse a top-level object chunk in the file - */ - void ParseObjectChunk(); - - // ------------------------------------------------------------------- - /** Parse a material chunk in the file - */ - void ParseMaterialChunk(); - - // ------------------------------------------------------------------- - /** Parse a mesh chunk in the file - */ - void ParseMeshChunk(); - - // ------------------------------------------------------------------- - /** Parse a light chunk in the file - */ - void ParseLightChunk(); - - // ------------------------------------------------------------------- - /** Parse a camera chunk in the file - */ - void ParseCameraChunk(); - - // ------------------------------------------------------------------- - /** Parse a face list chunk in the file - */ - void ParseFaceChunk(); - - // ------------------------------------------------------------------- - /** Parse a keyframe chunk in the file - */ - void ParseKeyframeChunk(); - - // ------------------------------------------------------------------- - /** Parse a hierarchy chunk in the file - */ - void ParseHierarchyChunk(uint16_t parent); - - // ------------------------------------------------------------------- - /** Parse a texture chunk in the file - */ - void ParseTextureChunk(D3DS::Texture* pcOut); - - // ------------------------------------------------------------------- - /** Convert the meshes in the file - */ - void ConvertMeshes(aiScene* pcOut); - - // ------------------------------------------------------------------- - /** Replace the default material in the scene - */ - void ReplaceDefaultMaterial(); - - // ------------------------------------------------------------------- - /** Convert the whole scene - */ - void ConvertScene(aiScene* pcOut); - - // ------------------------------------------------------------------- - /** generate unique vertices for a mesh - */ - void MakeUnique(D3DS::Mesh& sMesh); - - // ------------------------------------------------------------------- - /** Add a node to the node graph - */ - void AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,D3DS::Node* pcIn, - aiMatrix4x4& absTrafo); - - // ------------------------------------------------------------------- - /** Search for a node in the graph. - * Called recursively - */ - void InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent); - - // ------------------------------------------------------------------- - /** Apply the master scaling factor to the mesh - */ - void ApplyMasterScale(aiScene* pScene); - - // ------------------------------------------------------------------- - /** Clamp all indices in the file to a valid range - */ - void CheckIndices(D3DS::Mesh& sMesh); - - // ------------------------------------------------------------------- - /** Skip the TCB info in a track key - */ - void SkipTCBInfo(); - -protected: - - /** Stream to read from */ - StreamReaderLE* stream; - - /** Last touched node index */ - short mLastNodeIndex; - - /** Current node, root node */ - D3DS::Node* mCurrentNode, *mRootNode; - - /** Scene under construction */ - D3DS::Scene* mScene; - - /** Ambient base color of the scene */ - aiColor3D mClrAmbient; - - /** Master scaling factor of the scene */ - float mMasterScale; - - /** Path to the background image of the scene */ - std::string mBackgroundImage; - bool bHasBG; - - /** true if PRJ file */ - bool bIsPrj; -}; - -} // end of namespace Assimp - -#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER - -#endif // AI_3DSIMPORTER_H_INC + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file 3DSLoader.h + * @brief 3DS File format loader + */ +#ifndef AI_3DSIMPORTER_H_INC +#define AI_3DSIMPORTER_H_INC + +#include "BaseImporter.h" +#include "../include/assimp/types.h" + +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER + +#include "3DSHelper.h" +#include "StreamReader.h" + +struct aiNode; + +namespace Assimp { + + +using namespace D3DS; + +// --------------------------------------------------------------------------------- +/** Importer class for 3D Studio r3 and r4 3DS files + */ +class Discreet3DSImporter : public BaseImporter +{ +public: + + Discreet3DSImporter(); + ~Discreet3DSImporter(); + +public: + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. + */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, + bool checkSig) const; + + // ------------------------------------------------------------------- + /** Called prior to ReadFile(). + * The function is a request to the importer to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + +protected: + + // ------------------------------------------------------------------- + /** Return importer meta information. + * See #BaseImporter::GetInfo for the details + */ + const aiImporterDesc* GetInfo () const; + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, + IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Converts a temporary material to the outer representation + */ + void ConvertMaterial(D3DS::Material& p_cMat, + aiMaterial& p_pcOut); + + // ------------------------------------------------------------------- + /** Read a chunk + * + * @param pcOut Receives the current chunk + */ + void ReadChunk(Discreet3DS::Chunk* pcOut); + + // ------------------------------------------------------------------- + /** Parse a percentage chunk. mCurrent will point to the next + * chunk behind afterwards. If no percentage chunk is found + * QNAN is returned. + */ + float ParsePercentageChunk(); + + // ------------------------------------------------------------------- + /** Parse a color chunk. mCurrent will point to the next + * chunk behind afterwards. If no color chunk is found + * QNAN is returned in all members. + */ + void ParseColorChunk(aiColor3D* p_pcOut, + bool p_bAcceptPercent = true); + + + // ------------------------------------------------------------------- + /** Skip a chunk in the file + */ + void SkipChunk(); + + // ------------------------------------------------------------------- + /** Generate the nodegraph + */ + void GenerateNodeGraph(aiScene* pcOut); + + // ------------------------------------------------------------------- + /** Parse a main top-level chunk in the file + */ + void ParseMainChunk(); + + // ------------------------------------------------------------------- + /** Parse a top-level chunk in the file + */ + void ParseChunk(const char* name, unsigned int num); + + // ------------------------------------------------------------------- + /** Parse a top-level editor chunk in the file + */ + void ParseEditorChunk(); + + // ------------------------------------------------------------------- + /** Parse a top-level object chunk in the file + */ + void ParseObjectChunk(); + + // ------------------------------------------------------------------- + /** Parse a material chunk in the file + */ + void ParseMaterialChunk(); + + // ------------------------------------------------------------------- + /** Parse a mesh chunk in the file + */ + void ParseMeshChunk(); + + // ------------------------------------------------------------------- + /** Parse a light chunk in the file + */ + void ParseLightChunk(); + + // ------------------------------------------------------------------- + /** Parse a camera chunk in the file + */ + void ParseCameraChunk(); + + // ------------------------------------------------------------------- + /** Parse a face list chunk in the file + */ + void ParseFaceChunk(); + + // ------------------------------------------------------------------- + /** Parse a keyframe chunk in the file + */ + void ParseKeyframeChunk(); + + // ------------------------------------------------------------------- + /** Parse a hierarchy chunk in the file + */ + void ParseHierarchyChunk(uint16_t parent); + + // ------------------------------------------------------------------- + /** Parse a texture chunk in the file + */ + void ParseTextureChunk(D3DS::Texture* pcOut); + + // ------------------------------------------------------------------- + /** Convert the meshes in the file + */ + void ConvertMeshes(aiScene* pcOut); + + // ------------------------------------------------------------------- + /** Replace the default material in the scene + */ + void ReplaceDefaultMaterial(); + + // ------------------------------------------------------------------- + /** Convert the whole scene + */ + void ConvertScene(aiScene* pcOut); + + // ------------------------------------------------------------------- + /** generate unique vertices for a mesh + */ + void MakeUnique(D3DS::Mesh& sMesh); + + // ------------------------------------------------------------------- + /** Add a node to the node graph + */ + void AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,D3DS::Node* pcIn, + aiMatrix4x4& absTrafo); + + // ------------------------------------------------------------------- + /** Search for a node in the graph. + * Called recursively + */ + void InverseNodeSearch(D3DS::Node* pcNode,D3DS::Node* pcCurrent); + + // ------------------------------------------------------------------- + /** Apply the master scaling factor to the mesh + */ + void ApplyMasterScale(aiScene* pScene); + + // ------------------------------------------------------------------- + /** Clamp all indices in the file to a valid range + */ + void CheckIndices(D3DS::Mesh& sMesh); + + // ------------------------------------------------------------------- + /** Skip the TCB info in a track key + */ + void SkipTCBInfo(); + +protected: + + /** Stream to read from */ + StreamReaderLE* stream; + + /** Last touched node index */ + short mLastNodeIndex; + + /** Current node, root node */ + D3DS::Node* mCurrentNode, *mRootNode; + + /** Scene under construction */ + D3DS::Scene* mScene; + + /** Ambient base color of the scene */ + aiColor3D mClrAmbient; + + /** Master scaling factor of the scene */ + float mMasterScale; + + /** Path to the background image of the scene */ + std::string mBackgroundImage; + bool bHasBG; + + /** true if PRJ file */ + bool bIsPrj; +}; + +} // end of namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_3DS_IMPORTER + +#endif // AI_3DSIMPORTER_H_INC diff --git a/code/ACLoader.cpp b/code/ACLoader.cpp index 07620bec0..bb7951670 100644 --- a/code/ACLoader.cpp +++ b/code/ACLoader.cpp @@ -1,886 +1,886 @@ - -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of the AC3D importer class */ - - - -#ifndef ASSIMP_BUILD_NO_AC_IMPORTER - -// internal headers -#include "ACLoader.h" -#include "ParsingUtils.h" -#include "fast_atof.h" -#include "Subdivision.h" -#include "Importer.h" -#include "BaseImporter.h" -#include "../include/assimp/Importer.hpp" -#include "../include/assimp/light.h" -#include "../include/assimp/DefaultLogger.hpp" -#include "../include/assimp/material.h" -#include "../include/assimp/scene.h" -#include "../include/assimp/config.h" -#include "../include/assimp/IOSystem.hpp" -#include - -using namespace Assimp; - -static const aiImporterDesc desc = { - "AC3D Importer", - "", - "", - "", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "ac acc ac3d" -}; - -// ------------------------------------------------------------------------------------------------ -// skip to the next token -#define AI_AC_SKIP_TO_NEXT_TOKEN() \ - if (!SkipSpaces(&buffer)) \ - { \ - DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL"); \ - continue; \ - } - -// ------------------------------------------------------------------------------------------------ -// read a string (may be enclosed in double quotation marks). buffer must point to " -#define AI_AC_GET_STRING(out) \ - ++buffer; \ - const char* sz = buffer; \ - while ('\"' != *buffer) \ - { \ - if (IsLineEnd( *buffer )) \ - { \ - DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL in string"); \ - out = "ERROR"; \ - break; \ - } \ - ++buffer; \ - } \ - if (IsLineEnd( *buffer ))continue; \ - out = std::string(sz,(unsigned int)(buffer-sz)); \ - ++buffer; - - -// ------------------------------------------------------------------------------------------------ -// read 1 to n floats prefixed with an optional predefined identifier -#define AI_AC_CHECKED_LOAD_FLOAT_ARRAY(name,name_length,num,out) \ - AI_AC_SKIP_TO_NEXT_TOKEN(); \ - if (name_length) \ - { \ - if (strncmp(buffer,name,name_length) || !IsSpace(buffer[name_length])) \ - { \ - DefaultLogger::get()->error("AC3D: Unexpexted token. " name " was expected."); \ - continue; \ - } \ - buffer += name_length+1; \ - } \ - for (unsigned int i = 0; i < num;++i) \ - { \ - AI_AC_SKIP_TO_NEXT_TOKEN(); \ - buffer = fast_atoreal_move(buffer,((float*)out)[i]); \ - } - - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -AC3DImporter::AC3DImporter() -{ - // nothing to be done here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -AC3DImporter::~AC3DImporter() -{ - // nothing to be done here -} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. -bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ - std::string extension = GetExtension(pFile); - - // fixme: are acc and ac3d *really* used? Some sources say they are - if(extension == "ac" || extension == "ac3d" || extension == "acc") { - return true; - } - if (!extension.length() || checkSig) { - uint32_t token = AI_MAKE_MAGIC("AC3D"); - return CheckMagicToken(pIOHandler,pFile,&token,1,0); - } - return false; -} - -// ------------------------------------------------------------------------------------------------ -// Loader meta information -const aiImporterDesc* AC3DImporter::GetInfo () const -{ - return &desc; -} - -// ------------------------------------------------------------------------------------------------ -// Get a pointer to the next line from the file -bool AC3DImporter::GetNextLine( ) -{ - SkipLine(&buffer); - return SkipSpaces(&buffer); -} - -// ------------------------------------------------------------------------------------------------ -// Parse an object section in an AC file -void AC3DImporter::LoadObjectSection(std::vector& objects) -{ - if (!TokenMatch(buffer,"OBJECT",6)) - return; - - SkipSpaces(&buffer); - - ++mNumMeshes; - - objects.push_back(Object()); - Object& obj = objects.back(); - - aiLight* light = NULL; - if (!ASSIMP_strincmp(buffer,"light",5)) - { - // This is a light source. Add it to the list - mLights->push_back(light = new aiLight()); - - // Return a point light with no attenuation - light->mType = aiLightSource_POINT; - light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f,1.f,1.f); - light->mAttenuationConstant = 1.f; - - // Generate a default name for both the light source and the node - // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version. - light->mName.length = ::sprintf(light->mName.data,"ACLight_%i",static_cast(mLights->size())-1); - obj.name = std::string( light->mName.data ); - - DefaultLogger::get()->debug("AC3D: Light source encountered"); - obj.type = Object::Light; - } - else if (!ASSIMP_strincmp(buffer,"group",5)) - { - obj.type = Object::Group; - } - else if (!ASSIMP_strincmp(buffer,"world",5)) - { - obj.type = Object::World; - } - else obj.type = Object::Poly; - while (GetNextLine()) - { - if (TokenMatch(buffer,"kids",4)) - { - SkipSpaces(&buffer); - unsigned int num = strtoul10(buffer,&buffer); - GetNextLine(); - if (num) - { - // load the children of this object recursively - obj.children.reserve(num); - for (unsigned int i = 0; i < num; ++i) - LoadObjectSection(obj.children); - } - return; - } - else if (TokenMatch(buffer,"name",4)) - { - SkipSpaces(&buffer); - AI_AC_GET_STRING(obj.name); - - // If this is a light source, we'll also need to store - // the name of the node in it. - if (light) - { - light->mName.Set(obj.name); - } - } - else if (TokenMatch(buffer,"texture",7)) - { - SkipSpaces(&buffer); - AI_AC_GET_STRING(obj.texture); - } - else if (TokenMatch(buffer,"texrep",6)) - { - SkipSpaces(&buffer); - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texRepeat); - if (!obj.texRepeat.x || !obj.texRepeat.y) - obj.texRepeat = aiVector2D (1.f,1.f); - } - else if (TokenMatch(buffer,"texoff",6)) - { - SkipSpaces(&buffer); - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texOffset); - } - else if (TokenMatch(buffer,"rot",3)) - { - SkipSpaces(&buffer); - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,9,&obj.rotation); - } - else if (TokenMatch(buffer,"loc",3)) - { - SkipSpaces(&buffer); - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&obj.translation); - } - else if (TokenMatch(buffer,"subdiv",6)) - { - SkipSpaces(&buffer); - obj.subDiv = strtoul10(buffer,&buffer); - } - else if (TokenMatch(buffer,"crease",6)) - { - SkipSpaces(&buffer); - obj.crease = fast_atof(buffer); - } - else if (TokenMatch(buffer,"numvert",7)) - { - SkipSpaces(&buffer); - - unsigned int t = strtoul10(buffer,&buffer); - if (t >= std::numeric_limits::max() / sizeof(aiVector3D)) { - throw DeadlyImportError("AC3D: Too many vertices, would run out of memory"); - } - obj.vertices.reserve(t); - for (unsigned int i = 0; i < t;++i) - { - if (!GetNextLine()) - { - DefaultLogger::get()->error("AC3D: Unexpected EOF: not all vertices have been parsed yet"); - break; - } - else if (!IsNumeric(*buffer)) - { - DefaultLogger::get()->error("AC3D: Unexpected token: not all vertices have been parsed yet"); - --buffer; // make sure the line is processed a second time - break; - } - obj.vertices.push_back(aiVector3D()); - aiVector3D& v = obj.vertices.back(); - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&v.x); - } - } - else if (TokenMatch(buffer,"numsurf",7)) - { - SkipSpaces(&buffer); - - bool Q3DWorkAround = false; - - const unsigned int t = strtoul10(buffer,&buffer); - obj.surfaces.reserve(t); - for (unsigned int i = 0; i < t;++i) - { - GetNextLine(); - if (!TokenMatch(buffer,"SURF",4)) - { - // FIX: this can occur for some files - Quick 3D for - // example writes no surf chunks - if (!Q3DWorkAround) - { - DefaultLogger::get()->warn("AC3D: SURF token was expected"); - DefaultLogger::get()->debug("Continuing with Quick3D Workaround enabled"); - } - --buffer; // make sure the line is processed a second time - // break; --- see fix notes above - - Q3DWorkAround = true; - } - SkipSpaces(&buffer); - obj.surfaces.push_back(Surface()); - Surface& surf = obj.surfaces.back(); - surf.flags = strtoul_cppstyle(buffer); - - while (1) - { - if(!GetNextLine()) - { - DefaultLogger::get()->error("AC3D: Unexpected EOF: surface is incomplete"); - break; - } - if (TokenMatch(buffer,"mat",3)) - { - SkipSpaces(&buffer); - surf.mat = strtoul10(buffer); - } - else if (TokenMatch(buffer,"refs",4)) - { - // --- see fix notes above - if (Q3DWorkAround) - { - if (!surf.entries.empty()) - { - buffer -= 6; - break; - } - } - - SkipSpaces(&buffer); - const unsigned int m = strtoul10(buffer); - surf.entries.reserve(m); - - obj.numRefs += m; - - for (unsigned int k = 0; k < m; ++k) - { - if(!GetNextLine()) - { - DefaultLogger::get()->error("AC3D: Unexpected EOF: surface references are incomplete"); - break; - } - surf.entries.push_back(Surface::SurfaceEntry()); - Surface::SurfaceEntry& entry = surf.entries.back(); - - entry.first = strtoul10(buffer,&buffer); - SkipSpaces(&buffer); - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&entry.second); - } - } - else - { - - --buffer; // make sure the line is processed a second time - break; - } - } - } - } - } - DefaultLogger::get()->error("AC3D: Unexpected EOF: \'kids\' line was expected"); -} - -// ------------------------------------------------------------------------------------------------ -// Convert a material from AC3DImporter::Material to aiMaterial -void AC3DImporter::ConvertMaterial(const Object& object, - const Material& matSrc, - aiMaterial& matDest) -{ - aiString s; - - if (matSrc.name.length()) - { - s.Set(matSrc.name); - matDest.AddProperty(&s,AI_MATKEY_NAME); - } - if (object.texture.length()) - { - s.Set(object.texture); - matDest.AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); - - // UV transformation - if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y || - object.texOffset.x || object.texOffset.y) - { - aiUVTransform transform; - transform.mScaling = object.texRepeat; - transform.mTranslation = object.texOffset; - matDest.AddProperty(&transform,1,AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); - } - } - - matDest.AddProperty(&matSrc.rgb,1, AI_MATKEY_COLOR_DIFFUSE); - matDest.AddProperty(&matSrc.amb,1, AI_MATKEY_COLOR_AMBIENT); - matDest.AddProperty(&matSrc.emis,1,AI_MATKEY_COLOR_EMISSIVE); - matDest.AddProperty(&matSrc.spec,1,AI_MATKEY_COLOR_SPECULAR); - - int n; - if (matSrc.shin) - { - n = aiShadingMode_Phong; - matDest.AddProperty(&matSrc.shin,1,AI_MATKEY_SHININESS); - } - else n = aiShadingMode_Gouraud; - matDest.AddProperty(&n,1,AI_MATKEY_SHADING_MODEL); - - float f = 1.f - matSrc.trans; - matDest.AddProperty(&f,1,AI_MATKEY_OPACITY); -} - -// ------------------------------------------------------------------------------------------------ -// Converts the loaded data to the internal verbose representation -aiNode* AC3DImporter::ConvertObjectSection(Object& object, - std::vector& meshes, - std::vector& outMaterials, - const std::vector& materials, - aiNode* parent) -{ - aiNode* node = new aiNode(); - node->mParent = parent; - if (object.vertices.size()) - { - if (!object.surfaces.size() || !object.numRefs) - { - /* " An object with 7 vertices (no surfaces, no materials defined). - This is a good way of getting point data into AC3D. - The Vertex->create convex-surface/object can be used on these - vertices to 'wrap' a 3d shape around them " - (http://www.opencity.info/html/ac3dfileformat.html) - - therefore: if no surfaces are defined return point data only - */ - - DefaultLogger::get()->info("AC3D: No surfaces defined in object definition, " - "a point list is returned"); - - meshes.push_back(new aiMesh()); - aiMesh* mesh = meshes.back(); - - mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size(); - aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; - aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - - for (unsigned int i = 0; i < mesh->mNumVertices;++i,++faces,++verts) - { - *verts = object.vertices[i]; - faces->mNumIndices = 1; - faces->mIndices = new unsigned int[1]; - faces->mIndices[0] = i; - } - - // use the primary material in this case. this should be the - // default material if all objects of the file contain points - // and no faces. - mesh->mMaterialIndex = 0; - outMaterials.push_back(new aiMaterial()); - ConvertMaterial(object, materials[0], *outMaterials.back()); - } - else - { - // need to generate one or more meshes for this object. - // find out how many different materials we have - typedef std::pair< unsigned int, unsigned int > IntPair; - typedef std::vector< IntPair > MatTable; - MatTable needMat(materials.size(),IntPair(0,0)); - - std::vector::iterator it,end = object.surfaces.end(); - std::vector::iterator it2,end2; - - for (it = object.surfaces.begin(); it != end; ++it) - { - unsigned int idx = (*it).mat; - if (idx >= needMat.size()) - { - DefaultLogger::get()->error("AC3D: material index is out of range"); - idx = 0; - } - if ((*it).entries.empty()) - { - DefaultLogger::get()->warn("AC3D: surface her zero vertex references"); - } - - // validate all vertex indices to make sure we won't crash here - for (it2 = (*it).entries.begin(), - end2 = (*it).entries.end(); it2 != end2; ++it2) - { - if ((*it2).first >= object.vertices.size()) - { - DefaultLogger::get()->warn("AC3D: Invalid vertex reference"); - (*it2).first = 0; - } - } - - if (!needMat[idx].first)++node->mNumMeshes; - - switch ((*it).flags & 0xf) - { - // closed line - case 0x1: - - needMat[idx].first += (unsigned int)(*it).entries.size(); - needMat[idx].second += (unsigned int)(*it).entries.size()<<1u; - break; - - // unclosed line - case 0x2: - - needMat[idx].first += (unsigned int)(*it).entries.size()-1; - needMat[idx].second += ((unsigned int)(*it).entries.size()-1)<<1u; - break; - - // 0 == polygon, else unknown - default: - - if ((*it).flags & 0xf) - { - DefaultLogger::get()->warn("AC3D: The type flag of a surface is unknown"); - (*it).flags &= ~(0xf); - } - - // the number of faces increments by one, the number - // of vertices by surface.numref. - needMat[idx].first++; - needMat[idx].second += (unsigned int)(*it).entries.size(); - }; - } - unsigned int* pip = node->mMeshes = new unsigned int[node->mNumMeshes]; - unsigned int mat = 0; - const size_t oldm = meshes.size(); - for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end(); - cit != cend; ++cit, ++mat) - { - if (!(*cit).first)continue; - - // allocate a new aiMesh object - *pip++ = (unsigned int)meshes.size(); - aiMesh* mesh = new aiMesh(); - meshes.push_back(mesh); - - mesh->mMaterialIndex = (unsigned int)outMaterials.size(); - outMaterials.push_back(new aiMaterial()); - ConvertMaterial(object, materials[mat], *outMaterials.back()); - - // allocate storage for vertices and normals - mesh->mNumFaces = (*cit).first; - aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; - - mesh->mNumVertices = (*cit).second; - aiVector3D* vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - unsigned int cur = 0; - - // allocate UV coordinates, but only if the texture name for the - // surface is not empty - aiVector3D* uv = NULL; - if(object.texture.length()) - { - uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; - mesh->mNumUVComponents[0] = 2; - } - - for (it = object.surfaces.begin(); it != end; ++it) - { - if (mat == (*it).mat) - { - const Surface& src = *it; - - // closed polygon - unsigned int type = (*it).flags & 0xf; - if (!type) - { - aiFace& face = *faces++; - if((face.mNumIndices = (unsigned int)src.entries.size())) - { - face.mIndices = new unsigned int[face.mNumIndices]; - for (unsigned int i = 0; i < face.mNumIndices;++i,++vertices) - { - const Surface::SurfaceEntry& entry = src.entries[i]; - face.mIndices[i] = cur++; - - // copy vertex positions - if ((vertices - mesh->mVertices) >= mesh->mNumVertices) { - throw DeadlyImportError("AC3D: Invalid number of vertices"); - } - *vertices = object.vertices[entry.first] + object.translation; - - - // copy texture coordinates - if (uv) - { - uv->x = entry.second.x; - uv->y = entry.second.y; - ++uv; - } - } - } - } - else - { - - it2 = (*it).entries.begin(); - - // either a closed or an unclosed line - unsigned int tmp = (unsigned int)(*it).entries.size(); - if (0x2 == type)--tmp; - for (unsigned int m = 0; m < tmp;++m) - { - aiFace& face = *faces++; - - face.mNumIndices = 2; - face.mIndices = new unsigned int[2]; - face.mIndices[0] = cur++; - face.mIndices[1] = cur++; - - // copy vertex positions - if (it2 == (*it).entries.end() ) { - throw DeadlyImportError("AC3D: Bad line"); - } - ai_assert((*it2).first < object.vertices.size()); - *vertices++ = object.vertices[(*it2).first]; - - // copy texture coordinates - if (uv) - { - uv->x = (*it2).second.x; - uv->y = (*it2).second.y; - ++uv; - } - - - if (0x1 == type && tmp-1 == m) - { - // if this is a closed line repeat its beginning now - it2 = (*it).entries.begin(); - } - else ++it2; - - // second point - *vertices++ = object.vertices[(*it2).first]; - - if (uv) - { - uv->x = (*it2).second.x; - uv->y = (*it2).second.y; - ++uv; - } - } - } - } - } - } - - // Now apply catmull clark subdivision if necessary. We split meshes into - // materials which is not done by AC3D during smoothing, so we need to - // collect all meshes using the same material group. - if (object.subDiv) { - if (configEvalSubdivision) { - boost::scoped_ptr div(Subdivider::Create(Subdivider::CATMULL_CLARKE)); - DefaultLogger::get()->info("AC3D: Evaluating subdivision surface: "+object.name); - - std::vector cpy(meshes.size()-oldm,NULL); - div->Subdivide(&meshes[oldm],cpy.size(),&cpy.front(),object.subDiv,true); - std::copy(cpy.begin(),cpy.end(),meshes.begin()+oldm); - - // previous meshes are deleted vy Subdivide(). - } - else { - DefaultLogger::get()->info("AC3D: Letting the subdivision surface untouched due to my configuration: " - +object.name); - } - } - } - } - - if (object.name.length()) - node->mName.Set(object.name); - else - { - // generate a name depending on the type of the node - switch (object.type) - { - case Object::Group: - node->mName.length = ::sprintf(node->mName.data,"ACGroup_%i",groups++); - break; - case Object::Poly: - node->mName.length = ::sprintf(node->mName.data,"ACPoly_%i",polys++); - break; - case Object::Light: - node->mName.length = ::sprintf(node->mName.data,"ACLight_%i",lights++); - break; - - // there shouldn't be more than one world, but we don't care - case Object::World: - node->mName.length = ::sprintf(node->mName.data,"ACWorld_%i",worlds++); - break; - } - } - - - // setup the local transformation matrix of the object - // compute the transformation offset to the parent node - node->mTransformation = aiMatrix4x4 ( object.rotation ); - - if (object.type == Object::Group || !object.numRefs) - { - node->mTransformation.a4 = object.translation.x; - node->mTransformation.b4 = object.translation.y; - node->mTransformation.c4 = object.translation.z; - } - - // add children to the object - if (object.children.size()) - { - node->mNumChildren = (unsigned int)object.children.size(); - node->mChildren = new aiNode*[node->mNumChildren]; - for (unsigned int i = 0; i < node->mNumChildren;++i) - { - node->mChildren[i] = ConvertObjectSection(object.children[i],meshes,outMaterials,materials,node); - } - } - - return node; -} - -// ------------------------------------------------------------------------------------------------ -void AC3DImporter::SetupProperties(const Importer* pImp) -{ - configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL,1) ? true : false; - configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION,1) ? true : false; -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void AC3DImporter::InternReadFile( const std::string& pFile, - aiScene* pScene, IOSystem* pIOHandler) -{ - boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); - - // Check whether we can read from the file - if( file.get() == NULL) - throw DeadlyImportError( "Failed to open AC3D file " + pFile + "."); - - // allocate storage and copy the contents of the file to a memory buffer - std::vector mBuffer2; - TextFileToBuffer(file.get(),mBuffer2); - - buffer = &mBuffer2[0]; - mNumMeshes = 0; - - lights = polys = worlds = groups = 0; - - if (::strncmp(buffer,"AC3D",4)) { - throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found"); - } - - // print the file format version to the console - unsigned int version = HexDigitToDecimal( buffer[4] ); - char msg[3]; - ASSIMP_itoa10(msg,3,version); - DefaultLogger::get()->info(std::string("AC3D file format version: ") + msg); - - std::vector materials; - materials.reserve(5); - - std::vector rootObjects; - rootObjects.reserve(5); - - std::vector lights; - mLights = & lights; - - while (GetNextLine()) - { - if (TokenMatch(buffer,"MATERIAL",8)) - { - materials.push_back(Material()); - Material& mat = materials.back(); - - // manually parse the material ... sscanf would use the buldin atof ... - // Format: (name) rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f - - AI_AC_SKIP_TO_NEXT_TOKEN(); - if ('\"' == *buffer) - { - AI_AC_GET_STRING(mat.name); - AI_AC_SKIP_TO_NEXT_TOKEN(); - } - - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("rgb",3,3,&mat.rgb); - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("amb",3,3,&mat.amb); - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("emis",4,3,&mat.emis); - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("spec",4,3,&mat.spec); - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("shi",3,1,&mat.shin); - AI_AC_CHECKED_LOAD_FLOAT_ARRAY("trans",5,1,&mat.trans); - } - LoadObjectSection(rootObjects); - } - - if (rootObjects.empty() || !mNumMeshes) - { - throw DeadlyImportError("AC3D: No meshes have been loaded"); - } - if (materials.empty()) - { - DefaultLogger::get()->warn("AC3D: No material has been found"); - materials.push_back(Material()); - } - - mNumMeshes += (mNumMeshes>>2u) + 1; - std::vector meshes; - meshes.reserve(mNumMeshes); - - std::vector omaterials; - materials.reserve(mNumMeshes); - - // generate a dummy root if there are multiple objects on the top layer - Object* root; - if (1 == rootObjects.size()) - root = &rootObjects[0]; - else - { - root = new Object(); - } - - // now convert the imported stuff to our output data structure - pScene->mRootNode = ConvertObjectSection(*root,meshes,omaterials,materials); - if (1 != rootObjects.size())delete root; - - if (!::strncmp( pScene->mRootNode->mName.data, "Node", 4)) - pScene->mRootNode->mName.Set(""); - - // copy meshes - if (meshes.empty()) - { - throw DeadlyImportError("An unknown error occured during converting"); - } - pScene->mNumMeshes = (unsigned int)meshes.size(); - pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; - ::memcpy(pScene->mMeshes,&meshes[0],pScene->mNumMeshes*sizeof(void*)); - - // copy materials - pScene->mNumMaterials = (unsigned int)omaterials.size(); - pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; - ::memcpy(pScene->mMaterials,&omaterials[0],pScene->mNumMaterials*sizeof(void*)); - - // copy lights - pScene->mNumLights = (unsigned int)lights.size(); - if (lights.size()) - { - pScene->mLights = new aiLight*[lights.size()]; - ::memcpy(pScene->mLights,&lights[0],lights.size()*sizeof(void*)); - } -} - -#endif //!defined ASSIMP_BUILD_NO_AC_IMPORTER + +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of the AC3D importer class */ + + + +#ifndef ASSIMP_BUILD_NO_AC_IMPORTER + +// internal headers +#include "ACLoader.h" +#include "ParsingUtils.h" +#include "fast_atof.h" +#include "Subdivision.h" +#include "Importer.h" +#include "BaseImporter.h" +#include "../include/assimp/Importer.hpp" +#include "../include/assimp/light.h" +#include "../include/assimp/DefaultLogger.hpp" +#include "../include/assimp/material.h" +#include "../include/assimp/scene.h" +#include "../include/assimp/config.h" +#include "../include/assimp/IOSystem.hpp" +#include + +using namespace Assimp; + +static const aiImporterDesc desc = { + "AC3D Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "ac acc ac3d" +}; + +// ------------------------------------------------------------------------------------------------ +// skip to the next token +#define AI_AC_SKIP_TO_NEXT_TOKEN() \ + if (!SkipSpaces(&buffer)) \ + { \ + DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL"); \ + continue; \ + } + +// ------------------------------------------------------------------------------------------------ +// read a string (may be enclosed in double quotation marks). buffer must point to " +#define AI_AC_GET_STRING(out) \ + ++buffer; \ + const char* sz = buffer; \ + while ('\"' != *buffer) \ + { \ + if (IsLineEnd( *buffer )) \ + { \ + DefaultLogger::get()->error("AC3D: Unexpected EOF/EOL in string"); \ + out = "ERROR"; \ + break; \ + } \ + ++buffer; \ + } \ + if (IsLineEnd( *buffer ))continue; \ + out = std::string(sz,(unsigned int)(buffer-sz)); \ + ++buffer; + + +// ------------------------------------------------------------------------------------------------ +// read 1 to n floats prefixed with an optional predefined identifier +#define AI_AC_CHECKED_LOAD_FLOAT_ARRAY(name,name_length,num,out) \ + AI_AC_SKIP_TO_NEXT_TOKEN(); \ + if (name_length) \ + { \ + if (strncmp(buffer,name,name_length) || !IsSpace(buffer[name_length])) \ + { \ + DefaultLogger::get()->error("AC3D: Unexpexted token. " name " was expected."); \ + continue; \ + } \ + buffer += name_length+1; \ + } \ + for (unsigned int i = 0; i < num;++i) \ + { \ + AI_AC_SKIP_TO_NEXT_TOKEN(); \ + buffer = fast_atoreal_move(buffer,((float*)out)[i]); \ + } + + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +AC3DImporter::AC3DImporter() +{ + // nothing to be done here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +AC3DImporter::~AC3DImporter() +{ + // nothing to be done here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +{ + std::string extension = GetExtension(pFile); + + // fixme: are acc and ac3d *really* used? Some sources say they are + if(extension == "ac" || extension == "ac3d" || extension == "acc") { + return true; + } + if (!extension.length() || checkSig) { + uint32_t token = AI_MAKE_MAGIC("AC3D"); + return CheckMagicToken(pIOHandler,pFile,&token,1,0); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Loader meta information +const aiImporterDesc* AC3DImporter::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Get a pointer to the next line from the file +bool AC3DImporter::GetNextLine( ) +{ + SkipLine(&buffer); + return SkipSpaces(&buffer); +} + +// ------------------------------------------------------------------------------------------------ +// Parse an object section in an AC file +void AC3DImporter::LoadObjectSection(std::vector& objects) +{ + if (!TokenMatch(buffer,"OBJECT",6)) + return; + + SkipSpaces(&buffer); + + ++mNumMeshes; + + objects.push_back(Object()); + Object& obj = objects.back(); + + aiLight* light = NULL; + if (!ASSIMP_strincmp(buffer,"light",5)) + { + // This is a light source. Add it to the list + mLights->push_back(light = new aiLight()); + + // Return a point light with no attenuation + light->mType = aiLightSource_POINT; + light->mColorDiffuse = light->mColorSpecular = aiColor3D(1.f,1.f,1.f); + light->mAttenuationConstant = 1.f; + + // Generate a default name for both the light source and the node + // FIXME - what's the right way to print a size_t? Is 'zu' universally available? stick with the safe version. + light->mName.length = ::sprintf(light->mName.data,"ACLight_%i",static_cast(mLights->size())-1); + obj.name = std::string( light->mName.data ); + + DefaultLogger::get()->debug("AC3D: Light source encountered"); + obj.type = Object::Light; + } + else if (!ASSIMP_strincmp(buffer,"group",5)) + { + obj.type = Object::Group; + } + else if (!ASSIMP_strincmp(buffer,"world",5)) + { + obj.type = Object::World; + } + else obj.type = Object::Poly; + while (GetNextLine()) + { + if (TokenMatch(buffer,"kids",4)) + { + SkipSpaces(&buffer); + unsigned int num = strtoul10(buffer,&buffer); + GetNextLine(); + if (num) + { + // load the children of this object recursively + obj.children.reserve(num); + for (unsigned int i = 0; i < num; ++i) + LoadObjectSection(obj.children); + } + return; + } + else if (TokenMatch(buffer,"name",4)) + { + SkipSpaces(&buffer); + AI_AC_GET_STRING(obj.name); + + // If this is a light source, we'll also need to store + // the name of the node in it. + if (light) + { + light->mName.Set(obj.name); + } + } + else if (TokenMatch(buffer,"texture",7)) + { + SkipSpaces(&buffer); + AI_AC_GET_STRING(obj.texture); + } + else if (TokenMatch(buffer,"texrep",6)) + { + SkipSpaces(&buffer); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texRepeat); + if (!obj.texRepeat.x || !obj.texRepeat.y) + obj.texRepeat = aiVector2D (1.f,1.f); + } + else if (TokenMatch(buffer,"texoff",6)) + { + SkipSpaces(&buffer); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&obj.texOffset); + } + else if (TokenMatch(buffer,"rot",3)) + { + SkipSpaces(&buffer); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,9,&obj.rotation); + } + else if (TokenMatch(buffer,"loc",3)) + { + SkipSpaces(&buffer); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&obj.translation); + } + else if (TokenMatch(buffer,"subdiv",6)) + { + SkipSpaces(&buffer); + obj.subDiv = strtoul10(buffer,&buffer); + } + else if (TokenMatch(buffer,"crease",6)) + { + SkipSpaces(&buffer); + obj.crease = fast_atof(buffer); + } + else if (TokenMatch(buffer,"numvert",7)) + { + SkipSpaces(&buffer); + + unsigned int t = strtoul10(buffer,&buffer); + if (t >= std::numeric_limits::max() / sizeof(aiVector3D)) { + throw DeadlyImportError("AC3D: Too many vertices, would run out of memory"); + } + obj.vertices.reserve(t); + for (unsigned int i = 0; i < t;++i) + { + if (!GetNextLine()) + { + DefaultLogger::get()->error("AC3D: Unexpected EOF: not all vertices have been parsed yet"); + break; + } + else if (!IsNumeric(*buffer)) + { + DefaultLogger::get()->error("AC3D: Unexpected token: not all vertices have been parsed yet"); + --buffer; // make sure the line is processed a second time + break; + } + obj.vertices.push_back(aiVector3D()); + aiVector3D& v = obj.vertices.back(); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,3,&v.x); + } + } + else if (TokenMatch(buffer,"numsurf",7)) + { + SkipSpaces(&buffer); + + bool Q3DWorkAround = false; + + const unsigned int t = strtoul10(buffer,&buffer); + obj.surfaces.reserve(t); + for (unsigned int i = 0; i < t;++i) + { + GetNextLine(); + if (!TokenMatch(buffer,"SURF",4)) + { + // FIX: this can occur for some files - Quick 3D for + // example writes no surf chunks + if (!Q3DWorkAround) + { + DefaultLogger::get()->warn("AC3D: SURF token was expected"); + DefaultLogger::get()->debug("Continuing with Quick3D Workaround enabled"); + } + --buffer; // make sure the line is processed a second time + // break; --- see fix notes above + + Q3DWorkAround = true; + } + SkipSpaces(&buffer); + obj.surfaces.push_back(Surface()); + Surface& surf = obj.surfaces.back(); + surf.flags = strtoul_cppstyle(buffer); + + while (1) + { + if(!GetNextLine()) + { + DefaultLogger::get()->error("AC3D: Unexpected EOF: surface is incomplete"); + break; + } + if (TokenMatch(buffer,"mat",3)) + { + SkipSpaces(&buffer); + surf.mat = strtoul10(buffer); + } + else if (TokenMatch(buffer,"refs",4)) + { + // --- see fix notes above + if (Q3DWorkAround) + { + if (!surf.entries.empty()) + { + buffer -= 6; + break; + } + } + + SkipSpaces(&buffer); + const unsigned int m = strtoul10(buffer); + surf.entries.reserve(m); + + obj.numRefs += m; + + for (unsigned int k = 0; k < m; ++k) + { + if(!GetNextLine()) + { + DefaultLogger::get()->error("AC3D: Unexpected EOF: surface references are incomplete"); + break; + } + surf.entries.push_back(Surface::SurfaceEntry()); + Surface::SurfaceEntry& entry = surf.entries.back(); + + entry.first = strtoul10(buffer,&buffer); + SkipSpaces(&buffer); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("",0,2,&entry.second); + } + } + else + { + + --buffer; // make sure the line is processed a second time + break; + } + } + } + } + } + DefaultLogger::get()->error("AC3D: Unexpected EOF: \'kids\' line was expected"); +} + +// ------------------------------------------------------------------------------------------------ +// Convert a material from AC3DImporter::Material to aiMaterial +void AC3DImporter::ConvertMaterial(const Object& object, + const Material& matSrc, + aiMaterial& matDest) +{ + aiString s; + + if (matSrc.name.length()) + { + s.Set(matSrc.name); + matDest.AddProperty(&s,AI_MATKEY_NAME); + } + if (object.texture.length()) + { + s.Set(object.texture); + matDest.AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); + + // UV transformation + if (1.f != object.texRepeat.x || 1.f != object.texRepeat.y || + object.texOffset.x || object.texOffset.y) + { + aiUVTransform transform; + transform.mScaling = object.texRepeat; + transform.mTranslation = object.texOffset; + matDest.AddProperty(&transform,1,AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); + } + } + + matDest.AddProperty(&matSrc.rgb,1, AI_MATKEY_COLOR_DIFFUSE); + matDest.AddProperty(&matSrc.amb,1, AI_MATKEY_COLOR_AMBIENT); + matDest.AddProperty(&matSrc.emis,1,AI_MATKEY_COLOR_EMISSIVE); + matDest.AddProperty(&matSrc.spec,1,AI_MATKEY_COLOR_SPECULAR); + + int n; + if (matSrc.shin) + { + n = aiShadingMode_Phong; + matDest.AddProperty(&matSrc.shin,1,AI_MATKEY_SHININESS); + } + else n = aiShadingMode_Gouraud; + matDest.AddProperty(&n,1,AI_MATKEY_SHADING_MODEL); + + float f = 1.f - matSrc.trans; + matDest.AddProperty(&f,1,AI_MATKEY_OPACITY); +} + +// ------------------------------------------------------------------------------------------------ +// Converts the loaded data to the internal verbose representation +aiNode* AC3DImporter::ConvertObjectSection(Object& object, + std::vector& meshes, + std::vector& outMaterials, + const std::vector& materials, + aiNode* parent) +{ + aiNode* node = new aiNode(); + node->mParent = parent; + if (object.vertices.size()) + { + if (!object.surfaces.size() || !object.numRefs) + { + /* " An object with 7 vertices (no surfaces, no materials defined). + This is a good way of getting point data into AC3D. + The Vertex->create convex-surface/object can be used on these + vertices to 'wrap' a 3d shape around them " + (http://www.opencity.info/html/ac3dfileformat.html) + + therefore: if no surfaces are defined return point data only + */ + + DefaultLogger::get()->info("AC3D: No surfaces defined in object definition, " + "a point list is returned"); + + meshes.push_back(new aiMesh()); + aiMesh* mesh = meshes.back(); + + mesh->mNumFaces = mesh->mNumVertices = (unsigned int)object.vertices.size(); + aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; + aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + + for (unsigned int i = 0; i < mesh->mNumVertices;++i,++faces,++verts) + { + *verts = object.vertices[i]; + faces->mNumIndices = 1; + faces->mIndices = new unsigned int[1]; + faces->mIndices[0] = i; + } + + // use the primary material in this case. this should be the + // default material if all objects of the file contain points + // and no faces. + mesh->mMaterialIndex = 0; + outMaterials.push_back(new aiMaterial()); + ConvertMaterial(object, materials[0], *outMaterials.back()); + } + else + { + // need to generate one or more meshes for this object. + // find out how many different materials we have + typedef std::pair< unsigned int, unsigned int > IntPair; + typedef std::vector< IntPair > MatTable; + MatTable needMat(materials.size(),IntPair(0,0)); + + std::vector::iterator it,end = object.surfaces.end(); + std::vector::iterator it2,end2; + + for (it = object.surfaces.begin(); it != end; ++it) + { + unsigned int idx = (*it).mat; + if (idx >= needMat.size()) + { + DefaultLogger::get()->error("AC3D: material index is out of range"); + idx = 0; + } + if ((*it).entries.empty()) + { + DefaultLogger::get()->warn("AC3D: surface her zero vertex references"); + } + + // validate all vertex indices to make sure we won't crash here + for (it2 = (*it).entries.begin(), + end2 = (*it).entries.end(); it2 != end2; ++it2) + { + if ((*it2).first >= object.vertices.size()) + { + DefaultLogger::get()->warn("AC3D: Invalid vertex reference"); + (*it2).first = 0; + } + } + + if (!needMat[idx].first)++node->mNumMeshes; + + switch ((*it).flags & 0xf) + { + // closed line + case 0x1: + + needMat[idx].first += (unsigned int)(*it).entries.size(); + needMat[idx].second += (unsigned int)(*it).entries.size()<<1u; + break; + + // unclosed line + case 0x2: + + needMat[idx].first += (unsigned int)(*it).entries.size()-1; + needMat[idx].second += ((unsigned int)(*it).entries.size()-1)<<1u; + break; + + // 0 == polygon, else unknown + default: + + if ((*it).flags & 0xf) + { + DefaultLogger::get()->warn("AC3D: The type flag of a surface is unknown"); + (*it).flags &= ~(0xf); + } + + // the number of faces increments by one, the number + // of vertices by surface.numref. + needMat[idx].first++; + needMat[idx].second += (unsigned int)(*it).entries.size(); + }; + } + unsigned int* pip = node->mMeshes = new unsigned int[node->mNumMeshes]; + unsigned int mat = 0; + const size_t oldm = meshes.size(); + for (MatTable::const_iterator cit = needMat.begin(), cend = needMat.end(); + cit != cend; ++cit, ++mat) + { + if (!(*cit).first)continue; + + // allocate a new aiMesh object + *pip++ = (unsigned int)meshes.size(); + aiMesh* mesh = new aiMesh(); + meshes.push_back(mesh); + + mesh->mMaterialIndex = (unsigned int)outMaterials.size(); + outMaterials.push_back(new aiMaterial()); + ConvertMaterial(object, materials[mat], *outMaterials.back()); + + // allocate storage for vertices and normals + mesh->mNumFaces = (*cit).first; + aiFace* faces = mesh->mFaces = new aiFace[mesh->mNumFaces]; + + mesh->mNumVertices = (*cit).second; + aiVector3D* vertices = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + unsigned int cur = 0; + + // allocate UV coordinates, but only if the texture name for the + // surface is not empty + aiVector3D* uv = NULL; + if(object.texture.length()) + { + uv = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; + mesh->mNumUVComponents[0] = 2; + } + + for (it = object.surfaces.begin(); it != end; ++it) + { + if (mat == (*it).mat) + { + const Surface& src = *it; + + // closed polygon + unsigned int type = (*it).flags & 0xf; + if (!type) + { + aiFace& face = *faces++; + if((face.mNumIndices = (unsigned int)src.entries.size())) + { + face.mIndices = new unsigned int[face.mNumIndices]; + for (unsigned int i = 0; i < face.mNumIndices;++i,++vertices) + { + const Surface::SurfaceEntry& entry = src.entries[i]; + face.mIndices[i] = cur++; + + // copy vertex positions + if ((vertices - mesh->mVertices) >= mesh->mNumVertices) { + throw DeadlyImportError("AC3D: Invalid number of vertices"); + } + *vertices = object.vertices[entry.first] + object.translation; + + + // copy texture coordinates + if (uv) + { + uv->x = entry.second.x; + uv->y = entry.second.y; + ++uv; + } + } + } + } + else + { + + it2 = (*it).entries.begin(); + + // either a closed or an unclosed line + unsigned int tmp = (unsigned int)(*it).entries.size(); + if (0x2 == type)--tmp; + for (unsigned int m = 0; m < tmp;++m) + { + aiFace& face = *faces++; + + face.mNumIndices = 2; + face.mIndices = new unsigned int[2]; + face.mIndices[0] = cur++; + face.mIndices[1] = cur++; + + // copy vertex positions + if (it2 == (*it).entries.end() ) { + throw DeadlyImportError("AC3D: Bad line"); + } + ai_assert((*it2).first < object.vertices.size()); + *vertices++ = object.vertices[(*it2).first]; + + // copy texture coordinates + if (uv) + { + uv->x = (*it2).second.x; + uv->y = (*it2).second.y; + ++uv; + } + + + if (0x1 == type && tmp-1 == m) + { + // if this is a closed line repeat its beginning now + it2 = (*it).entries.begin(); + } + else ++it2; + + // second point + *vertices++ = object.vertices[(*it2).first]; + + if (uv) + { + uv->x = (*it2).second.x; + uv->y = (*it2).second.y; + ++uv; + } + } + } + } + } + } + + // Now apply catmull clark subdivision if necessary. We split meshes into + // materials which is not done by AC3D during smoothing, so we need to + // collect all meshes using the same material group. + if (object.subDiv) { + if (configEvalSubdivision) { + boost::scoped_ptr div(Subdivider::Create(Subdivider::CATMULL_CLARKE)); + DefaultLogger::get()->info("AC3D: Evaluating subdivision surface: "+object.name); + + std::vector cpy(meshes.size()-oldm,NULL); + div->Subdivide(&meshes[oldm],cpy.size(),&cpy.front(),object.subDiv,true); + std::copy(cpy.begin(),cpy.end(),meshes.begin()+oldm); + + // previous meshes are deleted vy Subdivide(). + } + else { + DefaultLogger::get()->info("AC3D: Letting the subdivision surface untouched due to my configuration: " + +object.name); + } + } + } + } + + if (object.name.length()) + node->mName.Set(object.name); + else + { + // generate a name depending on the type of the node + switch (object.type) + { + case Object::Group: + node->mName.length = ::sprintf(node->mName.data,"ACGroup_%i",groups++); + break; + case Object::Poly: + node->mName.length = ::sprintf(node->mName.data,"ACPoly_%i",polys++); + break; + case Object::Light: + node->mName.length = ::sprintf(node->mName.data,"ACLight_%i",lights++); + break; + + // there shouldn't be more than one world, but we don't care + case Object::World: + node->mName.length = ::sprintf(node->mName.data,"ACWorld_%i",worlds++); + break; + } + } + + + // setup the local transformation matrix of the object + // compute the transformation offset to the parent node + node->mTransformation = aiMatrix4x4 ( object.rotation ); + + if (object.type == Object::Group || !object.numRefs) + { + node->mTransformation.a4 = object.translation.x; + node->mTransformation.b4 = object.translation.y; + node->mTransformation.c4 = object.translation.z; + } + + // add children to the object + if (object.children.size()) + { + node->mNumChildren = (unsigned int)object.children.size(); + node->mChildren = new aiNode*[node->mNumChildren]; + for (unsigned int i = 0; i < node->mNumChildren;++i) + { + node->mChildren[i] = ConvertObjectSection(object.children[i],meshes,outMaterials,materials,node); + } + } + + return node; +} + +// ------------------------------------------------------------------------------------------------ +void AC3DImporter::SetupProperties(const Importer* pImp) +{ + configSplitBFCull = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_SEPARATE_BFCULL,1) ? true : false; + configEvalSubdivision = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_AC_EVAL_SUBDIVISION,1) ? true : false; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void AC3DImporter::InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) +{ + boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); + + // Check whether we can read from the file + if( file.get() == NULL) + throw DeadlyImportError( "Failed to open AC3D file " + pFile + "."); + + // allocate storage and copy the contents of the file to a memory buffer + std::vector mBuffer2; + TextFileToBuffer(file.get(),mBuffer2); + + buffer = &mBuffer2[0]; + mNumMeshes = 0; + + lights = polys = worlds = groups = 0; + + if (::strncmp(buffer,"AC3D",4)) { + throw DeadlyImportError("AC3D: No valid AC3D file, magic sequence not found"); + } + + // print the file format version to the console + unsigned int version = HexDigitToDecimal( buffer[4] ); + char msg[3]; + ASSIMP_itoa10(msg,3,version); + DefaultLogger::get()->info(std::string("AC3D file format version: ") + msg); + + std::vector materials; + materials.reserve(5); + + std::vector rootObjects; + rootObjects.reserve(5); + + std::vector lights; + mLights = & lights; + + while (GetNextLine()) + { + if (TokenMatch(buffer,"MATERIAL",8)) + { + materials.push_back(Material()); + Material& mat = materials.back(); + + // manually parse the material ... sscanf would use the buldin atof ... + // Format: (name) rgb %f %f %f amb %f %f %f emis %f %f %f spec %f %f %f shi %d trans %f + + AI_AC_SKIP_TO_NEXT_TOKEN(); + if ('\"' == *buffer) + { + AI_AC_GET_STRING(mat.name); + AI_AC_SKIP_TO_NEXT_TOKEN(); + } + + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("rgb",3,3,&mat.rgb); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("amb",3,3,&mat.amb); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("emis",4,3,&mat.emis); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("spec",4,3,&mat.spec); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("shi",3,1,&mat.shin); + AI_AC_CHECKED_LOAD_FLOAT_ARRAY("trans",5,1,&mat.trans); + } + LoadObjectSection(rootObjects); + } + + if (rootObjects.empty() || !mNumMeshes) + { + throw DeadlyImportError("AC3D: No meshes have been loaded"); + } + if (materials.empty()) + { + DefaultLogger::get()->warn("AC3D: No material has been found"); + materials.push_back(Material()); + } + + mNumMeshes += (mNumMeshes>>2u) + 1; + std::vector meshes; + meshes.reserve(mNumMeshes); + + std::vector omaterials; + materials.reserve(mNumMeshes); + + // generate a dummy root if there are multiple objects on the top layer + Object* root; + if (1 == rootObjects.size()) + root = &rootObjects[0]; + else + { + root = new Object(); + } + + // now convert the imported stuff to our output data structure + pScene->mRootNode = ConvertObjectSection(*root,meshes,omaterials,materials); + if (1 != rootObjects.size())delete root; + + if (!::strncmp( pScene->mRootNode->mName.data, "Node", 4)) + pScene->mRootNode->mName.Set(""); + + // copy meshes + if (meshes.empty()) + { + throw DeadlyImportError("An unknown error occured during converting"); + } + pScene->mNumMeshes = (unsigned int)meshes.size(); + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + ::memcpy(pScene->mMeshes,&meshes[0],pScene->mNumMeshes*sizeof(void*)); + + // copy materials + pScene->mNumMaterials = (unsigned int)omaterials.size(); + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; + ::memcpy(pScene->mMaterials,&omaterials[0],pScene->mNumMaterials*sizeof(void*)); + + // copy lights + pScene->mNumLights = (unsigned int)lights.size(); + if (lights.size()) + { + pScene->mLights = new aiLight*[lights.size()]; + ::memcpy(pScene->mLights,&lights[0],lights.size()*sizeof(void*)); + } +} + +#endif //!defined ASSIMP_BUILD_NO_AC_IMPORTER diff --git a/code/ACLoader.h b/code/ACLoader.h index 293331158..160acd2f7 100644 --- a/code/ACLoader.h +++ b/code/ACLoader.h @@ -1,273 +1,273 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file ACLoader.h - * @brief Declaration of the .ac importer class. - */ -#ifndef AI_AC3DLOADER_H_INCLUDED -#define AI_AC3DLOADER_H_INCLUDED - -#include - -#include "BaseImporter.h" -#include "../include/assimp/types.h" - -struct aiNode; -struct aiMesh; -struct aiMaterial; -struct aiLight; - - -namespace Assimp { - -// --------------------------------------------------------------------------- -/** AC3D (*.ac) importer class -*/ -class AC3DImporter : public BaseImporter -{ -public: - AC3DImporter(); - ~AC3DImporter(); - - - - // Represents an AC3D material - struct Material - { - Material() - : rgb (0.6f,0.6f,0.6f) - , spec (1.f,1.f,1.f) - , shin (0.f) - , trans (0.f) - {} - - // base color of the material - aiColor3D rgb; - - // ambient color of the material - aiColor3D amb; - - // emissive color of the material - aiColor3D emis; - - // specular color of the material - aiColor3D spec; - - // shininess exponent - float shin; - - // transparency. 0 == opaque - float trans; - - // name of the material. optional. - std::string name; - }; - - // Represents an AC3D surface - struct Surface - { - Surface() - : mat (0) - , flags (0) - {} - - unsigned int mat,flags; - - typedef std::pair SurfaceEntry; - std::vector< SurfaceEntry > entries; - }; - - // Represents an AC3D object - struct Object - { - Object() - : type (World) - , name( "" ) - , children() - , texture( "" ) - , texRepeat( 1.f, 1.f ) - , texOffset( 0.0f, 0.0f ) - , rotation() - , translation() - , vertices() - , surfaces() - , numRefs (0) - , subDiv (0) - {} - - // Type description - enum Type - { - World = 0x0, - Poly = 0x1, - Group = 0x2, - Light = 0x4 - } type; - - // name of the object - std::string name; - - // object children - std::vector children; - - // texture to be assigned to all surfaces of the object - std::string texture; - - // texture repat factors (scaling for all coordinates) - aiVector2D texRepeat, texOffset; - - // rotation matrix - aiMatrix3x3 rotation; - - // translation vector - aiVector3D translation; - - // vertices - std::vector vertices; - - // surfaces - std::vector surfaces; - - // number of indices (= num verts in verbose format) - unsigned int numRefs; - - // number of subdivisions to be performed on the - // imported data - unsigned int subDiv; - - // max angle limit for smoothing - float crease; - }; - - -public: - - // ------------------------------------------------------------------- - /** Returns whether the class can handle the format of the given file. - * See BaseImporter::CanRead() for details. - */ - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, - bool checkSig) const; - -protected: - - // ------------------------------------------------------------------- - /** Return importer meta information. - * See #BaseImporter::GetInfo for the details */ - const aiImporterDesc* GetInfo () const; - - // ------------------------------------------------------------------- - /** Imports the given file into the given scene structure. - * See BaseImporter::InternReadFile() for details*/ - void InternReadFile( const std::string& pFile, aiScene* pScene, - IOSystem* pIOHandler); - - // ------------------------------------------------------------------- - /** Called prior to ReadFile(). - * The function is a request to the importer to update its configuration - * basing on the Importer's configuration property list.*/ - void SetupProperties(const Importer* pImp); - -private: - - // ------------------------------------------------------------------- - /** Get the next line from the file. - * @return false if the end of the file was reached*/ - bool GetNextLine(); - - // ------------------------------------------------------------------- - /** Load the object section. This method is called recursively to - * load subobjects, the method returns after a 'kids 0' was - * encountered. - * @objects List of output objects*/ - void LoadObjectSection(std::vector& objects); - - // ------------------------------------------------------------------- - /** Convert all objects into meshes and nodes. - * @param object Current object to work on - * @param meshes Pointer to the list of output meshes - * @param outMaterials List of output materials - * @param materials Material list - * @param Scenegraph node for the object */ - aiNode* ConvertObjectSection(Object& object, - std::vector& meshes, - std::vector& outMaterials, - const std::vector& materials, - aiNode* parent = NULL); - - // ------------------------------------------------------------------- - /** Convert a material - * @param object Current object - * @param matSrc Source material description - * @param matDest Destination material to be filled */ - void ConvertMaterial(const Object& object, - const Material& matSrc, - aiMaterial& matDest); - -private: - - - // points to the next data line - const char* buffer; - - // Configuration option: if enabled, up to two meshes - // are generated per material: those faces who have - // their bf cull flags set are separated. - bool configSplitBFCull; - - // Configuration switch: subdivision surfaces are only - // evaluated if the value is true. - bool configEvalSubdivision; - - // counts how many objects we have in the tree. - // basing on this information we can find a - // good estimate how many meshes we'll have in the final scene. - unsigned int mNumMeshes; - - // current list of light sources - std::vector* mLights; - - // name counters - unsigned int lights, groups, polys, worlds; -}; - -} // end of namespace Assimp - -#endif // AI_AC3DIMPORTER_H_INC +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file ACLoader.h + * @brief Declaration of the .ac importer class. + */ +#ifndef AI_AC3DLOADER_H_INCLUDED +#define AI_AC3DLOADER_H_INCLUDED + +#include + +#include "BaseImporter.h" +#include "../include/assimp/types.h" + +struct aiNode; +struct aiMesh; +struct aiMaterial; +struct aiLight; + + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** AC3D (*.ac) importer class +*/ +class AC3DImporter : public BaseImporter +{ +public: + AC3DImporter(); + ~AC3DImporter(); + + + + // Represents an AC3D material + struct Material + { + Material() + : rgb (0.6f,0.6f,0.6f) + , spec (1.f,1.f,1.f) + , shin (0.f) + , trans (0.f) + {} + + // base color of the material + aiColor3D rgb; + + // ambient color of the material + aiColor3D amb; + + // emissive color of the material + aiColor3D emis; + + // specular color of the material + aiColor3D spec; + + // shininess exponent + float shin; + + // transparency. 0 == opaque + float trans; + + // name of the material. optional. + std::string name; + }; + + // Represents an AC3D surface + struct Surface + { + Surface() + : mat (0) + , flags (0) + {} + + unsigned int mat,flags; + + typedef std::pair SurfaceEntry; + std::vector< SurfaceEntry > entries; + }; + + // Represents an AC3D object + struct Object + { + Object() + : type (World) + , name( "" ) + , children() + , texture( "" ) + , texRepeat( 1.f, 1.f ) + , texOffset( 0.0f, 0.0f ) + , rotation() + , translation() + , vertices() + , surfaces() + , numRefs (0) + , subDiv (0) + {} + + // Type description + enum Type + { + World = 0x0, + Poly = 0x1, + Group = 0x2, + Light = 0x4 + } type; + + // name of the object + std::string name; + + // object children + std::vector children; + + // texture to be assigned to all surfaces of the object + std::string texture; + + // texture repat factors (scaling for all coordinates) + aiVector2D texRepeat, texOffset; + + // rotation matrix + aiMatrix3x3 rotation; + + // translation vector + aiVector3D translation; + + // vertices + std::vector vertices; + + // surfaces + std::vector surfaces; + + // number of indices (= num verts in verbose format) + unsigned int numRefs; + + // number of subdivisions to be performed on the + // imported data + unsigned int subDiv; + + // max angle limit for smoothing + float crease; + }; + + +public: + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. + */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, + bool checkSig) const; + +protected: + + // ------------------------------------------------------------------- + /** Return importer meta information. + * See #BaseImporter::GetInfo for the details */ + const aiImporterDesc* GetInfo () const; + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details*/ + void InternReadFile( const std::string& pFile, aiScene* pScene, + IOSystem* pIOHandler); + + // ------------------------------------------------------------------- + /** Called prior to ReadFile(). + * The function is a request to the importer to update its configuration + * basing on the Importer's configuration property list.*/ + void SetupProperties(const Importer* pImp); + +private: + + // ------------------------------------------------------------------- + /** Get the next line from the file. + * @return false if the end of the file was reached*/ + bool GetNextLine(); + + // ------------------------------------------------------------------- + /** Load the object section. This method is called recursively to + * load subobjects, the method returns after a 'kids 0' was + * encountered. + * @objects List of output objects*/ + void LoadObjectSection(std::vector& objects); + + // ------------------------------------------------------------------- + /** Convert all objects into meshes and nodes. + * @param object Current object to work on + * @param meshes Pointer to the list of output meshes + * @param outMaterials List of output materials + * @param materials Material list + * @param Scenegraph node for the object */ + aiNode* ConvertObjectSection(Object& object, + std::vector& meshes, + std::vector& outMaterials, + const std::vector& materials, + aiNode* parent = NULL); + + // ------------------------------------------------------------------- + /** Convert a material + * @param object Current object + * @param matSrc Source material description + * @param matDest Destination material to be filled */ + void ConvertMaterial(const Object& object, + const Material& matSrc, + aiMaterial& matDest); + +private: + + + // points to the next data line + const char* buffer; + + // Configuration option: if enabled, up to two meshes + // are generated per material: those faces who have + // their bf cull flags set are separated. + bool configSplitBFCull; + + // Configuration switch: subdivision surfaces are only + // evaluated if the value is true. + bool configEvalSubdivision; + + // counts how many objects we have in the tree. + // basing on this information we can find a + // good estimate how many meshes we'll have in the final scene. + unsigned int mNumMeshes; + + // current list of light sources + std::vector* mLights; + + // name counters + unsigned int lights, groups, polys, worlds; +}; + +} // end of namespace Assimp + +#endif // AI_AC3DIMPORTER_H_INC diff --git a/code/ASELoader.cpp b/code/ASELoader.cpp index 8c92bffad..0b9ec3eef 100644 --- a/code/ASELoader.cpp +++ b/code/ASELoader.cpp @@ -1,1321 +1,1321 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file ASELoader.cpp - * @brief Implementation of the ASE importer class - */ - -#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER - -// internal headers -#include "ASELoader.h" -#include "StringComparison.h" -#include "SkeletonMeshBuilder.h" -#include "TargetAnimation.h" -#include "../include/assimp/Importer.hpp" -#include -#include "../include/assimp/IOSystem.hpp" -#include "../include/assimp/DefaultLogger.hpp" -#include "../include/assimp/scene.h" - - -// utilities -#include "fast_atof.h" - -using namespace Assimp; -using namespace Assimp::ASE; - -static const aiImporterDesc desc = { - "ASE Importer", - "", - "", - "Similar to 3DS but text-encoded", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "ase ask" -}; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -ASEImporter::ASEImporter() -: noSkeletonMesh() -{} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -ASEImporter::~ASEImporter() -{} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. -bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const -{ - // check file extension - const std::string extension = GetExtension(pFile); - - if( extension == "ase" || extension == "ask") - return true; - - if ((!extension.length() || cs) && pIOHandler) { - const char* tokens[] = {"*3dsmax_asciiexport"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); - } - return false; -} - -// ------------------------------------------------------------------------------------------------ -// Loader meta information -const aiImporterDesc* ASEImporter::GetInfo () const -{ - return &desc; -} - -// ------------------------------------------------------------------------------------------------ -// Setup configuration options -void ASEImporter::SetupProperties(const Importer* pImp) -{ - configRecomputeNormals = (pImp->GetPropertyInteger( - AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false); - - noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void ASEImporter::InternReadFile( const std::string& pFile, - aiScene* pScene, IOSystem* pIOHandler) -{ - boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); - - // Check whether we can read from the file - if( file.get() == NULL) { - throw DeadlyImportError( "Failed to open ASE file " + pFile + "."); - } - - // Allocate storage and copy the contents of the file to a memory buffer - std::vector mBuffer2; - TextFileToBuffer(file.get(),mBuffer2); - - this->mBuffer = &mBuffer2[0]; - this->pcScene = pScene; - - // ------------------------------------------------------------------ - // Guess the file format by looking at the extension - // ASC is considered to be the older format 110, - // ASE is the actual version 200 (that is currently written by max) - // ------------------------------------------------------------------ - unsigned int defaultFormat; - std::string::size_type s = pFile.length()-1; - switch (pFile.c_str()[s]) { - - case 'C': - case 'c': - defaultFormat = AI_ASE_OLD_FILE_FORMAT; - break; - default: - defaultFormat = AI_ASE_NEW_FILE_FORMAT; - }; - - // Construct an ASE parser and parse the file - ASE::Parser parser(mBuffer,defaultFormat); - mParser = &parser; - mParser->Parse(); - - //------------------------------------------------------------------ - // Check whether we god at least one mesh. If we did - generate - // materials and copy meshes. - // ------------------------------------------------------------------ - if ( !mParser->m_vMeshes.empty()) { - - // If absolutely no material has been loaded from the file - // we need to generate a default material - GenerateDefaultMaterial(); - - // process all meshes - bool tookNormals = false; - std::vector avOutMeshes; - avOutMeshes.reserve(mParser->m_vMeshes.size()*2); - for (std::vector::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { - if ((*i).bSkip) { - continue; - } - BuildUniqueRepresentation(*i); - - // Need to generate proper vertex normals if necessary - if(GenerateNormals(*i)) { - tookNormals = true; - } - - // Convert all meshes to aiMesh objects - ConvertMeshes(*i,avOutMeshes); - } - if (tookNormals) { - DefaultLogger::get()->debug("ASE: Taking normals from the file. Use " - "the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS setting if you " - "experience problems"); - } - - // Now build the output mesh list. Remove dummies - pScene->mNumMeshes = (unsigned int)avOutMeshes.size(); - aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; - for (std::vector::const_iterator i = avOutMeshes.begin();i != avOutMeshes.end();++i) { - if (!(*i)->mNumFaces) { - continue; - } - *pp++ = *i; - } - pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes); - - // Build final material indices (remove submaterials and setup - // the final list) - BuildMaterialIndices(); - } - - // ------------------------------------------------------------------ - // Copy all scene graph nodes - lights, cameras, dummies and meshes - // into one huge list. - //------------------------------------------------------------------ - std::vector nodes; - nodes.reserve(mParser->m_vMeshes.size() +mParser->m_vLights.size() - + mParser->m_vCameras.size() + mParser->m_vDummies.size()); - - // Lights - for (std::vector::iterator it = mParser->m_vLights.begin(), - end = mParser->m_vLights.end();it != end; ++it)nodes.push_back(&(*it)); - // Cameras - for (std::vector::iterator it = mParser->m_vCameras.begin(), - end = mParser->m_vCameras.end();it != end; ++it)nodes.push_back(&(*it)); - // Meshes - for (std::vector::iterator it = mParser->m_vMeshes.begin(), - end = mParser->m_vMeshes.end();it != end; ++it)nodes.push_back(&(*it)); - // Dummies - for (std::vector::iterator it = mParser->m_vDummies.begin(), - end = mParser->m_vDummies.end();it != end; ++it)nodes.push_back(&(*it)); - - // build the final node graph - BuildNodes(nodes); - - // build output animations - BuildAnimations(nodes); - - // build output cameras - BuildCameras(); - - // build output lights - BuildLights(); - - // ------------------------------------------------------------------ - // If we have no meshes use the SkeletonMeshBuilder helper class - // to build a mesh for the animation skeleton - // FIXME: very strange results - // ------------------------------------------------------------------ - if (!pScene->mNumMeshes) { - pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; - if (!noSkeletonMesh) { - SkeletonMeshBuilder skeleton(pScene); - } - } -} -// ------------------------------------------------------------------------------------------------ -void ASEImporter::GenerateDefaultMaterial() -{ - ai_assert(NULL != mParser); - - bool bHas = false; - for (std::vector::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { - if ((*i).bSkip)continue; - if (ASE::Face::DEFAULT_MATINDEX == (*i).iMaterialIndex) { - (*i).iMaterialIndex = (unsigned int)mParser->m_vMaterials.size(); - bHas = true; - } - } - if (bHas || mParser->m_vMaterials.empty()) { - // add a simple material without submaterials to the parser's list - mParser->m_vMaterials.push_back ( ASE::Material() ); - ASE::Material& mat = mParser->m_vMaterials.back(); - - mat.mDiffuse = aiColor3D(0.6f,0.6f,0.6f); - mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f); - mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f); - mat.mShading = Discreet3DS::Gouraud; - mat.mName = AI_DEFAULT_MATERIAL_NAME; - } -} - -// ------------------------------------------------------------------------------------------------ -void ASEImporter::BuildAnimations(const std::vector& nodes) -{ - // check whether we have at least one mesh which has animations - std::vector::const_iterator i = nodes.begin(); - unsigned int iNum = 0; - for (;i != nodes.end();++i) { - - // TODO: Implement Bezier & TCB support - if ((*i)->mAnim.mPositionType != ASE::Animation::TRACK) { - DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " - "This is not supported."); - } - if ((*i)->mAnim.mRotationType != ASE::Animation::TRACK) { - DefaultLogger::get()->warn("ASE: Rotation controller uses Bezier/TCB keys. " - "This is not supported."); - } - if ((*i)->mAnim.mScalingType != ASE::Animation::TRACK) { - DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " - "This is not supported."); - } - - // We compare against 1 here - firstly one key is not - // really an animation and secondly MAX writes dummies - // that represent the node transformation. - if ((*i)->mAnim.akeyPositions.size()>1 || (*i)->mAnim.akeyRotations.size()>1 || (*i)->mAnim.akeyScaling.size()>1){ - ++iNum; - } - if ((*i)->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( (*i)->mTargetPosition.x )) { - ++iNum; - } - } - if (iNum) { - // Generate a new animation channel and setup everything for it - pcScene->mNumAnimations = 1; - pcScene->mAnimations = new aiAnimation*[1]; - aiAnimation* pcAnim = pcScene->mAnimations[0] = new aiAnimation(); - pcAnim->mNumChannels = iNum; - pcAnim->mChannels = new aiNodeAnim*[iNum]; - pcAnim->mTicksPerSecond = mParser->iFrameSpeed * mParser->iTicksPerFrame; - - iNum = 0; - - // Now iterate through all meshes and collect all data we can find - for (i = nodes.begin();i != nodes.end();++i) { - - ASE::BaseNode* me = *i; - if ( me->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( me->mTargetPosition.x )) { - // Generate an extra channel for the camera/light target. - // BuildNodes() does also generate an extra node, named - // .Target. - aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); - nd->mNodeName.Set(me->mName + ".Target"); - - // If there is no input position channel we will need - // to supply the default position from the node's - // local transformation matrix. - /*TargetAnimationHelper helper; - if (me->mAnim.akeyPositions.empty()) - { - aiMatrix4x4& mat = (*i)->mTransform; - helper.SetFixedMainAnimationChannel(aiVector3D( - mat.a4, mat.b4, mat.c4)); - } - else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions); - helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions); - - helper.Process(&me->mTargetAnim.akeyPositions);*/ - - // Allocate the key array and fill it - nd->mNumPositionKeys = (unsigned int) me->mTargetAnim.akeyPositions.size(); - nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; - - ::memcpy(nd->mPositionKeys,&me->mTargetAnim.akeyPositions[0], - nd->mNumPositionKeys * sizeof(aiVectorKey)); - } - - if (me->mAnim.akeyPositions.size() > 1 || me->mAnim.akeyRotations.size() > 1 || me->mAnim.akeyScaling.size() > 1) { - // Begin a new node animation channel for this node - aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); - nd->mNodeName.Set(me->mName); - - // copy position keys - if (me->mAnim.akeyPositions.size() > 1 ) - { - // Allocate the key array and fill it - nd->mNumPositionKeys = (unsigned int) me->mAnim.akeyPositions.size(); - nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; - - ::memcpy(nd->mPositionKeys,&me->mAnim.akeyPositions[0], - nd->mNumPositionKeys * sizeof(aiVectorKey)); - } - // copy rotation keys - if (me->mAnim.akeyRotations.size() > 1 ) { - // Allocate the key array and fill it - nd->mNumRotationKeys = (unsigned int) me->mAnim.akeyRotations.size(); - nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys]; - - // -------------------------------------------------------------------- - // Rotation keys are offsets to the previous keys. - // We have the quaternion representations of all - // of them, so we just need to concatenate all - // (unit-length) quaternions to get the absolute - // rotations. - // Rotation keys are ABSOLUTE for older files - // -------------------------------------------------------------------- - - aiQuaternion cur; - for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) { - aiQuatKey q = me->mAnim.akeyRotations[a]; - - if (mParser->iFileFormat > 110) { - cur = (a ? cur*q.mValue : q.mValue); - q.mValue = cur.Normalize(); - } - nd->mRotationKeys[a] = q; - - // need this to get to Assimp quaternion conventions - nd->mRotationKeys[a].mValue.w *= -1.f; - } - } - // copy scaling keys - if (me->mAnim.akeyScaling.size() > 1 ) { - // Allocate the key array and fill it - nd->mNumScalingKeys = (unsigned int) me->mAnim.akeyScaling.size(); - nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys]; - - ::memcpy(nd->mScalingKeys,&me->mAnim.akeyScaling[0], - nd->mNumScalingKeys * sizeof(aiVectorKey)); - } - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Build output cameras -void ASEImporter::BuildCameras() -{ - if (!mParser->m_vCameras.empty()) { - pcScene->mNumCameras = (unsigned int)mParser->m_vCameras.size(); - pcScene->mCameras = new aiCamera*[pcScene->mNumCameras]; - - for (unsigned int i = 0; i < pcScene->mNumCameras;++i) { - aiCamera* out = pcScene->mCameras[i] = new aiCamera(); - ASE::Camera& in = mParser->m_vCameras[i]; - - // copy members - out->mClipPlaneFar = in.mFar; - out->mClipPlaneNear = (in.mNear ? in.mNear : 0.1f); - out->mHorizontalFOV = in.mFOV; - - out->mName.Set(in.mName); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Build output lights -void ASEImporter::BuildLights() -{ - if (!mParser->m_vLights.empty()) { - pcScene->mNumLights = (unsigned int)mParser->m_vLights.size(); - pcScene->mLights = new aiLight*[pcScene->mNumLights]; - - for (unsigned int i = 0; i < pcScene->mNumLights;++i) { - aiLight* out = pcScene->mLights[i] = new aiLight(); - ASE::Light& in = mParser->m_vLights[i]; - - // The direction is encoded in the transformation matrix of the node. - // In 3DS MAX the light source points into negative Z direction if - // the node transformation is the identity. - out->mDirection = aiVector3D(0.f,0.f,-1.f); - - out->mName.Set(in.mName); - switch (in.mLightType) - { - case ASE::Light::TARGET: - out->mType = aiLightSource_SPOT; - out->mAngleInnerCone = AI_DEG_TO_RAD(in.mAngle); - out->mAngleOuterCone = (in.mFalloff ? AI_DEG_TO_RAD(in.mFalloff) : out->mAngleInnerCone); - break; - - case ASE::Light::DIRECTIONAL: - out->mType = aiLightSource_DIRECTIONAL; - break; - - default: - //case ASE::Light::OMNI: - out->mType = aiLightSource_POINT; - break; - }; - out->mColorDiffuse = out->mColorSpecular = in.mColor * in.mIntensity; - } - } -} - -// ------------------------------------------------------------------------------------------------ -void ASEImporter::AddNodes(const std::vector& nodes, - aiNode* pcParent,const char* szName) -{ - aiMatrix4x4 m; - AddNodes(nodes,pcParent,szName,m); -} - -// ------------------------------------------------------------------------------------------------ -// Add meshes to a given node -void ASEImporter::AddMeshes(const ASE::BaseNode* snode,aiNode* node) -{ - for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) { - // Get the name of the mesh (the mesh instance has been temporarily stored in the third vertex color) - const aiMesh* pcMesh = pcScene->mMeshes[i]; - const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; - - if (mesh == snode) { - ++node->mNumMeshes; - } - } - - if(node->mNumMeshes) { - node->mMeshes = new unsigned int[node->mNumMeshes]; - for (unsigned int i = 0, p = 0; i < pcScene->mNumMeshes;++i) { - - const aiMesh* pcMesh = pcScene->mMeshes[i]; - const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; - if (mesh == snode) { - node->mMeshes[p++] = i; - - // Transform all vertices of the mesh back into their local space -> - // at the moment they are pretransformed - aiMatrix4x4 m = mesh->mTransform; - m.Inverse(); - - aiVector3D* pvCurPtr = pcMesh->mVertices; - const aiVector3D* pvEndPtr = pvCurPtr + pcMesh->mNumVertices; - while (pvCurPtr != pvEndPtr) { - *pvCurPtr = m * (*pvCurPtr); - pvCurPtr++; - } - - // Do the same for the normal vectors, if we have them. - // As always, inverse transpose. - if (pcMesh->mNormals) { - aiMatrix3x3 m3 = aiMatrix3x3( mesh->mTransform ); - m3.Transpose(); - - pvCurPtr = pcMesh->mNormals; - pvEndPtr = pvCurPtr + pcMesh->mNumVertices; - while (pvCurPtr != pvEndPtr) { - *pvCurPtr = m3 * (*pvCurPtr); - pvCurPtr++; - } - } - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Add child nodes to a given parent node -void ASEImporter::AddNodes (const std::vector& nodes, - aiNode* pcParent, const char* szName, - const aiMatrix4x4& mat) -{ - const size_t len = szName ? ::strlen(szName) : 0; - ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS); - - // Receives child nodes for the pcParent node - std::vector apcNodes; - - // Now iterate through all nodes in the scene and search for one - // which has *us* as parent. - for (std::vector::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) { - const BaseNode* snode = *it; - if (szName) { - if (len != snode->mParent.length() || ::strcmp(szName,snode->mParent.c_str())) - continue; - } - else if (snode->mParent.length()) - continue; - - (*it)->mProcessed = true; - - // Allocate a new node and add it to the output data structure - apcNodes.push_back(new aiNode()); - aiNode* node = apcNodes.back(); - - node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node")); - node->mParent = pcParent; - - // Setup the transformation matrix of the node - aiMatrix4x4 mParentAdjust = mat; - mParentAdjust.Inverse(); - node->mTransformation = mParentAdjust*snode->mTransform; - - // Add sub nodes - prevent stack overflow due to recursive parenting - if (node->mName != node->mParent->mName) { - AddNodes(nodes,node,node->mName.data,snode->mTransform); - } - - // Further processing depends on the type of the node - if (snode->mType == ASE::BaseNode::Mesh) { - // If the type of this node is "Mesh" we need to search - // the list of output meshes in the data structure for - // all those that belonged to this node once. This is - // slightly inconvinient here and a better solution should - // be used when this code is refactored next. - AddMeshes(snode,node); - } - else if (is_not_qnan( snode->mTargetPosition.x )) { - // If this is a target camera or light we generate a small - // child node which marks the position of the camera - // target (the direction information is contained in *this* - // node's animation track but the exact target position - // would be lost otherwise) - if (!node->mNumChildren) { - node->mChildren = new aiNode*[1]; - } - - aiNode* nd = new aiNode(); - - nd->mName.Set ( snode->mName + ".Target" ); - - nd->mTransformation.a4 = snode->mTargetPosition.x - snode->mTransform.a4; - nd->mTransformation.b4 = snode->mTargetPosition.y - snode->mTransform.b4; - nd->mTransformation.c4 = snode->mTargetPosition.z - snode->mTransform.c4; - - nd->mParent = node; - - // The .Target node is always the first child node - for (unsigned int m = 0; m < node->mNumChildren;++m) - node->mChildren[m+1] = node->mChildren[m]; - - node->mChildren[0] = nd; - node->mNumChildren++; - - // What we did is so great, it is at least worth a debug message - DefaultLogger::get()->debug("ASE: Generating separate target node ("+snode->mName+")"); - } - } - - // Allocate enough space for the child nodes - // We allocate one slot more in case this is a target camera/light - pcParent->mNumChildren = (unsigned int)apcNodes.size(); - if (pcParent->mNumChildren) { - pcParent->mChildren = new aiNode*[apcNodes.size()+1 /* PLUS ONE !!! */]; - - // now build all nodes for our nice new children - for (unsigned int p = 0; p < apcNodes.size();++p) - pcParent->mChildren[p] = apcNodes[p]; - } - return; -} - -// ------------------------------------------------------------------------------------------------ -// Build the output node graph -void ASEImporter::BuildNodes(std::vector& nodes) { - ai_assert(NULL != pcScene); - - // allocate the one and only root node - aiNode* root = pcScene->mRootNode = new aiNode(); - root->mName.Set(""); - - // Setup the coordinate system transformation - pcScene->mRootNode->mNumChildren = 1; - pcScene->mRootNode->mChildren = new aiNode*[1]; - aiNode* ch = pcScene->mRootNode->mChildren[0] = new aiNode(); - ch->mParent = root; - - // Change the transformation matrix of all nodes - for (std::vector::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) { - aiMatrix4x4& m = (*it)->mTransform; - m.Transpose(); // row-order vs column-order - } - - // add all nodes - AddNodes(nodes,ch,NULL); - - // now iterate through al nodes and find those that have not yet - // been added to the nodegraph (= their parent could not be recognized) - std::vector aiList; - for (std::vector::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) { - if ((*it)->mProcessed) { - continue; - } - - // check whether our parent is known - bool bKnowParent = false; - - // search the list another time, starting *here* and try to find out whether - // there is a node that references *us* as a parent - for (std::vector::const_iterator it2 = nodes.begin();it2 != end; ++it2) { - if (it2 == it) { - continue; - } - - if ((*it2)->mName == (*it)->mParent) { - bKnowParent = true; - break; - } - } - if (!bKnowParent) { - aiList.push_back(*it); - } - } - - // Are there ane orphaned nodes? - if (!aiList.empty()) { - std::vector apcNodes; - apcNodes.reserve(aiList.size() + pcScene->mRootNode->mNumChildren); - - for (unsigned int i = 0; i < pcScene->mRootNode->mNumChildren;++i) - apcNodes.push_back(pcScene->mRootNode->mChildren[i]); - - delete[] pcScene->mRootNode->mChildren; - for (std::vector::/*const_*/iterator i = aiList.begin();i != aiList.end();++i) { - const ASE::BaseNode* src = *i; - - // The parent is not known, so we can assume that we must add - // this node to the root node of the whole scene - aiNode* pcNode = new aiNode(); - pcNode->mParent = pcScene->mRootNode; - pcNode->mName.Set(src->mName); - AddMeshes(src,pcNode); - AddNodes(nodes,pcNode,pcNode->mName.data); - apcNodes.push_back(pcNode); - } - - // Regenerate our output array - pcScene->mRootNode->mChildren = new aiNode*[apcNodes.size()]; - for (unsigned int i = 0; i < apcNodes.size();++i) - pcScene->mRootNode->mChildren[i] = apcNodes[i]; - - pcScene->mRootNode->mNumChildren = (unsigned int)apcNodes.size(); - } - - // Reset the third color set to NULL - we used this field to store a temporary pointer - for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) - pcScene->mMeshes[i]->mColors[2] = NULL; - - // The root node should not have at least one child or the file is valid - if (!pcScene->mRootNode->mNumChildren) { - throw DeadlyImportError("ASE: No nodes loaded. The file is either empty or corrupt"); - } - - // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system - pcScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f, - 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f); -} - -// ------------------------------------------------------------------------------------------------ -// Convert the imported data to the internal verbose representation -void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) { - // allocate output storage - std::vector mPositions; - std::vector amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; - std::vector mVertexColors; - std::vector mNormals; - std::vector mBoneVertices; - - unsigned int iSize = (unsigned int)mesh.mFaces.size() * 3; - mPositions.resize(iSize); - - // optional texture coordinates - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) { - if (!mesh.amTexCoords[i].empty()) { - amTexCoords[i].resize(iSize); - } - } - // optional vertex colors - if (!mesh.mVertexColors.empty()) { - mVertexColors.resize(iSize); - } - - // optional vertex normals (vertex normals can simply be copied) - if (!mesh.mNormals.empty()) { - mNormals.resize(iSize); - } - // bone vertices. There is no need to change the bone list - if (!mesh.mBoneVertices.empty()) { - mBoneVertices.resize(iSize); - } - - // iterate through all faces in the mesh - unsigned int iCurrent = 0, fi = 0; - for (std::vector::iterator i = mesh.mFaces.begin();i != mesh.mFaces.end();++i,++fi) { - for (unsigned int n = 0; n < 3;++n,++iCurrent) - { - mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]]; - - // add texture coordinates - for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { - if (mesh.amTexCoords[c].empty())break; - amTexCoords[c][iCurrent] = mesh.amTexCoords[c][(*i).amUVIndices[c][n]]; - } - // add vertex colors - if (!mesh.mVertexColors.empty()) { - mVertexColors[iCurrent] = mesh.mVertexColors[(*i).mColorIndices[n]]; - } - // add normal vectors - if (!mesh.mNormals.empty()) { - mNormals[iCurrent] = mesh.mNormals[fi*3+n]; - mNormals[iCurrent].Normalize(); - } - - // handle bone vertices - if ((*i).mIndices[n] < mesh.mBoneVertices.size()) { - // (sometimes this will cause bone verts to be duplicated - // however, I' quite sure Schrompf' JoinVerticesStep - // will fix that again ...) - mBoneVertices[iCurrent] = mesh.mBoneVertices[(*i).mIndices[n]]; - } - (*i).mIndices[n] = iCurrent; - } - } - - // replace the old arrays - mesh.mNormals = mNormals; - mesh.mPositions = mPositions; - mesh.mVertexColors = mVertexColors; - - for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) - mesh.amTexCoords[c] = amTexCoords[c]; -} - -// ------------------------------------------------------------------------------------------------ -// Copy a texture from the ASE structs to the output material -void CopyASETexture(aiMaterial& mat, ASE::Texture& texture, aiTextureType type) -{ - // Setup the texture name - aiString tex; - tex.Set( texture.mMapName); - mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0)); - - // Setup the texture blend factor - if (is_not_qnan(texture.mTextureBlend)) - mat.AddProperty( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0)); - - // Setup texture UV transformations - mat.AddProperty(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0)); -} - -// ------------------------------------------------------------------------------------------------ -// Convert from ASE material to output material -void ASEImporter::ConvertMaterial(ASE::Material& mat) -{ - // LARGE TODO: Much code her is copied from 3DS ... join them maybe? - - // Allocate the output material - mat.pcInstance = new aiMaterial(); - - // At first add the base ambient color of the - // scene to the material - mat.mAmbient.r += mParser->m_clrAmbient.r; - mat.mAmbient.g += mParser->m_clrAmbient.g; - mat.mAmbient.b += mParser->m_clrAmbient.b; - - aiString name; - name.Set( mat.mName); - mat.pcInstance->AddProperty( &name, AI_MATKEY_NAME); - - // material colors - mat.pcInstance->AddProperty( &mat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); - mat.pcInstance->AddProperty( &mat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - mat.pcInstance->AddProperty( &mat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); - mat.pcInstance->AddProperty( &mat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); - - // shininess - if (0.0f != mat.mSpecularExponent && 0.0f != mat.mShininessStrength) - { - mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS); - mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); - } - // If there is no shininess, we can disable phong lighting - else if (D3DS::Discreet3DS::Metal == mat.mShading || - D3DS::Discreet3DS::Phong == mat.mShading || - D3DS::Discreet3DS::Blinn == mat.mShading) - { - mat.mShading = D3DS::Discreet3DS::Gouraud; - } - - // opacity - mat.pcInstance->AddProperty( &mat.mTransparency,1,AI_MATKEY_OPACITY); - - // Two sided rendering? - if (mat.mTwoSided) - { - int i = 1; - mat.pcInstance->AddProperty(&i,1,AI_MATKEY_TWOSIDED); - } - - // shading mode - aiShadingMode eShading = aiShadingMode_NoShading; - switch (mat.mShading) - { - case D3DS::Discreet3DS::Flat: - eShading = aiShadingMode_Flat; break; - case D3DS::Discreet3DS::Phong : - eShading = aiShadingMode_Phong; break; - case D3DS::Discreet3DS::Blinn : - eShading = aiShadingMode_Blinn; break; - - // I don't know what "Wire" shading should be, - // assume it is simple lambertian diffuse (L dot N) shading - case D3DS::Discreet3DS::Wire: - { - // set the wireframe flag - unsigned int iWire = 1; - mat.pcInstance->AddProperty( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME); - } - case D3DS::Discreet3DS::Gouraud: - eShading = aiShadingMode_Gouraud; break; - case D3DS::Discreet3DS::Metal : - eShading = aiShadingMode_CookTorrance; break; - } - mat.pcInstance->AddProperty( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); - - // DIFFUSE texture - if( mat.sTexDiffuse.mMapName.length() > 0) - CopyASETexture(*mat.pcInstance,mat.sTexDiffuse, aiTextureType_DIFFUSE); - - // SPECULAR texture - if( mat.sTexSpecular.mMapName.length() > 0) - CopyASETexture(*mat.pcInstance,mat.sTexSpecular, aiTextureType_SPECULAR); - - // AMBIENT texture - if( mat.sTexAmbient.mMapName.length() > 0) - CopyASETexture(*mat.pcInstance,mat.sTexAmbient, aiTextureType_AMBIENT); - - // OPACITY texture - if( mat.sTexOpacity.mMapName.length() > 0) - CopyASETexture(*mat.pcInstance,mat.sTexOpacity, aiTextureType_OPACITY); - - // EMISSIVE texture - if( mat.sTexEmissive.mMapName.length() > 0) - CopyASETexture(*mat.pcInstance,mat.sTexEmissive, aiTextureType_EMISSIVE); - - // BUMP texture - if( mat.sTexBump.mMapName.length() > 0) - CopyASETexture(*mat.pcInstance,mat.sTexBump, aiTextureType_HEIGHT); - - // SHININESS texture - if( mat.sTexShininess.mMapName.length() > 0) - CopyASETexture(*mat.pcInstance,mat.sTexShininess, aiTextureType_SHININESS); - - // store the name of the material itself, too - if( mat.mName.length() > 0) { - aiString tex;tex.Set( mat.mName); - mat.pcInstance->AddProperty( &tex, AI_MATKEY_NAME); - } - return; -} - -// ------------------------------------------------------------------------------------------------ -// Build output meshes -void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector& avOutMeshes) -{ - // validate the material index of the mesh - if (mesh.iMaterialIndex >= mParser->m_vMaterials.size()) { - mesh.iMaterialIndex = (unsigned int)mParser->m_vMaterials.size()-1; - DefaultLogger::get()->warn("Material index is out of range"); - } - - // If the material the mesh is assigned to is consisting of submeshes, split it - if (!mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials.empty()) { - std::vector vSubMaterials = mParser-> - m_vMaterials[mesh.iMaterialIndex].avSubMaterials; - - std::vector* aiSplit = new std::vector[vSubMaterials.size()]; - - // build a list of all faces per submaterial - for (unsigned int i = 0; i < mesh.mFaces.size();++i) { - // check range - if (mesh.mFaces[i].iMaterial >= vSubMaterials.size()) { - DefaultLogger::get()->warn("Submaterial index is out of range"); - - // use the last material instead - aiSplit[vSubMaterials.size()-1].push_back(i); - } - else aiSplit[mesh.mFaces[i].iMaterial].push_back(i); - } - - // now generate submeshes - for (unsigned int p = 0; p < vSubMaterials.size();++p) { - if (!aiSplit[p].empty()) { - - aiMesh* p_pcOut = new aiMesh(); - p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - // let the sub material index - p_pcOut->mMaterialIndex = p; - - // we will need this material - mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials[p].bNeed = true; - - // store the real index here ... color channel 3 - p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; - - // store a pointer to the mesh in color channel 2 - p_pcOut->mColors[2] = (aiColor4D*) &mesh; - avOutMeshes.push_back(p_pcOut); - - // convert vertices - p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3; - p_pcOut->mNumFaces = (unsigned int)aiSplit[p].size(); - - // receive output vertex weights - std::vector > *avOutputBones = NULL; - if (!mesh.mBones.empty()) { - avOutputBones = new std::vector >[mesh.mBones.size()]; - } - - // allocate enough storage for faces - p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; - - unsigned int iBase = 0,iIndex; - if (p_pcOut->mNumVertices) { - p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices]; - p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices]; - for (unsigned int q = 0; q < aiSplit[p].size();++q) { - - iIndex = aiSplit[p][q]; - - p_pcOut->mFaces[q].mIndices = new unsigned int[3]; - p_pcOut->mFaces[q].mNumIndices = 3; - - for (unsigned int t = 0; t < 3;++t, ++iBase) { - const uint32_t iIndex2 = mesh.mFaces[iIndex].mIndices[t]; - - p_pcOut->mVertices[iBase] = mesh.mPositions [iIndex2]; - p_pcOut->mNormals [iBase] = mesh.mNormals [iIndex2]; - - // convert bones, if existing - if (!mesh.mBones.empty()) { - // check whether there is a vertex weight for this vertex index - if (iIndex2 < mesh.mBoneVertices.size()) { - - for (std::vector >::const_iterator - blubb = mesh.mBoneVertices[iIndex2].mBoneWeights.begin(); - blubb != mesh.mBoneVertices[iIndex2].mBoneWeights.end();++blubb) { - - // NOTE: illegal cases have already been filtered out - avOutputBones[(*blubb).first].push_back(std::pair( - iBase,(*blubb).second)); - } - } - } - p_pcOut->mFaces[q].mIndices[t] = iBase; - } - } - } - // convert texture coordinates (up to AI_MAX_NUMBER_OF_TEXTURECOORDS sets supported) - for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { - if (!mesh.amTexCoords[c].empty()) - { - p_pcOut->mTextureCoords[c] = new aiVector3D[p_pcOut->mNumVertices]; - iBase = 0; - for (unsigned int q = 0; q < aiSplit[p].size();++q) { - iIndex = aiSplit[p][q]; - for (unsigned int t = 0; t < 3;++t) { - p_pcOut->mTextureCoords[c][iBase++] = mesh.amTexCoords[c][mesh.mFaces[iIndex].mIndices[t]]; - } - } - // Setup the number of valid vertex components - p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; - } - } - - // Convert vertex colors (only one set supported) - if (!mesh.mVertexColors.empty()){ - p_pcOut->mColors[0] = new aiColor4D[p_pcOut->mNumVertices]; - iBase = 0; - for (unsigned int q = 0; q < aiSplit[p].size();++q) { - iIndex = aiSplit[p][q]; - for (unsigned int t = 0; t < 3;++t) { - p_pcOut->mColors[0][iBase++] = mesh.mVertexColors[mesh.mFaces[iIndex].mIndices[t]]; - } - } - } - // Copy bones - if (!mesh.mBones.empty()) { - p_pcOut->mNumBones = 0; - for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) - if (!avOutputBones[mrspock].empty())p_pcOut->mNumBones++; - - p_pcOut->mBones = new aiBone* [ p_pcOut->mNumBones ]; - aiBone** pcBone = p_pcOut->mBones; - for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) - { - if (!avOutputBones[mrspock].empty()) { - // we will need this bone. add it to the output mesh and - // add all per-vertex weights - aiBone* pc = *pcBone = new aiBone(); - pc->mName.Set(mesh.mBones[mrspock].mName); - - pc->mNumWeights = (unsigned int)avOutputBones[mrspock].size(); - pc->mWeights = new aiVertexWeight[pc->mNumWeights]; - - for (unsigned int captainkirk = 0; captainkirk < pc->mNumWeights;++captainkirk) - { - const std::pair& ref = avOutputBones[mrspock][captainkirk]; - pc->mWeights[captainkirk].mVertexId = ref.first; - pc->mWeights[captainkirk].mWeight = ref.second; - } - ++pcBone; - } - } - // delete allocated storage - delete[] avOutputBones; - } - } - } - // delete storage - delete[] aiSplit; - } - else - { - // Otherwise we can simply copy the data to one output mesh - // This codepath needs less memory and uses fast memcpy()s - // to do the actual copying. So I think it is worth the - // effort here. - - aiMesh* p_pcOut = new aiMesh(); - p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - // set an empty sub material index - p_pcOut->mMaterialIndex = ASE::Face::DEFAULT_MATINDEX; - mParser->m_vMaterials[mesh.iMaterialIndex].bNeed = true; - - // store the real index here ... in color channel 3 - p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; - - // store a pointer to the mesh in color channel 2 - p_pcOut->mColors[2] = (aiColor4D*) &mesh; - avOutMeshes.push_back(p_pcOut); - - // If the mesh hasn't faces or vertices, there are two cases - // possible: 1. the model is invalid. 2. This is a dummy - // helper object which we are going to remove later ... - if (mesh.mFaces.empty() || mesh.mPositions.empty()) { - return; - } - - // convert vertices - p_pcOut->mNumVertices = (unsigned int)mesh.mPositions.size(); - p_pcOut->mNumFaces = (unsigned int)mesh.mFaces.size(); - - // allocate enough storage for faces - p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; - - // copy vertices - p_pcOut->mVertices = new aiVector3D[mesh.mPositions.size()]; - memcpy(p_pcOut->mVertices,&mesh.mPositions[0], - mesh.mPositions.size() * sizeof(aiVector3D)); - - // copy normals - p_pcOut->mNormals = new aiVector3D[mesh.mNormals.size()]; - memcpy(p_pcOut->mNormals,&mesh.mNormals[0], - mesh.mNormals.size() * sizeof(aiVector3D)); - - // copy texture coordinates - for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { - if (!mesh.amTexCoords[c].empty()) { - p_pcOut->mTextureCoords[c] = new aiVector3D[mesh.amTexCoords[c].size()]; - memcpy(p_pcOut->mTextureCoords[c],&mesh.amTexCoords[c][0], - mesh.amTexCoords[c].size() * sizeof(aiVector3D)); - - // setup the number of valid vertex components - p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; - } - } - - // copy vertex colors - if (!mesh.mVertexColors.empty()) { - p_pcOut->mColors[0] = new aiColor4D[mesh.mVertexColors.size()]; - memcpy(p_pcOut->mColors[0],&mesh.mVertexColors[0], - mesh.mVertexColors.size() * sizeof(aiColor4D)); - } - - // copy faces - for (unsigned int iFace = 0; iFace < p_pcOut->mNumFaces;++iFace) { - p_pcOut->mFaces[iFace].mNumIndices = 3; - p_pcOut->mFaces[iFace].mIndices = new unsigned int[3]; - - // copy indices - p_pcOut->mFaces[iFace].mIndices[0] = mesh.mFaces[iFace].mIndices[0]; - p_pcOut->mFaces[iFace].mIndices[1] = mesh.mFaces[iFace].mIndices[1]; - p_pcOut->mFaces[iFace].mIndices[2] = mesh.mFaces[iFace].mIndices[2]; - } - - // copy vertex bones - if (!mesh.mBones.empty() && !mesh.mBoneVertices.empty()) { - std::vector > avBonesOut( mesh.mBones.size() ); - - // find all vertex weights for this bone - unsigned int quak = 0; - for (std::vector::const_iterator harrypotter = mesh.mBoneVertices.begin(); - harrypotter != mesh.mBoneVertices.end();++harrypotter,++quak) { - - for (std::vector >::const_iterator - ronaldweasley = (*harrypotter).mBoneWeights.begin(); - ronaldweasley != (*harrypotter).mBoneWeights.end();++ronaldweasley) - { - aiVertexWeight weight; - weight.mVertexId = quak; - weight.mWeight = (*ronaldweasley).second; - avBonesOut[(*ronaldweasley).first].push_back(weight); - } - } - - // now build a final bone list - p_pcOut->mNumBones = 0; - for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) - if (!avBonesOut[jfkennedy].empty())p_pcOut->mNumBones++; - - p_pcOut->mBones = new aiBone*[p_pcOut->mNumBones]; - aiBone** pcBone = p_pcOut->mBones; - for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) { - if (!avBonesOut[jfkennedy].empty()) { - aiBone* pc = *pcBone = new aiBone(); - pc->mName.Set(mesh.mBones[jfkennedy].mName); - pc->mNumWeights = (unsigned int)avBonesOut[jfkennedy].size(); - pc->mWeights = new aiVertexWeight[pc->mNumWeights]; - ::memcpy(pc->mWeights,&avBonesOut[jfkennedy][0], - sizeof(aiVertexWeight) * pc->mNumWeights); - ++pcBone; - } - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Setup proper material indices and build output materials -void ASEImporter::BuildMaterialIndices() -{ - ai_assert(NULL != pcScene); - - // iterate through all materials and check whether we need them - for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) - { - ASE::Material& mat = mParser->m_vMaterials[iMat]; - if (mat.bNeed) { - // Convert it to the aiMaterial layout - ConvertMaterial(mat); - ++pcScene->mNumMaterials; - } - for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) - { - ASE::Material& submat = mat.avSubMaterials[iSubMat]; - if (submat.bNeed) { - // Convert it to the aiMaterial layout - ConvertMaterial(submat); - ++pcScene->mNumMaterials; - } - } - } - - // allocate the output material array - pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials]; - D3DS::Material** pcIntMaterials = new D3DS::Material*[pcScene->mNumMaterials]; - - unsigned int iNum = 0; - for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) { - ASE::Material& mat = mParser->m_vMaterials[iMat]; - if (mat.bNeed) - { - ai_assert(NULL != mat.pcInstance); - pcScene->mMaterials[iNum] = mat.pcInstance; - - // Store the internal material, too - pcIntMaterials[iNum] = &mat; - - // Iterate through all meshes and search for one which is using - // this top-level material index - for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) - { - aiMesh* mesh = pcScene->mMeshes[iMesh]; - if (ASE::Face::DEFAULT_MATINDEX == mesh->mMaterialIndex && - iMat == (uintptr_t)mesh->mColors[3]) - { - mesh->mMaterialIndex = iNum; - mesh->mColors[3] = NULL; - } - } - iNum++; - } - for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) { - ASE::Material& submat = mat.avSubMaterials[iSubMat]; - if (submat.bNeed) { - ai_assert(NULL != submat.pcInstance); - pcScene->mMaterials[iNum] = submat.pcInstance; - - // Store the internal material, too - pcIntMaterials[iNum] = &submat; - - // Iterate through all meshes and search for one which is using - // this sub-level material index - for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) { - aiMesh* mesh = pcScene->mMeshes[iMesh]; - - if (iSubMat == mesh->mMaterialIndex && iMat == (uintptr_t)mesh->mColors[3]) { - mesh->mMaterialIndex = iNum; - mesh->mColors[3] = NULL; - } - } - iNum++; - } - } - } - - // Dekete our temporary array - delete[] pcIntMaterials; -} - -// ------------------------------------------------------------------------------------------------ -// Generate normal vectors basing on smoothing groups -bool ASEImporter::GenerateNormals(ASE::Mesh& mesh) { - - if (!mesh.mNormals.empty() && !configRecomputeNormals) - { - // Check whether there are only uninitialized normals. If there are - // some, skip all normals from the file and compute them on our own - for (std::vector::const_iterator qq = mesh.mNormals.begin();qq != mesh.mNormals.end();++qq) { - if ((*qq).x || (*qq).y || (*qq).z) - { - return true; - } - } - } - // The array is reused. - ComputeNormalsWithSmoothingsGroups(mesh); - return false; -} - -#endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file ASELoader.cpp + * @brief Implementation of the ASE importer class + */ + +#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER + +// internal headers +#include "ASELoader.h" +#include "StringComparison.h" +#include "SkeletonMeshBuilder.h" +#include "TargetAnimation.h" +#include "../include/assimp/Importer.hpp" +#include +#include "../include/assimp/IOSystem.hpp" +#include "../include/assimp/DefaultLogger.hpp" +#include "../include/assimp/scene.h" + + +// utilities +#include "fast_atof.h" + +using namespace Assimp; +using namespace Assimp::ASE; + +static const aiImporterDesc desc = { + "ASE Importer", + "", + "", + "Similar to 3DS but text-encoded", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "ase ask" +}; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +ASEImporter::ASEImporter() +: noSkeletonMesh() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +ASEImporter::~ASEImporter() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const +{ + // check file extension + const std::string extension = GetExtension(pFile); + + if( extension == "ase" || extension == "ask") + return true; + + if ((!extension.length() || cs) && pIOHandler) { + const char* tokens[] = {"*3dsmax_asciiexport"}; + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Loader meta information +const aiImporterDesc* ASEImporter::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration options +void ASEImporter::SetupProperties(const Importer* pImp) +{ + configRecomputeNormals = (pImp->GetPropertyInteger( + AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS,1) ? true : false); + + noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void ASEImporter::InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) +{ + boost::scoped_ptr file( pIOHandler->Open( pFile, "rb")); + + // Check whether we can read from the file + if( file.get() == NULL) { + throw DeadlyImportError( "Failed to open ASE file " + pFile + "."); + } + + // Allocate storage and copy the contents of the file to a memory buffer + std::vector mBuffer2; + TextFileToBuffer(file.get(),mBuffer2); + + this->mBuffer = &mBuffer2[0]; + this->pcScene = pScene; + + // ------------------------------------------------------------------ + // Guess the file format by looking at the extension + // ASC is considered to be the older format 110, + // ASE is the actual version 200 (that is currently written by max) + // ------------------------------------------------------------------ + unsigned int defaultFormat; + std::string::size_type s = pFile.length()-1; + switch (pFile.c_str()[s]) { + + case 'C': + case 'c': + defaultFormat = AI_ASE_OLD_FILE_FORMAT; + break; + default: + defaultFormat = AI_ASE_NEW_FILE_FORMAT; + }; + + // Construct an ASE parser and parse the file + ASE::Parser parser(mBuffer,defaultFormat); + mParser = &parser; + mParser->Parse(); + + //------------------------------------------------------------------ + // Check whether we god at least one mesh. If we did - generate + // materials and copy meshes. + // ------------------------------------------------------------------ + if ( !mParser->m_vMeshes.empty()) { + + // If absolutely no material has been loaded from the file + // we need to generate a default material + GenerateDefaultMaterial(); + + // process all meshes + bool tookNormals = false; + std::vector avOutMeshes; + avOutMeshes.reserve(mParser->m_vMeshes.size()*2); + for (std::vector::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { + if ((*i).bSkip) { + continue; + } + BuildUniqueRepresentation(*i); + + // Need to generate proper vertex normals if necessary + if(GenerateNormals(*i)) { + tookNormals = true; + } + + // Convert all meshes to aiMesh objects + ConvertMeshes(*i,avOutMeshes); + } + if (tookNormals) { + DefaultLogger::get()->debug("ASE: Taking normals from the file. Use " + "the AI_CONFIG_IMPORT_ASE_RECONSTRUCT_NORMALS setting if you " + "experience problems"); + } + + // Now build the output mesh list. Remove dummies + pScene->mNumMeshes = (unsigned int)avOutMeshes.size(); + aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + for (std::vector::const_iterator i = avOutMeshes.begin();i != avOutMeshes.end();++i) { + if (!(*i)->mNumFaces) { + continue; + } + *pp++ = *i; + } + pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes); + + // Build final material indices (remove submaterials and setup + // the final list) + BuildMaterialIndices(); + } + + // ------------------------------------------------------------------ + // Copy all scene graph nodes - lights, cameras, dummies and meshes + // into one huge list. + //------------------------------------------------------------------ + std::vector nodes; + nodes.reserve(mParser->m_vMeshes.size() +mParser->m_vLights.size() + + mParser->m_vCameras.size() + mParser->m_vDummies.size()); + + // Lights + for (std::vector::iterator it = mParser->m_vLights.begin(), + end = mParser->m_vLights.end();it != end; ++it)nodes.push_back(&(*it)); + // Cameras + for (std::vector::iterator it = mParser->m_vCameras.begin(), + end = mParser->m_vCameras.end();it != end; ++it)nodes.push_back(&(*it)); + // Meshes + for (std::vector::iterator it = mParser->m_vMeshes.begin(), + end = mParser->m_vMeshes.end();it != end; ++it)nodes.push_back(&(*it)); + // Dummies + for (std::vector::iterator it = mParser->m_vDummies.begin(), + end = mParser->m_vDummies.end();it != end; ++it)nodes.push_back(&(*it)); + + // build the final node graph + BuildNodes(nodes); + + // build output animations + BuildAnimations(nodes); + + // build output cameras + BuildCameras(); + + // build output lights + BuildLights(); + + // ------------------------------------------------------------------ + // If we have no meshes use the SkeletonMeshBuilder helper class + // to build a mesh for the animation skeleton + // FIXME: very strange results + // ------------------------------------------------------------------ + if (!pScene->mNumMeshes) { + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + if (!noSkeletonMesh) { + SkeletonMeshBuilder skeleton(pScene); + } + } +} +// ------------------------------------------------------------------------------------------------ +void ASEImporter::GenerateDefaultMaterial() +{ + ai_assert(NULL != mParser); + + bool bHas = false; + for (std::vector::iterator i = mParser->m_vMeshes.begin();i != mParser->m_vMeshes.end();++i) { + if ((*i).bSkip)continue; + if (ASE::Face::DEFAULT_MATINDEX == (*i).iMaterialIndex) { + (*i).iMaterialIndex = (unsigned int)mParser->m_vMaterials.size(); + bHas = true; + } + } + if (bHas || mParser->m_vMaterials.empty()) { + // add a simple material without submaterials to the parser's list + mParser->m_vMaterials.push_back ( ASE::Material() ); + ASE::Material& mat = mParser->m_vMaterials.back(); + + mat.mDiffuse = aiColor3D(0.6f,0.6f,0.6f); + mat.mSpecular = aiColor3D(1.0f,1.0f,1.0f); + mat.mAmbient = aiColor3D(0.05f,0.05f,0.05f); + mat.mShading = Discreet3DS::Gouraud; + mat.mName = AI_DEFAULT_MATERIAL_NAME; + } +} + +// ------------------------------------------------------------------------------------------------ +void ASEImporter::BuildAnimations(const std::vector& nodes) +{ + // check whether we have at least one mesh which has animations + std::vector::const_iterator i = nodes.begin(); + unsigned int iNum = 0; + for (;i != nodes.end();++i) { + + // TODO: Implement Bezier & TCB support + if ((*i)->mAnim.mPositionType != ASE::Animation::TRACK) { + DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " + "This is not supported."); + } + if ((*i)->mAnim.mRotationType != ASE::Animation::TRACK) { + DefaultLogger::get()->warn("ASE: Rotation controller uses Bezier/TCB keys. " + "This is not supported."); + } + if ((*i)->mAnim.mScalingType != ASE::Animation::TRACK) { + DefaultLogger::get()->warn("ASE: Position controller uses Bezier/TCB keys. " + "This is not supported."); + } + + // We compare against 1 here - firstly one key is not + // really an animation and secondly MAX writes dummies + // that represent the node transformation. + if ((*i)->mAnim.akeyPositions.size()>1 || (*i)->mAnim.akeyRotations.size()>1 || (*i)->mAnim.akeyScaling.size()>1){ + ++iNum; + } + if ((*i)->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( (*i)->mTargetPosition.x )) { + ++iNum; + } + } + if (iNum) { + // Generate a new animation channel and setup everything for it + pcScene->mNumAnimations = 1; + pcScene->mAnimations = new aiAnimation*[1]; + aiAnimation* pcAnim = pcScene->mAnimations[0] = new aiAnimation(); + pcAnim->mNumChannels = iNum; + pcAnim->mChannels = new aiNodeAnim*[iNum]; + pcAnim->mTicksPerSecond = mParser->iFrameSpeed * mParser->iTicksPerFrame; + + iNum = 0; + + // Now iterate through all meshes and collect all data we can find + for (i = nodes.begin();i != nodes.end();++i) { + + ASE::BaseNode* me = *i; + if ( me->mTargetAnim.akeyPositions.size() > 1 && is_not_qnan( me->mTargetPosition.x )) { + // Generate an extra channel for the camera/light target. + // BuildNodes() does also generate an extra node, named + // .Target. + aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); + nd->mNodeName.Set(me->mName + ".Target"); + + // If there is no input position channel we will need + // to supply the default position from the node's + // local transformation matrix. + /*TargetAnimationHelper helper; + if (me->mAnim.akeyPositions.empty()) + { + aiMatrix4x4& mat = (*i)->mTransform; + helper.SetFixedMainAnimationChannel(aiVector3D( + mat.a4, mat.b4, mat.c4)); + } + else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions); + helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions); + + helper.Process(&me->mTargetAnim.akeyPositions);*/ + + // Allocate the key array and fill it + nd->mNumPositionKeys = (unsigned int) me->mTargetAnim.akeyPositions.size(); + nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; + + ::memcpy(nd->mPositionKeys,&me->mTargetAnim.akeyPositions[0], + nd->mNumPositionKeys * sizeof(aiVectorKey)); + } + + if (me->mAnim.akeyPositions.size() > 1 || me->mAnim.akeyRotations.size() > 1 || me->mAnim.akeyScaling.size() > 1) { + // Begin a new node animation channel for this node + aiNodeAnim* nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); + nd->mNodeName.Set(me->mName); + + // copy position keys + if (me->mAnim.akeyPositions.size() > 1 ) + { + // Allocate the key array and fill it + nd->mNumPositionKeys = (unsigned int) me->mAnim.akeyPositions.size(); + nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; + + ::memcpy(nd->mPositionKeys,&me->mAnim.akeyPositions[0], + nd->mNumPositionKeys * sizeof(aiVectorKey)); + } + // copy rotation keys + if (me->mAnim.akeyRotations.size() > 1 ) { + // Allocate the key array and fill it + nd->mNumRotationKeys = (unsigned int) me->mAnim.akeyRotations.size(); + nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys]; + + // -------------------------------------------------------------------- + // Rotation keys are offsets to the previous keys. + // We have the quaternion representations of all + // of them, so we just need to concatenate all + // (unit-length) quaternions to get the absolute + // rotations. + // Rotation keys are ABSOLUTE for older files + // -------------------------------------------------------------------- + + aiQuaternion cur; + for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) { + aiQuatKey q = me->mAnim.akeyRotations[a]; + + if (mParser->iFileFormat > 110) { + cur = (a ? cur*q.mValue : q.mValue); + q.mValue = cur.Normalize(); + } + nd->mRotationKeys[a] = q; + + // need this to get to Assimp quaternion conventions + nd->mRotationKeys[a].mValue.w *= -1.f; + } + } + // copy scaling keys + if (me->mAnim.akeyScaling.size() > 1 ) { + // Allocate the key array and fill it + nd->mNumScalingKeys = (unsigned int) me->mAnim.akeyScaling.size(); + nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys]; + + ::memcpy(nd->mScalingKeys,&me->mAnim.akeyScaling[0], + nd->mNumScalingKeys * sizeof(aiVectorKey)); + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Build output cameras +void ASEImporter::BuildCameras() +{ + if (!mParser->m_vCameras.empty()) { + pcScene->mNumCameras = (unsigned int)mParser->m_vCameras.size(); + pcScene->mCameras = new aiCamera*[pcScene->mNumCameras]; + + for (unsigned int i = 0; i < pcScene->mNumCameras;++i) { + aiCamera* out = pcScene->mCameras[i] = new aiCamera(); + ASE::Camera& in = mParser->m_vCameras[i]; + + // copy members + out->mClipPlaneFar = in.mFar; + out->mClipPlaneNear = (in.mNear ? in.mNear : 0.1f); + out->mHorizontalFOV = in.mFOV; + + out->mName.Set(in.mName); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Build output lights +void ASEImporter::BuildLights() +{ + if (!mParser->m_vLights.empty()) { + pcScene->mNumLights = (unsigned int)mParser->m_vLights.size(); + pcScene->mLights = new aiLight*[pcScene->mNumLights]; + + for (unsigned int i = 0; i < pcScene->mNumLights;++i) { + aiLight* out = pcScene->mLights[i] = new aiLight(); + ASE::Light& in = mParser->m_vLights[i]; + + // The direction is encoded in the transformation matrix of the node. + // In 3DS MAX the light source points into negative Z direction if + // the node transformation is the identity. + out->mDirection = aiVector3D(0.f,0.f,-1.f); + + out->mName.Set(in.mName); + switch (in.mLightType) + { + case ASE::Light::TARGET: + out->mType = aiLightSource_SPOT; + out->mAngleInnerCone = AI_DEG_TO_RAD(in.mAngle); + out->mAngleOuterCone = (in.mFalloff ? AI_DEG_TO_RAD(in.mFalloff) : out->mAngleInnerCone); + break; + + case ASE::Light::DIRECTIONAL: + out->mType = aiLightSource_DIRECTIONAL; + break; + + default: + //case ASE::Light::OMNI: + out->mType = aiLightSource_POINT; + break; + }; + out->mColorDiffuse = out->mColorSpecular = in.mColor * in.mIntensity; + } + } +} + +// ------------------------------------------------------------------------------------------------ +void ASEImporter::AddNodes(const std::vector& nodes, + aiNode* pcParent,const char* szName) +{ + aiMatrix4x4 m; + AddNodes(nodes,pcParent,szName,m); +} + +// ------------------------------------------------------------------------------------------------ +// Add meshes to a given node +void ASEImporter::AddMeshes(const ASE::BaseNode* snode,aiNode* node) +{ + for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) { + // Get the name of the mesh (the mesh instance has been temporarily stored in the third vertex color) + const aiMesh* pcMesh = pcScene->mMeshes[i]; + const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; + + if (mesh == snode) { + ++node->mNumMeshes; + } + } + + if(node->mNumMeshes) { + node->mMeshes = new unsigned int[node->mNumMeshes]; + for (unsigned int i = 0, p = 0; i < pcScene->mNumMeshes;++i) { + + const aiMesh* pcMesh = pcScene->mMeshes[i]; + const ASE::Mesh* mesh = (const ASE::Mesh*)pcMesh->mColors[2]; + if (mesh == snode) { + node->mMeshes[p++] = i; + + // Transform all vertices of the mesh back into their local space -> + // at the moment they are pretransformed + aiMatrix4x4 m = mesh->mTransform; + m.Inverse(); + + aiVector3D* pvCurPtr = pcMesh->mVertices; + const aiVector3D* pvEndPtr = pvCurPtr + pcMesh->mNumVertices; + while (pvCurPtr != pvEndPtr) { + *pvCurPtr = m * (*pvCurPtr); + pvCurPtr++; + } + + // Do the same for the normal vectors, if we have them. + // As always, inverse transpose. + if (pcMesh->mNormals) { + aiMatrix3x3 m3 = aiMatrix3x3( mesh->mTransform ); + m3.Transpose(); + + pvCurPtr = pcMesh->mNormals; + pvEndPtr = pvCurPtr + pcMesh->mNumVertices; + while (pvCurPtr != pvEndPtr) { + *pvCurPtr = m3 * (*pvCurPtr); + pvCurPtr++; + } + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Add child nodes to a given parent node +void ASEImporter::AddNodes (const std::vector& nodes, + aiNode* pcParent, const char* szName, + const aiMatrix4x4& mat) +{ + const size_t len = szName ? ::strlen(szName) : 0; + ai_assert(4 <= AI_MAX_NUMBER_OF_COLOR_SETS); + + // Receives child nodes for the pcParent node + std::vector apcNodes; + + // Now iterate through all nodes in the scene and search for one + // which has *us* as parent. + for (std::vector::const_iterator it = nodes.begin(), end = nodes.end(); it != end; ++it) { + const BaseNode* snode = *it; + if (szName) { + if (len != snode->mParent.length() || ::strcmp(szName,snode->mParent.c_str())) + continue; + } + else if (snode->mParent.length()) + continue; + + (*it)->mProcessed = true; + + // Allocate a new node and add it to the output data structure + apcNodes.push_back(new aiNode()); + aiNode* node = apcNodes.back(); + + node->mName.Set((snode->mName.length() ? snode->mName.c_str() : "Unnamed_Node")); + node->mParent = pcParent; + + // Setup the transformation matrix of the node + aiMatrix4x4 mParentAdjust = mat; + mParentAdjust.Inverse(); + node->mTransformation = mParentAdjust*snode->mTransform; + + // Add sub nodes - prevent stack overflow due to recursive parenting + if (node->mName != node->mParent->mName) { + AddNodes(nodes,node,node->mName.data,snode->mTransform); + } + + // Further processing depends on the type of the node + if (snode->mType == ASE::BaseNode::Mesh) { + // If the type of this node is "Mesh" we need to search + // the list of output meshes in the data structure for + // all those that belonged to this node once. This is + // slightly inconvinient here and a better solution should + // be used when this code is refactored next. + AddMeshes(snode,node); + } + else if (is_not_qnan( snode->mTargetPosition.x )) { + // If this is a target camera or light we generate a small + // child node which marks the position of the camera + // target (the direction information is contained in *this* + // node's animation track but the exact target position + // would be lost otherwise) + if (!node->mNumChildren) { + node->mChildren = new aiNode*[1]; + } + + aiNode* nd = new aiNode(); + + nd->mName.Set ( snode->mName + ".Target" ); + + nd->mTransformation.a4 = snode->mTargetPosition.x - snode->mTransform.a4; + nd->mTransformation.b4 = snode->mTargetPosition.y - snode->mTransform.b4; + nd->mTransformation.c4 = snode->mTargetPosition.z - snode->mTransform.c4; + + nd->mParent = node; + + // The .Target node is always the first child node + for (unsigned int m = 0; m < node->mNumChildren;++m) + node->mChildren[m+1] = node->mChildren[m]; + + node->mChildren[0] = nd; + node->mNumChildren++; + + // What we did is so great, it is at least worth a debug message + DefaultLogger::get()->debug("ASE: Generating separate target node ("+snode->mName+")"); + } + } + + // Allocate enough space for the child nodes + // We allocate one slot more in case this is a target camera/light + pcParent->mNumChildren = (unsigned int)apcNodes.size(); + if (pcParent->mNumChildren) { + pcParent->mChildren = new aiNode*[apcNodes.size()+1 /* PLUS ONE !!! */]; + + // now build all nodes for our nice new children + for (unsigned int p = 0; p < apcNodes.size();++p) + pcParent->mChildren[p] = apcNodes[p]; + } + return; +} + +// ------------------------------------------------------------------------------------------------ +// Build the output node graph +void ASEImporter::BuildNodes(std::vector& nodes) { + ai_assert(NULL != pcScene); + + // allocate the one and only root node + aiNode* root = pcScene->mRootNode = new aiNode(); + root->mName.Set(""); + + // Setup the coordinate system transformation + pcScene->mRootNode->mNumChildren = 1; + pcScene->mRootNode->mChildren = new aiNode*[1]; + aiNode* ch = pcScene->mRootNode->mChildren[0] = new aiNode(); + ch->mParent = root; + + // Change the transformation matrix of all nodes + for (std::vector::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) { + aiMatrix4x4& m = (*it)->mTransform; + m.Transpose(); // row-order vs column-order + } + + // add all nodes + AddNodes(nodes,ch,NULL); + + // now iterate through al nodes and find those that have not yet + // been added to the nodegraph (= their parent could not be recognized) + std::vector aiList; + for (std::vector::iterator it = nodes.begin(), end = nodes.end();it != end; ++it) { + if ((*it)->mProcessed) { + continue; + } + + // check whether our parent is known + bool bKnowParent = false; + + // search the list another time, starting *here* and try to find out whether + // there is a node that references *us* as a parent + for (std::vector::const_iterator it2 = nodes.begin();it2 != end; ++it2) { + if (it2 == it) { + continue; + } + + if ((*it2)->mName == (*it)->mParent) { + bKnowParent = true; + break; + } + } + if (!bKnowParent) { + aiList.push_back(*it); + } + } + + // Are there ane orphaned nodes? + if (!aiList.empty()) { + std::vector apcNodes; + apcNodes.reserve(aiList.size() + pcScene->mRootNode->mNumChildren); + + for (unsigned int i = 0; i < pcScene->mRootNode->mNumChildren;++i) + apcNodes.push_back(pcScene->mRootNode->mChildren[i]); + + delete[] pcScene->mRootNode->mChildren; + for (std::vector::/*const_*/iterator i = aiList.begin();i != aiList.end();++i) { + const ASE::BaseNode* src = *i; + + // The parent is not known, so we can assume that we must add + // this node to the root node of the whole scene + aiNode* pcNode = new aiNode(); + pcNode->mParent = pcScene->mRootNode; + pcNode->mName.Set(src->mName); + AddMeshes(src,pcNode); + AddNodes(nodes,pcNode,pcNode->mName.data); + apcNodes.push_back(pcNode); + } + + // Regenerate our output array + pcScene->mRootNode->mChildren = new aiNode*[apcNodes.size()]; + for (unsigned int i = 0; i < apcNodes.size();++i) + pcScene->mRootNode->mChildren[i] = apcNodes[i]; + + pcScene->mRootNode->mNumChildren = (unsigned int)apcNodes.size(); + } + + // Reset the third color set to NULL - we used this field to store a temporary pointer + for (unsigned int i = 0; i < pcScene->mNumMeshes;++i) + pcScene->mMeshes[i]->mColors[2] = NULL; + + // The root node should not have at least one child or the file is valid + if (!pcScene->mRootNode->mNumChildren) { + throw DeadlyImportError("ASE: No nodes loaded. The file is either empty or corrupt"); + } + + // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system + pcScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f, + 0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f); +} + +// ------------------------------------------------------------------------------------------------ +// Convert the imported data to the internal verbose representation +void ASEImporter::BuildUniqueRepresentation(ASE::Mesh& mesh) { + // allocate output storage + std::vector mPositions; + std::vector amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + std::vector mVertexColors; + std::vector mNormals; + std::vector mBoneVertices; + + unsigned int iSize = (unsigned int)mesh.mFaces.size() * 3; + mPositions.resize(iSize); + + // optional texture coordinates + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) { + if (!mesh.amTexCoords[i].empty()) { + amTexCoords[i].resize(iSize); + } + } + // optional vertex colors + if (!mesh.mVertexColors.empty()) { + mVertexColors.resize(iSize); + } + + // optional vertex normals (vertex normals can simply be copied) + if (!mesh.mNormals.empty()) { + mNormals.resize(iSize); + } + // bone vertices. There is no need to change the bone list + if (!mesh.mBoneVertices.empty()) { + mBoneVertices.resize(iSize); + } + + // iterate through all faces in the mesh + unsigned int iCurrent = 0, fi = 0; + for (std::vector::iterator i = mesh.mFaces.begin();i != mesh.mFaces.end();++i,++fi) { + for (unsigned int n = 0; n < 3;++n,++iCurrent) + { + mPositions[iCurrent] = mesh.mPositions[(*i).mIndices[n]]; + + // add texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + if (mesh.amTexCoords[c].empty())break; + amTexCoords[c][iCurrent] = mesh.amTexCoords[c][(*i).amUVIndices[c][n]]; + } + // add vertex colors + if (!mesh.mVertexColors.empty()) { + mVertexColors[iCurrent] = mesh.mVertexColors[(*i).mColorIndices[n]]; + } + // add normal vectors + if (!mesh.mNormals.empty()) { + mNormals[iCurrent] = mesh.mNormals[fi*3+n]; + mNormals[iCurrent].Normalize(); + } + + // handle bone vertices + if ((*i).mIndices[n] < mesh.mBoneVertices.size()) { + // (sometimes this will cause bone verts to be duplicated + // however, I' quite sure Schrompf' JoinVerticesStep + // will fix that again ...) + mBoneVertices[iCurrent] = mesh.mBoneVertices[(*i).mIndices[n]]; + } + (*i).mIndices[n] = iCurrent; + } + } + + // replace the old arrays + mesh.mNormals = mNormals; + mesh.mPositions = mPositions; + mesh.mVertexColors = mVertexColors; + + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) + mesh.amTexCoords[c] = amTexCoords[c]; +} + +// ------------------------------------------------------------------------------------------------ +// Copy a texture from the ASE structs to the output material +void CopyASETexture(aiMaterial& mat, ASE::Texture& texture, aiTextureType type) +{ + // Setup the texture name + aiString tex; + tex.Set( texture.mMapName); + mat.AddProperty( &tex, AI_MATKEY_TEXTURE(type,0)); + + // Setup the texture blend factor + if (is_not_qnan(texture.mTextureBlend)) + mat.AddProperty( &texture.mTextureBlend, 1, AI_MATKEY_TEXBLEND(type,0)); + + // Setup texture UV transformations + mat.AddProperty(&texture.mOffsetU,5,AI_MATKEY_UVTRANSFORM(type,0)); +} + +// ------------------------------------------------------------------------------------------------ +// Convert from ASE material to output material +void ASEImporter::ConvertMaterial(ASE::Material& mat) +{ + // LARGE TODO: Much code her is copied from 3DS ... join them maybe? + + // Allocate the output material + mat.pcInstance = new aiMaterial(); + + // At first add the base ambient color of the + // scene to the material + mat.mAmbient.r += mParser->m_clrAmbient.r; + mat.mAmbient.g += mParser->m_clrAmbient.g; + mat.mAmbient.b += mParser->m_clrAmbient.b; + + aiString name; + name.Set( mat.mName); + mat.pcInstance->AddProperty( &name, AI_MATKEY_NAME); + + // material colors + mat.pcInstance->AddProperty( &mat.mAmbient, 1, AI_MATKEY_COLOR_AMBIENT); + mat.pcInstance->AddProperty( &mat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + mat.pcInstance->AddProperty( &mat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); + mat.pcInstance->AddProperty( &mat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); + + // shininess + if (0.0f != mat.mSpecularExponent && 0.0f != mat.mShininessStrength) + { + mat.pcInstance->AddProperty( &mat.mSpecularExponent, 1, AI_MATKEY_SHININESS); + mat.pcInstance->AddProperty( &mat.mShininessStrength, 1, AI_MATKEY_SHININESS_STRENGTH); + } + // If there is no shininess, we can disable phong lighting + else if (D3DS::Discreet3DS::Metal == mat.mShading || + D3DS::Discreet3DS::Phong == mat.mShading || + D3DS::Discreet3DS::Blinn == mat.mShading) + { + mat.mShading = D3DS::Discreet3DS::Gouraud; + } + + // opacity + mat.pcInstance->AddProperty( &mat.mTransparency,1,AI_MATKEY_OPACITY); + + // Two sided rendering? + if (mat.mTwoSided) + { + int i = 1; + mat.pcInstance->AddProperty(&i,1,AI_MATKEY_TWOSIDED); + } + + // shading mode + aiShadingMode eShading = aiShadingMode_NoShading; + switch (mat.mShading) + { + case D3DS::Discreet3DS::Flat: + eShading = aiShadingMode_Flat; break; + case D3DS::Discreet3DS::Phong : + eShading = aiShadingMode_Phong; break; + case D3DS::Discreet3DS::Blinn : + eShading = aiShadingMode_Blinn; break; + + // I don't know what "Wire" shading should be, + // assume it is simple lambertian diffuse (L dot N) shading + case D3DS::Discreet3DS::Wire: + { + // set the wireframe flag + unsigned int iWire = 1; + mat.pcInstance->AddProperty( (int*)&iWire,1,AI_MATKEY_ENABLE_WIREFRAME); + } + case D3DS::Discreet3DS::Gouraud: + eShading = aiShadingMode_Gouraud; break; + case D3DS::Discreet3DS::Metal : + eShading = aiShadingMode_CookTorrance; break; + } + mat.pcInstance->AddProperty( (int*)&eShading,1,AI_MATKEY_SHADING_MODEL); + + // DIFFUSE texture + if( mat.sTexDiffuse.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexDiffuse, aiTextureType_DIFFUSE); + + // SPECULAR texture + if( mat.sTexSpecular.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexSpecular, aiTextureType_SPECULAR); + + // AMBIENT texture + if( mat.sTexAmbient.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexAmbient, aiTextureType_AMBIENT); + + // OPACITY texture + if( mat.sTexOpacity.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexOpacity, aiTextureType_OPACITY); + + // EMISSIVE texture + if( mat.sTexEmissive.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexEmissive, aiTextureType_EMISSIVE); + + // BUMP texture + if( mat.sTexBump.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexBump, aiTextureType_HEIGHT); + + // SHININESS texture + if( mat.sTexShininess.mMapName.length() > 0) + CopyASETexture(*mat.pcInstance,mat.sTexShininess, aiTextureType_SHININESS); + + // store the name of the material itself, too + if( mat.mName.length() > 0) { + aiString tex;tex.Set( mat.mName); + mat.pcInstance->AddProperty( &tex, AI_MATKEY_NAME); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +// Build output meshes +void ASEImporter::ConvertMeshes(ASE::Mesh& mesh, std::vector& avOutMeshes) +{ + // validate the material index of the mesh + if (mesh.iMaterialIndex >= mParser->m_vMaterials.size()) { + mesh.iMaterialIndex = (unsigned int)mParser->m_vMaterials.size()-1; + DefaultLogger::get()->warn("Material index is out of range"); + } + + // If the material the mesh is assigned to is consisting of submeshes, split it + if (!mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials.empty()) { + std::vector vSubMaterials = mParser-> + m_vMaterials[mesh.iMaterialIndex].avSubMaterials; + + std::vector* aiSplit = new std::vector[vSubMaterials.size()]; + + // build a list of all faces per submaterial + for (unsigned int i = 0; i < mesh.mFaces.size();++i) { + // check range + if (mesh.mFaces[i].iMaterial >= vSubMaterials.size()) { + DefaultLogger::get()->warn("Submaterial index is out of range"); + + // use the last material instead + aiSplit[vSubMaterials.size()-1].push_back(i); + } + else aiSplit[mesh.mFaces[i].iMaterial].push_back(i); + } + + // now generate submeshes + for (unsigned int p = 0; p < vSubMaterials.size();++p) { + if (!aiSplit[p].empty()) { + + aiMesh* p_pcOut = new aiMesh(); + p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + // let the sub material index + p_pcOut->mMaterialIndex = p; + + // we will need this material + mParser->m_vMaterials[mesh.iMaterialIndex].avSubMaterials[p].bNeed = true; + + // store the real index here ... color channel 3 + p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; + + // store a pointer to the mesh in color channel 2 + p_pcOut->mColors[2] = (aiColor4D*) &mesh; + avOutMeshes.push_back(p_pcOut); + + // convert vertices + p_pcOut->mNumVertices = (unsigned int)aiSplit[p].size()*3; + p_pcOut->mNumFaces = (unsigned int)aiSplit[p].size(); + + // receive output vertex weights + std::vector > *avOutputBones = NULL; + if (!mesh.mBones.empty()) { + avOutputBones = new std::vector >[mesh.mBones.size()]; + } + + // allocate enough storage for faces + p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; + + unsigned int iBase = 0,iIndex; + if (p_pcOut->mNumVertices) { + p_pcOut->mVertices = new aiVector3D[p_pcOut->mNumVertices]; + p_pcOut->mNormals = new aiVector3D[p_pcOut->mNumVertices]; + for (unsigned int q = 0; q < aiSplit[p].size();++q) { + + iIndex = aiSplit[p][q]; + + p_pcOut->mFaces[q].mIndices = new unsigned int[3]; + p_pcOut->mFaces[q].mNumIndices = 3; + + for (unsigned int t = 0; t < 3;++t, ++iBase) { + const uint32_t iIndex2 = mesh.mFaces[iIndex].mIndices[t]; + + p_pcOut->mVertices[iBase] = mesh.mPositions [iIndex2]; + p_pcOut->mNormals [iBase] = mesh.mNormals [iIndex2]; + + // convert bones, if existing + if (!mesh.mBones.empty()) { + // check whether there is a vertex weight for this vertex index + if (iIndex2 < mesh.mBoneVertices.size()) { + + for (std::vector >::const_iterator + blubb = mesh.mBoneVertices[iIndex2].mBoneWeights.begin(); + blubb != mesh.mBoneVertices[iIndex2].mBoneWeights.end();++blubb) { + + // NOTE: illegal cases have already been filtered out + avOutputBones[(*blubb).first].push_back(std::pair( + iBase,(*blubb).second)); + } + } + } + p_pcOut->mFaces[q].mIndices[t] = iBase; + } + } + } + // convert texture coordinates (up to AI_MAX_NUMBER_OF_TEXTURECOORDS sets supported) + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + if (!mesh.amTexCoords[c].empty()) + { + p_pcOut->mTextureCoords[c] = new aiVector3D[p_pcOut->mNumVertices]; + iBase = 0; + for (unsigned int q = 0; q < aiSplit[p].size();++q) { + iIndex = aiSplit[p][q]; + for (unsigned int t = 0; t < 3;++t) { + p_pcOut->mTextureCoords[c][iBase++] = mesh.amTexCoords[c][mesh.mFaces[iIndex].mIndices[t]]; + } + } + // Setup the number of valid vertex components + p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; + } + } + + // Convert vertex colors (only one set supported) + if (!mesh.mVertexColors.empty()){ + p_pcOut->mColors[0] = new aiColor4D[p_pcOut->mNumVertices]; + iBase = 0; + for (unsigned int q = 0; q < aiSplit[p].size();++q) { + iIndex = aiSplit[p][q]; + for (unsigned int t = 0; t < 3;++t) { + p_pcOut->mColors[0][iBase++] = mesh.mVertexColors[mesh.mFaces[iIndex].mIndices[t]]; + } + } + } + // Copy bones + if (!mesh.mBones.empty()) { + p_pcOut->mNumBones = 0; + for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) + if (!avOutputBones[mrspock].empty())p_pcOut->mNumBones++; + + p_pcOut->mBones = new aiBone* [ p_pcOut->mNumBones ]; + aiBone** pcBone = p_pcOut->mBones; + for (unsigned int mrspock = 0; mrspock < mesh.mBones.size();++mrspock) + { + if (!avOutputBones[mrspock].empty()) { + // we will need this bone. add it to the output mesh and + // add all per-vertex weights + aiBone* pc = *pcBone = new aiBone(); + pc->mName.Set(mesh.mBones[mrspock].mName); + + pc->mNumWeights = (unsigned int)avOutputBones[mrspock].size(); + pc->mWeights = new aiVertexWeight[pc->mNumWeights]; + + for (unsigned int captainkirk = 0; captainkirk < pc->mNumWeights;++captainkirk) + { + const std::pair& ref = avOutputBones[mrspock][captainkirk]; + pc->mWeights[captainkirk].mVertexId = ref.first; + pc->mWeights[captainkirk].mWeight = ref.second; + } + ++pcBone; + } + } + // delete allocated storage + delete[] avOutputBones; + } + } + } + // delete storage + delete[] aiSplit; + } + else + { + // Otherwise we can simply copy the data to one output mesh + // This codepath needs less memory and uses fast memcpy()s + // to do the actual copying. So I think it is worth the + // effort here. + + aiMesh* p_pcOut = new aiMesh(); + p_pcOut->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + // set an empty sub material index + p_pcOut->mMaterialIndex = ASE::Face::DEFAULT_MATINDEX; + mParser->m_vMaterials[mesh.iMaterialIndex].bNeed = true; + + // store the real index here ... in color channel 3 + p_pcOut->mColors[3] = (aiColor4D*)(uintptr_t)mesh.iMaterialIndex; + + // store a pointer to the mesh in color channel 2 + p_pcOut->mColors[2] = (aiColor4D*) &mesh; + avOutMeshes.push_back(p_pcOut); + + // If the mesh hasn't faces or vertices, there are two cases + // possible: 1. the model is invalid. 2. This is a dummy + // helper object which we are going to remove later ... + if (mesh.mFaces.empty() || mesh.mPositions.empty()) { + return; + } + + // convert vertices + p_pcOut->mNumVertices = (unsigned int)mesh.mPositions.size(); + p_pcOut->mNumFaces = (unsigned int)mesh.mFaces.size(); + + // allocate enough storage for faces + p_pcOut->mFaces = new aiFace[p_pcOut->mNumFaces]; + + // copy vertices + p_pcOut->mVertices = new aiVector3D[mesh.mPositions.size()]; + memcpy(p_pcOut->mVertices,&mesh.mPositions[0], + mesh.mPositions.size() * sizeof(aiVector3D)); + + // copy normals + p_pcOut->mNormals = new aiVector3D[mesh.mNormals.size()]; + memcpy(p_pcOut->mNormals,&mesh.mNormals[0], + mesh.mNormals.size() * sizeof(aiVector3D)); + + // copy texture coordinates + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) { + if (!mesh.amTexCoords[c].empty()) { + p_pcOut->mTextureCoords[c] = new aiVector3D[mesh.amTexCoords[c].size()]; + memcpy(p_pcOut->mTextureCoords[c],&mesh.amTexCoords[c][0], + mesh.amTexCoords[c].size() * sizeof(aiVector3D)); + + // setup the number of valid vertex components + p_pcOut->mNumUVComponents[c] = mesh.mNumUVComponents[c]; + } + } + + // copy vertex colors + if (!mesh.mVertexColors.empty()) { + p_pcOut->mColors[0] = new aiColor4D[mesh.mVertexColors.size()]; + memcpy(p_pcOut->mColors[0],&mesh.mVertexColors[0], + mesh.mVertexColors.size() * sizeof(aiColor4D)); + } + + // copy faces + for (unsigned int iFace = 0; iFace < p_pcOut->mNumFaces;++iFace) { + p_pcOut->mFaces[iFace].mNumIndices = 3; + p_pcOut->mFaces[iFace].mIndices = new unsigned int[3]; + + // copy indices + p_pcOut->mFaces[iFace].mIndices[0] = mesh.mFaces[iFace].mIndices[0]; + p_pcOut->mFaces[iFace].mIndices[1] = mesh.mFaces[iFace].mIndices[1]; + p_pcOut->mFaces[iFace].mIndices[2] = mesh.mFaces[iFace].mIndices[2]; + } + + // copy vertex bones + if (!mesh.mBones.empty() && !mesh.mBoneVertices.empty()) { + std::vector > avBonesOut( mesh.mBones.size() ); + + // find all vertex weights for this bone + unsigned int quak = 0; + for (std::vector::const_iterator harrypotter = mesh.mBoneVertices.begin(); + harrypotter != mesh.mBoneVertices.end();++harrypotter,++quak) { + + for (std::vector >::const_iterator + ronaldweasley = (*harrypotter).mBoneWeights.begin(); + ronaldweasley != (*harrypotter).mBoneWeights.end();++ronaldweasley) + { + aiVertexWeight weight; + weight.mVertexId = quak; + weight.mWeight = (*ronaldweasley).second; + avBonesOut[(*ronaldweasley).first].push_back(weight); + } + } + + // now build a final bone list + p_pcOut->mNumBones = 0; + for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) + if (!avBonesOut[jfkennedy].empty())p_pcOut->mNumBones++; + + p_pcOut->mBones = new aiBone*[p_pcOut->mNumBones]; + aiBone** pcBone = p_pcOut->mBones; + for (unsigned int jfkennedy = 0; jfkennedy < mesh.mBones.size();++jfkennedy) { + if (!avBonesOut[jfkennedy].empty()) { + aiBone* pc = *pcBone = new aiBone(); + pc->mName.Set(mesh.mBones[jfkennedy].mName); + pc->mNumWeights = (unsigned int)avBonesOut[jfkennedy].size(); + pc->mWeights = new aiVertexWeight[pc->mNumWeights]; + ::memcpy(pc->mWeights,&avBonesOut[jfkennedy][0], + sizeof(aiVertexWeight) * pc->mNumWeights); + ++pcBone; + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Setup proper material indices and build output materials +void ASEImporter::BuildMaterialIndices() +{ + ai_assert(NULL != pcScene); + + // iterate through all materials and check whether we need them + for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) + { + ASE::Material& mat = mParser->m_vMaterials[iMat]; + if (mat.bNeed) { + // Convert it to the aiMaterial layout + ConvertMaterial(mat); + ++pcScene->mNumMaterials; + } + for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) + { + ASE::Material& submat = mat.avSubMaterials[iSubMat]; + if (submat.bNeed) { + // Convert it to the aiMaterial layout + ConvertMaterial(submat); + ++pcScene->mNumMaterials; + } + } + } + + // allocate the output material array + pcScene->mMaterials = new aiMaterial*[pcScene->mNumMaterials]; + D3DS::Material** pcIntMaterials = new D3DS::Material*[pcScene->mNumMaterials]; + + unsigned int iNum = 0; + for (unsigned int iMat = 0; iMat < mParser->m_vMaterials.size();++iMat) { + ASE::Material& mat = mParser->m_vMaterials[iMat]; + if (mat.bNeed) + { + ai_assert(NULL != mat.pcInstance); + pcScene->mMaterials[iNum] = mat.pcInstance; + + // Store the internal material, too + pcIntMaterials[iNum] = &mat; + + // Iterate through all meshes and search for one which is using + // this top-level material index + for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) + { + aiMesh* mesh = pcScene->mMeshes[iMesh]; + if (ASE::Face::DEFAULT_MATINDEX == mesh->mMaterialIndex && + iMat == (uintptr_t)mesh->mColors[3]) + { + mesh->mMaterialIndex = iNum; + mesh->mColors[3] = NULL; + } + } + iNum++; + } + for (unsigned int iSubMat = 0; iSubMat < mat.avSubMaterials.size();++iSubMat) { + ASE::Material& submat = mat.avSubMaterials[iSubMat]; + if (submat.bNeed) { + ai_assert(NULL != submat.pcInstance); + pcScene->mMaterials[iNum] = submat.pcInstance; + + // Store the internal material, too + pcIntMaterials[iNum] = &submat; + + // Iterate through all meshes and search for one which is using + // this sub-level material index + for (unsigned int iMesh = 0; iMesh < pcScene->mNumMeshes;++iMesh) { + aiMesh* mesh = pcScene->mMeshes[iMesh]; + + if (iSubMat == mesh->mMaterialIndex && iMat == (uintptr_t)mesh->mColors[3]) { + mesh->mMaterialIndex = iNum; + mesh->mColors[3] = NULL; + } + } + iNum++; + } + } + } + + // Dekete our temporary array + delete[] pcIntMaterials; +} + +// ------------------------------------------------------------------------------------------------ +// Generate normal vectors basing on smoothing groups +bool ASEImporter::GenerateNormals(ASE::Mesh& mesh) { + + if (!mesh.mNormals.empty() && !configRecomputeNormals) + { + // Check whether there are only uninitialized normals. If there are + // some, skip all normals from the file and compute them on our own + for (std::vector::const_iterator qq = mesh.mNormals.begin();qq != mesh.mNormals.end();++qq) { + if ((*qq).x || (*qq).y || (*qq).z) + { + return true; + } + } + } + // The array is reused. + ComputeNormalsWithSmoothingsGroups(mesh); + return false; +} + +#endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER diff --git a/code/ASELoader.h b/code/ASELoader.h index 018f8a479..a200b9f08 100644 --- a/code/ASELoader.h +++ b/code/ASELoader.h @@ -1,205 +1,205 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file ASELoader.h - * @brief Definition of the .ASE importer class. - */ -#ifndef AI_ASELOADER_H_INCLUDED -#define AI_ASELOADER_H_INCLUDED - -#include "BaseImporter.h" -#include "../include/assimp/types.h" - -struct aiNode; -#include "ASEParser.h" - -namespace Assimp { - - -// -------------------------------------------------------------------------------- -/** Importer class for the 3DS ASE ASCII format. - * - */ -class ASEImporter : public BaseImporter { -public: - ASEImporter(); - ~ASEImporter(); - - -public: - - // ------------------------------------------------------------------- - /** Returns whether the class can handle the format of the given file. - * See BaseImporter::CanRead() for details. - */ - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, - bool checkSig) const; - -protected: - - // ------------------------------------------------------------------- - /** Return importer meta information. - * See #BaseImporter::GetInfo for the details - */ - const aiImporterDesc* GetInfo () const; - - - // ------------------------------------------------------------------- - /** Imports the given file into the given scene structure. - * See BaseImporter::InternReadFile() for details - */ - void InternReadFile( const std::string& pFile, aiScene* pScene, - IOSystem* pIOHandler); - - - // ------------------------------------------------------------------- - /** Called prior to ReadFile(). - * The function is a request to the importer to update its configuration - * basing on the Importer's configuration property list. - */ - void SetupProperties(const Importer* pImp); - - -private: - - // ------------------------------------------------------------------- - /** Generate normal vectors basing on smoothing groups - * (in some cases the normal are already contained in the file) - * \param mesh Mesh to work on - * \return false if the normals have been recomputed - */ - bool GenerateNormals(ASE::Mesh& mesh); - - - // ------------------------------------------------------------------- - /** Create valid vertex/normal/UV/color/face lists. - * All elements are unique, faces have only one set of indices - * after this step occurs. - * \param mesh Mesh to work on - */ - void BuildUniqueRepresentation(ASE::Mesh& mesh); - - - /** Create one-material-per-mesh meshes ;-) - * \param mesh Mesh to work with - * \param Receives the list of all created meshes - */ - void ConvertMeshes(ASE::Mesh& mesh, std::vector& avOut); - - - // ------------------------------------------------------------------- - /** Convert a material to a aiMaterial object - * \param mat Input material - */ - void ConvertMaterial(ASE::Material& mat); - - - // ------------------------------------------------------------------- - /** Setup the final material indices for each mesh - */ - void BuildMaterialIndices(); - - - // ------------------------------------------------------------------- - /** Build the node graph - */ - void BuildNodes(std::vector& nodes); - - - // ------------------------------------------------------------------- - /** Build output cameras - */ - void BuildCameras(); - - - // ------------------------------------------------------------------- - /** Build output lights - */ - void BuildLights(); - - - // ------------------------------------------------------------------- - /** Build output animations - */ - void BuildAnimations(const std::vector& nodes); - - - // ------------------------------------------------------------------- - /** Add sub nodes to a node - * \param pcParent parent node to be filled - * \param szName Name of the parent node - * \param matrix Current transform - */ - void AddNodes(const std::vector& nodes, - aiNode* pcParent,const char* szName); - - void AddNodes(const std::vector& nodes, - aiNode* pcParent,const char* szName, - const aiMatrix4x4& matrix); - - void AddMeshes(const ASE::BaseNode* snode,aiNode* node); - - // ------------------------------------------------------------------- - /** Generate a default material and add it to the parser's list - * Called if no material has been found in the file (rare for ASE, - * but not impossible) - */ - void GenerateDefaultMaterial(); - -protected: - - /** Parser instance */ - ASE::Parser* mParser; - - /** Buffer to hold the loaded file */ - char* mBuffer; - - /** Scene to be filled */ - aiScene* pcScene; - - /** Config options: Recompute the normals in every case - WA - for 3DS Max broken ASE normal export */ - bool configRecomputeNormals; - bool noSkeletonMesh; -}; - -} // end of namespace Assimp - -#endif // AI_3DSIMPORTER_H_INC +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file ASELoader.h + * @brief Definition of the .ASE importer class. + */ +#ifndef AI_ASELOADER_H_INCLUDED +#define AI_ASELOADER_H_INCLUDED + +#include "BaseImporter.h" +#include "../include/assimp/types.h" + +struct aiNode; +#include "ASEParser.h" + +namespace Assimp { + + +// -------------------------------------------------------------------------------- +/** Importer class for the 3DS ASE ASCII format. + * + */ +class ASEImporter : public BaseImporter { +public: + ASEImporter(); + ~ASEImporter(); + + +public: + + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. + */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, + bool checkSig) const; + +protected: + + // ------------------------------------------------------------------- + /** Return importer meta information. + * See #BaseImporter::GetInfo for the details + */ + const aiImporterDesc* GetInfo () const; + + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, + IOSystem* pIOHandler); + + + // ------------------------------------------------------------------- + /** Called prior to ReadFile(). + * The function is a request to the importer to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + + +private: + + // ------------------------------------------------------------------- + /** Generate normal vectors basing on smoothing groups + * (in some cases the normal are already contained in the file) + * \param mesh Mesh to work on + * \return false if the normals have been recomputed + */ + bool GenerateNormals(ASE::Mesh& mesh); + + + // ------------------------------------------------------------------- + /** Create valid vertex/normal/UV/color/face lists. + * All elements are unique, faces have only one set of indices + * after this step occurs. + * \param mesh Mesh to work on + */ + void BuildUniqueRepresentation(ASE::Mesh& mesh); + + + /** Create one-material-per-mesh meshes ;-) + * \param mesh Mesh to work with + * \param Receives the list of all created meshes + */ + void ConvertMeshes(ASE::Mesh& mesh, std::vector& avOut); + + + // ------------------------------------------------------------------- + /** Convert a material to a aiMaterial object + * \param mat Input material + */ + void ConvertMaterial(ASE::Material& mat); + + + // ------------------------------------------------------------------- + /** Setup the final material indices for each mesh + */ + void BuildMaterialIndices(); + + + // ------------------------------------------------------------------- + /** Build the node graph + */ + void BuildNodes(std::vector& nodes); + + + // ------------------------------------------------------------------- + /** Build output cameras + */ + void BuildCameras(); + + + // ------------------------------------------------------------------- + /** Build output lights + */ + void BuildLights(); + + + // ------------------------------------------------------------------- + /** Build output animations + */ + void BuildAnimations(const std::vector& nodes); + + + // ------------------------------------------------------------------- + /** Add sub nodes to a node + * \param pcParent parent node to be filled + * \param szName Name of the parent node + * \param matrix Current transform + */ + void AddNodes(const std::vector& nodes, + aiNode* pcParent,const char* szName); + + void AddNodes(const std::vector& nodes, + aiNode* pcParent,const char* szName, + const aiMatrix4x4& matrix); + + void AddMeshes(const ASE::BaseNode* snode,aiNode* node); + + // ------------------------------------------------------------------- + /** Generate a default material and add it to the parser's list + * Called if no material has been found in the file (rare for ASE, + * but not impossible) + */ + void GenerateDefaultMaterial(); + +protected: + + /** Parser instance */ + ASE::Parser* mParser; + + /** Buffer to hold the loaded file */ + char* mBuffer; + + /** Scene to be filled */ + aiScene* pcScene; + + /** Config options: Recompute the normals in every case - WA + for 3DS Max broken ASE normal export */ + bool configRecomputeNormals; + bool noSkeletonMesh; +}; + +} // end of namespace Assimp + +#endif // AI_3DSIMPORTER_H_INC diff --git a/code/ASEParser.cpp b/code/ASEParser.cpp index fcb865b6f..f37e7bd48 100644 --- a/code/ASEParser.cpp +++ b/code/ASEParser.cpp @@ -1,2154 +1,2154 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file ASEParser.cpp - * @brief Implementation of the ASE parser class - */ - - -#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER - -// internal headers -#include "TextureTransform.h" -#include "ASELoader.h" -#include "MaterialSystem.h" -#include "fast_atof.h" -#include "../include/assimp/DefaultLogger.hpp" - -using namespace Assimp; -using namespace Assimp::ASE; - - -// ------------------------------------------------------------------------------------------------ -// Begin an ASE parsing function - -#define AI_ASE_PARSER_INIT() \ - int iDepth = 0; - -// ------------------------------------------------------------------------------------------------ -// Handle a "top-level" section in the file. EOF is no error in this case. - -#define AI_ASE_HANDLE_TOP_LEVEL_SECTION() \ - else if ('{' == *filePtr)iDepth++; \ - else if ('}' == *filePtr) \ - { \ - if (0 == --iDepth) \ - { \ - ++filePtr; \ - SkipToNextToken(); \ - return; \ - } \ - } \ - else if ('\0' == *filePtr) \ - { \ - return; \ - } \ - if(IsLineEnd(*filePtr) && !bLastWasEndLine) \ - { \ - ++iLineNumber; \ - bLastWasEndLine = true; \ - } else bLastWasEndLine = false; \ - ++filePtr; - -// ------------------------------------------------------------------------------------------------ -// Handle a nested section in the file. EOF is an error in this case -// @param level "Depth" of the section -// @param msg Full name of the section (including the asterisk) - -#define AI_ASE_HANDLE_SECTION(level, msg) \ - if ('{' == *filePtr)iDepth++; \ - else if ('}' == *filePtr) \ - { \ - if (0 == --iDepth) \ - { \ - ++filePtr; \ - SkipToNextToken(); \ - return; \ - } \ - } \ - else if ('\0' == *filePtr) \ - { \ - LogError("Encountered unexpected EOL while parsing a " msg \ - " chunk (Level " level ")"); \ - } \ - if(IsLineEnd(*filePtr) && !bLastWasEndLine) \ - { \ - ++iLineNumber; \ - bLastWasEndLine = true; \ - } else bLastWasEndLine = false; \ - ++filePtr; - -// ------------------------------------------------------------------------------------------------ -Parser::Parser (const char* szFile, unsigned int fileFormatDefault) -{ - ai_assert(NULL != szFile); - filePtr = szFile; - iFileFormat = fileFormatDefault; - - // make sure that the color values are invalid - m_clrBackground.r = get_qnan(); - m_clrAmbient.r = get_qnan(); - - // setup some default values - iLineNumber = 0; - iFirstFrame = 0; - iLastFrame = 0; - iFrameSpeed = 30; // use 30 as default value for this property - iTicksPerFrame = 1; // use 1 as default value for this property - bLastWasEndLine = false; // need to handle \r\n seqs due to binary file mapping -} - -// ------------------------------------------------------------------------------------------------ -void Parser::LogWarning(const char* szWarn) -{ - ai_assert(NULL != szWarn); - - char szTemp[1024]; -#if _MSC_VER >= 1400 - sprintf_s(szTemp,"Line %u: %s",iLineNumber,szWarn); -#else - snprintf(szTemp,1024,"Line %u: %s",iLineNumber,szWarn); -#endif - - // output the warning to the logger ... - DefaultLogger::get()->warn(szTemp); -} - -// ------------------------------------------------------------------------------------------------ -void Parser::LogInfo(const char* szWarn) -{ - ai_assert(NULL != szWarn); - - char szTemp[1024]; -#if _MSC_VER >= 1400 - sprintf_s(szTemp,"Line %u: %s",iLineNumber,szWarn); -#else - snprintf(szTemp,1024,"Line %u: %s",iLineNumber,szWarn); -#endif - - // output the information to the logger ... - DefaultLogger::get()->info(szTemp); -} - -// ------------------------------------------------------------------------------------------------ -AI_WONT_RETURN void Parser::LogError(const char* szWarn) -{ - ai_assert(NULL != szWarn); - - char szTemp[1024]; -#if _MSC_VER >= 1400 - sprintf_s(szTemp,"Line %u: %s",iLineNumber,szWarn); -#else - snprintf(szTemp,1024,"Line %u: %s",iLineNumber,szWarn); -#endif - - // throw an exception - throw DeadlyImportError(szTemp); -} - -// ------------------------------------------------------------------------------------------------ -bool Parser::SkipToNextToken() -{ - while (true) - { - char me = *filePtr; - - // increase the line number counter if necessary - if (IsLineEnd(me) && !bLastWasEndLine) - { - ++iLineNumber; - bLastWasEndLine = true; - } - else bLastWasEndLine = false; - if ('*' == me || '}' == me || '{' == me)return true; - if ('\0' == me)return false; - - ++filePtr; - } -} - -// ------------------------------------------------------------------------------------------------ -bool Parser::SkipSection() -{ - // must handle subsections ... - int iCnt = 0; - while (true) - { - if ('}' == *filePtr) - { - --iCnt; - if (0 == iCnt) - { - // go to the next valid token ... - ++filePtr; - SkipToNextToken(); - return true; - } - } - else if ('{' == *filePtr) - { - ++iCnt; - } - else if ('\0' == *filePtr) - { - LogWarning("Unable to parse block: Unexpected EOF, closing bracket \'}\' was expected [#1]"); - return false; - } - else if(IsLineEnd(*filePtr))++iLineNumber; - ++filePtr; - } -} - -// ------------------------------------------------------------------------------------------------ -void Parser::Parse() -{ - AI_ASE_PARSER_INIT(); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // Version should be 200. Validate this ... - if (TokenMatch(filePtr,"3DSMAX_ASCIIEXPORT",18)) - { - unsigned int fmt; - ParseLV4MeshLong(fmt); - - if (fmt > 200) - { - LogWarning("Unknown file format version: *3DSMAX_ASCIIEXPORT should \ - be <= 200"); - } - // ************************************************************* - // - fmt will be 0 if we're unable to read the version number - // there are some faulty files without a version number ... - // in this case we'll guess the exact file format by looking - // at the file extension (ASE, ASK, ASC) - // ************************************************************* - - if (fmt)iFileFormat = fmt; - continue; - } - // main scene information - if (TokenMatch(filePtr,"SCENE",5)) - { - ParseLV1SceneBlock(); - continue; - } - // "group" - no implementation yet, in facte - // we're just ignoring them for the moment - if (TokenMatch(filePtr,"GROUP",5)) - { - Parse(); - continue; - } - // material list - if (TokenMatch(filePtr,"MATERIAL_LIST",13)) - { - ParseLV1MaterialListBlock(); - continue; - } - // geometric object (mesh) - if (TokenMatch(filePtr,"GEOMOBJECT",10)) - - { - m_vMeshes.push_back(Mesh()); - ParseLV1ObjectBlock(m_vMeshes.back()); - continue; - } - // helper object = dummy in the hierarchy - if (TokenMatch(filePtr,"HELPEROBJECT",12)) - - { - m_vDummies.push_back(Dummy()); - ParseLV1ObjectBlock(m_vDummies.back()); - continue; - } - // light object - if (TokenMatch(filePtr,"LIGHTOBJECT",11)) - - { - m_vLights.push_back(Light()); - ParseLV1ObjectBlock(m_vLights.back()); - continue; - } - // camera object - if (TokenMatch(filePtr,"CAMERAOBJECT",12)) - { - m_vCameras.push_back(Camera()); - ParseLV1ObjectBlock(m_vCameras.back()); - continue; - } - // comment - print it on the console - if (TokenMatch(filePtr,"COMMENT",7)) - { - std::string out = ""; - ParseString(out,"*COMMENT"); - LogInfo(("Comment: " + out).c_str()); - continue; - } - // ASC bone weights - if (AI_ASE_IS_OLD_FILE_FORMAT() && TokenMatch(filePtr,"MESH_SOFTSKINVERTS",18)) - { - ParseLV1SoftSkinBlock(); - } - } - AI_ASE_HANDLE_TOP_LEVEL_SECTION(); - } - return; -} - -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV1SoftSkinBlock() -{ - // TODO: fix line counting here - - // ************************************************************** - // The soft skin block is formatted differently. There are no - // nested sections supported and the single elements aren't - // marked by keywords starting with an asterisk. - - /** - FORMAT BEGIN - - *MESH_SOFTSKINVERTS { - - - - [for times:] - [for times:] - } - - FORMAT END - */ - // ************************************************************** - while (true) - { - if (*filePtr == '}' ) {++filePtr;return;} - else if (*filePtr == '\0') return; - else if (*filePtr == '{' ) ++filePtr; - - else // if (!IsSpace(*filePtr) && !IsLineEnd(*filePtr)) - { - ASE::Mesh* curMesh = NULL; - unsigned int numVerts = 0; - - const char* sz = filePtr; - while (!IsSpaceOrNewLine(*filePtr))++filePtr; - - const unsigned int diff = (unsigned int)(filePtr-sz); - if (diff) - { - std::string name = std::string(sz,diff); - for (std::vector::iterator it = m_vMeshes.begin(); - it != m_vMeshes.end(); ++it) - { - if ((*it).mName == name) - { - curMesh = & (*it); - break; - } - } - if (!curMesh) - { - LogWarning("Encountered unknown mesh in *MESH_SOFTSKINVERTS section"); - - // Skip the mesh data - until we find a new mesh - // or the end of the *MESH_SOFTSKINVERTS section - while (true) - { - SkipSpacesAndLineEnd(&filePtr); - if (*filePtr == '}') - {++filePtr;return;} - else if (!IsNumeric(*filePtr)) - break; - - SkipLine(&filePtr); - } - } - else - { - SkipSpacesAndLineEnd(&filePtr); - ParseLV4MeshLong(numVerts); - - // Reserve enough storage - curMesh->mBoneVertices.reserve(numVerts); - - for (unsigned int i = 0; i < numVerts;++i) - { - SkipSpacesAndLineEnd(&filePtr); - unsigned int numWeights; - ParseLV4MeshLong(numWeights); - - curMesh->mBoneVertices.push_back(ASE::BoneVertex()); - ASE::BoneVertex& vert = curMesh->mBoneVertices.back(); - - // Reserve enough storage - vert.mBoneWeights.reserve(numWeights); - - for (unsigned int w = 0; w < numWeights;++w) - { - std::string bone; - ParseString(bone,"*MESH_SOFTSKINVERTS.Bone"); - - // Find the bone in the mesh's list - std::pair me; - me.first = -1; - - for (unsigned int n = 0; n < curMesh->mBones.size();++n) - { - if (curMesh->mBones[n].mName == bone) - { - me.first = n; - break; - } - } - if (-1 == me.first) - { - // We don't have this bone yet, so add it to the list - me.first = (int)curMesh->mBones.size(); - curMesh->mBones.push_back(ASE::Bone(bone)); - } - ParseLV4MeshFloat( me.second ); - - // Add the new bone weight to list - vert.mBoneWeights.push_back(me); - } - } - } - } - } - ++filePtr; - SkipSpacesAndLineEnd(&filePtr); - } -} - -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV1SceneBlock() -{ - AI_ASE_PARSER_INIT(); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - if (TokenMatch(filePtr,"SCENE_BACKGROUND_STATIC",23)) - - { - // parse a color triple and assume it is really the bg color - ParseLV4MeshFloatTriple( &m_clrBackground.r ); - continue; - } - if (TokenMatch(filePtr,"SCENE_AMBIENT_STATIC",20)) - - { - // parse a color triple and assume it is really the bg color - ParseLV4MeshFloatTriple( &m_clrAmbient.r ); - continue; - } - if (TokenMatch(filePtr,"SCENE_FIRSTFRAME",16)) - { - ParseLV4MeshLong(iFirstFrame); - continue; - } - if (TokenMatch(filePtr,"SCENE_LASTFRAME",15)) - { - ParseLV4MeshLong(iLastFrame); - continue; - } - if (TokenMatch(filePtr,"SCENE_FRAMESPEED",16)) - { - ParseLV4MeshLong(iFrameSpeed); - continue; - } - if (TokenMatch(filePtr,"SCENE_TICKSPERFRAME",19)) - { - ParseLV4MeshLong(iTicksPerFrame); - continue; - } - } - AI_ASE_HANDLE_TOP_LEVEL_SECTION(); - } -} - -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV1MaterialListBlock() -{ - AI_ASE_PARSER_INIT(); - - unsigned int iMaterialCount = 0; - unsigned int iOldMaterialCount = (unsigned int)m_vMaterials.size(); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - if (TokenMatch(filePtr,"MATERIAL_COUNT",14)) - { - ParseLV4MeshLong(iMaterialCount); - - // now allocate enough storage to hold all materials - m_vMaterials.resize(iOldMaterialCount+iMaterialCount); - continue; - } - if (TokenMatch(filePtr,"MATERIAL",8)) - { - unsigned int iIndex = 0; - ParseLV4MeshLong(iIndex); - - if (iIndex >= iMaterialCount) - { - LogWarning("Out of range: material index is too large"); - iIndex = iMaterialCount-1; - } - - // get a reference to the material - Material& sMat = m_vMaterials[iIndex+iOldMaterialCount]; - // parse the material block - ParseLV2MaterialBlock(sMat); - continue; - } - } - AI_ASE_HANDLE_TOP_LEVEL_SECTION(); - } -} - -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV2MaterialBlock(ASE::Material& mat) -{ - AI_ASE_PARSER_INIT(); - - unsigned int iNumSubMaterials = 0; - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - if (TokenMatch(filePtr,"MATERIAL_NAME",13)) - { - if (!ParseString(mat.mName,"*MATERIAL_NAME")) - SkipToNextToken(); - continue; - } - // ambient material color - if (TokenMatch(filePtr,"MATERIAL_AMBIENT",16)) - { - ParseLV4MeshFloatTriple(&mat.mAmbient.r); - continue; - } - // diffuse material color - if (TokenMatch(filePtr,"MATERIAL_DIFFUSE",16) ) - { - ParseLV4MeshFloatTriple(&mat.mDiffuse.r); - continue; - } - // specular material color - if (TokenMatch(filePtr,"MATERIAL_SPECULAR",17)) - { - ParseLV4MeshFloatTriple(&mat.mSpecular.r); - continue; - } - // material shading type - if (TokenMatch(filePtr,"MATERIAL_SHADING",16)) - { - if (TokenMatch(filePtr,"Blinn",5)) - { - mat.mShading = Discreet3DS::Blinn; - } - else if (TokenMatch(filePtr,"Phong",5)) - { - mat.mShading = Discreet3DS::Phong; - } - else if (TokenMatch(filePtr,"Flat",4)) - { - mat.mShading = Discreet3DS::Flat; - } - else if (TokenMatch(filePtr,"Wire",4)) - { - mat.mShading = Discreet3DS::Wire; - } - else - { - // assume gouraud shading - mat.mShading = Discreet3DS::Gouraud; - SkipToNextToken(); - } - continue; - } - // material transparency - if (TokenMatch(filePtr,"MATERIAL_TRANSPARENCY",21)) - { - ParseLV4MeshFloat(mat.mTransparency); - mat.mTransparency = 1.0f - mat.mTransparency;continue; - } - // material self illumination - if (TokenMatch(filePtr,"MATERIAL_SELFILLUM",18)) - { - float f = 0.0f; - ParseLV4MeshFloat(f); - - mat.mEmissive.r = f; - mat.mEmissive.g = f; - mat.mEmissive.b = f; - continue; - } - // material shininess - if (TokenMatch(filePtr,"MATERIAL_SHINE",14) ) - { - ParseLV4MeshFloat(mat.mSpecularExponent); - mat.mSpecularExponent *= 15; - continue; - } - // two-sided material - if (TokenMatch(filePtr,"MATERIAL_TWOSIDED",17) ) - { - mat.mTwoSided = true; - continue; - } - // material shininess strength - if (TokenMatch(filePtr,"MATERIAL_SHINESTRENGTH",22)) - { - ParseLV4MeshFloat(mat.mShininessStrength); - continue; - } - // diffuse color map - if (TokenMatch(filePtr,"MAP_DIFFUSE",11)) - { - // parse the texture block - ParseLV3MapBlock(mat.sTexDiffuse); - continue; - } - // ambient color map - if (TokenMatch(filePtr,"MAP_AMBIENT",11)) - { - // parse the texture block - ParseLV3MapBlock(mat.sTexAmbient); - continue; - } - // specular color map - if (TokenMatch(filePtr,"MAP_SPECULAR",12)) - { - // parse the texture block - ParseLV3MapBlock(mat.sTexSpecular); - continue; - } - // opacity map - if (TokenMatch(filePtr,"MAP_OPACITY",11)) - { - // parse the texture block - ParseLV3MapBlock(mat.sTexOpacity); - continue; - } - // emissive map - if (TokenMatch(filePtr,"MAP_SELFILLUM",13)) - { - // parse the texture block - ParseLV3MapBlock(mat.sTexEmissive); - continue; - } - // bump map - if (TokenMatch(filePtr,"MAP_BUMP",8)) - { - // parse the texture block - ParseLV3MapBlock(mat.sTexBump); - } - // specular/shininess map - if (TokenMatch(filePtr,"MAP_SHINESTRENGTH",17)) - { - // parse the texture block - ParseLV3MapBlock(mat.sTexShininess); - continue; - } - // number of submaterials - if (TokenMatch(filePtr,"NUMSUBMTLS",10)) - { - ParseLV4MeshLong(iNumSubMaterials); - - // allocate enough storage - mat.avSubMaterials.resize(iNumSubMaterials); - } - // submaterial chunks - if (TokenMatch(filePtr,"SUBMATERIAL",11)) - { - - unsigned int iIndex = 0; - ParseLV4MeshLong(iIndex); - - if (iIndex >= iNumSubMaterials) - { - LogWarning("Out of range: submaterial index is too large"); - iIndex = iNumSubMaterials-1; - } - - // get a reference to the material - Material& sMat = mat.avSubMaterials[iIndex]; - - // parse the material block - ParseLV2MaterialBlock(sMat); - continue; - } - } - AI_ASE_HANDLE_SECTION("2","*MATERIAL"); - } -} - -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3MapBlock(Texture& map) -{ - AI_ASE_PARSER_INIT(); - - // *********************************************************** - // *BITMAP should not be there if *MAP_CLASS is not BITMAP, - // but we need to expect that case ... if the path is - // empty the texture won't be used later. - // *********************************************************** - bool parsePath = true; - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - // type of map - if (TokenMatch(filePtr,"MAP_CLASS" ,9)) - { - std::string temp; - if(!ParseString(temp,"*MAP_CLASS")) - SkipToNextToken(); - if (temp != "Bitmap" && temp != "Normal Bump") - { - DefaultLogger::get()->warn("ASE: Skipping unknown map type: " + temp); - parsePath = false; - } - continue; - } - // path to the texture - if (parsePath && TokenMatch(filePtr,"BITMAP" ,6)) - { - if(!ParseString(map.mMapName,"*BITMAP")) - SkipToNextToken(); - - if (map.mMapName == "None") - { - // Files with 'None' as map name are produced by - // an Maja to ASE exporter which name I forgot .. - DefaultLogger::get()->warn("ASE: Skipping invalid map entry"); - map.mMapName = ""; - } - - continue; - } - // offset on the u axis - if (TokenMatch(filePtr,"UVW_U_OFFSET" ,12)) - { - ParseLV4MeshFloat(map.mOffsetU); - continue; - } - // offset on the v axis - if (TokenMatch(filePtr,"UVW_V_OFFSET" ,12)) - { - ParseLV4MeshFloat(map.mOffsetV); - continue; - } - // tiling on the u axis - if (TokenMatch(filePtr,"UVW_U_TILING" ,12)) - { - ParseLV4MeshFloat(map.mScaleU); - continue; - } - // tiling on the v axis - if (TokenMatch(filePtr,"UVW_V_TILING" ,12)) - { - ParseLV4MeshFloat(map.mScaleV); - continue; - } - // rotation around the z-axis - if (TokenMatch(filePtr,"UVW_ANGLE" ,9)) - { - ParseLV4MeshFloat(map.mRotation); - continue; - } - // map blending factor - if (TokenMatch(filePtr,"MAP_AMOUNT" ,10)) - { - ParseLV4MeshFloat(map.mTextureBlend); - continue; - } - } - AI_ASE_HANDLE_SECTION("3","*MAP_XXXXXX"); - } - return; -} - -// ------------------------------------------------------------------------------------------------ -bool Parser::ParseString(std::string& out,const char* szName) -{ - char szBuffer[1024]; - if (!SkipSpaces(&filePtr)) - { - - sprintf(szBuffer,"Unable to parse %s block: Unexpected EOL",szName); - LogWarning(szBuffer); - return false; - } - // there must be '"' - if ('\"' != *filePtr) - { - - sprintf(szBuffer,"Unable to parse %s block: Strings are expected " - "to be enclosed in double quotation marks",szName); - LogWarning(szBuffer); - return false; - } - ++filePtr; - const char* sz = filePtr; - while (true) - { - if ('\"' == *sz)break; - else if ('\0' == *sz) - { - sprintf(szBuffer,"Unable to parse %s block: Strings are expected to " - "be enclosed in double quotation marks but EOF was reached before " - "a closing quotation mark was encountered",szName); - LogWarning(szBuffer); - return false; - } - sz++; - } - out = std::string(filePtr,(uintptr_t)sz-(uintptr_t)filePtr); - filePtr = sz+1; - return true; -} - -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV1ObjectBlock(ASE::BaseNode& node) -{ - AI_ASE_PARSER_INIT(); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // first process common tokens such as node name and transform - // name of the mesh/node - if (TokenMatch(filePtr,"NODE_NAME" ,9)) - { - if(!ParseString(node.mName,"*NODE_NAME")) - SkipToNextToken(); - continue; - } - // name of the parent of the node - if (TokenMatch(filePtr,"NODE_PARENT" ,11) ) - { - if(!ParseString(node.mParent,"*NODE_PARENT")) - SkipToNextToken(); - continue; - } - // transformation matrix of the node - if (TokenMatch(filePtr,"NODE_TM" ,7)) - { - ParseLV2NodeTransformBlock(node); - continue; - } - // animation data of the node - if (TokenMatch(filePtr,"TM_ANIMATION" ,12)) - { - ParseLV2AnimationBlock(node); - continue; - } - - if (node.mType == BaseNode::Light) - { - // light settings - if (TokenMatch(filePtr,"LIGHT_SETTINGS" ,14)) - { - ParseLV2LightSettingsBlock((ASE::Light&)node); - continue; - } - // type of the light source - if (TokenMatch(filePtr,"LIGHT_TYPE" ,10)) - { - if (!ASSIMP_strincmp("omni",filePtr,4)) - { - ((ASE::Light&)node).mLightType = ASE::Light::OMNI; - } - else if (!ASSIMP_strincmp("target",filePtr,6)) - { - ((ASE::Light&)node).mLightType = ASE::Light::TARGET; - } - else if (!ASSIMP_strincmp("free",filePtr,4)) - { - ((ASE::Light&)node).mLightType = ASE::Light::FREE; - } - else if (!ASSIMP_strincmp("directional",filePtr,11)) - { - ((ASE::Light&)node).mLightType = ASE::Light::DIRECTIONAL; - } - else - { - LogWarning("Unknown kind of light source"); - } - continue; - } - } - else if (node.mType == BaseNode::Camera) - { - // Camera settings - if (TokenMatch(filePtr,"CAMERA_SETTINGS" ,15)) - { - ParseLV2CameraSettingsBlock((ASE::Camera&)node); - continue; - } - else if (TokenMatch(filePtr,"CAMERA_TYPE" ,11)) - { - if (!ASSIMP_strincmp("target",filePtr,6)) - { - ((ASE::Camera&)node).mCameraType = ASE::Camera::TARGET; - } - else if (!ASSIMP_strincmp("free",filePtr,4)) - { - ((ASE::Camera&)node).mCameraType = ASE::Camera::FREE; - } - else - { - LogWarning("Unknown kind of camera"); - } - continue; - } - } - else if (node.mType == BaseNode::Mesh) - { - // mesh data - // FIX: Older files use MESH_SOFTSKIN - if (TokenMatch(filePtr,"MESH" ,4) || - TokenMatch(filePtr,"MESH_SOFTSKIN",13)) - { - ParseLV2MeshBlock((ASE::Mesh&)node); - continue; - } - // mesh material index - if (TokenMatch(filePtr,"MATERIAL_REF" ,12)) - { - ParseLV4MeshLong(((ASE::Mesh&)node).iMaterialIndex); - continue; - } - } - } - AI_ASE_HANDLE_TOP_LEVEL_SECTION(); - } - return; -} - -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV2CameraSettingsBlock(ASE::Camera& camera) -{ - AI_ASE_PARSER_INIT(); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - if (TokenMatch(filePtr,"CAMERA_NEAR" ,11)) - { - ParseLV4MeshFloat(camera.mNear); - continue; - } - if (TokenMatch(filePtr,"CAMERA_FAR" ,10)) - { - ParseLV4MeshFloat(camera.mFar); - continue; - } - if (TokenMatch(filePtr,"CAMERA_FOV" ,10)) - { - ParseLV4MeshFloat(camera.mFOV); - continue; - } - } - AI_ASE_HANDLE_SECTION("2","CAMERA_SETTINGS"); - } - return; -} - -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV2LightSettingsBlock(ASE::Light& light) -{ - AI_ASE_PARSER_INIT(); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - if (TokenMatch(filePtr,"LIGHT_COLOR" ,11)) - { - ParseLV4MeshFloatTriple(&light.mColor.r); - continue; - } - if (TokenMatch(filePtr,"LIGHT_INTENS" ,12)) - { - ParseLV4MeshFloat(light.mIntensity); - continue; - } - if (TokenMatch(filePtr,"LIGHT_HOTSPOT" ,13)) - { - ParseLV4MeshFloat(light.mAngle); - continue; - } - if (TokenMatch(filePtr,"LIGHT_FALLOFF" ,13)) - { - ParseLV4MeshFloat(light.mFalloff); - continue; - } - } - AI_ASE_HANDLE_SECTION("2","LIGHT_SETTINGS"); - } - return; -} - -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV2AnimationBlock(ASE::BaseNode& mesh) -{ - AI_ASE_PARSER_INIT(); - - ASE::Animation* anim = &mesh.mAnim; - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - if (TokenMatch(filePtr,"NODE_NAME" ,9)) - { - std::string temp; - if(!ParseString(temp,"*NODE_NAME")) - SkipToNextToken(); - - // If the name of the node contains .target it - // represents an animated camera or spot light - // target. - if (std::string::npos != temp.find(".Target")) - { - if ((mesh.mType != BaseNode::Camera || ((ASE::Camera&)mesh).mCameraType != ASE::Camera::TARGET) && - ( mesh.mType != BaseNode::Light || ((ASE::Light&)mesh).mLightType != ASE::Light::TARGET)) - { - - DefaultLogger::get()->error("ASE: Found target animation channel " - "but the node is neither a camera nor a spot light"); - anim = NULL; - } - else anim = &mesh.mTargetAnim; - } - continue; - } - - // position keyframes - if (TokenMatch(filePtr,"CONTROL_POS_TRACK" ,17) || - TokenMatch(filePtr,"CONTROL_POS_BEZIER" ,18) || - TokenMatch(filePtr,"CONTROL_POS_TCB" ,15)) - { - if (!anim)SkipSection(); - else ParseLV3PosAnimationBlock(*anim); - continue; - } - // scaling keyframes - if (TokenMatch(filePtr,"CONTROL_SCALE_TRACK" ,19) || - TokenMatch(filePtr,"CONTROL_SCALE_BEZIER" ,20) || - TokenMatch(filePtr,"CONTROL_SCALE_TCB" ,17)) - { - if (!anim || anim == &mesh.mTargetAnim) - { - // Target animation channels may have no rotation channels - DefaultLogger::get()->error("ASE: Ignoring scaling channel in target animation"); - SkipSection(); - } - else ParseLV3ScaleAnimationBlock(*anim); - continue; - } - // rotation keyframes - if (TokenMatch(filePtr,"CONTROL_ROT_TRACK" ,17) || - TokenMatch(filePtr,"CONTROL_ROT_BEZIER" ,18) || - TokenMatch(filePtr,"CONTROL_ROT_TCB" ,15)) - { - if (!anim || anim == &mesh.mTargetAnim) - { - // Target animation channels may have no rotation channels - DefaultLogger::get()->error("ASE: Ignoring rotation channel in target animation"); - SkipSection(); - } - else ParseLV3RotAnimationBlock(*anim); - continue; - } - } - AI_ASE_HANDLE_SECTION("2","TM_ANIMATION"); - } -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3ScaleAnimationBlock(ASE::Animation& anim) -{ - AI_ASE_PARSER_INIT(); - unsigned int iIndex; - - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - bool b = false; - - // For the moment we're just reading the three floats - - // we ignore the ádditional information for bezier's and TCBs - - // simple scaling keyframe - if (TokenMatch(filePtr,"CONTROL_SCALE_SAMPLE" ,20)) - { - b = true; - anim.mScalingType = ASE::Animation::TRACK; - } - - // Bezier scaling keyframe - if (TokenMatch(filePtr,"CONTROL_BEZIER_SCALE_KEY" ,24)) - { - b = true; - anim.mScalingType = ASE::Animation::BEZIER; - } - // TCB scaling keyframe - if (TokenMatch(filePtr,"CONTROL_TCB_SCALE_KEY" ,21)) - { - b = true; - anim.mScalingType = ASE::Animation::TCB; - } - if (b) - { - anim.akeyScaling.push_back(aiVectorKey()); - aiVectorKey& key = anim.akeyScaling.back(); - ParseLV4MeshFloatTriple(&key.mValue.x,iIndex); - key.mTime = (double)iIndex; - } - } - AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK"); - } -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3PosAnimationBlock(ASE::Animation& anim) -{ - AI_ASE_PARSER_INIT(); - unsigned int iIndex; - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - bool b = false; - - // For the moment we're just reading the three floats - - // we ignore the ádditional information for bezier's and TCBs - - // simple scaling keyframe - if (TokenMatch(filePtr,"CONTROL_POS_SAMPLE" ,18)) - { - b = true; - anim.mPositionType = ASE::Animation::TRACK; - } - - // Bezier scaling keyframe - if (TokenMatch(filePtr,"CONTROL_BEZIER_POS_KEY" ,22)) - { - b = true; - anim.mPositionType = ASE::Animation::BEZIER; - } - // TCB scaling keyframe - if (TokenMatch(filePtr,"CONTROL_TCB_POS_KEY" ,19)) - { - b = true; - anim.mPositionType = ASE::Animation::TCB; - } - if (b) - { - anim.akeyPositions.push_back(aiVectorKey()); - aiVectorKey& key = anim.akeyPositions.back(); - ParseLV4MeshFloatTriple(&key.mValue.x,iIndex); - key.mTime = (double)iIndex; - } - } - AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK"); - } -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3RotAnimationBlock(ASE::Animation& anim) -{ - AI_ASE_PARSER_INIT(); - unsigned int iIndex; - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - bool b = false; - - // For the moment we're just reading the floats - - // we ignore the ádditional information for bezier's and TCBs - - // simple scaling keyframe - if (TokenMatch(filePtr,"CONTROL_ROT_SAMPLE" ,18)) - { - b = true; - anim.mRotationType = ASE::Animation::TRACK; - } - - // Bezier scaling keyframe - if (TokenMatch(filePtr,"CONTROL_BEZIER_ROT_KEY" ,22)) - { - b = true; - anim.mRotationType = ASE::Animation::BEZIER; - } - // TCB scaling keyframe - if (TokenMatch(filePtr,"CONTROL_TCB_ROT_KEY" ,19)) - { - b = true; - anim.mRotationType = ASE::Animation::TCB; - } - if (b) - { - anim.akeyRotations.push_back(aiQuatKey()); - aiQuatKey& key = anim.akeyRotations.back(); - aiVector3D v;float f; - ParseLV4MeshFloatTriple(&v.x,iIndex); - ParseLV4MeshFloat(f); - key.mTime = (double)iIndex; - key.mValue = aiQuaternion(v,f); - } - } - AI_ASE_HANDLE_SECTION("3","*CONTROL_ROT_TRACK"); - } -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode& mesh) -{ - AI_ASE_PARSER_INIT(); - int mode = 0; - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - // name of the node - if (TokenMatch(filePtr,"NODE_NAME" ,9)) - { - std::string temp; - if(!ParseString(temp,"*NODE_NAME")) - SkipToNextToken(); - - std::string::size_type s; - if (temp == mesh.mName) - { - mode = 1; - } - else if (std::string::npos != (s = temp.find(".Target")) && - mesh.mName == temp.substr(0,s)) - { - // This should be either a target light or a target camera - if ( (mesh.mType == BaseNode::Light && ((ASE::Light&)mesh) .mLightType == ASE::Light::TARGET) || - (mesh.mType == BaseNode::Camera && ((ASE::Camera&)mesh).mCameraType == ASE::Camera::TARGET)) - { - mode = 2; - } - else DefaultLogger::get()->error("ASE: Ignoring target transform, " - "this is no spot light or target camera"); - } - else - { - DefaultLogger::get()->error("ASE: Unknown node transformation: " + temp); - // mode = 0 - } - continue; - } - if (mode) - { - // fourth row of the transformation matrix - and also the - // only information here that is interesting for targets - if (TokenMatch(filePtr,"TM_ROW3" ,7)) - { - ParseLV4MeshFloatTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x)); - continue; - } - if (mode == 1) - { - // first row of the transformation matrix - if (TokenMatch(filePtr,"TM_ROW0" ,7)) - { - ParseLV4MeshFloatTriple(mesh.mTransform[0]); - continue; - } - // second row of the transformation matrix - if (TokenMatch(filePtr,"TM_ROW1" ,7)) - { - ParseLV4MeshFloatTriple(mesh.mTransform[1]); - continue; - } - // third row of the transformation matrix - if (TokenMatch(filePtr,"TM_ROW2" ,7)) - { - ParseLV4MeshFloatTriple(mesh.mTransform[2]); - continue; - } - // inherited position axes - if (TokenMatch(filePtr,"INHERIT_POS" ,11)) - { - unsigned int aiVal[3]; - ParseLV4MeshLongTriple(aiVal); - - for (unsigned int i = 0; i < 3;++i) - mesh.inherit.abInheritPosition[i] = aiVal[i] != 0; - continue; - } - // inherited rotation axes - if (TokenMatch(filePtr,"INHERIT_ROT" ,11)) - { - unsigned int aiVal[3]; - ParseLV4MeshLongTriple(aiVal); - - for (unsigned int i = 0; i < 3;++i) - mesh.inherit.abInheritRotation[i] = aiVal[i] != 0; - continue; - } - // inherited scaling axes - if (TokenMatch(filePtr,"INHERIT_SCL" ,11)) - { - unsigned int aiVal[3]; - ParseLV4MeshLongTriple(aiVal); - - for (unsigned int i = 0; i < 3;++i) - mesh.inherit.abInheritScaling[i] = aiVal[i] != 0; - continue; - } - } - } - } - AI_ASE_HANDLE_SECTION("2","*NODE_TM"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV2MeshBlock(ASE::Mesh& mesh) -{ - AI_ASE_PARSER_INIT(); - - unsigned int iNumVertices = 0; - unsigned int iNumFaces = 0; - unsigned int iNumTVertices = 0; - unsigned int iNumTFaces = 0; - unsigned int iNumCVertices = 0; - unsigned int iNumCFaces = 0; - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - // Number of vertices in the mesh - if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14)) - { - ParseLV4MeshLong(iNumVertices); - continue; - } - // Number of texture coordinates in the mesh - if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15)) - { - ParseLV4MeshLong(iNumTVertices); - continue; - } - // Number of vertex colors in the mesh - if (TokenMatch(filePtr,"MESH_NUMCVERTEX" ,15)) - { - ParseLV4MeshLong(iNumCVertices); - continue; - } - // Number of regular faces in the mesh - if (TokenMatch(filePtr,"MESH_NUMFACES" ,13)) - { - ParseLV4MeshLong(iNumFaces); - continue; - } - // Number of UVWed faces in the mesh - if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15)) - { - ParseLV4MeshLong(iNumTFaces); - continue; - } - // Number of colored faces in the mesh - if (TokenMatch(filePtr,"MESH_NUMCVFACES" ,15)) - { - ParseLV4MeshLong(iNumCFaces); - continue; - } - // mesh vertex list block - if (TokenMatch(filePtr,"MESH_VERTEX_LIST" ,16)) - { - ParseLV3MeshVertexListBlock(iNumVertices,mesh); - continue; - } - // mesh face list block - if (TokenMatch(filePtr,"MESH_FACE_LIST" ,14)) - { - ParseLV3MeshFaceListBlock(iNumFaces,mesh); - continue; - } - // mesh texture vertex list block - if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14)) - { - ParseLV3MeshTListBlock(iNumTVertices,mesh); - continue; - } - // mesh texture face block - if (TokenMatch(filePtr,"MESH_TFACELIST" ,14)) - { - ParseLV3MeshTFaceListBlock(iNumTFaces,mesh); - continue; - } - // mesh color vertex list block - if (TokenMatch(filePtr,"MESH_CVERTLIST" ,14)) - { - ParseLV3MeshCListBlock(iNumCVertices,mesh); - continue; - } - // mesh color face block - if (TokenMatch(filePtr,"MESH_CFACELIST" ,14)) - { - ParseLV3MeshCFaceListBlock(iNumCFaces,mesh); - continue; - } - // mesh normals - if (TokenMatch(filePtr,"MESH_NORMALS" ,12)) - { - ParseLV3MeshNormalListBlock(mesh); - continue; - } - // another mesh UV channel ... - if (TokenMatch(filePtr,"MESH_MAPPINGCHANNEL" ,19)) - { - - unsigned int iIndex = 0; - ParseLV4MeshLong(iIndex); - - if (iIndex < 2) - { - LogWarning("Mapping channel has an invalid index. Skipping UV channel"); - // skip it ... - SkipSection(); - } - if (iIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS) - { - LogWarning("Too many UV channels specified. Skipping channel .."); - // skip it ... - SkipSection(); - } - else - { - // parse the mapping channel - ParseLV3MappingChannel(iIndex-1,mesh); - } - continue; - } - // mesh animation keyframe. Not supported - if (TokenMatch(filePtr,"MESH_ANIMATION" ,14)) - { - - LogWarning("Found *MESH_ANIMATION element in ASE/ASK file. " - "Keyframe animation is not supported by Assimp, this element " - "will be ignored"); - //SkipSection(); - continue; - } - if (TokenMatch(filePtr,"MESH_WEIGHTS" ,12)) - { - ParseLV3MeshWeightsBlock(mesh);continue; - } - } - AI_ASE_HANDLE_SECTION("2","*MESH"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh& mesh) -{ - AI_ASE_PARSER_INIT(); - - unsigned int iNumVertices = 0, iNumBones = 0; - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // Number of bone vertices ... - if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14)) - { - ParseLV4MeshLong(iNumVertices); - continue; - } - // Number of bones - if (TokenMatch(filePtr,"MESH_NUMBONE" ,12)) - { - ParseLV4MeshLong(iNumBones); - continue; - } - // parse the list of bones - if (TokenMatch(filePtr,"MESH_BONE_LIST" ,14)) - { - ParseLV4MeshBones(iNumBones,mesh); - continue; - } - // parse the list of bones vertices - if (TokenMatch(filePtr,"MESH_BONE_VERTEX_LIST" ,21) ) - { - ParseLV4MeshBonesVertices(iNumVertices,mesh); - continue; - } - } - AI_ASE_HANDLE_SECTION("3","*MESH_WEIGHTS"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshBones(unsigned int iNumBones,ASE::Mesh& mesh) -{ - AI_ASE_PARSER_INIT(); - mesh.mBones.resize(iNumBones); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // Mesh bone with name ... - if (TokenMatch(filePtr,"MESH_BONE_NAME" ,14)) - { - // parse an index ... - if(SkipSpaces(&filePtr)) - { - unsigned int iIndex = strtoul10(filePtr,&filePtr); - if (iIndex >= iNumBones) - { - LogWarning("Bone index is out of bounds"); - continue; - } - if (!ParseString(mesh.mBones[iIndex].mName,"*MESH_BONE_NAME")) - SkipToNextToken(); - continue; - } - } - } - AI_ASE_HANDLE_SECTION("3","*MESH_BONE_LIST"); - } -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices,ASE::Mesh& mesh) -{ - AI_ASE_PARSER_INIT(); - mesh.mBoneVertices.resize(iNumVertices); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // Mesh bone vertex - if (TokenMatch(filePtr,"MESH_BONE_VERTEX" ,16)) - { - // read the vertex index - unsigned int iIndex = strtoul10(filePtr,&filePtr); - if (iIndex >= mesh.mPositions.size()) - { - iIndex = (unsigned int)mesh.mPositions.size()-1; - LogWarning("Bone vertex index is out of bounds. Using the largest valid " - "bone vertex index instead"); - } - - // --- ignored - float afVert[3]; - ParseLV4MeshFloatTriple(afVert); - - std::pair pairOut; - while (true) - { - // first parse the bone index ... - if (!SkipSpaces(&filePtr))break; - pairOut.first = strtoul10(filePtr,&filePtr); - - // then parse the vertex weight - if (!SkipSpaces(&filePtr))break; - filePtr = fast_atoreal_move(filePtr,pairOut.second); - - // -1 marks unused entries - if (-1 != pairOut.first) - { - mesh.mBoneVertices[iIndex].mBoneWeights.push_back(pairOut); - } - } - continue; - } - } - AI_ASE_HANDLE_SECTION("4","*MESH_BONE_VERTEX"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3MeshVertexListBlock( - unsigned int iNumVertices, ASE::Mesh& mesh) -{ - AI_ASE_PARSER_INIT(); - - // allocate enough storage in the array - mesh.mPositions.resize(iNumVertices); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // Vertex entry - if (TokenMatch(filePtr,"MESH_VERTEX" ,11)) - { - - aiVector3D vTemp; - unsigned int iIndex; - ParseLV4MeshFloatTriple(&vTemp.x,iIndex); - - if (iIndex >= iNumVertices) - { - LogWarning("Invalid vertex index. It will be ignored"); - } - else mesh.mPositions[iIndex] = vTemp; - continue; - } - } - AI_ASE_HANDLE_SECTION("3","*MESH_VERTEX_LIST"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh) -{ - AI_ASE_PARSER_INIT(); - - // allocate enough storage in the face array - mesh.mFaces.resize(iNumFaces); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // Face entry - if (TokenMatch(filePtr,"MESH_FACE" ,9)) - { - - ASE::Face mFace; - ParseLV4MeshFace(mFace); - - if (mFace.iFace >= iNumFaces) - { - LogWarning("Face has an invalid index. It will be ignored"); - } - else mesh.mFaces[mFace.iFace] = mFace; - continue; - } - } - AI_ASE_HANDLE_SECTION("3","*MESH_FACE_LIST"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices, - ASE::Mesh& mesh, unsigned int iChannel) -{ - AI_ASE_PARSER_INIT(); - - // allocate enough storage in the array - mesh.amTexCoords[iChannel].resize(iNumVertices); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // Vertex entry - if (TokenMatch(filePtr,"MESH_TVERT" ,10)) - { - aiVector3D vTemp; - unsigned int iIndex; - ParseLV4MeshFloatTriple(&vTemp.x,iIndex); - - if (iIndex >= iNumVertices) - { - LogWarning("Tvertex has an invalid index. It will be ignored"); - } - else mesh.amTexCoords[iChannel][iIndex] = vTemp; - - if (0.0f != vTemp.z) - { - // we need 3 coordinate channels - mesh.mNumUVComponents[iChannel] = 3; - } - continue; - } - } - AI_ASE_HANDLE_SECTION("3","*MESH_TVERT_LIST"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces, - ASE::Mesh& mesh, unsigned int iChannel) -{ - AI_ASE_PARSER_INIT(); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // Face entry - if (TokenMatch(filePtr,"MESH_TFACE" ,10)) - { - unsigned int aiValues[3]; - unsigned int iIndex = 0; - - ParseLV4MeshLongTriple(aiValues,iIndex); - if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) - { - LogWarning("UV-Face has an invalid index. It will be ignored"); - } - else - { - // copy UV indices - mesh.mFaces[iIndex].amUVIndices[iChannel][0] = aiValues[0]; - mesh.mFaces[iIndex].amUVIndices[iChannel][1] = aiValues[1]; - mesh.mFaces[iIndex].amUVIndices[iChannel][2] = aiValues[2]; - } - continue; - } - } - AI_ASE_HANDLE_SECTION("3","*MESH_TFACE_LIST"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh& mesh) -{ - AI_ASE_PARSER_INIT(); - - unsigned int iNumTVertices = 0; - unsigned int iNumTFaces = 0; - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // Number of texture coordinates in the mesh - if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15)) - { - ParseLV4MeshLong(iNumTVertices); - continue; - } - // Number of UVWed faces in the mesh - if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15)) - { - ParseLV4MeshLong(iNumTFaces); - continue; - } - // mesh texture vertex list block - if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14)) - { - ParseLV3MeshTListBlock(iNumTVertices,mesh,iChannel); - continue; - } - // mesh texture face block - if (TokenMatch(filePtr,"MESH_TFACELIST" ,14)) - { - ParseLV3MeshTFaceListBlock(iNumTFaces,mesh, iChannel); - continue; - } - } - AI_ASE_HANDLE_SECTION("3","*MESH_MAPPING_CHANNEL"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh& mesh) -{ - AI_ASE_PARSER_INIT(); - - // allocate enough storage in the array - mesh.mVertexColors.resize(iNumVertices); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // Vertex entry - if (TokenMatch(filePtr,"MESH_VERTCOL" ,12)) - { - aiColor4D vTemp; - vTemp.a = 1.0f; - unsigned int iIndex; - ParseLV4MeshFloatTriple(&vTemp.r,iIndex); - - if (iIndex >= iNumVertices) - { - LogWarning("Vertex color has an invalid index. It will be ignored"); - } - else mesh.mVertexColors[iIndex] = vTemp; - continue; - } - } - AI_ASE_HANDLE_SECTION("3","*MESH_CVERTEX_LIST"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh) -{ - AI_ASE_PARSER_INIT(); - while (true) - { - if ('*' == *filePtr) - { - ++filePtr; - - // Face entry - if (TokenMatch(filePtr,"MESH_CFACE" ,11)) - { - unsigned int aiValues[3]; - unsigned int iIndex = 0; - - ParseLV4MeshLongTriple(aiValues,iIndex); - if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) - { - LogWarning("UV-Face has an invalid index. It will be ignored"); - } - else - { - // copy color indices - mesh.mFaces[iIndex].mColorIndices[0] = aiValues[0]; - mesh.mFaces[iIndex].mColorIndices[1] = aiValues[1]; - mesh.mFaces[iIndex].mColorIndices[2] = aiValues[2]; - } - continue; - } - } - AI_ASE_HANDLE_SECTION("3","*MESH_CFACE_LIST"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh) -{ - AI_ASE_PARSER_INIT(); - - // Allocate enough storage for the normals - sMesh.mNormals.resize(sMesh.mFaces.size()*3,aiVector3D( 0.f, 0.f, 0.f )); - unsigned int index, faceIdx = UINT_MAX; - - // FIXME: rewrite this and find out how to interpret the normals - // correctly. This is crap. - - // Smooth the vertex and face normals together. The result - // will be edgy then, but otherwise everything would be soft ... - while (true) { - if ('*' == *filePtr) { - ++filePtr; - if (faceIdx != UINT_MAX && TokenMatch(filePtr,"MESH_VERTEXNORMAL",17)) { - aiVector3D vNormal; - ParseLV4MeshFloatTriple(&vNormal.x,index); - if (faceIdx >= sMesh.mFaces.size()) - continue; - - // Make sure we assign it to the correct face - const ASE::Face& face = sMesh.mFaces[faceIdx]; - if (index == face.mIndices[0]) - index = 0; - else if (index == face.mIndices[1]) - index = 1; - else if (index == face.mIndices[2]) - index = 2; - else { - DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_VERTEXNORMAL section"); - continue; - } - // We'll renormalize later - sMesh.mNormals[faceIdx*3+index] += vNormal; - continue; - } - if (TokenMatch(filePtr,"MESH_FACENORMAL",15)) { - aiVector3D vNormal; - ParseLV4MeshFloatTriple(&vNormal.x,faceIdx); - - if (faceIdx >= sMesh.mFaces.size()) { - DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_FACENORMAL section"); - continue; - } - - // We'll renormalize later - sMesh.mNormals[faceIdx*3] += vNormal; - sMesh.mNormals[faceIdx*3+1] += vNormal; - sMesh.mNormals[faceIdx*3+2] += vNormal; - continue; - } - } - AI_ASE_HANDLE_SECTION("3","*MESH_NORMALS"); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshFace(ASE::Face& out) -{ - // skip spaces and tabs - if(!SkipSpaces(&filePtr)) - { - LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL [#1]"); - SkipToNextToken(); - return; - } - - // parse the face index - out.iFace = strtoul10(filePtr,&filePtr); - - // next character should be ':' - if(!SkipSpaces(&filePtr)) - { - // FIX: there are some ASE files which haven't got : here .... - LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. \':\' expected [#2]"); - SkipToNextToken(); - return; - } - // FIX: There are some ASE files which haven't got ':' here - if(':' == *filePtr)++filePtr; - - // Parse all mesh indices - for (unsigned int i = 0; i < 3;++i) - { - unsigned int iIndex = 0; - if(!SkipSpaces(&filePtr)) - { - LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL"); - SkipToNextToken(); - return; - } - switch (*filePtr) - { - case 'A': - case 'a': - break; - case 'B': - case 'b': - iIndex = 1; - break; - case 'C': - case 'c': - iIndex = 2; - break; - default: - LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " - "A,B or C expected [#3]"); - SkipToNextToken(); - return; - }; - ++filePtr; - - // next character should be ':' - if(!SkipSpaces(&filePtr) || ':' != *filePtr) - { - LogWarning("Unable to parse *MESH_FACE Element: " - "Unexpected EOL. \':\' expected [#2]"); - SkipToNextToken(); - return; - } - - ++filePtr; - if(!SkipSpaces(&filePtr)) - { - LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " - "Vertex index ecpected [#4]"); - SkipToNextToken(); - return; - } - out.mIndices[iIndex] = strtoul10(filePtr,&filePtr); - } - - // now we need to skip the AB, BC, CA blocks. - while (true) - { - if ('*' == *filePtr)break; - if (IsLineEnd(*filePtr)) - { - //iLineNumber++; - return; - } - filePtr++; - } - - // parse the smoothing group of the face - if (TokenMatch(filePtr,"*MESH_SMOOTHING",15)) - { - if(!SkipSpaces(&filePtr)) - { - LogWarning("Unable to parse *MESH_SMOOTHING Element: " - "Unexpected EOL. Smoothing group(s) expected [#5]"); - SkipToNextToken(); - return; - } - - // Parse smoothing groups until we don't anymore see commas - // FIX: There needn't always be a value, sad but true - while (true) - { - if (*filePtr < '9' && *filePtr >= '0') - { - out.iSmoothGroup |= (1 << strtoul10(filePtr,&filePtr)); - } - SkipSpaces(&filePtr); - if (',' != *filePtr) - { - break; - } - ++filePtr; - SkipSpaces(&filePtr); - } - } - - // *MESH_MTLID is optional, too - while (true) - { - if ('*' == *filePtr)break; - if (IsLineEnd(*filePtr)) - { - return; - } - filePtr++; - } - - if (TokenMatch(filePtr,"*MESH_MTLID",11)) - { - if(!SkipSpaces(&filePtr)) - { - LogWarning("Unable to parse *MESH_MTLID Element: Unexpected EOL. " - "Material index expected [#6]"); - SkipToNextToken(); - return; - } - out.iMaterial = strtoul10(filePtr,&filePtr); - } - return; -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshLongTriple(unsigned int* apOut) -{ - ai_assert(NULL != apOut); - - for (unsigned int i = 0; i < 3;++i) - ParseLV4MeshLong(apOut[i]); -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut) -{ - ai_assert(NULL != apOut); - - // parse the index - ParseLV4MeshLong(rIndexOut); - - // parse the three others - ParseLV4MeshLongTriple(apOut); -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut) -{ - ai_assert(NULL != apOut); - - // parse the index - ParseLV4MeshLong(rIndexOut); - - // parse the three others - ParseLV4MeshFloatTriple(apOut); -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshFloatTriple(float* apOut) -{ - ai_assert(NULL != apOut); - - for (unsigned int i = 0; i < 3;++i) - ParseLV4MeshFloat(apOut[i]); -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshFloat(float& fOut) -{ - // skip spaces and tabs - if(!SkipSpaces(&filePtr)) - { - // LOG - LogWarning("Unable to parse float: unexpected EOL [#1]"); - fOut = 0.0f; - ++iLineNumber; - return; - } - // parse the first float - filePtr = fast_atoreal_move(filePtr,fOut); -} -// ------------------------------------------------------------------------------------------------ -void Parser::ParseLV4MeshLong(unsigned int& iOut) -{ - // Skip spaces and tabs - if(!SkipSpaces(&filePtr)) - { - // LOG - LogWarning("Unable to parse long: unexpected EOL [#1]"); - iOut = 0; - ++iLineNumber; - return; - } - // parse the value - iOut = strtoul10(filePtr,&filePtr); -} - -#endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file ASEParser.cpp + * @brief Implementation of the ASE parser class + */ + + +#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER + +// internal headers +#include "TextureTransform.h" +#include "ASELoader.h" +#include "MaterialSystem.h" +#include "fast_atof.h" +#include "../include/assimp/DefaultLogger.hpp" + +using namespace Assimp; +using namespace Assimp::ASE; + + +// ------------------------------------------------------------------------------------------------ +// Begin an ASE parsing function + +#define AI_ASE_PARSER_INIT() \ + int iDepth = 0; + +// ------------------------------------------------------------------------------------------------ +// Handle a "top-level" section in the file. EOF is no error in this case. + +#define AI_ASE_HANDLE_TOP_LEVEL_SECTION() \ + else if ('{' == *filePtr)iDepth++; \ + else if ('}' == *filePtr) \ + { \ + if (0 == --iDepth) \ + { \ + ++filePtr; \ + SkipToNextToken(); \ + return; \ + } \ + } \ + else if ('\0' == *filePtr) \ + { \ + return; \ + } \ + if(IsLineEnd(*filePtr) && !bLastWasEndLine) \ + { \ + ++iLineNumber; \ + bLastWasEndLine = true; \ + } else bLastWasEndLine = false; \ + ++filePtr; + +// ------------------------------------------------------------------------------------------------ +// Handle a nested section in the file. EOF is an error in this case +// @param level "Depth" of the section +// @param msg Full name of the section (including the asterisk) + +#define AI_ASE_HANDLE_SECTION(level, msg) \ + if ('{' == *filePtr)iDepth++; \ + else if ('}' == *filePtr) \ + { \ + if (0 == --iDepth) \ + { \ + ++filePtr; \ + SkipToNextToken(); \ + return; \ + } \ + } \ + else if ('\0' == *filePtr) \ + { \ + LogError("Encountered unexpected EOL while parsing a " msg \ + " chunk (Level " level ")"); \ + } \ + if(IsLineEnd(*filePtr) && !bLastWasEndLine) \ + { \ + ++iLineNumber; \ + bLastWasEndLine = true; \ + } else bLastWasEndLine = false; \ + ++filePtr; + +// ------------------------------------------------------------------------------------------------ +Parser::Parser (const char* szFile, unsigned int fileFormatDefault) +{ + ai_assert(NULL != szFile); + filePtr = szFile; + iFileFormat = fileFormatDefault; + + // make sure that the color values are invalid + m_clrBackground.r = get_qnan(); + m_clrAmbient.r = get_qnan(); + + // setup some default values + iLineNumber = 0; + iFirstFrame = 0; + iLastFrame = 0; + iFrameSpeed = 30; // use 30 as default value for this property + iTicksPerFrame = 1; // use 1 as default value for this property + bLastWasEndLine = false; // need to handle \r\n seqs due to binary file mapping +} + +// ------------------------------------------------------------------------------------------------ +void Parser::LogWarning(const char* szWarn) +{ + ai_assert(NULL != szWarn); + + char szTemp[1024]; +#if _MSC_VER >= 1400 + sprintf_s(szTemp,"Line %u: %s",iLineNumber,szWarn); +#else + snprintf(szTemp,1024,"Line %u: %s",iLineNumber,szWarn); +#endif + + // output the warning to the logger ... + DefaultLogger::get()->warn(szTemp); +} + +// ------------------------------------------------------------------------------------------------ +void Parser::LogInfo(const char* szWarn) +{ + ai_assert(NULL != szWarn); + + char szTemp[1024]; +#if _MSC_VER >= 1400 + sprintf_s(szTemp,"Line %u: %s",iLineNumber,szWarn); +#else + snprintf(szTemp,1024,"Line %u: %s",iLineNumber,szWarn); +#endif + + // output the information to the logger ... + DefaultLogger::get()->info(szTemp); +} + +// ------------------------------------------------------------------------------------------------ +AI_WONT_RETURN void Parser::LogError(const char* szWarn) +{ + ai_assert(NULL != szWarn); + + char szTemp[1024]; +#if _MSC_VER >= 1400 + sprintf_s(szTemp,"Line %u: %s",iLineNumber,szWarn); +#else + snprintf(szTemp,1024,"Line %u: %s",iLineNumber,szWarn); +#endif + + // throw an exception + throw DeadlyImportError(szTemp); +} + +// ------------------------------------------------------------------------------------------------ +bool Parser::SkipToNextToken() +{ + while (true) + { + char me = *filePtr; + + // increase the line number counter if necessary + if (IsLineEnd(me) && !bLastWasEndLine) + { + ++iLineNumber; + bLastWasEndLine = true; + } + else bLastWasEndLine = false; + if ('*' == me || '}' == me || '{' == me)return true; + if ('\0' == me)return false; + + ++filePtr; + } +} + +// ------------------------------------------------------------------------------------------------ +bool Parser::SkipSection() +{ + // must handle subsections ... + int iCnt = 0; + while (true) + { + if ('}' == *filePtr) + { + --iCnt; + if (0 == iCnt) + { + // go to the next valid token ... + ++filePtr; + SkipToNextToken(); + return true; + } + } + else if ('{' == *filePtr) + { + ++iCnt; + } + else if ('\0' == *filePtr) + { + LogWarning("Unable to parse block: Unexpected EOF, closing bracket \'}\' was expected [#1]"); + return false; + } + else if(IsLineEnd(*filePtr))++iLineNumber; + ++filePtr; + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::Parse() +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Version should be 200. Validate this ... + if (TokenMatch(filePtr,"3DSMAX_ASCIIEXPORT",18)) + { + unsigned int fmt; + ParseLV4MeshLong(fmt); + + if (fmt > 200) + { + LogWarning("Unknown file format version: *3DSMAX_ASCIIEXPORT should \ + be <= 200"); + } + // ************************************************************* + // - fmt will be 0 if we're unable to read the version number + // there are some faulty files without a version number ... + // in this case we'll guess the exact file format by looking + // at the file extension (ASE, ASK, ASC) + // ************************************************************* + + if (fmt)iFileFormat = fmt; + continue; + } + // main scene information + if (TokenMatch(filePtr,"SCENE",5)) + { + ParseLV1SceneBlock(); + continue; + } + // "group" - no implementation yet, in facte + // we're just ignoring them for the moment + if (TokenMatch(filePtr,"GROUP",5)) + { + Parse(); + continue; + } + // material list + if (TokenMatch(filePtr,"MATERIAL_LIST",13)) + { + ParseLV1MaterialListBlock(); + continue; + } + // geometric object (mesh) + if (TokenMatch(filePtr,"GEOMOBJECT",10)) + + { + m_vMeshes.push_back(Mesh()); + ParseLV1ObjectBlock(m_vMeshes.back()); + continue; + } + // helper object = dummy in the hierarchy + if (TokenMatch(filePtr,"HELPEROBJECT",12)) + + { + m_vDummies.push_back(Dummy()); + ParseLV1ObjectBlock(m_vDummies.back()); + continue; + } + // light object + if (TokenMatch(filePtr,"LIGHTOBJECT",11)) + + { + m_vLights.push_back(Light()); + ParseLV1ObjectBlock(m_vLights.back()); + continue; + } + // camera object + if (TokenMatch(filePtr,"CAMERAOBJECT",12)) + { + m_vCameras.push_back(Camera()); + ParseLV1ObjectBlock(m_vCameras.back()); + continue; + } + // comment - print it on the console + if (TokenMatch(filePtr,"COMMENT",7)) + { + std::string out = ""; + ParseString(out,"*COMMENT"); + LogInfo(("Comment: " + out).c_str()); + continue; + } + // ASC bone weights + if (AI_ASE_IS_OLD_FILE_FORMAT() && TokenMatch(filePtr,"MESH_SOFTSKINVERTS",18)) + { + ParseLV1SoftSkinBlock(); + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1SoftSkinBlock() +{ + // TODO: fix line counting here + + // ************************************************************** + // The soft skin block is formatted differently. There are no + // nested sections supported and the single elements aren't + // marked by keywords starting with an asterisk. + + /** + FORMAT BEGIN + + *MESH_SOFTSKINVERTS { + + + + [for times:] + [for times:] + } + + FORMAT END + */ + // ************************************************************** + while (true) + { + if (*filePtr == '}' ) {++filePtr;return;} + else if (*filePtr == '\0') return; + else if (*filePtr == '{' ) ++filePtr; + + else // if (!IsSpace(*filePtr) && !IsLineEnd(*filePtr)) + { + ASE::Mesh* curMesh = NULL; + unsigned int numVerts = 0; + + const char* sz = filePtr; + while (!IsSpaceOrNewLine(*filePtr))++filePtr; + + const unsigned int diff = (unsigned int)(filePtr-sz); + if (diff) + { + std::string name = std::string(sz,diff); + for (std::vector::iterator it = m_vMeshes.begin(); + it != m_vMeshes.end(); ++it) + { + if ((*it).mName == name) + { + curMesh = & (*it); + break; + } + } + if (!curMesh) + { + LogWarning("Encountered unknown mesh in *MESH_SOFTSKINVERTS section"); + + // Skip the mesh data - until we find a new mesh + // or the end of the *MESH_SOFTSKINVERTS section + while (true) + { + SkipSpacesAndLineEnd(&filePtr); + if (*filePtr == '}') + {++filePtr;return;} + else if (!IsNumeric(*filePtr)) + break; + + SkipLine(&filePtr); + } + } + else + { + SkipSpacesAndLineEnd(&filePtr); + ParseLV4MeshLong(numVerts); + + // Reserve enough storage + curMesh->mBoneVertices.reserve(numVerts); + + for (unsigned int i = 0; i < numVerts;++i) + { + SkipSpacesAndLineEnd(&filePtr); + unsigned int numWeights; + ParseLV4MeshLong(numWeights); + + curMesh->mBoneVertices.push_back(ASE::BoneVertex()); + ASE::BoneVertex& vert = curMesh->mBoneVertices.back(); + + // Reserve enough storage + vert.mBoneWeights.reserve(numWeights); + + for (unsigned int w = 0; w < numWeights;++w) + { + std::string bone; + ParseString(bone,"*MESH_SOFTSKINVERTS.Bone"); + + // Find the bone in the mesh's list + std::pair me; + me.first = -1; + + for (unsigned int n = 0; n < curMesh->mBones.size();++n) + { + if (curMesh->mBones[n].mName == bone) + { + me.first = n; + break; + } + } + if (-1 == me.first) + { + // We don't have this bone yet, so add it to the list + me.first = (int)curMesh->mBones.size(); + curMesh->mBones.push_back(ASE::Bone(bone)); + } + ParseLV4MeshFloat( me.second ); + + // Add the new bone weight to list + vert.mBoneWeights.push_back(me); + } + } + } + } + } + ++filePtr; + SkipSpacesAndLineEnd(&filePtr); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1SceneBlock() +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"SCENE_BACKGROUND_STATIC",23)) + + { + // parse a color triple and assume it is really the bg color + ParseLV4MeshFloatTriple( &m_clrBackground.r ); + continue; + } + if (TokenMatch(filePtr,"SCENE_AMBIENT_STATIC",20)) + + { + // parse a color triple and assume it is really the bg color + ParseLV4MeshFloatTriple( &m_clrAmbient.r ); + continue; + } + if (TokenMatch(filePtr,"SCENE_FIRSTFRAME",16)) + { + ParseLV4MeshLong(iFirstFrame); + continue; + } + if (TokenMatch(filePtr,"SCENE_LASTFRAME",15)) + { + ParseLV4MeshLong(iLastFrame); + continue; + } + if (TokenMatch(filePtr,"SCENE_FRAMESPEED",16)) + { + ParseLV4MeshLong(iFrameSpeed); + continue; + } + if (TokenMatch(filePtr,"SCENE_TICKSPERFRAME",19)) + { + ParseLV4MeshLong(iTicksPerFrame); + continue; + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1MaterialListBlock() +{ + AI_ASE_PARSER_INIT(); + + unsigned int iMaterialCount = 0; + unsigned int iOldMaterialCount = (unsigned int)m_vMaterials.size(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"MATERIAL_COUNT",14)) + { + ParseLV4MeshLong(iMaterialCount); + + // now allocate enough storage to hold all materials + m_vMaterials.resize(iOldMaterialCount+iMaterialCount); + continue; + } + if (TokenMatch(filePtr,"MATERIAL",8)) + { + unsigned int iIndex = 0; + ParseLV4MeshLong(iIndex); + + if (iIndex >= iMaterialCount) + { + LogWarning("Out of range: material index is too large"); + iIndex = iMaterialCount-1; + } + + // get a reference to the material + Material& sMat = m_vMaterials[iIndex+iOldMaterialCount]; + // parse the material block + ParseLV2MaterialBlock(sMat); + continue; + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2MaterialBlock(ASE::Material& mat) +{ + AI_ASE_PARSER_INIT(); + + unsigned int iNumSubMaterials = 0; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"MATERIAL_NAME",13)) + { + if (!ParseString(mat.mName,"*MATERIAL_NAME")) + SkipToNextToken(); + continue; + } + // ambient material color + if (TokenMatch(filePtr,"MATERIAL_AMBIENT",16)) + { + ParseLV4MeshFloatTriple(&mat.mAmbient.r); + continue; + } + // diffuse material color + if (TokenMatch(filePtr,"MATERIAL_DIFFUSE",16) ) + { + ParseLV4MeshFloatTriple(&mat.mDiffuse.r); + continue; + } + // specular material color + if (TokenMatch(filePtr,"MATERIAL_SPECULAR",17)) + { + ParseLV4MeshFloatTriple(&mat.mSpecular.r); + continue; + } + // material shading type + if (TokenMatch(filePtr,"MATERIAL_SHADING",16)) + { + if (TokenMatch(filePtr,"Blinn",5)) + { + mat.mShading = Discreet3DS::Blinn; + } + else if (TokenMatch(filePtr,"Phong",5)) + { + mat.mShading = Discreet3DS::Phong; + } + else if (TokenMatch(filePtr,"Flat",4)) + { + mat.mShading = Discreet3DS::Flat; + } + else if (TokenMatch(filePtr,"Wire",4)) + { + mat.mShading = Discreet3DS::Wire; + } + else + { + // assume gouraud shading + mat.mShading = Discreet3DS::Gouraud; + SkipToNextToken(); + } + continue; + } + // material transparency + if (TokenMatch(filePtr,"MATERIAL_TRANSPARENCY",21)) + { + ParseLV4MeshFloat(mat.mTransparency); + mat.mTransparency = 1.0f - mat.mTransparency;continue; + } + // material self illumination + if (TokenMatch(filePtr,"MATERIAL_SELFILLUM",18)) + { + float f = 0.0f; + ParseLV4MeshFloat(f); + + mat.mEmissive.r = f; + mat.mEmissive.g = f; + mat.mEmissive.b = f; + continue; + } + // material shininess + if (TokenMatch(filePtr,"MATERIAL_SHINE",14) ) + { + ParseLV4MeshFloat(mat.mSpecularExponent); + mat.mSpecularExponent *= 15; + continue; + } + // two-sided material + if (TokenMatch(filePtr,"MATERIAL_TWOSIDED",17) ) + { + mat.mTwoSided = true; + continue; + } + // material shininess strength + if (TokenMatch(filePtr,"MATERIAL_SHINESTRENGTH",22)) + { + ParseLV4MeshFloat(mat.mShininessStrength); + continue; + } + // diffuse color map + if (TokenMatch(filePtr,"MAP_DIFFUSE",11)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexDiffuse); + continue; + } + // ambient color map + if (TokenMatch(filePtr,"MAP_AMBIENT",11)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexAmbient); + continue; + } + // specular color map + if (TokenMatch(filePtr,"MAP_SPECULAR",12)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexSpecular); + continue; + } + // opacity map + if (TokenMatch(filePtr,"MAP_OPACITY",11)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexOpacity); + continue; + } + // emissive map + if (TokenMatch(filePtr,"MAP_SELFILLUM",13)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexEmissive); + continue; + } + // bump map + if (TokenMatch(filePtr,"MAP_BUMP",8)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexBump); + } + // specular/shininess map + if (TokenMatch(filePtr,"MAP_SHINESTRENGTH",17)) + { + // parse the texture block + ParseLV3MapBlock(mat.sTexShininess); + continue; + } + // number of submaterials + if (TokenMatch(filePtr,"NUMSUBMTLS",10)) + { + ParseLV4MeshLong(iNumSubMaterials); + + // allocate enough storage + mat.avSubMaterials.resize(iNumSubMaterials); + } + // submaterial chunks + if (TokenMatch(filePtr,"SUBMATERIAL",11)) + { + + unsigned int iIndex = 0; + ParseLV4MeshLong(iIndex); + + if (iIndex >= iNumSubMaterials) + { + LogWarning("Out of range: submaterial index is too large"); + iIndex = iNumSubMaterials-1; + } + + // get a reference to the material + Material& sMat = mat.avSubMaterials[iIndex]; + + // parse the material block + ParseLV2MaterialBlock(sMat); + continue; + } + } + AI_ASE_HANDLE_SECTION("2","*MATERIAL"); + } +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MapBlock(Texture& map) +{ + AI_ASE_PARSER_INIT(); + + // *********************************************************** + // *BITMAP should not be there if *MAP_CLASS is not BITMAP, + // but we need to expect that case ... if the path is + // empty the texture won't be used later. + // *********************************************************** + bool parsePath = true; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + // type of map + if (TokenMatch(filePtr,"MAP_CLASS" ,9)) + { + std::string temp; + if(!ParseString(temp,"*MAP_CLASS")) + SkipToNextToken(); + if (temp != "Bitmap" && temp != "Normal Bump") + { + DefaultLogger::get()->warn("ASE: Skipping unknown map type: " + temp); + parsePath = false; + } + continue; + } + // path to the texture + if (parsePath && TokenMatch(filePtr,"BITMAP" ,6)) + { + if(!ParseString(map.mMapName,"*BITMAP")) + SkipToNextToken(); + + if (map.mMapName == "None") + { + // Files with 'None' as map name are produced by + // an Maja to ASE exporter which name I forgot .. + DefaultLogger::get()->warn("ASE: Skipping invalid map entry"); + map.mMapName = ""; + } + + continue; + } + // offset on the u axis + if (TokenMatch(filePtr,"UVW_U_OFFSET" ,12)) + { + ParseLV4MeshFloat(map.mOffsetU); + continue; + } + // offset on the v axis + if (TokenMatch(filePtr,"UVW_V_OFFSET" ,12)) + { + ParseLV4MeshFloat(map.mOffsetV); + continue; + } + // tiling on the u axis + if (TokenMatch(filePtr,"UVW_U_TILING" ,12)) + { + ParseLV4MeshFloat(map.mScaleU); + continue; + } + // tiling on the v axis + if (TokenMatch(filePtr,"UVW_V_TILING" ,12)) + { + ParseLV4MeshFloat(map.mScaleV); + continue; + } + // rotation around the z-axis + if (TokenMatch(filePtr,"UVW_ANGLE" ,9)) + { + ParseLV4MeshFloat(map.mRotation); + continue; + } + // map blending factor + if (TokenMatch(filePtr,"MAP_AMOUNT" ,10)) + { + ParseLV4MeshFloat(map.mTextureBlend); + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MAP_XXXXXX"); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +bool Parser::ParseString(std::string& out,const char* szName) +{ + char szBuffer[1024]; + if (!SkipSpaces(&filePtr)) + { + + sprintf(szBuffer,"Unable to parse %s block: Unexpected EOL",szName); + LogWarning(szBuffer); + return false; + } + // there must be '"' + if ('\"' != *filePtr) + { + + sprintf(szBuffer,"Unable to parse %s block: Strings are expected " + "to be enclosed in double quotation marks",szName); + LogWarning(szBuffer); + return false; + } + ++filePtr; + const char* sz = filePtr; + while (true) + { + if ('\"' == *sz)break; + else if ('\0' == *sz) + { + sprintf(szBuffer,"Unable to parse %s block: Strings are expected to " + "be enclosed in double quotation marks but EOF was reached before " + "a closing quotation mark was encountered",szName); + LogWarning(szBuffer); + return false; + } + sz++; + } + out = std::string(filePtr,(uintptr_t)sz-(uintptr_t)filePtr); + filePtr = sz+1; + return true; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV1ObjectBlock(ASE::BaseNode& node) +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // first process common tokens such as node name and transform + // name of the mesh/node + if (TokenMatch(filePtr,"NODE_NAME" ,9)) + { + if(!ParseString(node.mName,"*NODE_NAME")) + SkipToNextToken(); + continue; + } + // name of the parent of the node + if (TokenMatch(filePtr,"NODE_PARENT" ,11) ) + { + if(!ParseString(node.mParent,"*NODE_PARENT")) + SkipToNextToken(); + continue; + } + // transformation matrix of the node + if (TokenMatch(filePtr,"NODE_TM" ,7)) + { + ParseLV2NodeTransformBlock(node); + continue; + } + // animation data of the node + if (TokenMatch(filePtr,"TM_ANIMATION" ,12)) + { + ParseLV2AnimationBlock(node); + continue; + } + + if (node.mType == BaseNode::Light) + { + // light settings + if (TokenMatch(filePtr,"LIGHT_SETTINGS" ,14)) + { + ParseLV2LightSettingsBlock((ASE::Light&)node); + continue; + } + // type of the light source + if (TokenMatch(filePtr,"LIGHT_TYPE" ,10)) + { + if (!ASSIMP_strincmp("omni",filePtr,4)) + { + ((ASE::Light&)node).mLightType = ASE::Light::OMNI; + } + else if (!ASSIMP_strincmp("target",filePtr,6)) + { + ((ASE::Light&)node).mLightType = ASE::Light::TARGET; + } + else if (!ASSIMP_strincmp("free",filePtr,4)) + { + ((ASE::Light&)node).mLightType = ASE::Light::FREE; + } + else if (!ASSIMP_strincmp("directional",filePtr,11)) + { + ((ASE::Light&)node).mLightType = ASE::Light::DIRECTIONAL; + } + else + { + LogWarning("Unknown kind of light source"); + } + continue; + } + } + else if (node.mType == BaseNode::Camera) + { + // Camera settings + if (TokenMatch(filePtr,"CAMERA_SETTINGS" ,15)) + { + ParseLV2CameraSettingsBlock((ASE::Camera&)node); + continue; + } + else if (TokenMatch(filePtr,"CAMERA_TYPE" ,11)) + { + if (!ASSIMP_strincmp("target",filePtr,6)) + { + ((ASE::Camera&)node).mCameraType = ASE::Camera::TARGET; + } + else if (!ASSIMP_strincmp("free",filePtr,4)) + { + ((ASE::Camera&)node).mCameraType = ASE::Camera::FREE; + } + else + { + LogWarning("Unknown kind of camera"); + } + continue; + } + } + else if (node.mType == BaseNode::Mesh) + { + // mesh data + // FIX: Older files use MESH_SOFTSKIN + if (TokenMatch(filePtr,"MESH" ,4) || + TokenMatch(filePtr,"MESH_SOFTSKIN",13)) + { + ParseLV2MeshBlock((ASE::Mesh&)node); + continue; + } + // mesh material index + if (TokenMatch(filePtr,"MATERIAL_REF" ,12)) + { + ParseLV4MeshLong(((ASE::Mesh&)node).iMaterialIndex); + continue; + } + } + } + AI_ASE_HANDLE_TOP_LEVEL_SECTION(); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2CameraSettingsBlock(ASE::Camera& camera) +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"CAMERA_NEAR" ,11)) + { + ParseLV4MeshFloat(camera.mNear); + continue; + } + if (TokenMatch(filePtr,"CAMERA_FAR" ,10)) + { + ParseLV4MeshFloat(camera.mFar); + continue; + } + if (TokenMatch(filePtr,"CAMERA_FOV" ,10)) + { + ParseLV4MeshFloat(camera.mFOV); + continue; + } + } + AI_ASE_HANDLE_SECTION("2","CAMERA_SETTINGS"); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2LightSettingsBlock(ASE::Light& light) +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"LIGHT_COLOR" ,11)) + { + ParseLV4MeshFloatTriple(&light.mColor.r); + continue; + } + if (TokenMatch(filePtr,"LIGHT_INTENS" ,12)) + { + ParseLV4MeshFloat(light.mIntensity); + continue; + } + if (TokenMatch(filePtr,"LIGHT_HOTSPOT" ,13)) + { + ParseLV4MeshFloat(light.mAngle); + continue; + } + if (TokenMatch(filePtr,"LIGHT_FALLOFF" ,13)) + { + ParseLV4MeshFloat(light.mFalloff); + continue; + } + } + AI_ASE_HANDLE_SECTION("2","LIGHT_SETTINGS"); + } + return; +} + +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2AnimationBlock(ASE::BaseNode& mesh) +{ + AI_ASE_PARSER_INIT(); + + ASE::Animation* anim = &mesh.mAnim; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + if (TokenMatch(filePtr,"NODE_NAME" ,9)) + { + std::string temp; + if(!ParseString(temp,"*NODE_NAME")) + SkipToNextToken(); + + // If the name of the node contains .target it + // represents an animated camera or spot light + // target. + if (std::string::npos != temp.find(".Target")) + { + if ((mesh.mType != BaseNode::Camera || ((ASE::Camera&)mesh).mCameraType != ASE::Camera::TARGET) && + ( mesh.mType != BaseNode::Light || ((ASE::Light&)mesh).mLightType != ASE::Light::TARGET)) + { + + DefaultLogger::get()->error("ASE: Found target animation channel " + "but the node is neither a camera nor a spot light"); + anim = NULL; + } + else anim = &mesh.mTargetAnim; + } + continue; + } + + // position keyframes + if (TokenMatch(filePtr,"CONTROL_POS_TRACK" ,17) || + TokenMatch(filePtr,"CONTROL_POS_BEZIER" ,18) || + TokenMatch(filePtr,"CONTROL_POS_TCB" ,15)) + { + if (!anim)SkipSection(); + else ParseLV3PosAnimationBlock(*anim); + continue; + } + // scaling keyframes + if (TokenMatch(filePtr,"CONTROL_SCALE_TRACK" ,19) || + TokenMatch(filePtr,"CONTROL_SCALE_BEZIER" ,20) || + TokenMatch(filePtr,"CONTROL_SCALE_TCB" ,17)) + { + if (!anim || anim == &mesh.mTargetAnim) + { + // Target animation channels may have no rotation channels + DefaultLogger::get()->error("ASE: Ignoring scaling channel in target animation"); + SkipSection(); + } + else ParseLV3ScaleAnimationBlock(*anim); + continue; + } + // rotation keyframes + if (TokenMatch(filePtr,"CONTROL_ROT_TRACK" ,17) || + TokenMatch(filePtr,"CONTROL_ROT_BEZIER" ,18) || + TokenMatch(filePtr,"CONTROL_ROT_TCB" ,15)) + { + if (!anim || anim == &mesh.mTargetAnim) + { + // Target animation channels may have no rotation channels + DefaultLogger::get()->error("ASE: Ignoring rotation channel in target animation"); + SkipSection(); + } + else ParseLV3RotAnimationBlock(*anim); + continue; + } + } + AI_ASE_HANDLE_SECTION("2","TM_ANIMATION"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3ScaleAnimationBlock(ASE::Animation& anim) +{ + AI_ASE_PARSER_INIT(); + unsigned int iIndex; + + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + bool b = false; + + // For the moment we're just reading the three floats - + // we ignore the ádditional information for bezier's and TCBs + + // simple scaling keyframe + if (TokenMatch(filePtr,"CONTROL_SCALE_SAMPLE" ,20)) + { + b = true; + anim.mScalingType = ASE::Animation::TRACK; + } + + // Bezier scaling keyframe + if (TokenMatch(filePtr,"CONTROL_BEZIER_SCALE_KEY" ,24)) + { + b = true; + anim.mScalingType = ASE::Animation::BEZIER; + } + // TCB scaling keyframe + if (TokenMatch(filePtr,"CONTROL_TCB_SCALE_KEY" ,21)) + { + b = true; + anim.mScalingType = ASE::Animation::TCB; + } + if (b) + { + anim.akeyScaling.push_back(aiVectorKey()); + aiVectorKey& key = anim.akeyScaling.back(); + ParseLV4MeshFloatTriple(&key.mValue.x,iIndex); + key.mTime = (double)iIndex; + } + } + AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3PosAnimationBlock(ASE::Animation& anim) +{ + AI_ASE_PARSER_INIT(); + unsigned int iIndex; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + bool b = false; + + // For the moment we're just reading the three floats - + // we ignore the ádditional information for bezier's and TCBs + + // simple scaling keyframe + if (TokenMatch(filePtr,"CONTROL_POS_SAMPLE" ,18)) + { + b = true; + anim.mPositionType = ASE::Animation::TRACK; + } + + // Bezier scaling keyframe + if (TokenMatch(filePtr,"CONTROL_BEZIER_POS_KEY" ,22)) + { + b = true; + anim.mPositionType = ASE::Animation::BEZIER; + } + // TCB scaling keyframe + if (TokenMatch(filePtr,"CONTROL_TCB_POS_KEY" ,19)) + { + b = true; + anim.mPositionType = ASE::Animation::TCB; + } + if (b) + { + anim.akeyPositions.push_back(aiVectorKey()); + aiVectorKey& key = anim.akeyPositions.back(); + ParseLV4MeshFloatTriple(&key.mValue.x,iIndex); + key.mTime = (double)iIndex; + } + } + AI_ASE_HANDLE_SECTION("3","*CONTROL_POS_TRACK"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3RotAnimationBlock(ASE::Animation& anim) +{ + AI_ASE_PARSER_INIT(); + unsigned int iIndex; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + bool b = false; + + // For the moment we're just reading the floats - + // we ignore the ádditional information for bezier's and TCBs + + // simple scaling keyframe + if (TokenMatch(filePtr,"CONTROL_ROT_SAMPLE" ,18)) + { + b = true; + anim.mRotationType = ASE::Animation::TRACK; + } + + // Bezier scaling keyframe + if (TokenMatch(filePtr,"CONTROL_BEZIER_ROT_KEY" ,22)) + { + b = true; + anim.mRotationType = ASE::Animation::BEZIER; + } + // TCB scaling keyframe + if (TokenMatch(filePtr,"CONTROL_TCB_ROT_KEY" ,19)) + { + b = true; + anim.mRotationType = ASE::Animation::TCB; + } + if (b) + { + anim.akeyRotations.push_back(aiQuatKey()); + aiQuatKey& key = anim.akeyRotations.back(); + aiVector3D v;float f; + ParseLV4MeshFloatTriple(&v.x,iIndex); + ParseLV4MeshFloat(f); + key.mTime = (double)iIndex; + key.mValue = aiQuaternion(v,f); + } + } + AI_ASE_HANDLE_SECTION("3","*CONTROL_ROT_TRACK"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode& mesh) +{ + AI_ASE_PARSER_INIT(); + int mode = 0; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + // name of the node + if (TokenMatch(filePtr,"NODE_NAME" ,9)) + { + std::string temp; + if(!ParseString(temp,"*NODE_NAME")) + SkipToNextToken(); + + std::string::size_type s; + if (temp == mesh.mName) + { + mode = 1; + } + else if (std::string::npos != (s = temp.find(".Target")) && + mesh.mName == temp.substr(0,s)) + { + // This should be either a target light or a target camera + if ( (mesh.mType == BaseNode::Light && ((ASE::Light&)mesh) .mLightType == ASE::Light::TARGET) || + (mesh.mType == BaseNode::Camera && ((ASE::Camera&)mesh).mCameraType == ASE::Camera::TARGET)) + { + mode = 2; + } + else DefaultLogger::get()->error("ASE: Ignoring target transform, " + "this is no spot light or target camera"); + } + else + { + DefaultLogger::get()->error("ASE: Unknown node transformation: " + temp); + // mode = 0 + } + continue; + } + if (mode) + { + // fourth row of the transformation matrix - and also the + // only information here that is interesting for targets + if (TokenMatch(filePtr,"TM_ROW3" ,7)) + { + ParseLV4MeshFloatTriple((mode == 1 ? mesh.mTransform[3] : &mesh.mTargetPosition.x)); + continue; + } + if (mode == 1) + { + // first row of the transformation matrix + if (TokenMatch(filePtr,"TM_ROW0" ,7)) + { + ParseLV4MeshFloatTriple(mesh.mTransform[0]); + continue; + } + // second row of the transformation matrix + if (TokenMatch(filePtr,"TM_ROW1" ,7)) + { + ParseLV4MeshFloatTriple(mesh.mTransform[1]); + continue; + } + // third row of the transformation matrix + if (TokenMatch(filePtr,"TM_ROW2" ,7)) + { + ParseLV4MeshFloatTriple(mesh.mTransform[2]); + continue; + } + // inherited position axes + if (TokenMatch(filePtr,"INHERIT_POS" ,11)) + { + unsigned int aiVal[3]; + ParseLV4MeshLongTriple(aiVal); + + for (unsigned int i = 0; i < 3;++i) + mesh.inherit.abInheritPosition[i] = aiVal[i] != 0; + continue; + } + // inherited rotation axes + if (TokenMatch(filePtr,"INHERIT_ROT" ,11)) + { + unsigned int aiVal[3]; + ParseLV4MeshLongTriple(aiVal); + + for (unsigned int i = 0; i < 3;++i) + mesh.inherit.abInheritRotation[i] = aiVal[i] != 0; + continue; + } + // inherited scaling axes + if (TokenMatch(filePtr,"INHERIT_SCL" ,11)) + { + unsigned int aiVal[3]; + ParseLV4MeshLongTriple(aiVal); + + for (unsigned int i = 0; i < 3;++i) + mesh.inherit.abInheritScaling[i] = aiVal[i] != 0; + continue; + } + } + } + } + AI_ASE_HANDLE_SECTION("2","*NODE_TM"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV2MeshBlock(ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + unsigned int iNumVertices = 0; + unsigned int iNumFaces = 0; + unsigned int iNumTVertices = 0; + unsigned int iNumTFaces = 0; + unsigned int iNumCVertices = 0; + unsigned int iNumCFaces = 0; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + // Number of vertices in the mesh + if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14)) + { + ParseLV4MeshLong(iNumVertices); + continue; + } + // Number of texture coordinates in the mesh + if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15)) + { + ParseLV4MeshLong(iNumTVertices); + continue; + } + // Number of vertex colors in the mesh + if (TokenMatch(filePtr,"MESH_NUMCVERTEX" ,15)) + { + ParseLV4MeshLong(iNumCVertices); + continue; + } + // Number of regular faces in the mesh + if (TokenMatch(filePtr,"MESH_NUMFACES" ,13)) + { + ParseLV4MeshLong(iNumFaces); + continue; + } + // Number of UVWed faces in the mesh + if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15)) + { + ParseLV4MeshLong(iNumTFaces); + continue; + } + // Number of colored faces in the mesh + if (TokenMatch(filePtr,"MESH_NUMCVFACES" ,15)) + { + ParseLV4MeshLong(iNumCFaces); + continue; + } + // mesh vertex list block + if (TokenMatch(filePtr,"MESH_VERTEX_LIST" ,16)) + { + ParseLV3MeshVertexListBlock(iNumVertices,mesh); + continue; + } + // mesh face list block + if (TokenMatch(filePtr,"MESH_FACE_LIST" ,14)) + { + ParseLV3MeshFaceListBlock(iNumFaces,mesh); + continue; + } + // mesh texture vertex list block + if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14)) + { + ParseLV3MeshTListBlock(iNumTVertices,mesh); + continue; + } + // mesh texture face block + if (TokenMatch(filePtr,"MESH_TFACELIST" ,14)) + { + ParseLV3MeshTFaceListBlock(iNumTFaces,mesh); + continue; + } + // mesh color vertex list block + if (TokenMatch(filePtr,"MESH_CVERTLIST" ,14)) + { + ParseLV3MeshCListBlock(iNumCVertices,mesh); + continue; + } + // mesh color face block + if (TokenMatch(filePtr,"MESH_CFACELIST" ,14)) + { + ParseLV3MeshCFaceListBlock(iNumCFaces,mesh); + continue; + } + // mesh normals + if (TokenMatch(filePtr,"MESH_NORMALS" ,12)) + { + ParseLV3MeshNormalListBlock(mesh); + continue; + } + // another mesh UV channel ... + if (TokenMatch(filePtr,"MESH_MAPPINGCHANNEL" ,19)) + { + + unsigned int iIndex = 0; + ParseLV4MeshLong(iIndex); + + if (iIndex < 2) + { + LogWarning("Mapping channel has an invalid index. Skipping UV channel"); + // skip it ... + SkipSection(); + } + if (iIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS) + { + LogWarning("Too many UV channels specified. Skipping channel .."); + // skip it ... + SkipSection(); + } + else + { + // parse the mapping channel + ParseLV3MappingChannel(iIndex-1,mesh); + } + continue; + } + // mesh animation keyframe. Not supported + if (TokenMatch(filePtr,"MESH_ANIMATION" ,14)) + { + + LogWarning("Found *MESH_ANIMATION element in ASE/ASK file. " + "Keyframe animation is not supported by Assimp, this element " + "will be ignored"); + //SkipSection(); + continue; + } + if (TokenMatch(filePtr,"MESH_WEIGHTS" ,12)) + { + ParseLV3MeshWeightsBlock(mesh);continue; + } + } + AI_ASE_HANDLE_SECTION("2","*MESH"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshWeightsBlock(ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + unsigned int iNumVertices = 0, iNumBones = 0; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Number of bone vertices ... + if (TokenMatch(filePtr,"MESH_NUMVERTEX" ,14)) + { + ParseLV4MeshLong(iNumVertices); + continue; + } + // Number of bones + if (TokenMatch(filePtr,"MESH_NUMBONE" ,12)) + { + ParseLV4MeshLong(iNumBones); + continue; + } + // parse the list of bones + if (TokenMatch(filePtr,"MESH_BONE_LIST" ,14)) + { + ParseLV4MeshBones(iNumBones,mesh); + continue; + } + // parse the list of bones vertices + if (TokenMatch(filePtr,"MESH_BONE_VERTEX_LIST" ,21) ) + { + ParseLV4MeshBonesVertices(iNumVertices,mesh); + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_WEIGHTS"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshBones(unsigned int iNumBones,ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + mesh.mBones.resize(iNumBones); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Mesh bone with name ... + if (TokenMatch(filePtr,"MESH_BONE_NAME" ,14)) + { + // parse an index ... + if(SkipSpaces(&filePtr)) + { + unsigned int iIndex = strtoul10(filePtr,&filePtr); + if (iIndex >= iNumBones) + { + LogWarning("Bone index is out of bounds"); + continue; + } + if (!ParseString(mesh.mBones[iIndex].mName,"*MESH_BONE_NAME")) + SkipToNextToken(); + continue; + } + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_BONE_LIST"); + } +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshBonesVertices(unsigned int iNumVertices,ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + mesh.mBoneVertices.resize(iNumVertices); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Mesh bone vertex + if (TokenMatch(filePtr,"MESH_BONE_VERTEX" ,16)) + { + // read the vertex index + unsigned int iIndex = strtoul10(filePtr,&filePtr); + if (iIndex >= mesh.mPositions.size()) + { + iIndex = (unsigned int)mesh.mPositions.size()-1; + LogWarning("Bone vertex index is out of bounds. Using the largest valid " + "bone vertex index instead"); + } + + // --- ignored + float afVert[3]; + ParseLV4MeshFloatTriple(afVert); + + std::pair pairOut; + while (true) + { + // first parse the bone index ... + if (!SkipSpaces(&filePtr))break; + pairOut.first = strtoul10(filePtr,&filePtr); + + // then parse the vertex weight + if (!SkipSpaces(&filePtr))break; + filePtr = fast_atoreal_move(filePtr,pairOut.second); + + // -1 marks unused entries + if (-1 != pairOut.first) + { + mesh.mBoneVertices[iIndex].mBoneWeights.push_back(pairOut); + } + } + continue; + } + } + AI_ASE_HANDLE_SECTION("4","*MESH_BONE_VERTEX"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshVertexListBlock( + unsigned int iNumVertices, ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the array + mesh.mPositions.resize(iNumVertices); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Vertex entry + if (TokenMatch(filePtr,"MESH_VERTEX" ,11)) + { + + aiVector3D vTemp; + unsigned int iIndex; + ParseLV4MeshFloatTriple(&vTemp.x,iIndex); + + if (iIndex >= iNumVertices) + { + LogWarning("Invalid vertex index. It will be ignored"); + } + else mesh.mPositions[iIndex] = vTemp; + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_VERTEX_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the face array + mesh.mFaces.resize(iNumFaces); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Face entry + if (TokenMatch(filePtr,"MESH_FACE" ,9)) + { + + ASE::Face mFace; + ParseLV4MeshFace(mFace); + + if (mFace.iFace >= iNumFaces) + { + LogWarning("Face has an invalid index. It will be ignored"); + } + else mesh.mFaces[mFace.iFace] = mFace; + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_FACE_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshTListBlock(unsigned int iNumVertices, + ASE::Mesh& mesh, unsigned int iChannel) +{ + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the array + mesh.amTexCoords[iChannel].resize(iNumVertices); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Vertex entry + if (TokenMatch(filePtr,"MESH_TVERT" ,10)) + { + aiVector3D vTemp; + unsigned int iIndex; + ParseLV4MeshFloatTriple(&vTemp.x,iIndex); + + if (iIndex >= iNumVertices) + { + LogWarning("Tvertex has an invalid index. It will be ignored"); + } + else mesh.amTexCoords[iChannel][iIndex] = vTemp; + + if (0.0f != vTemp.z) + { + // we need 3 coordinate channels + mesh.mNumUVComponents[iChannel] = 3; + } + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_TVERT_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshTFaceListBlock(unsigned int iNumFaces, + ASE::Mesh& mesh, unsigned int iChannel) +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Face entry + if (TokenMatch(filePtr,"MESH_TFACE" ,10)) + { + unsigned int aiValues[3]; + unsigned int iIndex = 0; + + ParseLV4MeshLongTriple(aiValues,iIndex); + if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) + { + LogWarning("UV-Face has an invalid index. It will be ignored"); + } + else + { + // copy UV indices + mesh.mFaces[iIndex].amUVIndices[iChannel][0] = aiValues[0]; + mesh.mFaces[iIndex].amUVIndices[iChannel][1] = aiValues[1]; + mesh.mFaces[iIndex].amUVIndices[iChannel][2] = aiValues[2]; + } + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_TFACE_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MappingChannel(unsigned int iChannel, ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + unsigned int iNumTVertices = 0; + unsigned int iNumTFaces = 0; + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Number of texture coordinates in the mesh + if (TokenMatch(filePtr,"MESH_NUMTVERTEX" ,15)) + { + ParseLV4MeshLong(iNumTVertices); + continue; + } + // Number of UVWed faces in the mesh + if (TokenMatch(filePtr,"MESH_NUMTVFACES" ,15)) + { + ParseLV4MeshLong(iNumTFaces); + continue; + } + // mesh texture vertex list block + if (TokenMatch(filePtr,"MESH_TVERTLIST" ,14)) + { + ParseLV3MeshTListBlock(iNumTVertices,mesh,iChannel); + continue; + } + // mesh texture face block + if (TokenMatch(filePtr,"MESH_TFACELIST" ,14)) + { + ParseLV3MeshTFaceListBlock(iNumTFaces,mesh, iChannel); + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_MAPPING_CHANNEL"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshCListBlock(unsigned int iNumVertices, ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + + // allocate enough storage in the array + mesh.mVertexColors.resize(iNumVertices); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Vertex entry + if (TokenMatch(filePtr,"MESH_VERTCOL" ,12)) + { + aiColor4D vTemp; + vTemp.a = 1.0f; + unsigned int iIndex; + ParseLV4MeshFloatTriple(&vTemp.r,iIndex); + + if (iIndex >= iNumVertices) + { + LogWarning("Vertex color has an invalid index. It will be ignored"); + } + else mesh.mVertexColors[iIndex] = vTemp; + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_CVERTEX_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshCFaceListBlock(unsigned int iNumFaces, ASE::Mesh& mesh) +{ + AI_ASE_PARSER_INIT(); + while (true) + { + if ('*' == *filePtr) + { + ++filePtr; + + // Face entry + if (TokenMatch(filePtr,"MESH_CFACE" ,11)) + { + unsigned int aiValues[3]; + unsigned int iIndex = 0; + + ParseLV4MeshLongTriple(aiValues,iIndex); + if (iIndex >= iNumFaces || iIndex >= mesh.mFaces.size()) + { + LogWarning("UV-Face has an invalid index. It will be ignored"); + } + else + { + // copy color indices + mesh.mFaces[iIndex].mColorIndices[0] = aiValues[0]; + mesh.mFaces[iIndex].mColorIndices[1] = aiValues[1]; + mesh.mFaces[iIndex].mColorIndices[2] = aiValues[2]; + } + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_CFACE_LIST"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV3MeshNormalListBlock(ASE::Mesh& sMesh) +{ + AI_ASE_PARSER_INIT(); + + // Allocate enough storage for the normals + sMesh.mNormals.resize(sMesh.mFaces.size()*3,aiVector3D( 0.f, 0.f, 0.f )); + unsigned int index, faceIdx = UINT_MAX; + + // FIXME: rewrite this and find out how to interpret the normals + // correctly. This is crap. + + // Smooth the vertex and face normals together. The result + // will be edgy then, but otherwise everything would be soft ... + while (true) { + if ('*' == *filePtr) { + ++filePtr; + if (faceIdx != UINT_MAX && TokenMatch(filePtr,"MESH_VERTEXNORMAL",17)) { + aiVector3D vNormal; + ParseLV4MeshFloatTriple(&vNormal.x,index); + if (faceIdx >= sMesh.mFaces.size()) + continue; + + // Make sure we assign it to the correct face + const ASE::Face& face = sMesh.mFaces[faceIdx]; + if (index == face.mIndices[0]) + index = 0; + else if (index == face.mIndices[1]) + index = 1; + else if (index == face.mIndices[2]) + index = 2; + else { + DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_VERTEXNORMAL section"); + continue; + } + // We'll renormalize later + sMesh.mNormals[faceIdx*3+index] += vNormal; + continue; + } + if (TokenMatch(filePtr,"MESH_FACENORMAL",15)) { + aiVector3D vNormal; + ParseLV4MeshFloatTriple(&vNormal.x,faceIdx); + + if (faceIdx >= sMesh.mFaces.size()) { + DefaultLogger::get()->error("ASE: Invalid vertex index in MESH_FACENORMAL section"); + continue; + } + + // We'll renormalize later + sMesh.mNormals[faceIdx*3] += vNormal; + sMesh.mNormals[faceIdx*3+1] += vNormal; + sMesh.mNormals[faceIdx*3+2] += vNormal; + continue; + } + } + AI_ASE_HANDLE_SECTION("3","*MESH_NORMALS"); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFace(ASE::Face& out) +{ + // skip spaces and tabs + if(!SkipSpaces(&filePtr)) + { + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL [#1]"); + SkipToNextToken(); + return; + } + + // parse the face index + out.iFace = strtoul10(filePtr,&filePtr); + + // next character should be ':' + if(!SkipSpaces(&filePtr)) + { + // FIX: there are some ASE files which haven't got : here .... + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. \':\' expected [#2]"); + SkipToNextToken(); + return; + } + // FIX: There are some ASE files which haven't got ':' here + if(':' == *filePtr)++filePtr; + + // Parse all mesh indices + for (unsigned int i = 0; i < 3;++i) + { + unsigned int iIndex = 0; + if(!SkipSpaces(&filePtr)) + { + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL"); + SkipToNextToken(); + return; + } + switch (*filePtr) + { + case 'A': + case 'a': + break; + case 'B': + case 'b': + iIndex = 1; + break; + case 'C': + case 'c': + iIndex = 2; + break; + default: + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " + "A,B or C expected [#3]"); + SkipToNextToken(); + return; + }; + ++filePtr; + + // next character should be ':' + if(!SkipSpaces(&filePtr) || ':' != *filePtr) + { + LogWarning("Unable to parse *MESH_FACE Element: " + "Unexpected EOL. \':\' expected [#2]"); + SkipToNextToken(); + return; + } + + ++filePtr; + if(!SkipSpaces(&filePtr)) + { + LogWarning("Unable to parse *MESH_FACE Element: Unexpected EOL. " + "Vertex index ecpected [#4]"); + SkipToNextToken(); + return; + } + out.mIndices[iIndex] = strtoul10(filePtr,&filePtr); + } + + // now we need to skip the AB, BC, CA blocks. + while (true) + { + if ('*' == *filePtr)break; + if (IsLineEnd(*filePtr)) + { + //iLineNumber++; + return; + } + filePtr++; + } + + // parse the smoothing group of the face + if (TokenMatch(filePtr,"*MESH_SMOOTHING",15)) + { + if(!SkipSpaces(&filePtr)) + { + LogWarning("Unable to parse *MESH_SMOOTHING Element: " + "Unexpected EOL. Smoothing group(s) expected [#5]"); + SkipToNextToken(); + return; + } + + // Parse smoothing groups until we don't anymore see commas + // FIX: There needn't always be a value, sad but true + while (true) + { + if (*filePtr < '9' && *filePtr >= '0') + { + out.iSmoothGroup |= (1 << strtoul10(filePtr,&filePtr)); + } + SkipSpaces(&filePtr); + if (',' != *filePtr) + { + break; + } + ++filePtr; + SkipSpaces(&filePtr); + } + } + + // *MESH_MTLID is optional, too + while (true) + { + if ('*' == *filePtr)break; + if (IsLineEnd(*filePtr)) + { + return; + } + filePtr++; + } + + if (TokenMatch(filePtr,"*MESH_MTLID",11)) + { + if(!SkipSpaces(&filePtr)) + { + LogWarning("Unable to parse *MESH_MTLID Element: Unexpected EOL. " + "Material index expected [#6]"); + SkipToNextToken(); + return; + } + out.iMaterial = strtoul10(filePtr,&filePtr); + } + return; +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshLongTriple(unsigned int* apOut) +{ + ai_assert(NULL != apOut); + + for (unsigned int i = 0; i < 3;++i) + ParseLV4MeshLong(apOut[i]); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut) +{ + ai_assert(NULL != apOut); + + // parse the index + ParseLV4MeshLong(rIndexOut); + + // parse the three others + ParseLV4MeshLongTriple(apOut); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut) +{ + ai_assert(NULL != apOut); + + // parse the index + ParseLV4MeshLong(rIndexOut); + + // parse the three others + ParseLV4MeshFloatTriple(apOut); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloatTriple(float* apOut) +{ + ai_assert(NULL != apOut); + + for (unsigned int i = 0; i < 3;++i) + ParseLV4MeshFloat(apOut[i]); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshFloat(float& fOut) +{ + // skip spaces and tabs + if(!SkipSpaces(&filePtr)) + { + // LOG + LogWarning("Unable to parse float: unexpected EOL [#1]"); + fOut = 0.0f; + ++iLineNumber; + return; + } + // parse the first float + filePtr = fast_atoreal_move(filePtr,fOut); +} +// ------------------------------------------------------------------------------------------------ +void Parser::ParseLV4MeshLong(unsigned int& iOut) +{ + // Skip spaces and tabs + if(!SkipSpaces(&filePtr)) + { + // LOG + LogWarning("Unable to parse long: unexpected EOL [#1]"); + iOut = 0; + ++iLineNumber; + return; + } + // parse the value + iOut = strtoul10(filePtr,&filePtr); +} + +#endif // !! ASSIMP_BUILD_NO_BASE_IMPORTER diff --git a/code/ASEParser.h b/code/ASEParser.h index aa848dee9..e0a628606 100644 --- a/code/ASEParser.h +++ b/code/ASEParser.h @@ -1,669 +1,669 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - - -/** @file Defines the helper data structures for importing ASE files */ -#ifndef AI_ASEFILEHELPER_H_INC -#define AI_ASEFILEHELPER_H_INC - -// STL/CRT headers -#include -#include -#include - -// public ASSIMP headers -#include "../include/assimp/types.h" -#include "../include/assimp/mesh.h" -#include "../include/assimp/anim.h" - -// for some helper routines like IsSpace() -#include "ParsingUtils.h" -#include "qnan.h" - -// ASE is quite similar to 3ds. We can reuse some structures -#include "3DSLoader.h" - -namespace Assimp { -namespace ASE { - -using namespace D3DS; - -// --------------------------------------------------------------------------- -/** Helper structure representing an ASE material */ -struct Material : public D3DS::Material -{ - //! Default constructor - Material() : pcInstance(NULL), bNeed (false) - {} - - //! Contains all sub materials of this material - std::vector avSubMaterials; - - //! aiMaterial object - aiMaterial* pcInstance; - - //! Can we remove this material? - bool bNeed; -}; - -// --------------------------------------------------------------------------- -/** Helper structure to represent an ASE file face */ -struct Face : public FaceWithSmoothingGroup -{ - //! Default constructor. Initializes everything with 0 - Face() - { - mColorIndices[0] = mColorIndices[1] = mColorIndices[2] = 0; - for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) - { - amUVIndices[i][0] = amUVIndices[i][1] = amUVIndices[i][2] = 0; - } - - iMaterial = DEFAULT_MATINDEX; - iFace = 0; - } - - //! special value to indicate that no material index has - //! been assigned to a face. The default material index - //! will replace this value later. - static const unsigned int DEFAULT_MATINDEX = 0xFFFFFFFF; - - - - //! Indices into each list of texture coordinates - unsigned int amUVIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS][3]; - - //! Index into the list of vertex colors - unsigned int mColorIndices[3]; - - //! (Sub)Material index to be assigned to this face - unsigned int iMaterial; - - //! Index of the face. It is not specified whether it is - //! a requirement of the file format that all faces are - //! written in sequential order, so we have to expect this case - unsigned int iFace; -}; - -// --------------------------------------------------------------------------- -/** Helper structure to represent an ASE file bone */ -struct Bone -{ - //! Constructor - Bone() - { - static int iCnt = 0; - - // Generate a default name for the bone - char szTemp[128]; - ::sprintf(szTemp,"UNNAMED_%i",iCnt++); - mName = szTemp; - } - - //! Construction from an existing name - Bone( const std::string& name) - : mName (name) - {} - - //! Name of the bone - std::string mName; -}; - -// --------------------------------------------------------------------------- -/** Helper structure to represent an ASE file bone vertex */ -struct BoneVertex -{ - //! Bone and corresponding vertex weight. - //! -1 for unrequired bones .... - std::vector > mBoneWeights; - - //! Position of the bone vertex. - //! MUST be identical to the vertex position - //aiVector3D mPosition; -}; - -// --------------------------------------------------------------------------- -/** Helper structure to represent an ASE file animation */ -struct Animation -{ - enum Type - { - TRACK = 0x0, - BEZIER = 0x1, - TCB = 0x2 - } mRotationType, mScalingType, mPositionType; - - Animation() - : mRotationType (TRACK) - , mScalingType (TRACK) - , mPositionType (TRACK) - {} - - //! List of track rotation keyframes - std::vector< aiQuatKey > akeyRotations; - - //! List of track position keyframes - std::vector< aiVectorKey > akeyPositions; - - //! List of track scaling keyframes - std::vector< aiVectorKey > akeyScaling; - -}; - -// --------------------------------------------------------------------------- -/** Helper structure to represent the inheritance information of an ASE node */ -struct InheritanceInfo -{ - //! Default constructor - InheritanceInfo() - { - // set the inheritance flag for all axes by default to true - for (unsigned int i = 0; i < 3;++i) - abInheritPosition[i] = abInheritRotation[i] = abInheritScaling[i] = true; - } - - //! Inherit the parent's position?, axis order is x,y,z - bool abInheritPosition[3]; - - //! Inherit the parent's rotation?, axis order is x,y,z - bool abInheritRotation[3]; - - //! Inherit the parent's scaling?, axis order is x,y,z - bool abInheritScaling[3]; -}; - -// --------------------------------------------------------------------------- -/** Represents an ASE file node. Base class for mesh, light and cameras */ -struct BaseNode -{ - enum Type {Light, Camera, Mesh, Dummy} mType; - - //! Constructor. Creates a default name for the node - BaseNode(Type _mType) - : mType (_mType) - , mProcessed (false) - { - // generate a default name for the node - static int iCnt = 0; - char szTemp[128]; // should be sufficiently large - ::sprintf(szTemp,"UNNAMED_%i",iCnt++); - mName = szTemp; - - // Set mTargetPosition to qnan - const float qnan = get_qnan(); - mTargetPosition.x = qnan; - } - - //! Name of the mesh - std::string mName; - - //! Name of the parent of the node - //! "" if there is no parent ... - std::string mParent; - - //! Transformation matrix of the node - aiMatrix4x4 mTransform; - - //! Target position (target lights and cameras) - aiVector3D mTargetPosition; - - //! Specifies which axes transformations a node inherits - //! from its parent ... - InheritanceInfo inherit; - - //! Animation channels for the node - Animation mAnim; - - //! Needed for lights and cameras: target animation channel - //! Should contain position keys only. - Animation mTargetAnim; - - bool mProcessed; -}; - -// --------------------------------------------------------------------------- -/** Helper structure to represent an ASE file mesh */ -struct Mesh : public MeshWithSmoothingGroups, public BaseNode -{ - //! Constructor. - Mesh() - : BaseNode (BaseNode::Mesh) - , bSkip (false) - { - // use 2 texture vertex components by default - for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) - this->mNumUVComponents[c] = 2; - - // setup the default material index by default - iMaterialIndex = Face::DEFAULT_MATINDEX; - } - - //! List of all texture coordinate sets - std::vector amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; - - //! List of all vertex color sets. - std::vector mVertexColors; - - //! List of all bone vertices - std::vector mBoneVertices; - - //! List of all bones - std::vector mBones; - - //! Material index of the mesh - unsigned int iMaterialIndex; - - //! Number of vertex components for each UVW set - unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS]; - - //! used internally - bool bSkip; -}; - -// --------------------------------------------------------------------------- -/** Helper structure to represent an ASE light source */ -struct Light : public BaseNode -{ - enum LightType - { - OMNI, - TARGET, - FREE, - DIRECTIONAL - }; - - //! Constructor. - Light() - : BaseNode (BaseNode::Light) - , mLightType (OMNI) - , mColor (1.f,1.f,1.f) - , mIntensity (1.f) // light is white by default - , mAngle (45.f) - , mFalloff (0.f) - { - } - - LightType mLightType; - aiColor3D mColor; - float mIntensity; - float mAngle; // in degrees - float mFalloff; -}; - -// --------------------------------------------------------------------------- -/** Helper structure to represent an ASE camera */ -struct Camera : public BaseNode -{ - enum CameraType - { - FREE, - TARGET - }; - - //! Constructor - Camera() - : BaseNode (BaseNode::Camera) - , mFOV (0.75f) // in radians - , mNear (0.1f) - , mFar (1000.f) // could be zero - , mCameraType (FREE) - { - } - - float mFOV, mNear, mFar; - CameraType mCameraType; -}; - -// --------------------------------------------------------------------------- -/** Helper structure to represent an ASE helper object (dummy) */ -struct Dummy : public BaseNode -{ - //! Constructor - Dummy() - : BaseNode (BaseNode::Dummy) - { - } -}; - -// Parameters to Parser::Parse() -#define AI_ASE_NEW_FILE_FORMAT 200 -#define AI_ASE_OLD_FILE_FORMAT 110 - -// Internally we're a little bit more tolerant -#define AI_ASE_IS_NEW_FILE_FORMAT() (iFileFormat >= 200) -#define AI_ASE_IS_OLD_FILE_FORMAT() (iFileFormat < 200) - -// ------------------------------------------------------------------------------- -/** \brief Class to parse ASE files - */ -class Parser -{ - -private: - - Parser() {} - -public: - - // ------------------------------------------------------------------- - //! Construct a parser from a given input file which is - //! guaranted to be terminated with zero. - //! @param szFile Input file - //! @param fileFormatDefault Assumed file format version. If the - //! file format is specified in the file the new value replaces - //! the default value. - Parser (const char* szFile, unsigned int fileFormatDefault); - - // ------------------------------------------------------------------- - //! Parses the file into the parsers internal representation - void Parse(); - - -private: - - // ------------------------------------------------------------------- - //! Parse the *SCENE block in a file - void ParseLV1SceneBlock(); - - // ------------------------------------------------------------------- - //! Parse the *MESH_SOFTSKINVERTS block in a file - void ParseLV1SoftSkinBlock(); - - // ------------------------------------------------------------------- - //! Parse the *MATERIAL_LIST block in a file - void ParseLV1MaterialListBlock(); - - // ------------------------------------------------------------------- - //! Parse a *OBJECT block in a file - //! \param mesh Node to be filled - void ParseLV1ObjectBlock(BaseNode& mesh); - - // ------------------------------------------------------------------- - //! Parse a *MATERIAL blocks in a material list - //! \param mat Material structure to be filled - void ParseLV2MaterialBlock(Material& mat); - - // ------------------------------------------------------------------- - //! Parse a *NODE_TM block in a file - //! \param mesh Node (!) object to be filled - void ParseLV2NodeTransformBlock(BaseNode& mesh); - - // ------------------------------------------------------------------- - //! Parse a *TM_ANIMATION block in a file - //! \param mesh Mesh object to be filled - void ParseLV2AnimationBlock(BaseNode& mesh); - void ParseLV3PosAnimationBlock(ASE::Animation& anim); - void ParseLV3ScaleAnimationBlock(ASE::Animation& anim); - void ParseLV3RotAnimationBlock(ASE::Animation& anim); - - // ------------------------------------------------------------------- - //! Parse a *MESH block in a file - //! \param mesh Mesh object to be filled - void ParseLV2MeshBlock(Mesh& mesh); - - // ------------------------------------------------------------------- - //! Parse a *LIGHT_SETTINGS block in a file - //! \param light Light object to be filled - void ParseLV2LightSettingsBlock(Light& light); - - // ------------------------------------------------------------------- - //! Parse a *CAMERA_SETTINGS block in a file - //! \param cam Camera object to be filled - void ParseLV2CameraSettingsBlock(Camera& cam); - - // ------------------------------------------------------------------- - //! Parse the *MAP_XXXXXX blocks in a material - //! \param map Texture structure to be filled - void ParseLV3MapBlock(Texture& map); - - // ------------------------------------------------------------------- - //! Parse a *MESH_VERTEX_LIST block in a file - //! \param iNumVertices Value of *MESH_NUMVERTEX, if present. - //! Otherwise zero. This is used to check the consistency of the file. - //! A warning is sent to the logger if the validations fails. - //! \param mesh Mesh object to be filled - void ParseLV3MeshVertexListBlock( - unsigned int iNumVertices,Mesh& mesh); - - // ------------------------------------------------------------------- - //! Parse a *MESH_FACE_LIST block in a file - //! \param iNumFaces Value of *MESH_NUMFACES, if present. - //! Otherwise zero. This is used to check the consistency of the file. - //! A warning is sent to the logger if the validations fails. - //! \param mesh Mesh object to be filled - void ParseLV3MeshFaceListBlock( - unsigned int iNumFaces,Mesh& mesh); - - // ------------------------------------------------------------------- - //! Parse a *MESH_TVERT_LIST block in a file - //! \param iNumVertices Value of *MESH_NUMTVERTEX, if present. - //! Otherwise zero. This is used to check the consistency of the file. - //! A warning is sent to the logger if the validations fails. - //! \param mesh Mesh object to be filled - //! \param iChannel Output UVW channel - void ParseLV3MeshTListBlock( - unsigned int iNumVertices,Mesh& mesh, unsigned int iChannel = 0); - - // ------------------------------------------------------------------- - //! Parse a *MESH_TFACELIST block in a file - //! \param iNumFaces Value of *MESH_NUMTVFACES, if present. - //! Otherwise zero. This is used to check the consistency of the file. - //! A warning is sent to the logger if the validations fails. - //! \param mesh Mesh object to be filled - //! \param iChannel Output UVW channel - void ParseLV3MeshTFaceListBlock( - unsigned int iNumFaces,Mesh& mesh, unsigned int iChannel = 0); - - // ------------------------------------------------------------------- - //! Parse an additional mapping channel - //! (specified via *MESH_MAPPINGCHANNEL) - //! \param iChannel Channel index to be filled - //! \param mesh Mesh object to be filled - void ParseLV3MappingChannel( - unsigned int iChannel, Mesh& mesh); - - // ------------------------------------------------------------------- - //! Parse a *MESH_CVERTLIST block in a file - //! \param iNumVertices Value of *MESH_NUMCVERTEX, if present. - //! Otherwise zero. This is used to check the consistency of the file. - //! A warning is sent to the logger if the validations fails. - //! \param mesh Mesh object to be filled - void ParseLV3MeshCListBlock( - unsigned int iNumVertices, Mesh& mesh); - - // ------------------------------------------------------------------- - //! Parse a *MESH_CFACELIST block in a file - //! \param iNumFaces Value of *MESH_NUMCVFACES, if present. - //! Otherwise zero. This is used to check the consistency of the file. - //! A warning is sent to the logger if the validations fails. - //! \param mesh Mesh object to be filled - void ParseLV3MeshCFaceListBlock( - unsigned int iNumFaces, Mesh& mesh); - - // ------------------------------------------------------------------- - //! Parse a *MESH_NORMALS block in a file - //! \param mesh Mesh object to be filled - void ParseLV3MeshNormalListBlock(Mesh& mesh); - - // ------------------------------------------------------------------- - //! Parse a *MESH_WEIGHTSblock in a file - //! \param mesh Mesh object to be filled - void ParseLV3MeshWeightsBlock(Mesh& mesh); - - // ------------------------------------------------------------------- - //! Parse the bone list of a file - //! \param mesh Mesh object to be filled - //! \param iNumBones Number of bones in the mesh - void ParseLV4MeshBones(unsigned int iNumBones,Mesh& mesh); - - // ------------------------------------------------------------------- - //! Parse the bone vertices list of a file - //! \param mesh Mesh object to be filled - //! \param iNumVertices Number of vertices to be parsed - void ParseLV4MeshBonesVertices(unsigned int iNumVertices,Mesh& mesh); - - // ------------------------------------------------------------------- - //! Parse a *MESH_FACE block in a file - //! \param out receive the face data - void ParseLV4MeshFace(ASE::Face& out); - - // ------------------------------------------------------------------- - //! Parse a *MESH_VERT block in a file - //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...) - //! \param apOut Output buffer (3 floats) - //! \param rIndexOut Output index - void ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut); - - // ------------------------------------------------------------------- - //! Parse a *MESH_VERT block in a file - //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...) - //! \param apOut Output buffer (3 floats) - void ParseLV4MeshFloatTriple(float* apOut); - - // ------------------------------------------------------------------- - //! Parse a *MESH_TFACE block in a file - //! (also works for MESH_CFACE) - //! \param apOut Output buffer (3 ints) - //! \param rIndexOut Output index - void ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut); - - // ------------------------------------------------------------------- - //! Parse a *MESH_TFACE block in a file - //! (also works for MESH_CFACE) - //! \param apOut Output buffer (3 ints) - void ParseLV4MeshLongTriple(unsigned int* apOut); - - // ------------------------------------------------------------------- - //! Parse a single float element - //! \param fOut Output float - void ParseLV4MeshFloat(float& fOut); - - // ------------------------------------------------------------------- - //! Parse a single int element - //! \param iOut Output integer - void ParseLV4MeshLong(unsigned int& iOut); - - // ------------------------------------------------------------------- - //! Skip everything to the next: '*' or '\0' - bool SkipToNextToken(); - - // ------------------------------------------------------------------- - //! Skip the current section until the token after the closing }. - //! This function handles embedded subsections correctly - bool SkipSection(); - - // ------------------------------------------------------------------- - //! Output a warning to the logger - //! \param szWarn Warn message - void LogWarning(const char* szWarn); - - // ------------------------------------------------------------------- - //! Output a message to the logger - //! \param szWarn Message - void LogInfo(const char* szWarn); - - // ------------------------------------------------------------------- - //! Output an error to the logger - //! \param szWarn Error message - AI_WONT_RETURN void LogError(const char* szWarn) AI_WONT_RETURN_SUFFIX; - - // ------------------------------------------------------------------- - //! Parse a string, enclosed in double quotation marks - //! \param out Output string - //! \param szName Name of the enclosing element -> used in error - //! messages. - //! \return false if an error occured - bool ParseString(std::string& out,const char* szName); - -public: - - //! Pointer to current data - const char* filePtr; - - //! background color to be passed to the viewer - //! QNAN if none was found - aiColor3D m_clrBackground; - - //! Base ambient color to be passed to all materials - //! QNAN if none was found - aiColor3D m_clrAmbient; - - //! List of all materials found in the file - std::vector m_vMaterials; - - //! List of all meshes found in the file - std::vector m_vMeshes; - - //! List of all dummies found in the file - std::vector m_vDummies; - - //! List of all lights found in the file - std::vector m_vLights; - - //! List of all cameras found in the file - std::vector m_vCameras; - - //! Current line in the file - unsigned int iLineNumber; - - //! First frame - unsigned int iFirstFrame; - - //! Last frame - unsigned int iLastFrame; - - //! Frame speed - frames per second - unsigned int iFrameSpeed; - - //! Ticks per frame - unsigned int iTicksPerFrame; - - //! true if the last character read was an end-line character - bool bLastWasEndLine; - - //! File format version - unsigned int iFileFormat; -}; - - -} // Namespace ASE -} // Namespace ASSIMP - -#endif // !! include guard +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + + +/** @file Defines the helper data structures for importing ASE files */ +#ifndef AI_ASEFILEHELPER_H_INC +#define AI_ASEFILEHELPER_H_INC + +// STL/CRT headers +#include +#include +#include + +// public ASSIMP headers +#include "../include/assimp/types.h" +#include "../include/assimp/mesh.h" +#include "../include/assimp/anim.h" + +// for some helper routines like IsSpace() +#include "ParsingUtils.h" +#include "qnan.h" + +// ASE is quite similar to 3ds. We can reuse some structures +#include "3DSLoader.h" + +namespace Assimp { +namespace ASE { + +using namespace D3DS; + +// --------------------------------------------------------------------------- +/** Helper structure representing an ASE material */ +struct Material : public D3DS::Material +{ + //! Default constructor + Material() : pcInstance(NULL), bNeed (false) + {} + + //! Contains all sub materials of this material + std::vector avSubMaterials; + + //! aiMaterial object + aiMaterial* pcInstance; + + //! Can we remove this material? + bool bNeed; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file face */ +struct Face : public FaceWithSmoothingGroup +{ + //! Default constructor. Initializes everything with 0 + Face() + { + mColorIndices[0] = mColorIndices[1] = mColorIndices[2] = 0; + for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) + { + amUVIndices[i][0] = amUVIndices[i][1] = amUVIndices[i][2] = 0; + } + + iMaterial = DEFAULT_MATINDEX; + iFace = 0; + } + + //! special value to indicate that no material index has + //! been assigned to a face. The default material index + //! will replace this value later. + static const unsigned int DEFAULT_MATINDEX = 0xFFFFFFFF; + + + + //! Indices into each list of texture coordinates + unsigned int amUVIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS][3]; + + //! Index into the list of vertex colors + unsigned int mColorIndices[3]; + + //! (Sub)Material index to be assigned to this face + unsigned int iMaterial; + + //! Index of the face. It is not specified whether it is + //! a requirement of the file format that all faces are + //! written in sequential order, so we have to expect this case + unsigned int iFace; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file bone */ +struct Bone +{ + //! Constructor + Bone() + { + static int iCnt = 0; + + // Generate a default name for the bone + char szTemp[128]; + ::sprintf(szTemp,"UNNAMED_%i",iCnt++); + mName = szTemp; + } + + //! Construction from an existing name + Bone( const std::string& name) + : mName (name) + {} + + //! Name of the bone + std::string mName; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file bone vertex */ +struct BoneVertex +{ + //! Bone and corresponding vertex weight. + //! -1 for unrequired bones .... + std::vector > mBoneWeights; + + //! Position of the bone vertex. + //! MUST be identical to the vertex position + //aiVector3D mPosition; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file animation */ +struct Animation +{ + enum Type + { + TRACK = 0x0, + BEZIER = 0x1, + TCB = 0x2 + } mRotationType, mScalingType, mPositionType; + + Animation() + : mRotationType (TRACK) + , mScalingType (TRACK) + , mPositionType (TRACK) + {} + + //! List of track rotation keyframes + std::vector< aiQuatKey > akeyRotations; + + //! List of track position keyframes + std::vector< aiVectorKey > akeyPositions; + + //! List of track scaling keyframes + std::vector< aiVectorKey > akeyScaling; + +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent the inheritance information of an ASE node */ +struct InheritanceInfo +{ + //! Default constructor + InheritanceInfo() + { + // set the inheritance flag for all axes by default to true + for (unsigned int i = 0; i < 3;++i) + abInheritPosition[i] = abInheritRotation[i] = abInheritScaling[i] = true; + } + + //! Inherit the parent's position?, axis order is x,y,z + bool abInheritPosition[3]; + + //! Inherit the parent's rotation?, axis order is x,y,z + bool abInheritRotation[3]; + + //! Inherit the parent's scaling?, axis order is x,y,z + bool abInheritScaling[3]; +}; + +// --------------------------------------------------------------------------- +/** Represents an ASE file node. Base class for mesh, light and cameras */ +struct BaseNode +{ + enum Type {Light, Camera, Mesh, Dummy} mType; + + //! Constructor. Creates a default name for the node + BaseNode(Type _mType) + : mType (_mType) + , mProcessed (false) + { + // generate a default name for the node + static int iCnt = 0; + char szTemp[128]; // should be sufficiently large + ::sprintf(szTemp,"UNNAMED_%i",iCnt++); + mName = szTemp; + + // Set mTargetPosition to qnan + const float qnan = get_qnan(); + mTargetPosition.x = qnan; + } + + //! Name of the mesh + std::string mName; + + //! Name of the parent of the node + //! "" if there is no parent ... + std::string mParent; + + //! Transformation matrix of the node + aiMatrix4x4 mTransform; + + //! Target position (target lights and cameras) + aiVector3D mTargetPosition; + + //! Specifies which axes transformations a node inherits + //! from its parent ... + InheritanceInfo inherit; + + //! Animation channels for the node + Animation mAnim; + + //! Needed for lights and cameras: target animation channel + //! Should contain position keys only. + Animation mTargetAnim; + + bool mProcessed; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE file mesh */ +struct Mesh : public MeshWithSmoothingGroups, public BaseNode +{ + //! Constructor. + Mesh() + : BaseNode (BaseNode::Mesh) + , bSkip (false) + { + // use 2 texture vertex components by default + for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS;++c) + this->mNumUVComponents[c] = 2; + + // setup the default material index by default + iMaterialIndex = Face::DEFAULT_MATINDEX; + } + + //! List of all texture coordinate sets + std::vector amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + //! List of all vertex color sets. + std::vector mVertexColors; + + //! List of all bone vertices + std::vector mBoneVertices; + + //! List of all bones + std::vector mBones; + + //! Material index of the mesh + unsigned int iMaterialIndex; + + //! Number of vertex components for each UVW set + unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + + //! used internally + bool bSkip; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE light source */ +struct Light : public BaseNode +{ + enum LightType + { + OMNI, + TARGET, + FREE, + DIRECTIONAL + }; + + //! Constructor. + Light() + : BaseNode (BaseNode::Light) + , mLightType (OMNI) + , mColor (1.f,1.f,1.f) + , mIntensity (1.f) // light is white by default + , mAngle (45.f) + , mFalloff (0.f) + { + } + + LightType mLightType; + aiColor3D mColor; + float mIntensity; + float mAngle; // in degrees + float mFalloff; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE camera */ +struct Camera : public BaseNode +{ + enum CameraType + { + FREE, + TARGET + }; + + //! Constructor + Camera() + : BaseNode (BaseNode::Camera) + , mFOV (0.75f) // in radians + , mNear (0.1f) + , mFar (1000.f) // could be zero + , mCameraType (FREE) + { + } + + float mFOV, mNear, mFar; + CameraType mCameraType; +}; + +// --------------------------------------------------------------------------- +/** Helper structure to represent an ASE helper object (dummy) */ +struct Dummy : public BaseNode +{ + //! Constructor + Dummy() + : BaseNode (BaseNode::Dummy) + { + } +}; + +// Parameters to Parser::Parse() +#define AI_ASE_NEW_FILE_FORMAT 200 +#define AI_ASE_OLD_FILE_FORMAT 110 + +// Internally we're a little bit more tolerant +#define AI_ASE_IS_NEW_FILE_FORMAT() (iFileFormat >= 200) +#define AI_ASE_IS_OLD_FILE_FORMAT() (iFileFormat < 200) + +// ------------------------------------------------------------------------------- +/** \brief Class to parse ASE files + */ +class Parser +{ + +private: + + Parser() {} + +public: + + // ------------------------------------------------------------------- + //! Construct a parser from a given input file which is + //! guaranted to be terminated with zero. + //! @param szFile Input file + //! @param fileFormatDefault Assumed file format version. If the + //! file format is specified in the file the new value replaces + //! the default value. + Parser (const char* szFile, unsigned int fileFormatDefault); + + // ------------------------------------------------------------------- + //! Parses the file into the parsers internal representation + void Parse(); + + +private: + + // ------------------------------------------------------------------- + //! Parse the *SCENE block in a file + void ParseLV1SceneBlock(); + + // ------------------------------------------------------------------- + //! Parse the *MESH_SOFTSKINVERTS block in a file + void ParseLV1SoftSkinBlock(); + + // ------------------------------------------------------------------- + //! Parse the *MATERIAL_LIST block in a file + void ParseLV1MaterialListBlock(); + + // ------------------------------------------------------------------- + //! Parse a *OBJECT block in a file + //! \param mesh Node to be filled + void ParseLV1ObjectBlock(BaseNode& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MATERIAL blocks in a material list + //! \param mat Material structure to be filled + void ParseLV2MaterialBlock(Material& mat); + + // ------------------------------------------------------------------- + //! Parse a *NODE_TM block in a file + //! \param mesh Node (!) object to be filled + void ParseLV2NodeTransformBlock(BaseNode& mesh); + + // ------------------------------------------------------------------- + //! Parse a *TM_ANIMATION block in a file + //! \param mesh Mesh object to be filled + void ParseLV2AnimationBlock(BaseNode& mesh); + void ParseLV3PosAnimationBlock(ASE::Animation& anim); + void ParseLV3ScaleAnimationBlock(ASE::Animation& anim); + void ParseLV3RotAnimationBlock(ASE::Animation& anim); + + // ------------------------------------------------------------------- + //! Parse a *MESH block in a file + //! \param mesh Mesh object to be filled + void ParseLV2MeshBlock(Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *LIGHT_SETTINGS block in a file + //! \param light Light object to be filled + void ParseLV2LightSettingsBlock(Light& light); + + // ------------------------------------------------------------------- + //! Parse a *CAMERA_SETTINGS block in a file + //! \param cam Camera object to be filled + void ParseLV2CameraSettingsBlock(Camera& cam); + + // ------------------------------------------------------------------- + //! Parse the *MAP_XXXXXX blocks in a material + //! \param map Texture structure to be filled + void ParseLV3MapBlock(Texture& map); + + // ------------------------------------------------------------------- + //! Parse a *MESH_VERTEX_LIST block in a file + //! \param iNumVertices Value of *MESH_NUMVERTEX, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + void ParseLV3MeshVertexListBlock( + unsigned int iNumVertices,Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_FACE_LIST block in a file + //! \param iNumFaces Value of *MESH_NUMFACES, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + void ParseLV3MeshFaceListBlock( + unsigned int iNumFaces,Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_TVERT_LIST block in a file + //! \param iNumVertices Value of *MESH_NUMTVERTEX, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + //! \param iChannel Output UVW channel + void ParseLV3MeshTListBlock( + unsigned int iNumVertices,Mesh& mesh, unsigned int iChannel = 0); + + // ------------------------------------------------------------------- + //! Parse a *MESH_TFACELIST block in a file + //! \param iNumFaces Value of *MESH_NUMTVFACES, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + //! \param iChannel Output UVW channel + void ParseLV3MeshTFaceListBlock( + unsigned int iNumFaces,Mesh& mesh, unsigned int iChannel = 0); + + // ------------------------------------------------------------------- + //! Parse an additional mapping channel + //! (specified via *MESH_MAPPINGCHANNEL) + //! \param iChannel Channel index to be filled + //! \param mesh Mesh object to be filled + void ParseLV3MappingChannel( + unsigned int iChannel, Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_CVERTLIST block in a file + //! \param iNumVertices Value of *MESH_NUMCVERTEX, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + void ParseLV3MeshCListBlock( + unsigned int iNumVertices, Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_CFACELIST block in a file + //! \param iNumFaces Value of *MESH_NUMCVFACES, if present. + //! Otherwise zero. This is used to check the consistency of the file. + //! A warning is sent to the logger if the validations fails. + //! \param mesh Mesh object to be filled + void ParseLV3MeshCFaceListBlock( + unsigned int iNumFaces, Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_NORMALS block in a file + //! \param mesh Mesh object to be filled + void ParseLV3MeshNormalListBlock(Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_WEIGHTSblock in a file + //! \param mesh Mesh object to be filled + void ParseLV3MeshWeightsBlock(Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse the bone list of a file + //! \param mesh Mesh object to be filled + //! \param iNumBones Number of bones in the mesh + void ParseLV4MeshBones(unsigned int iNumBones,Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse the bone vertices list of a file + //! \param mesh Mesh object to be filled + //! \param iNumVertices Number of vertices to be parsed + void ParseLV4MeshBonesVertices(unsigned int iNumVertices,Mesh& mesh); + + // ------------------------------------------------------------------- + //! Parse a *MESH_FACE block in a file + //! \param out receive the face data + void ParseLV4MeshFace(ASE::Face& out); + + // ------------------------------------------------------------------- + //! Parse a *MESH_VERT block in a file + //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...) + //! \param apOut Output buffer (3 floats) + //! \param rIndexOut Output index + void ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut); + + // ------------------------------------------------------------------- + //! Parse a *MESH_VERT block in a file + //! (also works for MESH_TVERT, MESH_CFACE, MESH_VERTCOL ...) + //! \param apOut Output buffer (3 floats) + void ParseLV4MeshFloatTriple(float* apOut); + + // ------------------------------------------------------------------- + //! Parse a *MESH_TFACE block in a file + //! (also works for MESH_CFACE) + //! \param apOut Output buffer (3 ints) + //! \param rIndexOut Output index + void ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut); + + // ------------------------------------------------------------------- + //! Parse a *MESH_TFACE block in a file + //! (also works for MESH_CFACE) + //! \param apOut Output buffer (3 ints) + void ParseLV4MeshLongTriple(unsigned int* apOut); + + // ------------------------------------------------------------------- + //! Parse a single float element + //! \param fOut Output float + void ParseLV4MeshFloat(float& fOut); + + // ------------------------------------------------------------------- + //! Parse a single int element + //! \param iOut Output integer + void ParseLV4MeshLong(unsigned int& iOut); + + // ------------------------------------------------------------------- + //! Skip everything to the next: '*' or '\0' + bool SkipToNextToken(); + + // ------------------------------------------------------------------- + //! Skip the current section until the token after the closing }. + //! This function handles embedded subsections correctly + bool SkipSection(); + + // ------------------------------------------------------------------- + //! Output a warning to the logger + //! \param szWarn Warn message + void LogWarning(const char* szWarn); + + // ------------------------------------------------------------------- + //! Output a message to the logger + //! \param szWarn Message + void LogInfo(const char* szWarn); + + // ------------------------------------------------------------------- + //! Output an error to the logger + //! \param szWarn Error message + AI_WONT_RETURN void LogError(const char* szWarn) AI_WONT_RETURN_SUFFIX; + + // ------------------------------------------------------------------- + //! Parse a string, enclosed in double quotation marks + //! \param out Output string + //! \param szName Name of the enclosing element -> used in error + //! messages. + //! \return false if an error occured + bool ParseString(std::string& out,const char* szName); + +public: + + //! Pointer to current data + const char* filePtr; + + //! background color to be passed to the viewer + //! QNAN if none was found + aiColor3D m_clrBackground; + + //! Base ambient color to be passed to all materials + //! QNAN if none was found + aiColor3D m_clrAmbient; + + //! List of all materials found in the file + std::vector m_vMaterials; + + //! List of all meshes found in the file + std::vector m_vMeshes; + + //! List of all dummies found in the file + std::vector m_vDummies; + + //! List of all lights found in the file + std::vector m_vLights; + + //! List of all cameras found in the file + std::vector m_vCameras; + + //! Current line in the file + unsigned int iLineNumber; + + //! First frame + unsigned int iFirstFrame; + + //! Last frame + unsigned int iLastFrame; + + //! Frame speed - frames per second + unsigned int iFrameSpeed; + + //! Ticks per frame + unsigned int iTicksPerFrame; + + //! true if the last character read was an end-line character + bool bLastWasEndLine; + + //! File format version + unsigned int iFileFormat; +}; + + +} // Namespace ASE +} // Namespace ASSIMP + +#endif // !! include guard diff --git a/code/AssbinExporter.cpp b/code/AssbinExporter.cpp index 2792e0f2e..652405c84 100644 --- a/code/AssbinExporter.cpp +++ b/code/AssbinExporter.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2015, 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 +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -23,16 +23,16 @@ following conditions are met: 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 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- @@ -50,9 +50,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #ifdef ASSIMP_BUILD_NO_OWN_ZLIB -# include +# include #else -# include "../contrib/zlib/zlib.h" +# include "../contrib/zlib/zlib.h" #endif #include @@ -63,12 +63,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; -namespace Assimp { +namespace Assimp { -template +template size_t Write(IOStream * stream, const T& v) { - return stream->Write( &v, sizeof(T), 1 ); + return stream->Write( &v, sizeof(T), 1 ); } @@ -77,10 +77,10 @@ size_t Write(IOStream * stream, const T& v) template <> inline size_t Write(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; + const size_t s2 = (uint32_t)s.length; + stream->Write(&s,4,1); + stream->Write(s.data,s2,1); + return s2+4; } // ----------------------------------------------------------------------------------- @@ -88,14 +88,14 @@ inline size_t Write(IOStream * stream, const aiString& s) template <> inline size_t Write(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"); - } + 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; + stream->Write(&t,4,1); + return 4; } // ----------------------------------------------------------------------------------- @@ -103,9 +103,9 @@ inline size_t Write(IOStream * stream, const unsigned int& w) template <> inline size_t Write(IOStream * stream, const uint16_t& w) { - BOOST_STATIC_ASSERT(sizeof(uint16_t)==2); - stream->Write(&w,2,1); - return 2; + BOOST_STATIC_ASSERT(sizeof(uint16_t)==2); + stream->Write(&w,2,1); + return 2; } // ----------------------------------------------------------------------------------- @@ -113,9 +113,9 @@ inline size_t Write(IOStream * stream, const uint16_t& w) template <> inline size_t Write(IOStream * stream, const float& f) { - BOOST_STATIC_ASSERT(sizeof(float)==4); - stream->Write(&f,4,1); - return 4; + BOOST_STATIC_ASSERT(sizeof(float)==4); + stream->Write(&f,4,1); + return 4; } // ----------------------------------------------------------------------------------- @@ -123,9 +123,9 @@ inline size_t Write(IOStream * stream, const float& f) template <> inline size_t Write(IOStream * stream, const double& f) { - BOOST_STATIC_ASSERT(sizeof(double)==8); - stream->Write(&f,8,1); - return 8; + BOOST_STATIC_ASSERT(sizeof(double)==8); + stream->Write(&f,8,1); + return 8; } // ----------------------------------------------------------------------------------- @@ -133,10 +133,10 @@ inline size_t Write(IOStream * stream, const double& f) template <> inline size_t Write(IOStream * stream, const aiVector3D& v) { - size_t t = Write(stream,v.x); - t += Write(stream,v.y); - t += Write(stream,v.z); - return t; + size_t t = Write(stream,v.x); + t += Write(stream,v.y); + t += Write(stream,v.z); + return t; } // ----------------------------------------------------------------------------------- @@ -144,11 +144,11 @@ inline size_t Write(IOStream * stream, const aiVector3D& v) template <> inline size_t Write(IOStream * stream, const aiColor4D& v) { - size_t t = Write(stream,v.r); - t += Write(stream,v.g); - t += Write(stream,v.b); - t += Write(stream,v.a); - return t; + size_t t = Write(stream,v.r); + t += Write(stream,v.g); + t += Write(stream,v.b); + t += Write(stream,v.a); + return t; } // ----------------------------------------------------------------------------------- @@ -156,11 +156,11 @@ inline size_t Write(IOStream * stream, const aiColor4D& v) template <> inline size_t Write(IOStream * stream, const aiQuaternion& v) { - size_t t = Write(stream,v.w); - t += Write(stream,v.x); - t += Write(stream,v.y); - t += Write(stream,v.z); - return 16; + size_t t = Write(stream,v.w); + t += Write(stream,v.x); + t += Write(stream,v.y); + t += Write(stream,v.z); + return 16; } @@ -169,8 +169,8 @@ inline size_t Write(IOStream * stream, const aiQuaternion& v) template <> inline size_t Write(IOStream * stream, const aiVertexWeight& v) { - size_t t = Write(stream,v.mVertexId); - return t+Write(stream,v.mWeight); + size_t t = Write(stream,v.mVertexId); + return t+Write(stream,v.mWeight); } // ----------------------------------------------------------------------------------- @@ -178,12 +178,12 @@ inline size_t Write(IOStream * stream, const aiVertexWeight& v) template <> inline size_t Write(IOStream * stream, const aiMatrix4x4& m) { - for (unsigned int i = 0; i < 4;++i) { - for (unsigned int i2 = 0; i2 < 4;++i2) { - Write(stream,m[i][i2]); - } - } - return 64; + for (unsigned int i = 0; i < 4;++i) { + for (unsigned int i2 = 0; i2 < 4;++i2) { + Write(stream,m[i][i2]); + } + } + return 64; } // ----------------------------------------------------------------------------------- @@ -191,8 +191,8 @@ inline size_t Write(IOStream * stream, const aiMatrix4x4& m) template <> inline size_t Write(IOStream * stream, const aiVectorKey& v) { - const size_t t = Write(stream,v.mTime); - return t + Write(stream,v.mValue); + const size_t t = Write(stream,v.mTime); + return t + Write(stream,v.mValue); } // ----------------------------------------------------------------------------------- @@ -200,18 +200,18 @@ inline size_t Write(IOStream * stream, const aiVectorKey& v) template <> inline size_t Write(IOStream * stream, const aiQuatKey& v) { - const size_t t = Write(stream,v.mTime); - return t + Write(stream,v.mValue); + const size_t t = Write(stream,v.mTime); + return t + Write(stream,v.mValue); } template inline size_t WriteBounds(IOStream * stream, const T* in, unsigned int size) { - T minc,maxc; - ArrayBounds(in,size,minc,maxc); + T minc,maxc; + ArrayBounds(in,size,minc,maxc); - const size_t t = Write(stream,minc); - return t + Write(stream,maxc); + const size_t t = Write(stream,minc); + return t + Write(stream,maxc); } // We use this to write out non-byte arrays so that we write using the specializations. @@ -219,549 +219,549 @@ inline size_t WriteBounds(IOStream * stream, const T* in, unsigned int size) template inline size_t WriteArray(IOStream * stream, const T* in, unsigned int size) { - size_t n = 0; - for (unsigned int i=0; i(stream,in[i]); - return n; + size_t n = 0; + for (unsigned int i=0; i(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(&chunk,node->mName); - Write(&chunk,node->mTransformation); - Write(&chunk,node->mNumChildren); - Write(&chunk,node->mNumMeshes); - - for (unsigned int i = 0; i < node->mNumMeshes;++i) { - Write(&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(&chunk,tex->mWidth); - Write(&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(&chunk,b->mName); - Write(&chunk,b->mNumWeights); - Write(&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(&chunk,b->mWeights,b->mNumWeights); - } - - // ----------------------------------------------------------------------------------- - void WriteBinaryMesh(IOStream * container, const aiMesh* mesh) - { - AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIMESH ); - - Write(&chunk,mesh->mPrimitiveTypes); - Write(&chunk,mesh->mNumVertices); - Write(&chunk,mesh->mNumFaces); - Write(&chunk,mesh->mNumBones); - Write(&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(&chunk,c); - - aiVector3D minVec, maxVec; - if (mesh->mVertices) { - if (shortened) { - WriteBounds(&chunk,mesh->mVertices,mesh->mNumVertices); - } // else write as usual - else WriteArray(&chunk,mesh->mVertices,mesh->mNumVertices); - } - if (mesh->mNormals) { - if (shortened) { - WriteBounds(&chunk,mesh->mNormals,mesh->mNumVertices); - } // else write as usual - else WriteArray(&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(&chunk,mesh->mTangents,mesh->mNumVertices); - WriteArray(&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(&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(&chunk,mesh->mNumUVComponents[n]); - - if (shortened) { - WriteBounds(&chunk,mesh->mTextureCoords[n],mesh->mNumVertices); - } // else write as usual - else WriteArray(&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(&tmp),sizeof tmp,hash); - for (unsigned int i = 0; i < f.mNumIndices; ++i) { - BOOST_STATIC_ASSERT(AI_MAX_VERTICES <= 0xffffffff); - tmp = static_cast( f.mIndices[i] ); - hash = SuperFastHash(reinterpret_cast(&tmp),sizeof tmp,hash); - } - } - Write(&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(&chunk,f.mNumIndices); - - for (unsigned int a = 0; a < f.mNumIndices;++a) { - if (mesh->mNumVertices < (1u<<16)) { - Write(&chunk,f.mIndices[a]); - } - else Write(&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(&chunk,prop->mKey); - Write(&chunk,prop->mSemantic); - Write(&chunk,prop->mIndex); - - Write(&chunk,prop->mDataLength); - Write(&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(&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(&chunk,nd->mNodeName); - Write(&chunk,nd->mNumPositionKeys); - Write(&chunk,nd->mNumRotationKeys); - Write(&chunk,nd->mNumScalingKeys); - Write(&chunk,nd->mPreState); - Write(&chunk,nd->mPostState); - - if (nd->mPositionKeys) { - if (shortened) { - WriteBounds(&chunk,nd->mPositionKeys,nd->mNumPositionKeys); - - } // else write as usual - else WriteArray(&chunk,nd->mPositionKeys,nd->mNumPositionKeys); - } - if (nd->mRotationKeys) { - if (shortened) { - WriteBounds(&chunk,nd->mRotationKeys,nd->mNumRotationKeys); - - } // else write as usual - else WriteArray(&chunk,nd->mRotationKeys,nd->mNumRotationKeys); - } - if (nd->mScalingKeys) { - if (shortened) { - WriteBounds(&chunk,nd->mScalingKeys,nd->mNumScalingKeys); - - } // else write as usual - else WriteArray(&chunk,nd->mScalingKeys,nd->mNumScalingKeys); - } - } - - - // ----------------------------------------------------------------------------------- - void WriteBinaryAnim( IOStream * container, const aiAnimation* anim ) - { - AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIANIMATION ); - - Write(&chunk,anim->mName); - Write(&chunk,anim->mDuration); - Write(&chunk,anim->mTicksPerSecond); - Write(&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(&chunk,l->mName); - Write(&chunk,l->mType); - - if (l->mType != aiLightSource_DIRECTIONAL) { - Write(&chunk,l->mAttenuationConstant); - Write(&chunk,l->mAttenuationLinear); - Write(&chunk,l->mAttenuationQuadratic); - } - - Write(&chunk,(const aiVector3D&)l->mColorDiffuse); - Write(&chunk,(const aiVector3D&)l->mColorSpecular); - Write(&chunk,(const aiVector3D&)l->mColorAmbient); - - if (l->mType == aiLightSource_SPOT) { - Write(&chunk,l->mAngleInnerCone); - Write(&chunk,l->mAngleOuterCone); - } - - } - - // ----------------------------------------------------------------------------------- - void WriteBinaryCamera( IOStream * container, const aiCamera* cam ) - { - AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AICAMERA ); - - Write(&chunk,cam->mName); - Write(&chunk,cam->mPosition); - Write(&chunk,cam->mLookAt); - Write(&chunk,cam->mUp); - Write(&chunk,cam->mHorizontalFOV); - Write(&chunk,cam->mClipPlaneNear); - Write(&chunk,cam->mClipPlaneFar); - Write(&chunk,cam->mAspect); - } - - // ----------------------------------------------------------------------------------- - void WriteBinaryScene( IOStream * container, const aiScene* scene) - { - AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AISCENE ); - - // basic scene information - Write(&chunk,scene->mFlags); - Write(&chunk,scene->mNumMeshes); - Write(&chunk,scene->mNumMaterials); - Write(&chunk,scene->mNumAnimations); - Write(&chunk,scene->mNumTextures); - Write(&chunk,scene->mNumLights); - Write(&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 ); + // ---------------------------------------------------------------------------------- + /** @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(&chunk,node->mName); + Write(&chunk,node->mTransformation); + Write(&chunk,node->mNumChildren); + Write(&chunk,node->mNumMeshes); + + for (unsigned int i = 0; i < node->mNumMeshes;++i) { + Write(&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(&chunk,tex->mWidth); + Write(&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(&chunk,b->mName); + Write(&chunk,b->mNumWeights); + Write(&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(&chunk,b->mWeights,b->mNumWeights); + } + + // ----------------------------------------------------------------------------------- + void WriteBinaryMesh(IOStream * container, const aiMesh* mesh) + { + AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIMESH ); + + Write(&chunk,mesh->mPrimitiveTypes); + Write(&chunk,mesh->mNumVertices); + Write(&chunk,mesh->mNumFaces); + Write(&chunk,mesh->mNumBones); + Write(&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(&chunk,c); + + aiVector3D minVec, maxVec; + if (mesh->mVertices) { + if (shortened) { + WriteBounds(&chunk,mesh->mVertices,mesh->mNumVertices); + } // else write as usual + else WriteArray(&chunk,mesh->mVertices,mesh->mNumVertices); + } + if (mesh->mNormals) { + if (shortened) { + WriteBounds(&chunk,mesh->mNormals,mesh->mNumVertices); + } // else write as usual + else WriteArray(&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(&chunk,mesh->mTangents,mesh->mNumVertices); + WriteArray(&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(&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(&chunk,mesh->mNumUVComponents[n]); + + if (shortened) { + WriteBounds(&chunk,mesh->mTextureCoords[n],mesh->mNumVertices); + } // else write as usual + else WriteArray(&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(&tmp),sizeof tmp,hash); + for (unsigned int i = 0; i < f.mNumIndices; ++i) { + BOOST_STATIC_ASSERT(AI_MAX_VERTICES <= 0xffffffff); + tmp = static_cast( f.mIndices[i] ); + hash = SuperFastHash(reinterpret_cast(&tmp),sizeof tmp,hash); + } + } + Write(&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(&chunk,f.mNumIndices); + + for (unsigned int a = 0; a < f.mNumIndices;++a) { + if (mesh->mNumVertices < (1u<<16)) { + Write(&chunk,f.mIndices[a]); + } + else Write(&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(&chunk,prop->mKey); + Write(&chunk,prop->mSemantic); + Write(&chunk,prop->mIndex); + + Write(&chunk,prop->mDataLength); + Write(&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(&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(&chunk,nd->mNodeName); + Write(&chunk,nd->mNumPositionKeys); + Write(&chunk,nd->mNumRotationKeys); + Write(&chunk,nd->mNumScalingKeys); + Write(&chunk,nd->mPreState); + Write(&chunk,nd->mPostState); + + if (nd->mPositionKeys) { + if (shortened) { + WriteBounds(&chunk,nd->mPositionKeys,nd->mNumPositionKeys); + + } // else write as usual + else WriteArray(&chunk,nd->mPositionKeys,nd->mNumPositionKeys); + } + if (nd->mRotationKeys) { + if (shortened) { + WriteBounds(&chunk,nd->mRotationKeys,nd->mNumRotationKeys); + + } // else write as usual + else WriteArray(&chunk,nd->mRotationKeys,nd->mNumRotationKeys); + } + if (nd->mScalingKeys) { + if (shortened) { + WriteBounds(&chunk,nd->mScalingKeys,nd->mNumScalingKeys); + + } // else write as usual + else WriteArray(&chunk,nd->mScalingKeys,nd->mNumScalingKeys); + } + } + + + // ----------------------------------------------------------------------------------- + void WriteBinaryAnim( IOStream * container, const aiAnimation* anim ) + { + AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AIANIMATION ); + + Write(&chunk,anim->mName); + Write(&chunk,anim->mDuration); + Write(&chunk,anim->mTicksPerSecond); + Write(&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(&chunk,l->mName); + Write(&chunk,l->mType); + + if (l->mType != aiLightSource_DIRECTIONAL) { + Write(&chunk,l->mAttenuationConstant); + Write(&chunk,l->mAttenuationLinear); + Write(&chunk,l->mAttenuationQuadratic); + } + + Write(&chunk,(const aiVector3D&)l->mColorDiffuse); + Write(&chunk,(const aiVector3D&)l->mColorSpecular); + Write(&chunk,(const aiVector3D&)l->mColorAmbient); + + if (l->mType == aiLightSource_SPOT) { + Write(&chunk,l->mAngleInnerCone); + Write(&chunk,l->mAngleOuterCone); + } + + } + + // ----------------------------------------------------------------------------------- + void WriteBinaryCamera( IOStream * container, const aiCamera* cam ) + { + AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AICAMERA ); + + Write(&chunk,cam->mName); + Write(&chunk,cam->mPosition); + Write(&chunk,cam->mLookAt); + Write(&chunk,cam->mUp); + Write(&chunk,cam->mHorizontalFOV); + Write(&chunk,cam->mClipPlaneNear); + Write(&chunk,cam->mClipPlaneFar); + Write(&chunk,cam->mAspect); + } + + // ----------------------------------------------------------------------------------- + void WriteBinaryScene( IOStream * container, const aiScene* scene) + { + AssbinChunkWriter chunk( container, ASSBIN_CHUNK_AISCENE ); + + // basic scene information + Write(&chunk,scene->mFlags); + Write(&chunk,scene->mNumMeshes); + Write(&chunk,scene->mNumMaterials); + Write(&chunk,scene->mNumAnimations); + Write(&chunk,scene->mNumTextures); + Write(&chunk,scene->mNumLights); + Write(&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)); + sprintf_s(s,"ASSIMP.binary-dump.%s",asctime(p)); #else - snprintf(s,64,"ASSIMP.binary-dump.%s",asctime(p)); + snprintf(s,64,"ASSIMP.binary-dump.%s",asctime(p)); #endif - out->Write( s, 44, 1 ); - // == 44 bytes + out->Write( s, 44, 1 ); + // == 44 bytes - Write( out, ASSBIN_VERSION_MAJOR ); - Write( out, ASSBIN_VERSION_MINOR ); - Write( out, aiGetVersionRevision() ); - Write( out, aiGetCompileFlags() ); - Write( out, shortened ); - Write( out, compressed ); - // == 20 bytes + Write( out, ASSBIN_VERSION_MAJOR ); + Write( out, ASSBIN_VERSION_MINOR ); + Write( out, aiGetVersionRevision() ); + Write( out, aiGetCompileFlags() ); + Write( out, shortened ); + Write( out, compressed ); + // == 20 bytes - char buff[256]; - strncpy(buff,pFile,256); - out->Write(buff,sizeof(char),256); + 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); + 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 + // 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 ); + // ==== 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 ); + // 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 ]; + 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 ); + compress2( compressedBuffer, &compressedSize, (const Bytef*)uncompressedStream.GetBufferPointer(), uncompressedSize, 9 ); - out->Write( &uncompressedSize, sizeof(uint32_t), 1 ); - out->Write( compressedBuffer, sizeof(char), compressedSize ); + out->Write( &uncompressedSize, sizeof(uint32_t), 1 ); + out->Write( compressedBuffer, sizeof(char), compressedSize ); - delete[] compressedBuffer; - } - else - { - WriteBinaryScene( out, pScene ); - } + delete[] compressedBuffer; + } + else + { + WriteBinaryScene( out, pScene ); + } - pIOSystem->Close( out ); - } - }; + pIOSystem->Close( out ); + } + }; void ExportSceneAssbin(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) { - AssbinExport exporter; - exporter.WriteBinaryDump( pFile, pIOSystem, pScene ); + AssbinExport exporter; + exporter.WriteBinaryDump( pFile, pIOSystem, pScene ); } } // end of namespace Assimp diff --git a/code/AssbinExporter.h b/code/AssbinExporter.h index b81072bf4..e16393213 100644 --- a/code/AssbinExporter.h +++ b/code/AssbinExporter.h @@ -5,8 +5,8 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2015, 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 +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -23,16 +23,16 @@ following conditions are met: 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 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- diff --git a/code/AssbinLoader.cpp b/code/AssbinLoader.cpp index 469d065bb..dd949695e 100644 --- a/code/AssbinLoader.cpp +++ b/code/AssbinLoader.cpp @@ -7,8 +7,8 @@ Copyright (c) 2006-2015, 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 +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -25,16 +25,16 @@ conditions are met: 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 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ @@ -57,631 +57,631 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #ifdef ASSIMP_BUILD_NO_OWN_ZLIB -# include +# include #else -# include "../contrib/zlib/zlib.h" +# 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" + ".assbin Importer", + "Gargaj / Conspiracy", + "", + "", + aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour, + 0, + 0, + 0, + 0, + "assbin" }; const aiImporterDesc* AssbinImporter::GetInfo() const { - return &desc; + return &desc; } bool AssbinImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/ ) const { - IOStream * in = pIOHandler->Open(pFile); - if (!in) - return false; + IOStream * in = pIOHandler->Open(pFile); + if (!in) + return false; - char s[32]; - in->Read( s, sizeof(char), 32 ); + char s[32]; + in->Read( s, sizeof(char), 32 ); - pIOHandler->Close(in); + pIOHandler->Close(in); - return strncmp( s, "ASSIMP.binary-dump.", 19 ) == 0; + return strncmp( s, "ASSIMP.binary-dump.", 19 ) == 0; } template T Read(IOStream * stream) { - T t; - stream->Read( &t, sizeof(T), 1 ); - return t; + T t; + stream->Read( &t, sizeof(T), 1 ); + return t; } template <> aiVector3D Read(IOStream * stream) { - aiVector3D v; - v.x = Read(stream); - v.y = Read(stream); - v.z = Read(stream); - return v; + aiVector3D v; + v.x = Read(stream); + v.y = Read(stream); + v.z = Read(stream); + return v; } template <> aiColor4D Read(IOStream * stream) { - aiColor4D c; - c.r = Read(stream); - c.g = Read(stream); - c.b = Read(stream); - c.a = Read(stream); - return c; + aiColor4D c; + c.r = Read(stream); + c.g = Read(stream); + c.b = Read(stream); + c.a = Read(stream); + return c; } template <> aiQuaternion Read(IOStream * stream) { - aiQuaternion v; - v.w = Read(stream); - v.x = Read(stream); - v.y = Read(stream); - v.z = Read(stream); - return v; + aiQuaternion v; + v.w = Read(stream); + v.x = Read(stream); + v.y = Read(stream); + v.z = Read(stream); + return v; } template <> aiString Read(IOStream * stream) { - aiString s; - stream->Read(&s.length,4,1); - stream->Read(s.data,s.length,1); - s.data[s.length] = 0; - return s; + 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(IOStream * stream) { - aiVertexWeight w; - w.mVertexId = Read(stream); - w.mWeight = Read(stream); - return w; + aiVertexWeight w; + w.mVertexId = Read(stream); + w.mWeight = Read(stream); + return w; } template <> aiMatrix4x4 Read(IOStream * stream) { - aiMatrix4x4 m; - for (unsigned int i = 0; i < 4;++i) { - for (unsigned int i2 = 0; i2 < 4;++i2) { - m[i][i2] = Read(stream); - } - } - return m; + aiMatrix4x4 m; + for (unsigned int i = 0; i < 4;++i) { + for (unsigned int i2 = 0; i2 < 4;++i2) { + m[i][i2] = Read(stream); + } + } + return m; } template <> aiVectorKey Read(IOStream * stream) { - aiVectorKey v; - v.mTime = Read(stream); - v.mValue = Read(stream); - return v; + aiVectorKey v; + v.mTime = Read(stream); + v.mValue = Read(stream); + return v; } template <> aiQuatKey Read(IOStream * stream) { - aiQuatKey v; - v.mTime = Read(stream); - v.mValue = Read(stream); - return v; + aiQuatKey v; + v.mTime = Read(stream); + v.mValue = Read(stream); + return v; } template void ReadArray(IOStream * stream, T * out, unsigned int size) -{ - for (unsigned int i=0; i(stream); +{ + for (unsigned int i=0; i(stream); } template 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 ); + // 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 ) { - uint32_t chunkID = Read(stream); - ai_assert(chunkID == ASSBIN_CHUNK_AINODE); - /*uint32_t size =*/ Read(stream); + uint32_t chunkID = Read(stream); + ai_assert(chunkID == ASSBIN_CHUNK_AINODE); + /*uint32_t size =*/ Read(stream); - *node = new aiNode(); + *node = new aiNode(); - (*node)->mName = Read(stream); - (*node)->mTransformation = Read(stream); - (*node)->mNumChildren = Read(stream); - (*node)->mNumMeshes = Read(stream); + (*node)->mName = Read(stream); + (*node)->mTransformation = Read(stream); + (*node)->mNumChildren = Read(stream); + (*node)->mNumMeshes = Read(stream); - if ((*node)->mNumMeshes) - { - (*node)->mMeshes = new unsigned int[(*node)->mNumMeshes]; - for (unsigned int i = 0; i < (*node)->mNumMeshes; ++i) { - (*node)->mMeshes[i] = Read(stream); - } - } + if ((*node)->mNumMeshes) + { + (*node)->mMeshes = new unsigned int[(*node)->mNumMeshes]; + for (unsigned int i = 0; i < (*node)->mNumMeshes; ++i) { + (*node)->mMeshes[i] = Read(stream); + } + } - if ((*node)->mNumChildren) - { - (*node)->mChildren = new aiNode*[(*node)->mNumChildren]; - for (unsigned int i = 0; i < (*node)->mNumChildren; ++i) { - ReadBinaryNode( stream, &(*node)->mChildren[i] ); - } - } + 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 ) { - uint32_t chunkID = Read(stream); - ai_assert(chunkID == ASSBIN_CHUNK_AIBONE); - /*uint32_t size =*/ Read(stream); + uint32_t chunkID = Read(stream); + ai_assert(chunkID == ASSBIN_CHUNK_AIBONE); + /*uint32_t size =*/ Read(stream); - b->mName = Read(stream); - b->mNumWeights = Read(stream); - b->mOffsetMatrix = Read(stream); + b->mName = Read(stream); + b->mNumWeights = Read(stream); + b->mOffsetMatrix = Read(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(stream,b->mWeights,b->mNumWeights); - } + // 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(stream,b->mWeights,b->mNumWeights); + } } void AssbinImporter::ReadBinaryMesh( IOStream * stream, aiMesh* mesh ) { - uint32_t chunkID = Read(stream); - ai_assert(chunkID == ASSBIN_CHUNK_AIMESH); - /*uint32_t size =*/ Read(stream); + uint32_t chunkID = Read(stream); + ai_assert(chunkID == ASSBIN_CHUNK_AIMESH); + /*uint32_t size =*/ Read(stream); - mesh->mPrimitiveTypes = Read(stream); - mesh->mNumVertices = Read(stream); - mesh->mNumFaces = Read(stream); - mesh->mNumBones = Read(stream); - mesh->mMaterialIndex = Read(stream); + mesh->mPrimitiveTypes = Read(stream); + mesh->mNumVertices = Read(stream); + mesh->mNumFaces = Read(stream); + mesh->mNumBones = Read(stream); + mesh->mMaterialIndex = Read(stream); - // first of all, write bits for all existent vertex components - unsigned int c = Read(stream); + // first of all, write bits for all existent vertex components + unsigned int c = Read(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(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(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(stream,mesh->mTangents,mesh->mNumVertices); - mesh->mBitangents = new aiVector3D[mesh->mNumVertices]; - ReadArray(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 (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(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(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(stream,mesh->mTangents,mesh->mNumVertices); + mesh->mBitangents = new aiVector3D[mesh->mNumVertices]; + ReadArray(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(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; + if (shortened) + { + ReadBounds(stream,mesh->mColors[n],mesh->mNumVertices); + } // else write as usual + else + { + mesh->mColors[n] = new aiColor4D[mesh->mNumVertices]; + ReadArray(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(stream); + // write number of UV components + mesh->mNumUVComponents[n] = Read(stream); - if (shortened) { - ReadBounds(stream,mesh->mTextureCoords[n],mesh->mNumVertices); - } // else write as usual - else - { - mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices]; - ReadArray(stream,mesh->mTextureCoords[n],mesh->mNumVertices); - } - } + if (shortened) { + ReadBounds(stream,mesh->mTextureCoords[n],mesh->mNumVertices); + } // else write as usual + else + { + mesh->mTextureCoords[n] = new aiVector3D[mesh->mNumVertices]; + ReadArray(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(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]; + // 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(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(stream); - f.mIndices = new unsigned int[f.mNumIndices]; + BOOST_STATIC_ASSERT(AI_MAX_FACE_INDICES <= 0xffff); + f.mNumIndices = Read(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(stream); - } - else - { - f.mIndices[a] = Read(stream); - } - } - } - } + for (unsigned int a = 0; a < f.mNumIndices;++a) { + if (mesh->mNumVertices < (1u<<16)) + { + f.mIndices[a] = Read(stream); + } + else + { + f.mIndices[a] = Read(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]); - } - } + // 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) { - uint32_t chunkID = Read(stream); - ai_assert(chunkID == ASSBIN_CHUNK_AIMATERIALPROPERTY); - /*uint32_t size =*/ Read(stream); + uint32_t chunkID = Read(stream); + ai_assert(chunkID == ASSBIN_CHUNK_AIMATERIALPROPERTY); + /*uint32_t size =*/ Read(stream); - prop->mKey = Read(stream); - prop->mSemantic = Read(stream); - prop->mIndex = Read(stream); + prop->mKey = Read(stream); + prop->mSemantic = Read(stream); + prop->mIndex = Read(stream); - prop->mDataLength = Read(stream); - prop->mType = (aiPropertyTypeInfo)Read(stream); - prop->mData = new char [ prop->mDataLength ]; - stream->Read(prop->mData,1,prop->mDataLength); + prop->mDataLength = Read(stream); + prop->mType = (aiPropertyTypeInfo)Read(stream); + prop->mData = new char [ prop->mDataLength ]; + stream->Read(prop->mData,1,prop->mDataLength); } // ----------------------------------------------------------------------------------- void AssbinImporter::ReadBinaryMaterial(IOStream * stream, aiMaterial* mat) { - uint32_t chunkID = Read(stream); - ai_assert(chunkID == ASSBIN_CHUNK_AIMATERIAL); - /*uint32_t size =*/ Read(stream); + uint32_t chunkID = Read(stream); + ai_assert(chunkID == ASSBIN_CHUNK_AIMATERIAL); + /*uint32_t size =*/ Read(stream); - mat->mNumAllocated = mat->mNumProperties = Read(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]); - } - } + mat->mNumAllocated = mat->mNumProperties = Read(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) { - uint32_t chunkID = Read(stream); - ai_assert(chunkID == ASSBIN_CHUNK_AINODEANIM); - /*uint32_t size =*/ Read(stream); + uint32_t chunkID = Read(stream); + ai_assert(chunkID == ASSBIN_CHUNK_AINODEANIM); + /*uint32_t size =*/ Read(stream); - nd->mNodeName = Read(stream); - nd->mNumPositionKeys = Read(stream); - nd->mNumRotationKeys = Read(stream); - nd->mNumScalingKeys = Read(stream); - nd->mPreState = (aiAnimBehaviour)Read(stream); - nd->mPostState = (aiAnimBehaviour)Read(stream); + nd->mNodeName = Read(stream); + nd->mNumPositionKeys = Read(stream); + nd->mNumRotationKeys = Read(stream); + nd->mNumScalingKeys = Read(stream); + nd->mPreState = (aiAnimBehaviour)Read(stream); + nd->mPostState = (aiAnimBehaviour)Read(stream); - if (nd->mNumPositionKeys) { - if (shortened) { - ReadBounds(stream,nd->mPositionKeys,nd->mNumPositionKeys); + if (nd->mNumPositionKeys) { + if (shortened) { + ReadBounds(stream,nd->mPositionKeys,nd->mNumPositionKeys); - } // else write as usual - else { - nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; - ReadArray(stream,nd->mPositionKeys,nd->mNumPositionKeys); - } - } - if (nd->mNumRotationKeys) { - if (shortened) { - ReadBounds(stream,nd->mRotationKeys,nd->mNumRotationKeys); + } // else write as usual + else { + nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; + ReadArray(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(stream,nd->mRotationKeys,nd->mNumRotationKeys); - } - } - if (nd->mNumScalingKeys) { - if (shortened) { - ReadBounds(stream,nd->mScalingKeys,nd->mNumScalingKeys); + } // else write as usual + else + { + nd->mRotationKeys = new aiQuatKey[nd->mNumRotationKeys]; + ReadArray(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(stream,nd->mScalingKeys,nd->mNumScalingKeys); - } - } + } // else write as usual + else + { + nd->mScalingKeys = new aiVectorKey[nd->mNumScalingKeys]; + ReadArray(stream,nd->mScalingKeys,nd->mNumScalingKeys); + } + } } // ----------------------------------------------------------------------------------- void AssbinImporter::ReadBinaryAnim( IOStream * stream, aiAnimation* anim ) { - uint32_t chunkID = Read(stream); - ai_assert(chunkID == ASSBIN_CHUNK_AIANIMATION); - /*uint32_t size =*/ Read(stream); + uint32_t chunkID = Read(stream); + ai_assert(chunkID == ASSBIN_CHUNK_AIANIMATION); + /*uint32_t size =*/ Read(stream); - anim->mName = Read (stream); - anim->mDuration = Read (stream); - anim->mTicksPerSecond = Read (stream); - anim->mNumChannels = Read(stream); + anim->mName = Read (stream); + anim->mDuration = Read (stream); + anim->mTicksPerSecond = Read (stream); + anim->mNumChannels = Read(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]); - } - } + 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) { - uint32_t chunkID = Read(stream); - ai_assert(chunkID == ASSBIN_CHUNK_AITEXTURE); - /*uint32_t size =*/ Read(stream); + uint32_t chunkID = Read(stream); + ai_assert(chunkID == ASSBIN_CHUNK_AITEXTURE); + /*uint32_t size =*/ Read(stream); - tex->mWidth = Read(stream); - tex->mHeight = Read(stream); - stream->Read( tex->achFormatHint, sizeof(char), 4 ); + tex->mWidth = Read(stream); + tex->mHeight = Read(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); - } - } + 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 ) { - uint32_t chunkID = Read(stream); - ai_assert(chunkID == ASSBIN_CHUNK_AILIGHT); - /*uint32_t size =*/ Read(stream); + uint32_t chunkID = Read(stream); + ai_assert(chunkID == ASSBIN_CHUNK_AILIGHT); + /*uint32_t size =*/ Read(stream); - l->mName = Read(stream); - l->mType = (aiLightSourceType)Read(stream); + l->mName = Read(stream); + l->mType = (aiLightSourceType)Read(stream); - if (l->mType != aiLightSource_DIRECTIONAL) { - l->mAttenuationConstant = Read(stream); - l->mAttenuationLinear = Read(stream); - l->mAttenuationQuadratic = Read(stream); - } + if (l->mType != aiLightSource_DIRECTIONAL) { + l->mAttenuationConstant = Read(stream); + l->mAttenuationLinear = Read(stream); + l->mAttenuationQuadratic = Read(stream); + } - l->mColorDiffuse = Read(stream); - l->mColorSpecular = Read(stream); - l->mColorAmbient = Read(stream); + l->mColorDiffuse = Read(stream); + l->mColorSpecular = Read(stream); + l->mColorAmbient = Read(stream); - if (l->mType == aiLightSource_SPOT) { - l->mAngleInnerCone = Read(stream); - l->mAngleOuterCone = Read(stream); - } + if (l->mType == aiLightSource_SPOT) { + l->mAngleInnerCone = Read(stream); + l->mAngleOuterCone = Read(stream); + } } // ----------------------------------------------------------------------------------- void AssbinImporter::ReadBinaryCamera( IOStream * stream, aiCamera* cam ) { - uint32_t chunkID = Read(stream); - ai_assert(chunkID == ASSBIN_CHUNK_AICAMERA); - /*uint32_t size =*/ Read(stream); + uint32_t chunkID = Read(stream); + ai_assert(chunkID == ASSBIN_CHUNK_AICAMERA); + /*uint32_t size =*/ Read(stream); - cam->mName = Read(stream); - cam->mPosition = Read(stream); - cam->mLookAt = Read(stream); - cam->mUp = Read(stream); - cam->mHorizontalFOV = Read(stream); - cam->mClipPlaneNear = Read(stream); - cam->mClipPlaneFar = Read(stream); - cam->mAspect = Read(stream); + cam->mName = Read(stream); + cam->mPosition = Read(stream); + cam->mLookAt = Read(stream); + cam->mUp = Read(stream); + cam->mHorizontalFOV = Read(stream); + cam->mClipPlaneNear = Read(stream); + cam->mClipPlaneFar = Read(stream); + cam->mAspect = Read(stream); } void AssbinImporter::ReadBinaryScene( IOStream * stream, aiScene* scene ) { - uint32_t chunkID = Read(stream); - ai_assert(chunkID == ASSBIN_CHUNK_AISCENE); - /*uint32_t size =*/ Read(stream); + uint32_t chunkID = Read(stream); + ai_assert(chunkID == ASSBIN_CHUNK_AISCENE); + /*uint32_t size =*/ Read(stream); - scene->mFlags = Read(stream); - scene->mNumMeshes = Read(stream); - scene->mNumMaterials = Read(stream); - scene->mNumAnimations = Read(stream); - scene->mNumTextures = Read(stream); - scene->mNumLights = Read(stream); - scene->mNumCameras = Read(stream); + scene->mFlags = Read(stream); + scene->mNumMeshes = Read(stream); + scene->mNumMaterials = Read(stream); + scene->mNumAnimations = Read(stream); + scene->mNumTextures = Read(stream); + scene->mNumLights = Read(stream); + scene->mNumCameras = Read(stream); - // Read node graph - scene->mRootNode = new aiNode[1]; - ReadBinaryNode( stream, &scene->mRootNode ); + // 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 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 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 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 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 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]); - } - } + // 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; + IOStream * stream = pIOHandler->Open(pFile,"rb"); + if (!stream) + return; - stream->Seek( 44, aiOrigin_CUR ); // signature + stream->Seek( 44, aiOrigin_CUR ); // signature - /*unsigned int versionMajor =*/ Read(stream); - /*unsigned int versionMinor =*/ Read(stream); - /*unsigned int versionRevision =*/ Read(stream); - /*unsigned int compileFlags =*/ Read(stream); + /*unsigned int versionMajor =*/ Read(stream); + /*unsigned int versionMinor =*/ Read(stream); + /*unsigned int versionRevision =*/ Read(stream); + /*unsigned int compileFlags =*/ Read(stream); - shortened = Read(stream) > 0; - compressed = Read(stream) > 0; + shortened = Read(stream) > 0; + compressed = Read(stream) > 0; - if (shortened) - throw DeadlyImportError( "Shortened binaries are not supported!" ); + 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 + stream->Seek( 256, aiOrigin_CUR ); // original filename + stream->Seek( 128, aiOrigin_CUR ); // options + stream->Seek( 64, aiOrigin_CUR ); // padding - if (compressed) - { - uLongf uncompressedSize = Read(stream); - uLongf compressedSize = stream->FileSize() - stream->Tell(); + if (compressed) + { + uLongf uncompressedSize = Read(stream); + uLongf compressedSize = stream->FileSize() - stream->Tell(); - unsigned char * compressedData = new unsigned char[ compressedSize ]; - stream->Read( compressedData, 1, compressedSize ); + unsigned char * compressedData = new unsigned char[ compressedSize ]; + stream->Read( compressedData, 1, compressedSize ); - unsigned char * uncompressedData = new unsigned char[ uncompressedSize ]; + unsigned char * uncompressedData = new unsigned char[ uncompressedSize ]; - uncompress( uncompressedData, &uncompressedSize, compressedData, compressedSize ); + uncompress( uncompressedData, &uncompressedSize, compressedData, compressedSize ); - MemoryIOStream io( uncompressedData, uncompressedSize ); + MemoryIOStream io( uncompressedData, uncompressedSize ); - ReadBinaryScene(&io,pScene); + ReadBinaryScene(&io,pScene); - delete[] uncompressedData; - delete[] compressedData; - } - else - { - ReadBinaryScene(stream,pScene); - } - - pIOHandler->Close(stream); + delete[] uncompressedData; + delete[] compressedData; + } + else + { + ReadBinaryScene(stream,pScene); + } + + pIOHandler->Close(stream); } #endif // !! ASSIMP_BUILD_NO_ASSBIN_IMPORTER diff --git a/code/AssbinLoader.h b/code/AssbinLoader.h index 1ffe83341..4f2638fb8 100644 --- a/code/AssbinLoader.h +++ b/code/AssbinLoader.h @@ -6,8 +6,8 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2015, 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 +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -24,16 +24,16 @@ following conditions are met: 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 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- @@ -61,7 +61,7 @@ struct aiCamera; #ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER -namespace Assimp { +namespace Assimp { // --------------------------------------------------------------------------------- /** Importer class for 3D Studio r3 and r4 3DS files @@ -74,15 +74,15 @@ private: protected: public: - virtual bool CanRead( - const std::string& pFile, - IOSystem* pIOHandler, + 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, + virtual void InternReadFile( + const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler ); void ReadBinaryScene( IOStream * stream, aiScene* pScene ); diff --git a/code/Assimp.cpp b/code/Assimp.cpp index 41128e525..03c39671b 100644 --- a/code/Assimp.cpp +++ b/code/Assimp.cpp @@ -1,654 +1,654 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ -/** @file Assimp.cpp - * @brief Implementation of the Plain-C API - */ - -#include -#include -#include -#include -#include -#include - -#include "GenericProperty.h" -#include "CInterfaceIOWrapper.h" -#include "Importer.h" -#include "Exceptional.h" -#include "ScenePrivate.h" -#include "BaseImporter.h" -#include - -// ------------------------------------------------------------------------------------------------ -#ifndef ASSIMP_BUILD_SINGLETHREADED -# include -# include -#endif -// ------------------------------------------------------------------------------------------------ -using namespace Assimp; - -namespace Assimp -{ - // underlying structure for aiPropertyStore - typedef BatchLoader::PropertyMap PropertyMap; - - /** Stores the LogStream objects for all active C log streams */ - struct mpred { - bool operator () (const aiLogStream& s0, const aiLogStream& s1) const { - return s0.callback LogStreamMap; - - /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */ - typedef std::list PredefLogStreamMap; - - /** Local storage of all active log streams */ - static LogStreamMap gActiveLogStreams; - - /** Local storage of LogStreams allocated by #aiGetPredefinedLogStream */ - static PredefLogStreamMap gPredefinedStreams; - - /** Error message of the last failed import process */ - static std::string gLastErrorString; - - /** Verbose logging active or not? */ - static aiBool gVerboseLogging = false; - - /** will return all registered importers. */ - void GetImporterInstanceList(std::vector< BaseImporter* >& out); - -} // namespace assimp - - -#ifndef ASSIMP_BUILD_SINGLETHREADED -/** Global mutex to manage the access to the logstream map */ -static boost::mutex gLogStreamMutex; -#endif - - -// ------------------------------------------------------------------------------------------------ -// Custom LogStream implementation for the C-API -class LogToCallbackRedirector : public LogStream -{ -public: - LogToCallbackRedirector(const aiLogStream& s) - : stream (s) { - ai_assert(NULL != s.callback); - } - - ~LogToCallbackRedirector() { -#ifndef ASSIMP_BUILD_SINGLETHREADED - boost::mutex::scoped_lock lock(gLogStreamMutex); -#endif - // (HACK) Check whether the 'stream.user' pointer points to a - // custom LogStream allocated by #aiGetPredefinedLogStream. - // In this case, we need to delete it, too. Of course, this - // might cause strange problems, but the chance is quite low. - - PredefLogStreamMap::iterator it = std::find(gPredefinedStreams.begin(), - gPredefinedStreams.end(), (Assimp::LogStream*)stream.user); - - if (it != gPredefinedStreams.end()) { - delete *it; - gPredefinedStreams.erase(it); - } - } - - /** @copydoc LogStream::write */ - void write(const char* message) { - stream.callback(message,stream.user); - } - -private: - aiLogStream stream; -}; - -// ------------------------------------------------------------------------------------------------ -void ReportSceneNotFoundError() -{ - DefaultLogger::get()->error("Unable to find the Assimp::Importer for this aiScene. " - "The C-API does not accept scenes produced by the C++ API and vice versa"); - - assert(false); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the given file and returns its content. -const aiScene* aiImportFile( const char* pFile, unsigned int pFlags) -{ - return aiImportFileEx(pFile,pFlags,NULL); -} - -// ------------------------------------------------------------------------------------------------ -const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags, aiFileIO* pFS) -{ - return aiImportFileExWithProperties(pFile, pFlags, pFS, NULL); -} - -// ------------------------------------------------------------------------------------------------ -const aiScene* aiImportFileExWithProperties( const char* pFile, unsigned int pFlags, - aiFileIO* pFS, - const aiPropertyStore* props) -{ - ai_assert(NULL != pFile); - - const aiScene* scene = NULL; - ASSIMP_BEGIN_EXCEPTION_REGION(); - - // create an Importer for this file - Assimp::Importer* imp = new Assimp::Importer(); - - // copy properties - if(props) { - const PropertyMap* pp = reinterpret_cast(props); - ImporterPimpl* pimpl = imp->Pimpl(); - pimpl->mIntProperties = pp->ints; - pimpl->mFloatProperties = pp->floats; - pimpl->mStringProperties = pp->strings; - pimpl->mMatrixProperties = pp->matrices; - } - // setup a custom IO system if necessary - if (pFS) { - imp->SetIOHandler( new CIOSystemWrapper (pFS) ); - } - - // and have it read the file - scene = imp->ReadFile( pFile, pFlags); - - // if succeeded, store the importer in the scene and keep it alive - if( scene) { - ScenePrivateData* priv = const_cast( ScenePriv(scene) ); - priv->mOrigImporter = imp; - } - else { - // if failed, extract error code and destroy the import - gLastErrorString = imp->GetErrorString(); - delete imp; - } - - // return imported data. If the import failed the pointer is NULL anyways - ASSIMP_END_EXCEPTION_REGION(const aiScene*); - return scene; -} - -// ------------------------------------------------------------------------------------------------ -const aiScene* aiImportFileFromMemory( - const char* pBuffer, - unsigned int pLength, - unsigned int pFlags, - const char* pHint) -{ - return aiImportFileFromMemoryWithProperties(pBuffer, pLength, pFlags, pHint, NULL); -} - -// ------------------------------------------------------------------------------------------------ -const aiScene* aiImportFileFromMemoryWithProperties( - const char* pBuffer, - unsigned int pLength, - unsigned int pFlags, - const char* pHint, - const aiPropertyStore* props) -{ - ai_assert(NULL != pBuffer && 0 != pLength); - - const aiScene* scene = NULL; - ASSIMP_BEGIN_EXCEPTION_REGION(); - - // create an Importer for this file - Assimp::Importer* imp = new Assimp::Importer(); - - // copy properties - if(props) { - const PropertyMap* pp = reinterpret_cast(props); - ImporterPimpl* pimpl = imp->Pimpl(); - pimpl->mIntProperties = pp->ints; - pimpl->mFloatProperties = pp->floats; - pimpl->mStringProperties = pp->strings; - pimpl->mMatrixProperties = pp->matrices; - } - - // and have it read the file from the memory buffer - scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint); - - // if succeeded, store the importer in the scene and keep it alive - if( scene) { - ScenePrivateData* priv = const_cast( ScenePriv(scene) ); - priv->mOrigImporter = imp; - } - else { - // if failed, extract error code and destroy the import - gLastErrorString = imp->GetErrorString(); - delete imp; - } - // return imported data. If the import failed the pointer is NULL anyways - ASSIMP_END_EXCEPTION_REGION(const aiScene*); - return scene; -} - -// ------------------------------------------------------------------------------------------------ -// Releases all resources associated with the given import process. -void aiReleaseImport( const aiScene* pScene) -{ - if (!pScene) { - return; - } - - ASSIMP_BEGIN_EXCEPTION_REGION(); - - // find the importer associated with this data - const ScenePrivateData* priv = ScenePriv(pScene); - if( !priv || !priv->mOrigImporter) { - delete pScene; - } - else { - // deleting the Importer also deletes the scene - // Note: the reason that this is not written as 'delete priv->mOrigImporter' - // is a suspected bug in gcc 4.4+ (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52339) - Importer* importer = priv->mOrigImporter; - delete importer; - } - - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API const aiScene* aiApplyPostProcessing(const aiScene* pScene, - unsigned int pFlags) -{ - const aiScene* sc = NULL; - - - ASSIMP_BEGIN_EXCEPTION_REGION(); - - // find the importer associated with this data - const ScenePrivateData* priv = ScenePriv(pScene); - if( !priv || !priv->mOrigImporter) { - ReportSceneNotFoundError(); - return NULL; - } - - sc = priv->mOrigImporter->ApplyPostProcessing(pFlags); - - if (!sc) { - aiReleaseImport(pScene); - return NULL; - } - - ASSIMP_END_EXCEPTION_REGION(const aiScene*); - return sc; -} - -// ------------------------------------------------------------------------------------------------ -void CallbackToLogRedirector (const char* msg, char* dt) -{ - ai_assert(NULL != msg && NULL != dt); - LogStream* s = (LogStream*)dt; - - s->write(msg); -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream,const char* file) -{ - aiLogStream sout; - - ASSIMP_BEGIN_EXCEPTION_REGION(); - LogStream* stream = LogStream::createDefaultStream(pStream,file); - if (!stream) { - sout.callback = NULL; - sout.user = NULL; - } - else { - sout.callback = &CallbackToLogRedirector; - sout.user = (char*)stream; - } - gPredefinedStreams.push_back(stream); - ASSIMP_END_EXCEPTION_REGION(aiLogStream); - return sout; -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API void aiAttachLogStream( const aiLogStream* stream ) -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - -#ifndef ASSIMP_BUILD_SINGLETHREADED - boost::mutex::scoped_lock lock(gLogStreamMutex); -#endif - - LogStream* lg = new LogToCallbackRedirector(*stream); - gActiveLogStreams[*stream] = lg; - - if (DefaultLogger::isNullLogger()) { - DefaultLogger::create(NULL,(gVerboseLogging == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); - } - DefaultLogger::get()->attachStream(lg); - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API aiReturn aiDetachLogStream( const aiLogStream* stream) -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - -#ifndef ASSIMP_BUILD_SINGLETHREADED - boost::mutex::scoped_lock lock(gLogStreamMutex); -#endif - // find the logstream associated with this data - LogStreamMap::iterator it = gActiveLogStreams.find( *stream); - // it should be there... else the user is playing fools with us - if( it == gActiveLogStreams.end()) { - return AI_FAILURE; - } - DefaultLogger::get()->detatchStream( it->second ); - delete it->second; - - gActiveLogStreams.erase( it); - - if (gActiveLogStreams.empty()) { - DefaultLogger::kill(); - } - ASSIMP_END_EXCEPTION_REGION(aiReturn); - return AI_SUCCESS; -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API void aiDetachAllLogStreams(void) -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); -#ifndef ASSIMP_BUILD_SINGLETHREADED - boost::mutex::scoped_lock lock(gLogStreamMutex); -#endif - for (LogStreamMap::iterator it = gActiveLogStreams.begin(); it != gActiveLogStreams.end(); ++it) { - DefaultLogger::get()->detatchStream( it->second ); - delete it->second; - } - gActiveLogStreams.clear(); - DefaultLogger::kill(); - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API void aiEnableVerboseLogging(aiBool d) -{ - if (!DefaultLogger::isNullLogger()) { - DefaultLogger::get()->setLogSeverity((d == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); - } - gVerboseLogging = d; -} - -// ------------------------------------------------------------------------------------------------ -// Returns the error text of the last failed import process. -const char* aiGetErrorString() -{ - return gLastErrorString.c_str(); -} - -// ----------------------------------------------------------------------------------------------- -// Return the description of a importer given its index -const aiImporterDesc* aiGetImportFormatDescription( size_t pIndex) -{ - return Importer().GetImporterInfo(pIndex); -} - -// ----------------------------------------------------------------------------------------------- -// Return the number of importers -size_t aiGetImportFormatCount(void) -{ - return Importer().GetImporterCount(); -} - - -// ------------------------------------------------------------------------------------------------ -// Returns the error text of the last failed import process. -aiBool aiIsExtensionSupported(const char* szExtension) -{ - ai_assert(NULL != szExtension); - aiBool candoit=AI_FALSE; - ASSIMP_BEGIN_EXCEPTION_REGION(); - - // FIXME: no need to create a temporary Importer instance just for that .. - Assimp::Importer tmp; - candoit = tmp.IsExtensionSupported(std::string(szExtension)) ? AI_TRUE : AI_FALSE; - - ASSIMP_END_EXCEPTION_REGION(aiBool); - return candoit; -} - -// ------------------------------------------------------------------------------------------------ -// Get a list of all file extensions supported by ASSIMP -void aiGetExtensionList(aiString* szOut) -{ - ai_assert(NULL != szOut); - ASSIMP_BEGIN_EXCEPTION_REGION(); - - // FIXME: no need to create a temporary Importer instance just for that .. - Assimp::Importer tmp; - tmp.GetExtensionList(*szOut); - - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -// Get the memory requirements for a particular import. -void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn, - C_STRUCT aiMemoryInfo* in) -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - - // find the importer associated with this data - const ScenePrivateData* priv = ScenePriv(pIn); - if( !priv || !priv->mOrigImporter) { - ReportSceneNotFoundError(); - return; - } - - return priv->mOrigImporter->GetMemoryRequirements(*in); - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API aiPropertyStore* aiCreatePropertyStore(void) -{ - return reinterpret_cast( new PropertyMap() ); -} - - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API void aiReleasePropertyStore(aiPropertyStore* p) -{ - delete reinterpret_cast(p); -} - -// ------------------------------------------------------------------------------------------------ -// Importer::SetPropertyInteger -ASSIMP_API void aiSetImportPropertyInteger(aiPropertyStore* p, const char* szName, int value) -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - PropertyMap* pp = reinterpret_cast(p); - SetGenericProperty(pp->ints,szName,value); - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -// Importer::SetPropertyFloat -ASSIMP_API void aiSetImportPropertyFloat(aiPropertyStore* p, const char* szName, float value) -{ - ASSIMP_BEGIN_EXCEPTION_REGION(); - PropertyMap* pp = reinterpret_cast(p); - SetGenericProperty(pp->floats,szName,value); - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -// Importer::SetPropertyString -ASSIMP_API void aiSetImportPropertyString(aiPropertyStore* p, const char* szName, - const C_STRUCT aiString* st) -{ - if (!st) { - return; - } - ASSIMP_BEGIN_EXCEPTION_REGION(); - PropertyMap* pp = reinterpret_cast(p); - SetGenericProperty(pp->strings,szName,std::string(st->C_Str())); - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -// Importer::SetPropertyMatrix -ASSIMP_API void aiSetImportPropertyMatrix(aiPropertyStore* p, const char* szName, - const C_STRUCT aiMatrix4x4* mat) -{ - if (!mat) { - return; - } - ASSIMP_BEGIN_EXCEPTION_REGION(); - PropertyMap* pp = reinterpret_cast(p); - SetGenericProperty(pp->matrices,szName,*mat); - ASSIMP_END_EXCEPTION_REGION(void); -} - -// ------------------------------------------------------------------------------------------------ -// Rotation matrix to quaternion -ASSIMP_API void aiCreateQuaternionFromMatrix(aiQuaternion* quat,const aiMatrix3x3* mat) -{ - ai_assert(NULL != quat && NULL != mat); - *quat = aiQuaternion(*mat); -} - -// ------------------------------------------------------------------------------------------------ -// Matrix decomposition -ASSIMP_API void aiDecomposeMatrix(const aiMatrix4x4* mat,aiVector3D* scaling, - aiQuaternion* rotation, - aiVector3D* position) -{ - ai_assert(NULL != rotation && NULL != position && NULL != scaling && NULL != mat); - mat->Decompose(*scaling,*rotation,*position); -} - -// ------------------------------------------------------------------------------------------------ -// Matrix transpose -ASSIMP_API void aiTransposeMatrix3(aiMatrix3x3* mat) -{ - ai_assert(NULL != mat); - mat->Transpose(); -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API void aiTransposeMatrix4(aiMatrix4x4* mat) -{ - ai_assert(NULL != mat); - mat->Transpose(); -} - -// ------------------------------------------------------------------------------------------------ -// Vector transformation -ASSIMP_API void aiTransformVecByMatrix3(aiVector3D* vec, - const aiMatrix3x3* mat) -{ - ai_assert(NULL != mat && NULL != vec); - *vec *= (*mat); -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API void aiTransformVecByMatrix4(aiVector3D* vec, - const aiMatrix4x4* mat) -{ - ai_assert(NULL != mat && NULL != vec); - *vec *= (*mat); -} - -// ------------------------------------------------------------------------------------------------ -// Matrix multiplication -ASSIMP_API void aiMultiplyMatrix4( - aiMatrix4x4* dst, - const aiMatrix4x4* src) -{ - ai_assert(NULL != dst && NULL != src); - *dst = (*dst) * (*src); -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API void aiMultiplyMatrix3( - aiMatrix3x3* dst, - const aiMatrix3x3* src) -{ - ai_assert(NULL != dst && NULL != src); - *dst = (*dst) * (*src); -} - -// ------------------------------------------------------------------------------------------------ -// Matrix identity -ASSIMP_API void aiIdentityMatrix3( - aiMatrix3x3* mat) -{ - ai_assert(NULL != mat); - *mat = aiMatrix3x3(); -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API void aiIdentityMatrix4( - aiMatrix4x4* mat) -{ - ai_assert(NULL != mat); - *mat = aiMatrix4x4(); -} - -// ------------------------------------------------------------------------------------------------ -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; -} - -// ------------------------------------------------------------------------------------------------ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +/** @file Assimp.cpp + * @brief Implementation of the Plain-C API + */ + +#include +#include +#include +#include +#include +#include + +#include "GenericProperty.h" +#include "CInterfaceIOWrapper.h" +#include "Importer.h" +#include "Exceptional.h" +#include "ScenePrivate.h" +#include "BaseImporter.h" +#include + +// ------------------------------------------------------------------------------------------------ +#ifndef ASSIMP_BUILD_SINGLETHREADED +# include +# include +#endif +// ------------------------------------------------------------------------------------------------ +using namespace Assimp; + +namespace Assimp +{ + // underlying structure for aiPropertyStore + typedef BatchLoader::PropertyMap PropertyMap; + + /** Stores the LogStream objects for all active C log streams */ + struct mpred { + bool operator () (const aiLogStream& s0, const aiLogStream& s1) const { + return s0.callback LogStreamMap; + + /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */ + typedef std::list PredefLogStreamMap; + + /** Local storage of all active log streams */ + static LogStreamMap gActiveLogStreams; + + /** Local storage of LogStreams allocated by #aiGetPredefinedLogStream */ + static PredefLogStreamMap gPredefinedStreams; + + /** Error message of the last failed import process */ + static std::string gLastErrorString; + + /** Verbose logging active or not? */ + static aiBool gVerboseLogging = false; + + /** will return all registered importers. */ + void GetImporterInstanceList(std::vector< BaseImporter* >& out); + +} // namespace assimp + + +#ifndef ASSIMP_BUILD_SINGLETHREADED +/** Global mutex to manage the access to the logstream map */ +static boost::mutex gLogStreamMutex; +#endif + + +// ------------------------------------------------------------------------------------------------ +// Custom LogStream implementation for the C-API +class LogToCallbackRedirector : public LogStream +{ +public: + LogToCallbackRedirector(const aiLogStream& s) + : stream (s) { + ai_assert(NULL != s.callback); + } + + ~LogToCallbackRedirector() { +#ifndef ASSIMP_BUILD_SINGLETHREADED + boost::mutex::scoped_lock lock(gLogStreamMutex); +#endif + // (HACK) Check whether the 'stream.user' pointer points to a + // custom LogStream allocated by #aiGetPredefinedLogStream. + // In this case, we need to delete it, too. Of course, this + // might cause strange problems, but the chance is quite low. + + PredefLogStreamMap::iterator it = std::find(gPredefinedStreams.begin(), + gPredefinedStreams.end(), (Assimp::LogStream*)stream.user); + + if (it != gPredefinedStreams.end()) { + delete *it; + gPredefinedStreams.erase(it); + } + } + + /** @copydoc LogStream::write */ + void write(const char* message) { + stream.callback(message,stream.user); + } + +private: + aiLogStream stream; +}; + +// ------------------------------------------------------------------------------------------------ +void ReportSceneNotFoundError() +{ + DefaultLogger::get()->error("Unable to find the Assimp::Importer for this aiScene. " + "The C-API does not accept scenes produced by the C++ API and vice versa"); + + assert(false); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the given file and returns its content. +const aiScene* aiImportFile( const char* pFile, unsigned int pFlags) +{ + return aiImportFileEx(pFile,pFlags,NULL); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags, aiFileIO* pFS) +{ + return aiImportFileExWithProperties(pFile, pFlags, pFS, NULL); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileExWithProperties( const char* pFile, unsigned int pFlags, + aiFileIO* pFS, + const aiPropertyStore* props) +{ + ai_assert(NULL != pFile); + + const aiScene* scene = NULL; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // create an Importer for this file + Assimp::Importer* imp = new Assimp::Importer(); + + // copy properties + if(props) { + const PropertyMap* pp = reinterpret_cast(props); + ImporterPimpl* pimpl = imp->Pimpl(); + pimpl->mIntProperties = pp->ints; + pimpl->mFloatProperties = pp->floats; + pimpl->mStringProperties = pp->strings; + pimpl->mMatrixProperties = pp->matrices; + } + // setup a custom IO system if necessary + if (pFS) { + imp->SetIOHandler( new CIOSystemWrapper (pFS) ); + } + + // and have it read the file + scene = imp->ReadFile( pFile, pFlags); + + // if succeeded, store the importer in the scene and keep it alive + if( scene) { + ScenePrivateData* priv = const_cast( ScenePriv(scene) ); + priv->mOrigImporter = imp; + } + else { + // if failed, extract error code and destroy the import + gLastErrorString = imp->GetErrorString(); + delete imp; + } + + // return imported data. If the import failed the pointer is NULL anyways + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return scene; +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileFromMemory( + const char* pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char* pHint) +{ + return aiImportFileFromMemoryWithProperties(pBuffer, pLength, pFlags, pHint, NULL); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileFromMemoryWithProperties( + const char* pBuffer, + unsigned int pLength, + unsigned int pFlags, + const char* pHint, + const aiPropertyStore* props) +{ + ai_assert(NULL != pBuffer && 0 != pLength); + + const aiScene* scene = NULL; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // create an Importer for this file + Assimp::Importer* imp = new Assimp::Importer(); + + // copy properties + if(props) { + const PropertyMap* pp = reinterpret_cast(props); + ImporterPimpl* pimpl = imp->Pimpl(); + pimpl->mIntProperties = pp->ints; + pimpl->mFloatProperties = pp->floats; + pimpl->mStringProperties = pp->strings; + pimpl->mMatrixProperties = pp->matrices; + } + + // and have it read the file from the memory buffer + scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint); + + // if succeeded, store the importer in the scene and keep it alive + if( scene) { + ScenePrivateData* priv = const_cast( ScenePriv(scene) ); + priv->mOrigImporter = imp; + } + else { + // if failed, extract error code and destroy the import + gLastErrorString = imp->GetErrorString(); + delete imp; + } + // return imported data. If the import failed the pointer is NULL anyways + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return scene; +} + +// ------------------------------------------------------------------------------------------------ +// Releases all resources associated with the given import process. +void aiReleaseImport( const aiScene* pScene) +{ + if (!pScene) { + return; + } + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData* priv = ScenePriv(pScene); + if( !priv || !priv->mOrigImporter) { + delete pScene; + } + else { + // deleting the Importer also deletes the scene + // Note: the reason that this is not written as 'delete priv->mOrigImporter' + // is a suspected bug in gcc 4.4+ (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52339) + Importer* importer = priv->mOrigImporter; + delete importer; + } + + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const aiScene* aiApplyPostProcessing(const aiScene* pScene, + unsigned int pFlags) +{ + const aiScene* sc = NULL; + + + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData* priv = ScenePriv(pScene); + if( !priv || !priv->mOrigImporter) { + ReportSceneNotFoundError(); + return NULL; + } + + sc = priv->mOrigImporter->ApplyPostProcessing(pFlags); + + if (!sc) { + aiReleaseImport(pScene); + return NULL; + } + + ASSIMP_END_EXCEPTION_REGION(const aiScene*); + return sc; +} + +// ------------------------------------------------------------------------------------------------ +void CallbackToLogRedirector (const char* msg, char* dt) +{ + ai_assert(NULL != msg && NULL != dt); + LogStream* s = (LogStream*)dt; + + s->write(msg); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiLogStream aiGetPredefinedLogStream(aiDefaultLogStream pStream,const char* file) +{ + aiLogStream sout; + + ASSIMP_BEGIN_EXCEPTION_REGION(); + LogStream* stream = LogStream::createDefaultStream(pStream,file); + if (!stream) { + sout.callback = NULL; + sout.user = NULL; + } + else { + sout.callback = &CallbackToLogRedirector; + sout.user = (char*)stream; + } + gPredefinedStreams.push_back(stream); + ASSIMP_END_EXCEPTION_REGION(aiLogStream); + return sout; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiAttachLogStream( const aiLogStream* stream ) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + +#ifndef ASSIMP_BUILD_SINGLETHREADED + boost::mutex::scoped_lock lock(gLogStreamMutex); +#endif + + LogStream* lg = new LogToCallbackRedirector(*stream); + gActiveLogStreams[*stream] = lg; + + if (DefaultLogger::isNullLogger()) { + DefaultLogger::create(NULL,(gVerboseLogging == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); + } + DefaultLogger::get()->attachStream(lg); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiDetachLogStream( const aiLogStream* stream) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + +#ifndef ASSIMP_BUILD_SINGLETHREADED + boost::mutex::scoped_lock lock(gLogStreamMutex); +#endif + // find the logstream associated with this data + LogStreamMap::iterator it = gActiveLogStreams.find( *stream); + // it should be there... else the user is playing fools with us + if( it == gActiveLogStreams.end()) { + return AI_FAILURE; + } + DefaultLogger::get()->detatchStream( it->second ); + delete it->second; + + gActiveLogStreams.erase( it); + + if (gActiveLogStreams.empty()) { + DefaultLogger::kill(); + } + ASSIMP_END_EXCEPTION_REGION(aiReturn); + return AI_SUCCESS; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiDetachAllLogStreams(void) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); +#ifndef ASSIMP_BUILD_SINGLETHREADED + boost::mutex::scoped_lock lock(gLogStreamMutex); +#endif + for (LogStreamMap::iterator it = gActiveLogStreams.begin(); it != gActiveLogStreams.end(); ++it) { + DefaultLogger::get()->detatchStream( it->second ); + delete it->second; + } + gActiveLogStreams.clear(); + DefaultLogger::kill(); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiEnableVerboseLogging(aiBool d) +{ + if (!DefaultLogger::isNullLogger()) { + DefaultLogger::get()->setLogSeverity((d == AI_TRUE ? Logger::VERBOSE : Logger::NORMAL)); + } + gVerboseLogging = d; +} + +// ------------------------------------------------------------------------------------------------ +// Returns the error text of the last failed import process. +const char* aiGetErrorString() +{ + return gLastErrorString.c_str(); +} + +// ----------------------------------------------------------------------------------------------- +// Return the description of a importer given its index +const aiImporterDesc* aiGetImportFormatDescription( size_t pIndex) +{ + return Importer().GetImporterInfo(pIndex); +} + +// ----------------------------------------------------------------------------------------------- +// Return the number of importers +size_t aiGetImportFormatCount(void) +{ + return Importer().GetImporterCount(); +} + + +// ------------------------------------------------------------------------------------------------ +// Returns the error text of the last failed import process. +aiBool aiIsExtensionSupported(const char* szExtension) +{ + ai_assert(NULL != szExtension); + aiBool candoit=AI_FALSE; + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // FIXME: no need to create a temporary Importer instance just for that .. + Assimp::Importer tmp; + candoit = tmp.IsExtensionSupported(std::string(szExtension)) ? AI_TRUE : AI_FALSE; + + ASSIMP_END_EXCEPTION_REGION(aiBool); + return candoit; +} + +// ------------------------------------------------------------------------------------------------ +// Get a list of all file extensions supported by ASSIMP +void aiGetExtensionList(aiString* szOut) +{ + ai_assert(NULL != szOut); + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // FIXME: no need to create a temporary Importer instance just for that .. + Assimp::Importer tmp; + tmp.GetExtensionList(*szOut); + + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Get the memory requirements for a particular import. +void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn, + C_STRUCT aiMemoryInfo* in) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + + // find the importer associated with this data + const ScenePrivateData* priv = ScenePriv(pIn); + if( !priv || !priv->mOrigImporter) { + ReportSceneNotFoundError(); + return; + } + + return priv->mOrigImporter->GetMemoryRequirements(*in); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiPropertyStore* aiCreatePropertyStore(void) +{ + return reinterpret_cast( new PropertyMap() ); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiReleasePropertyStore(aiPropertyStore* p) +{ + delete reinterpret_cast(p); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyInteger +ASSIMP_API void aiSetImportPropertyInteger(aiPropertyStore* p, const char* szName, int value) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap* pp = reinterpret_cast(p); + SetGenericProperty(pp->ints,szName,value); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyFloat +ASSIMP_API void aiSetImportPropertyFloat(aiPropertyStore* p, const char* szName, float value) +{ + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap* pp = reinterpret_cast(p); + SetGenericProperty(pp->floats,szName,value); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyString +ASSIMP_API void aiSetImportPropertyString(aiPropertyStore* p, const char* szName, + const C_STRUCT aiString* st) +{ + if (!st) { + return; + } + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap* pp = reinterpret_cast(p); + SetGenericProperty(pp->strings,szName,std::string(st->C_Str())); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Importer::SetPropertyMatrix +ASSIMP_API void aiSetImportPropertyMatrix(aiPropertyStore* p, const char* szName, + const C_STRUCT aiMatrix4x4* mat) +{ + if (!mat) { + return; + } + ASSIMP_BEGIN_EXCEPTION_REGION(); + PropertyMap* pp = reinterpret_cast(p); + SetGenericProperty(pp->matrices,szName,*mat); + ASSIMP_END_EXCEPTION_REGION(void); +} + +// ------------------------------------------------------------------------------------------------ +// Rotation matrix to quaternion +ASSIMP_API void aiCreateQuaternionFromMatrix(aiQuaternion* quat,const aiMatrix3x3* mat) +{ + ai_assert(NULL != quat && NULL != mat); + *quat = aiQuaternion(*mat); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix decomposition +ASSIMP_API void aiDecomposeMatrix(const aiMatrix4x4* mat,aiVector3D* scaling, + aiQuaternion* rotation, + aiVector3D* position) +{ + ai_assert(NULL != rotation && NULL != position && NULL != scaling && NULL != mat); + mat->Decompose(*scaling,*rotation,*position); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix transpose +ASSIMP_API void aiTransposeMatrix3(aiMatrix3x3* mat) +{ + ai_assert(NULL != mat); + mat->Transpose(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiTransposeMatrix4(aiMatrix4x4* mat) +{ + ai_assert(NULL != mat); + mat->Transpose(); +} + +// ------------------------------------------------------------------------------------------------ +// Vector transformation +ASSIMP_API void aiTransformVecByMatrix3(aiVector3D* vec, + const aiMatrix3x3* mat) +{ + ai_assert(NULL != mat && NULL != vec); + *vec *= (*mat); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiTransformVecByMatrix4(aiVector3D* vec, + const aiMatrix4x4* mat) +{ + ai_assert(NULL != mat && NULL != vec); + *vec *= (*mat); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix multiplication +ASSIMP_API void aiMultiplyMatrix4( + aiMatrix4x4* dst, + const aiMatrix4x4* src) +{ + ai_assert(NULL != dst && NULL != src); + *dst = (*dst) * (*src); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiMultiplyMatrix3( + aiMatrix3x3* dst, + const aiMatrix3x3* src) +{ + ai_assert(NULL != dst && NULL != src); + *dst = (*dst) * (*src); +} + +// ------------------------------------------------------------------------------------------------ +// Matrix identity +ASSIMP_API void aiIdentityMatrix3( + aiMatrix3x3* mat) +{ + ai_assert(NULL != mat); + *mat = aiMatrix3x3(); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiIdentityMatrix4( + aiMatrix4x4* mat) +{ + ai_assert(NULL != mat); + *mat = aiMatrix4x4(); +} + +// ------------------------------------------------------------------------------------------------ +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; +} + +// ------------------------------------------------------------------------------------------------ diff --git a/code/AssimpCExport.cpp b/code/AssimpCExport.cpp index 0607945d6..6578eeb88 100644 --- a/code/AssimpCExport.cpp +++ b/code/AssimpCExport.cpp @@ -1,129 +1,129 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file AssimpCExport.cpp -Assimp C export interface. See Exporter.cpp for some notes. -*/ - -#ifndef ASSIMP_BUILD_NO_EXPORT -#include "CInterfaceIOWrapper.h" -#include "SceneCombiner.h" -#include "ScenePrivate.h" -#include "../include/assimp/Exporter.hpp" - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API size_t aiGetExportFormatCount(void) -{ - return Exporter().GetExportFormatCount(); -} - - -// ------------------------------------------------------------------------------------------------ -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); -} - - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API void aiCopyScene(const aiScene* pIn, aiScene** pOut) -{ - if (!pOut || !pIn) { - return; - } - - SceneCombiner::CopyScene(pOut,pIn,true); - ScenePriv(*pOut)->mIsCopy = true; -} - - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn) -{ - // note: aiReleaseImport() is also able to delete scene copies, but in addition - // it also handles scenes with import metadata. - delete pIn; -} - - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API aiReturn aiExportScene( const aiScene* pScene, const char* pFormatId, const char* pFileName, unsigned int pPreprocessing ) -{ - return ::aiExportSceneEx(pScene,pFormatId,pFileName,NULL,pPreprocessing); -} - - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API aiReturn aiExportSceneEx( const aiScene* pScene, const char* pFormatId, const char* pFileName, aiFileIO* pIO, unsigned int pPreprocessing ) -{ - Exporter exp; - - if (pIO) { - exp.SetIOHandler(new CIOSystemWrapper(pIO)); - } - return exp.Export(pScene,pFormatId,pFileName,pPreprocessing); -} - - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing ) -{ - Exporter exp; - if (!exp.ExportToBlob(pScene,pFormatId,pPreprocessing)) { - return NULL; - } - const aiExportDataBlob* blob = exp.GetOrphanedBlob(); - ai_assert(blob); - - return blob; -} - -// ------------------------------------------------------------------------------------------------ -ASSIMP_API C_STRUCT void aiReleaseExportBlob( const aiExportDataBlob* pData ) -{ - delete pData; -} - -#endif // !ASSIMP_BUILD_NO_EXPORT +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file AssimpCExport.cpp +Assimp C export interface. See Exporter.cpp for some notes. +*/ + +#ifndef ASSIMP_BUILD_NO_EXPORT +#include "CInterfaceIOWrapper.h" +#include "SceneCombiner.h" +#include "ScenePrivate.h" +#include "../include/assimp/Exporter.hpp" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API size_t aiGetExportFormatCount(void) +{ + return Exporter().GetExportFormatCount(); +} + + +// ------------------------------------------------------------------------------------------------ +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); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiCopyScene(const aiScene* pIn, aiScene** pOut) +{ + if (!pOut || !pIn) { + return; + } + + SceneCombiner::CopyScene(pOut,pIn,true); + ScenePriv(*pOut)->mIsCopy = true; +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiFreeScene(const C_STRUCT aiScene* pIn) +{ + // note: aiReleaseImport() is also able to delete scene copies, but in addition + // it also handles scenes with import metadata. + delete pIn; +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiExportScene( const aiScene* pScene, const char* pFormatId, const char* pFileName, unsigned int pPreprocessing ) +{ + return ::aiExportSceneEx(pScene,pFormatId,pFileName,NULL,pPreprocessing); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API aiReturn aiExportSceneEx( const aiScene* pScene, const char* pFormatId, const char* pFileName, aiFileIO* pIO, unsigned int pPreprocessing ) +{ + Exporter exp; + + if (pIO) { + exp.SetIOHandler(new CIOSystemWrapper(pIO)); + } + return exp.Export(pScene,pFormatId,pFileName,pPreprocessing); +} + + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API const C_STRUCT aiExportDataBlob* aiExportSceneToBlob( const aiScene* pScene, const char* pFormatId, unsigned int pPreprocessing ) +{ + Exporter exp; + if (!exp.ExportToBlob(pScene,pFormatId,pPreprocessing)) { + return NULL; + } + const aiExportDataBlob* blob = exp.GetOrphanedBlob(); + ai_assert(blob); + + return blob; +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API C_STRUCT void aiReleaseExportBlob( const aiExportDataBlob* pData ) +{ + delete pData; +} + +#endif // !ASSIMP_BUILD_NO_EXPORT diff --git a/code/AssxmlExporter.cpp b/code/AssxmlExporter.cpp index eab3bd412..f1b6a2232 100644 --- a/code/AssxmlExporter.cpp +++ b/code/AssxmlExporter.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2015, 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 +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -23,16 +23,16 @@ following conditions are met: 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 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- @@ -48,9 +48,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../include/assimp/Exporter.hpp" #ifdef ASSIMP_BUILD_NO_OWN_ZLIB -# include +# include #else -# include "../contrib/zlib/zlib.h" +# include "../contrib/zlib/zlib.h" #endif #include @@ -61,94 +61,94 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; -namespace Assimp { +namespace Assimp { -namespace AssxmlExport { +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 ); + char sz[4096]; + va_list va; + va_start( va, format ); + int nSize = vsnprintf( sz, 4096, format, va ); ai_assert( nSize < 4096 ); - va_end( va ); + va_end( va ); - io->Write( sz, sizeof(char), nSize ); + io->Write( sz, sizeof(char), nSize ); - return 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("<");break; - case '>': - out.Append(">");break; - case '&': - out.Append("&");break; - case '\"': - out.Append(""");break; - case '\'': - out.Append("'");break; - default: - out.data[out.length++] = in.data[i]; - } - } - out.data[out.length] = 0; + out.length = 0; + for (unsigned int i = 0; i < in.length; ++i) { + switch (in.data[i]) { + case '<': + out.Append("<");break; + case '>': + out.Append(">");break; + case '&': + out.Append("&");break; + case '\"': + out.Append(""");break; + case '\'': + out.Append("'");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'; + char prefix[512]; + for (unsigned int i = 0; i < depth;++i) + prefix[i] = '\t'; + prefix[depth] = '\0'; - const aiMatrix4x4& m = node->mTransformation; + const aiMatrix4x4& m = node->mTransformation; - aiString name; - ConvertName(name,node->mName); - ioprintf(io,"%s \n" - "%s\t \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 \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); + aiString name; + ConvertName(name,node->mName); + ioprintf(io,"%s \n" + "%s\t \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 \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\n%s\t", - prefix,node->mNumMeshes,prefix); + if (node->mNumMeshes) { + ioprintf(io, "%s\t\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\n",prefix); - } + for (unsigned int i = 0; i < node->mNumMeshes;++i) { + ioprintf(io,"%i ",node->mMeshes[i]); + } + ioprintf(io,"\n%s\t\n",prefix); + } - if (node->mNumChildren) { - ioprintf(io,"%s\t\n", - prefix,node->mNumChildren); + if (node->mNumChildren) { + ioprintf(io,"%s\t\n", + prefix,node->mNumChildren); - for (unsigned int i = 0; i < node->mNumChildren;++i) { - WriteNode(node->mChildren[i],io,depth+2); - } - ioprintf(io,"%s\t\n",prefix); - } - ioprintf(io,"%s\n",prefix); + for (unsigned int i = 0; i < node->mNumChildren;++i) { + WriteNode(node->mChildren[i],io,depth+2); + } + ioprintf(io,"%s\t\n",prefix); + } + ioprintf(io,"%s\n",prefix); } @@ -156,19 +156,19 @@ void WriteNode(const aiNode* node, IOStream * io, unsigned int depth) // 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("&"); break; - case '\"': buffer.append("""); break; - case '\'': buffer.append("'"); break; - case '<': buffer.append("<"); break; - case '>': buffer.append(">"); break; - default: buffer.append(&data[pos], 1); break; - } - } - return buffer; + std::string buffer; + buffer.reserve(data.size()); + for(size_t pos = 0; pos != data.size(); ++pos) { + switch(data[pos]) { + case '&': buffer.append("&"); break; + case '\"': buffer.append("""); break; + case '\'': buffer.append("'"); break; + case '<': buffer.append("<"); break; + case '>': buffer.append(">"); break; + default: buffer.append(&data[pos], 1); break; + } + } + return buffer; } @@ -177,462 +177,462 @@ static std::string encodeXML(const std::string& data) { // Write a text model dump void WriteDump(const aiScene* scene, IOStream* io, bool shortened) { - time_t tt = ::time(NULL); - tm* p = ::gmtime(&tt); + time_t tt = ::time(NULL); + tm* p = ::gmtime(&tt); - aiString name; + aiString name; - // write header - ioprintf(io, - "\n" - "\n\n" + // write header + ioprintf(io, + "\n" + "\n\n" - "" - " \n\n" - "\n", - - aiGetVersionMajor(),aiGetVersionMinor(),aiGetVersionRevision(),asctime(p), - scene->mFlags, - 0 /*globalImporter->GetEffectivePostProcessing()*/); + "" + " \n\n" + "\n", - // write the node graph - WriteNode(scene->mRootNode, io, 0); + 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); + // 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\n" - "\t\t %0 8f %0 8f %0 8f \n" - "\t\t %0 8f %0 8f %0 8f \n" - "\t\t %0 8f %0 8f %0 8f \n" - "\t\t %f \n" - "\t\t %f \n" - "\t\t %f \n" - "\t\t %f \n" - "\t\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); - } + // camera header + ioprintf(io,"\t\n" + "\t\t %0 8f %0 8f %0 8f \n" + "\t\t %0 8f %0 8f %0 8f \n" + "\t\t %0 8f %0 8f %0 8f \n" + "\t\t %f \n" + "\t\t %f \n" + "\t\t %f \n" + "\t\t %f \n" + "\t\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); + // 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 type=\"%s\"\n" - "\t\t %0 8f %0 8f %0 8f \n" - "\t\t %0 8f %0 8f %0 8f \n" - "\t\t %0 8f %0 8f %0 8f \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); + // light header + ioprintf(io,"\t type=\"%s\"\n" + "\t\t %0 8f %0 8f %0 8f \n" + "\t\t %0 8f %0 8f %0 8f \n" + "\t\t %0 8f %0 8f %0 8f \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 %0 8f %0 8f %0 8f \n" - "\t\t %f \n" - "\t\t %f \n" - "\t\t %f \n", - l->mPosition.x,l->mPosition.y,l->mPosition.z, - l->mAttenuationConstant,l->mAttenuationLinear,l->mAttenuationQuadratic); - } + if (l->mType != aiLightSource_DIRECTIONAL) { + ioprintf(io, + "\t\t %0 8f %0 8f %0 8f \n" + "\t\t %f \n" + "\t\t %f \n" + "\t\t %f \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 %0 8f %0 8f %0 8f \n", - l->mDirection.x,l->mDirection.y,l->mDirection.z); - } + if (l->mType != aiLightSource_POINT) { + ioprintf(io, + "\t\t %0 8f %0 8f %0 8f \n", + l->mDirection.x,l->mDirection.y,l->mDirection.z); + } - if (l->mType == aiLightSource_SPOT) { - ioprintf(io, - "\t\t %f \n" - "\t\t %f \n", - l->mAngleOuterCone,l->mAngleInnerCone); - } - ioprintf(io,"\t\n"); - } + if (l->mType == aiLightSource_SPOT) { + ioprintf(io, + "\t\t %f \n" + "\t\t %f \n", + l->mAngleOuterCone,l->mAngleInnerCone); + } + ioprintf(io,"\t\n"); + } #endif - // write textures - if (scene->mNumTextures) { - ioprintf(io,"\n",scene->mNumTextures); - for (unsigned int i = 0; i < scene->mNumTextures;++i) { - aiTexture* tex = scene->mTextures[i]; - bool compressed = (tex->mHeight == 0); + // write textures + if (scene->mNumTextures) { + ioprintf(io,"\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 \n", - (compressed ? -1 : tex->mWidth),(compressed ? -1 : tex->mHeight), - (compressed ? "true" : "false")); + // mesh header + ioprintf(io,"\t \n", + (compressed ? -1 : tex->mWidth),(compressed ? -1 : tex->mHeight), + (compressed ? "true" : "false")); - if (compressed) { - ioprintf(io,"\t\t \n",tex->mWidth); + if (compressed) { + ioprintf(io,"\t\t \n",tex->mWidth); - if (!shortened) { - for (unsigned int n = 0; n < tex->mWidth;++n) { - ioprintf(io,"\t\t\t%2x",reinterpret_cast(tex->pcData)[n]); - if (n && !(n % 50)) { - ioprintf(io,"\n"); - } - } - } - } - else if (!shortened){ - ioprintf(io,"\t\t \n",tex->mWidth*tex->mHeight*4); + if (!shortened) { + for (unsigned int n = 0; n < tex->mWidth;++n) { + ioprintf(io,"\t\t\t%2x",reinterpret_cast(tex->pcData)[n]); + if (n && !(n % 50)) { + ioprintf(io,"\n"); + } + } + } + } + else if (!shortened){ + ioprintf(io,"\t\t \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); + // 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\n\t\n"); - } - ioprintf(io,"\n"); - } + // group by four for readibility + if (0 == (x+y*tex->mWidth) % 4) + ioprintf(io,"\n"); + } + } + } + ioprintf(io,"\t\t\n\t\n"); + } + ioprintf(io,"\n"); + } - // write materials - if (scene->mNumMaterials) { - ioprintf(io,"\n",scene->mNumMaterials); - for (unsigned int i = 0; i< scene->mNumMaterials; ++i) { - const aiMaterial* mat = scene->mMaterials[i]; + // write materials + if (scene->mNumMaterials) { + ioprintf(io,"\n",scene->mNumMaterials); + for (unsigned int i = 0; i< scene->mNumMaterials; ++i) { + const aiMaterial* mat = scene->mMaterials[i]; - ioprintf(io,"\t\n"); - ioprintf(io,"\t\t\n",mat->mNumProperties); - for (unsigned int n = 0; n < mat->mNumProperties;++n) { + ioprintf(io,"\t\n"); + ioprintf(io,"\t\t\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"; - } + 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\tmKey.data, sz, - ::TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex); + ioprintf(io,"\t\t\tmKey.data, sz, + ::TextureTypeToString((aiTextureType)prop->mSemantic),prop->mIndex); - if (prop->mType == aiPTI_Float) { - ioprintf(io," size=\"%i\">\n\t\t\t\t", - static_cast(prop->mDataLength/sizeof(float))); + if (prop->mType == aiPTI_Float) { + ioprintf(io," size=\"%i\">\n\t\t\t\t", + static_cast(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(prop->mDataLength/sizeof(int))); + 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(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(prop->mDataLength)); + 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(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\n"); - } - ioprintf(io,"\t\t\n"); - ioprintf(io,"\t\n"); - } - ioprintf(io,"\n"); - } + 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\n"); + } + ioprintf(io,"\t\t\n"); + ioprintf(io,"\t\n"); + } + ioprintf(io,"\n"); + } - // write animations - if (scene->mNumAnimations) { - ioprintf(io,"\n",scene->mNumAnimations); - for (unsigned int i = 0; i < scene->mNumAnimations;++i) { - aiAnimation* anim = scene->mAnimations[i]; + // write animations + if (scene->mNumAnimations) { + ioprintf(io,"\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\n", - name.data, anim->mDuration, anim->mTicksPerSecond); + // anim header + ConvertName(name,anim->mName); + ioprintf(io,"\t\n", + name.data, anim->mDuration, anim->mTicksPerSecond); - // write bone animation channels - if (anim->mNumChannels) { - ioprintf(io,"\t\t\n",anim->mNumChannels); - for (unsigned int n = 0; n < anim->mNumChannels;++n) { - aiNodeAnim* nd = anim->mChannels[n]; + // write bone animation channels + if (anim->mNumChannels) { + ioprintf(io,"\t\t\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\n",name.data); + // node anim header + ConvertName(name,nd->mNodeName); + ioprintf(io,"\t\t\t\n",name.data); - if (!shortened) { - // write position keys - if (nd->mNumPositionKeys) { - ioprintf(io,"\t\t\t\t\n",nd->mNumPositionKeys); - for (unsigned int a = 0; a < nd->mNumPositionKeys;++a) { - aiVectorKey* vc = nd->mPositionKeys+a; - ioprintf(io,"\t\t\t\t\t\n" - "\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t\n", - vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z); - } - ioprintf(io,"\t\t\t\t\n"); - } + if (!shortened) { + // write position keys + if (nd->mNumPositionKeys) { + ioprintf(io,"\t\t\t\t\n",nd->mNumPositionKeys); + for (unsigned int a = 0; a < nd->mNumPositionKeys;++a) { + aiVectorKey* vc = nd->mPositionKeys+a; + ioprintf(io,"\t\t\t\t\t\n" + "\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t\n", + vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z); + } + ioprintf(io,"\t\t\t\t\n"); + } - // write scaling keys - if (nd->mNumScalingKeys) { - ioprintf(io,"\t\t\t\t\n",nd->mNumScalingKeys); - for (unsigned int a = 0; a < nd->mNumScalingKeys;++a) { - aiVectorKey* vc = nd->mScalingKeys+a; - ioprintf(io,"\t\t\t\t\t\n" - "\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t\n", - vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z); - } - ioprintf(io,"\t\t\t\t\n"); - } + // write scaling keys + if (nd->mNumScalingKeys) { + ioprintf(io,"\t\t\t\t\n",nd->mNumScalingKeys); + for (unsigned int a = 0; a < nd->mNumScalingKeys;++a) { + aiVectorKey* vc = nd->mScalingKeys+a; + ioprintf(io,"\t\t\t\t\t\n" + "\t\t\t\t\t\t%0 8f %0 8f %0 8f\n\t\t\t\t\t\n", + vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z); + } + ioprintf(io,"\t\t\t\t\n"); + } - // write rotation keys - if (nd->mNumRotationKeys) { - ioprintf(io,"\t\t\t\t\n",nd->mNumRotationKeys); - for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) { - aiQuatKey* vc = nd->mRotationKeys+a; - ioprintf(io,"\t\t\t\t\t\n" - "\t\t\t\t\t\t%0 8f %0 8f %0 8f %0 8f\n\t\t\t\t\t\n", - vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z,vc->mValue.w); - } - ioprintf(io,"\t\t\t\t\n"); - } - } - ioprintf(io,"\t\t\t\n"); - } - ioprintf(io,"\t\t\n"); - } - ioprintf(io,"\t\n"); - } - ioprintf(io,"\n"); - } + // write rotation keys + if (nd->mNumRotationKeys) { + ioprintf(io,"\t\t\t\t\n",nd->mNumRotationKeys); + for (unsigned int a = 0; a < nd->mNumRotationKeys;++a) { + aiQuatKey* vc = nd->mRotationKeys+a; + ioprintf(io,"\t\t\t\t\t\n" + "\t\t\t\t\t\t%0 8f %0 8f %0 8f %0 8f\n\t\t\t\t\t\n", + vc->mTime,vc->mValue.x,vc->mValue.y,vc->mValue.z,vc->mValue.w); + } + ioprintf(io,"\t\t\t\t\n"); + } + } + ioprintf(io,"\t\t\t\n"); + } + ioprintf(io,"\t\t\n"); + } + ioprintf(io,"\t\n"); + } + ioprintf(io,"\n"); + } - // write meshes - if (scene->mNumMeshes) { - ioprintf(io,"\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; + // write meshes + if (scene->mNumMeshes) { + ioprintf(io,"\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\n", - (mesh->mPrimitiveTypes & aiPrimitiveType_POINT ? "points" : ""), - (mesh->mPrimitiveTypes & aiPrimitiveType_LINE ? "lines" : ""), - (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE ? "triangles" : ""), - (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON ? "polygons" : ""), - mesh->mMaterialIndex); + // mesh header + ioprintf(io,"\t\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\n",mesh->mNumBones); + // bones + if (mesh->mNumBones) { + ioprintf(io,"\t\t\n",mesh->mNumBones); - for (unsigned int n = 0; n < mesh->mNumBones;++n) { - aiBone* bone = mesh->mBones[n]; + 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\n" - "\t\t\t\t \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 \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); + ConvertName(name,bone->mName); + // bone header + ioprintf(io,"\t\t\t\n" + "\t\t\t\t \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 \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\n",bone->mNumWeights); + if (!shortened && bone->mNumWeights) { + ioprintf(io,"\t\t\t\t\n",bone->mNumWeights); - // bone weights - for (unsigned int a = 0; a < bone->mNumWeights;++a) { - aiVertexWeight* wght = bone->mWeights+a; + // bone weights + for (unsigned int a = 0; a < bone->mNumWeights;++a) { + aiVertexWeight* wght = bone->mWeights+a; - ioprintf(io,"\t\t\t\t\t\n\t\t\t\t\t\t%f\n\t\t\t\t\t\n", - wght->mVertexId,wght->mWeight); - } - ioprintf(io,"\t\t\t\t\n"); - } - ioprintf(io,"\t\t\t\n"); - } - ioprintf(io,"\t\t\n"); - } + ioprintf(io,"\t\t\t\t\t\n\t\t\t\t\t\t%f\n\t\t\t\t\t\n", + wght->mVertexId,wght->mWeight); + } + ioprintf(io,"\t\t\t\t\n"); + } + ioprintf(io,"\t\t\t\n"); + } + ioprintf(io,"\t\t\n"); + } - // faces - if (!shortened && mesh->mNumFaces) { - ioprintf(io,"\t\t\n",mesh->mNumFaces); - for (unsigned int n = 0; n < mesh->mNumFaces; ++n) { - aiFace& f = mesh->mFaces[n]; - ioprintf(io,"\t\t\t\n" - "\t\t\t\t",f.mNumIndices); + // faces + if (!shortened && mesh->mNumFaces) { + ioprintf(io,"\t\t\n",mesh->mNumFaces); + for (unsigned int n = 0; n < mesh->mNumFaces; ++n) { + aiFace& f = mesh->mFaces[n]; + ioprintf(io,"\t\t\t\n" + "\t\t\t\t",f.mNumIndices); - for (unsigned int j = 0; j < f.mNumIndices;++j) - ioprintf(io,"%i ",f.mIndices[j]); + for (unsigned int j = 0; j < f.mNumIndices;++j) + ioprintf(io,"%i ",f.mIndices[j]); - ioprintf(io,"\n\t\t\t\n"); - } - ioprintf(io,"\t\t\n"); - } + ioprintf(io,"\n\t\t\t\n"); + } + ioprintf(io,"\t\t\n"); + } - // vertex positions - if (mesh->HasPositions()) { - ioprintf(io,"\t\t \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\n"); - } + // vertex positions + if (mesh->HasPositions()) { + ioprintf(io,"\t\t \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\n"); + } - // vertex normals - if (mesh->HasNormals()) { - ioprintf(io,"\t\t \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\n"); - } + // vertex normals + if (mesh->HasNormals()) { + ioprintf(io,"\t\t \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\n"); + } - // vertex tangents and bitangents - if (mesh->HasTangentsAndBitangents()) { - ioprintf(io,"\t\t \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\n"); + // vertex tangents and bitangents + if (mesh->HasTangentsAndBitangents()) { + ioprintf(io,"\t\t \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\n"); - ioprintf(io,"\t\t \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\n"); - } + ioprintf(io,"\t\t \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\n"); + } - // texture coordinates - for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { - if (!mesh->mTextureCoords[a]) - break; + // texture coordinates + for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) { + if (!mesh->mTextureCoords[a]) + break; - ioprintf(io,"\t\t \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\n"); - } + ioprintf(io,"\t\t \n",mesh->mNumVertices, + a,mesh->mNumUVComponents[a]); - // vertex colors - for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a) { - if (!mesh->mColors[a]) - break; - ioprintf(io,"\t\t \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\n"); - } - ioprintf(io,"\t\n"); - } - ioprintf(io,"\n"); - } - ioprintf(io,"\n"); + 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\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 \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\n"); + } + ioprintf(io,"\t\n"); + } + ioprintf(io,"\n"); + } + ioprintf(io,"\n"); } } // end of namespace AssxmlExport void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties) { - IOStream * out = pIOSystem->Open( pFile, "wt" ); - if (!out) return; + IOStream * out = pIOSystem->Open( pFile, "wt" ); + if (!out) return; - bool shortened = false; - AssxmlExport::WriteDump( pScene, out, shortened ); + bool shortened = false; + AssxmlExport::WriteDump( pScene, out, shortened ); - pIOSystem->Close( out ); + pIOSystem->Close( out ); } } // end of namespace Assimp diff --git a/code/AssxmlExporter.h b/code/AssxmlExporter.h index 89534c7c9..37a91242a 100644 --- a/code/AssxmlExporter.h +++ b/code/AssxmlExporter.h @@ -5,8 +5,8 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2015, 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 +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -23,16 +23,16 @@ following conditions are met: 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 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- diff --git a/code/B3DImporter.cpp b/code/B3DImporter.cpp index c12309238..2bb4ff68f 100644 --- a/code/B3DImporter.cpp +++ b/code/B3DImporter.cpp @@ -1,695 +1,695 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file B3DImporter.cpp - * @brief Implementation of the b3d importer class - */ - - -#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER - -// internal headers -#include "B3DImporter.h" -#include "TextureTransform.h" +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file B3DImporter.cpp + * @brief Implementation of the b3d importer class + */ + + +#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER + +// internal headers +#include "B3DImporter.h" +#include "TextureTransform.h" #include "ConvertToLHProcess.h" -#include +#include #include "../include/assimp/IOSystem.hpp" -#include "../include/assimp/anim.h" -#include "../include/assimp/scene.h" -#include "../include/assimp/DefaultLogger.hpp" - - -using namespace Assimp; -using namespace std; - -static const aiImporterDesc desc = { - "BlitzBasic 3D Importer", - "", - "", - "http://www.blitzbasic.com/", - aiImporterFlags_SupportBinaryFlavour, - 0, - 0, - 0, - 0, - "b3d" -}; - -// (fixme, Aramis) quick workaround to get rid of all those signed to unsigned warnings -#ifdef _MSC_VER -# pragma warning (disable: 4018) -#endif - -//#define DEBUG_B3D - -// ------------------------------------------------------------------------------------------------ -bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const{ - - size_t pos=pFile.find_last_of( '.' ); - if( pos==string::npos ) return false; - - string ext=pFile.substr( pos+1 ); - if( ext.size()!=3 ) return false; - - return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D'); -} - -// ------------------------------------------------------------------------------------------------ -// Loader meta information -const aiImporterDesc* B3DImporter::GetInfo () const -{ - return &desc; -} - -#ifdef DEBUG_B3D - extern "C"{ void _stdcall AllocConsole(); } -#endif -// ------------------------------------------------------------------------------------------------ -void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){ - -#ifdef DEBUG_B3D - AllocConsole(); - freopen( "conin$","r",stdin ); - freopen( "conout$","w",stdout ); - freopen( "conout$","w",stderr ); - cout<<"Hello world from the B3DImporter!"< file( pIOHandler->Open( pFile)); - - // Check whether we can read from the file - if( file.get() == NULL) - throw DeadlyImportError( "Failed to open B3D file " + pFile + "."); - - // check whether the .b3d file is large enough to contain - // at least one chunk. - size_t fileSize = file->FileSize(); - if( fileSize<8 ) throw DeadlyImportError( "B3D File is too small."); - - _pos=0; - _buf.resize( fileSize ); - file->Read( &_buf[0],1,fileSize ); - _stack.clear(); - - ReadBB3D( pScene ); -} - -// ------------------------------------------------------------------------------------------------ -AI_WONT_RETURN void B3DImporter::Oops(){ - throw DeadlyImportError( "B3D Importer - INTERNAL ERROR" ); -} - -// ------------------------------------------------------------------------------------------------ -AI_WONT_RETURN void B3DImporter::Fail( string str ){ -#ifdef DEBUG_B3D - cout<<"Error in B3D file data: "< -T *B3DImporter::to_array( const vector &v ){ - if( v.empty() ) { - return 0; - } - T *p=new T[ v.size() ]; - for( size_t i=0;i8 ){ - Fail( "Bad texture count" ); - } - while( ChunkSize() ){ - string name=ReadString(); - aiVector3D color=ReadVec3(); - float alpha=ReadFloat(); - float shiny=ReadFloat(); - /*int blend=**/ReadInt(); - int fx=ReadInt(); - - aiMaterial *mat=new aiMaterial; - _materials.push_back( mat ); - - // Name - aiString ainame( name ); - mat->AddProperty( &ainame,AI_MATKEY_NAME ); - - // Diffuse color - mat->AddProperty( &color,1,AI_MATKEY_COLOR_DIFFUSE ); - - // Opacity - mat->AddProperty( &alpha,1,AI_MATKEY_OPACITY ); - - // Specular color - aiColor3D speccolor( shiny,shiny,shiny ); - mat->AddProperty( &speccolor,1,AI_MATKEY_COLOR_SPECULAR ); - - // Specular power - float specpow=shiny*128; - mat->AddProperty( &specpow,1,AI_MATKEY_SHININESS ); - - // Double sided - if( fx & 0x10 ){ - int i=1; - mat->AddProperty( &i,1,AI_MATKEY_TWOSIDED ); - } - - //Textures - for( int i=0;i=0 && texid>=static_cast(_textures.size())) ){ - Fail( "Bad texture id" ); - } - if( i==0 && texid>=0 ){ - aiString texname( _textures[texid] ); - mat->AddProperty( &texname,AI_MATKEY_TEXTURE_DIFFUSE(0) ); - } - } - } -} - -// ------------------------------------------------------------------------------------------------ -void B3DImporter::ReadVRTS(){ - _vflags=ReadInt(); - _tcsets=ReadInt(); - _tcsize=ReadInt(); - if( _tcsets<0 || _tcsets>4 || _tcsize<0 || _tcsize>4 ){ - Fail( "Bad texcoord data" ); - } - - int sz=12+(_vflags&1?12:0)+(_vflags&2?16:0)+(_tcsets*_tcsize*4); - int n_verts=ChunkSize()/sz; - - int v0=_vertices.size(); - _vertices.resize( v0+n_verts ); - - for( int i=0;i=(int)_materials.size() ){ -#ifdef DEBUG_B3D - cout<<"material id="<mMaterialIndex=matid; - mesh->mNumFaces=0; - mesh->mPrimitiveTypes=aiPrimitiveType_TRIANGLE; - - int n_tris=ChunkSize()/12; - aiFace *face=mesh->mFaces=new aiFace[n_tris]; - - for( int i=0;i=(int)_vertices.size() || i1<0 || i1>=(int)_vertices.size() || i2<0 || i2>=(int)_vertices.size() ){ -#ifdef DEBUG_B3D - cout<<"Bad triangle index: i0="<mNumIndices=3; - face->mIndices=new unsigned[3]; - face->mIndices[0]=i0; - face->mIndices[1]=i1; - face->mIndices[2]=i2; - ++mesh->mNumFaces; - ++face; - } -} - -// ------------------------------------------------------------------------------------------------ -void B3DImporter::ReadMESH(){ - /*int matid=*/ReadInt(); - - int v0=_vertices.size(); - - while( ChunkSize() ){ - string t=ReadChunk(); - if( t=="VRTS" ){ - ReadVRTS(); - }else if( t=="TRIS" ){ - ReadTRIS( v0 ); - } - ExitChunk(); - } -} - -// ------------------------------------------------------------------------------------------------ -void B3DImporter::ReadBONE( int id ){ - while( ChunkSize() ){ - int vertex=ReadInt(); - float weight=ReadFloat(); - if( vertex<0 || vertex>=(int)_vertices.size() ){ - Fail( "Bad vertex index" ); - } - - Vertex &v=_vertices[vertex]; - int i; - for( i=0;i<4;++i ){ - if( !v.weights[i] ){ - v.bones[i]=id; - v.weights[i]=weight; - break; - } - } -#ifdef DEBUG_B3D - if( i==4 ){ - cout<<"Too many bone weights"< trans,scale; - vector rot; - int flags=ReadInt(); - while( ChunkSize() ){ - int frame=ReadInt(); - if( flags & 1 ){ - trans.push_back( aiVectorKey( frame,ReadVec3() ) ); - } - if( flags & 2 ){ - scale.push_back( aiVectorKey( frame,ReadVec3() ) ); - } - if( flags & 4 ){ - rot.push_back( aiQuatKey( frame,ReadQuat() ) ); - } - } - - if( flags & 1 ){ - nodeAnim->mNumPositionKeys=trans.size(); - nodeAnim->mPositionKeys=to_array( trans ); - } - - if( flags & 2 ){ - nodeAnim->mNumScalingKeys=scale.size(); - nodeAnim->mScalingKeys=to_array( scale ); - } - - if( flags & 4 ){ - nodeAnim->mNumRotationKeys=rot.size(); - nodeAnim->mRotationKeys=to_array( rot ); - } -} - -// ------------------------------------------------------------------------------------------------ -void B3DImporter::ReadANIM(){ - /*int flags=*/ReadInt(); - int frames=ReadInt(); - float fps=ReadFloat(); - - aiAnimation *anim=new aiAnimation; - _animations.push_back( anim ); - - anim->mDuration=frames; - anim->mTicksPerSecond=fps; -} - -// ------------------------------------------------------------------------------------------------ -aiNode *B3DImporter::ReadNODE( aiNode *parent ){ - - string name=ReadString(); - aiVector3D t=ReadVec3(); - aiVector3D s=ReadVec3(); - aiQuaternion r=ReadQuat(); - - aiMatrix4x4 trans,scale,rot; - - aiMatrix4x4::Translation( t,trans ); - aiMatrix4x4::Scaling( s,scale ); - rot=aiMatrix4x4( r.GetMatrix() ); - - aiMatrix4x4 tform=trans * rot * scale; - - int nodeid=_nodes.size(); - - aiNode *node=new aiNode( name ); - _nodes.push_back( node ); - - node->mParent=parent; - node->mTransformation=tform; - - aiNodeAnim *nodeAnim=0; - vector meshes; - vector children; - - while( ChunkSize() ){ - string t=ReadChunk(); - if( t=="MESH" ){ - int n=_meshes.size(); - ReadMESH(); - for( int i=n;i<(int)_meshes.size();++i ){ - meshes.push_back( i ); - } - }else if( t=="BONE" ){ - ReadBONE( nodeid ); - }else if( t=="ANIM" ){ - ReadANIM(); - }else if( t=="KEYS" ){ - if( !nodeAnim ){ - nodeAnim=new aiNodeAnim; - _nodeAnims.push_back( nodeAnim ); - nodeAnim->mNodeName=node->mName; - } - ReadKEYS( nodeAnim ); - }else if( t=="NODE" ){ - aiNode *child=ReadNODE( node ); - children.push_back( child ); - } - ExitChunk(); - } - - node->mNumMeshes=meshes.size(); - node->mMeshes=to_array( meshes ); - - node->mNumChildren=children.size(); - node->mChildren=to_array( children ); - - return node; -} - -// ------------------------------------------------------------------------------------------------ -void B3DImporter::ReadBB3D( aiScene *scene ){ - - _textures.clear(); - _materials.clear(); - - _vertices.clear(); - _meshes.clear(); - - _nodes.clear(); - _nodeAnims.clear(); - _animations.clear(); - - string t=ReadChunk(); - if( t=="BB3D" ){ - int version=ReadInt(); - - if (!DefaultLogger::isNullLogger()) { - char dmp[128]; - sprintf(dmp,"B3D file format version: %i",version); - DefaultLogger::get()->info(dmp); - } - - while( ChunkSize() ){ - string t=ReadChunk(); - if( t=="TEXS" ){ - ReadTEXS(); - }else if( t=="BRUS" ){ - ReadBRUS(); - }else if( t=="NODE" ){ - ReadNODE( 0 ); - } - ExitChunk(); - } - } - ExitChunk(); - - if( !_nodes.size() ) Fail( "No nodes" ); - - if( !_meshes.size() ) Fail( "No meshes" ); - - //Fix nodes/meshes/bones - for(size_t i=0;i<_nodes.size();++i ){ - aiNode *node=_nodes[i]; - - for( size_t j=0;jmNumMeshes;++j ){ - aiMesh *mesh=_meshes[node->mMeshes[j]]; - - int n_tris=mesh->mNumFaces; - int n_verts=mesh->mNumVertices=n_tris * 3; - - aiVector3D *mv=mesh->mVertices=new aiVector3D[ n_verts ],*mn=0,*mc=0; - if( _vflags & 1 ) mn=mesh->mNormals=new aiVector3D[ n_verts ]; - if( _tcsets ) mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ]; - - aiFace *face=mesh->mFaces; - - vector< vector > vweights( _nodes.size() ); - - for( int i=0;imIndices[j]]; - - *mv++=v.vertex; - if( mn ) *mn++=v.normal; - if( mc ) *mc++=v.texcoords; - - face->mIndices[j]=i+j; - - for( int k=0;k<4;++k ){ - if( !v.weights[k] ) break; - - int bone=v.bones[k]; - float weight=v.weights[k]; - - vweights[bone].push_back( aiVertexWeight(i+j,weight) ); - } - } - ++face; - } - - vector bones; - for(size_t i=0;i &weights=vweights[i]; - if( !weights.size() ) continue; - - aiBone *bone=new aiBone; - bones.push_back( bone ); - - aiNode *bnode=_nodes[i]; - - bone->mName=bnode->mName; - bone->mNumWeights=weights.size(); - bone->mWeights=to_array( weights ); - - aiMatrix4x4 mat=bnode->mTransformation; - while( bnode->mParent ){ - bnode=bnode->mParent; - mat=bnode->mTransformation * mat; - } - bone->mOffsetMatrix=mat.Inverse(); - } - mesh->mNumBones=bones.size(); - mesh->mBones=to_array( bones ); - } - } - - //nodes - scene->mRootNode=_nodes[0]; - - //material - if( !_materials.size() ){ - _materials.push_back( new aiMaterial ); - } - scene->mNumMaterials=_materials.size(); - scene->mMaterials=to_array( _materials ); - - //meshes - scene->mNumMeshes=_meshes.size(); - scene->mMeshes=to_array( _meshes ); - - //animations - if( _animations.size()==1 && _nodeAnims.size() ){ - - aiAnimation *anim=_animations.back(); - anim->mNumChannels=_nodeAnims.size(); - anim->mChannels=to_array( _nodeAnims ); - - scene->mNumAnimations=_animations.size(); - scene->mAnimations=to_array( _animations ); - } - - // convert to RH - MakeLeftHandedProcess makeleft; - makeleft.Execute( scene ); - - FlipWindingOrderProcess flip; - flip.Execute( scene ); -} - -#endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER +#include "../include/assimp/anim.h" +#include "../include/assimp/scene.h" +#include "../include/assimp/DefaultLogger.hpp" + + +using namespace Assimp; +using namespace std; + +static const aiImporterDesc desc = { + "BlitzBasic 3D Importer", + "", + "", + "http://www.blitzbasic.com/", + aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "b3d" +}; + +// (fixme, Aramis) quick workaround to get rid of all those signed to unsigned warnings +#ifdef _MSC_VER +# pragma warning (disable: 4018) +#endif + +//#define DEBUG_B3D + +// ------------------------------------------------------------------------------------------------ +bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const{ + + size_t pos=pFile.find_last_of( '.' ); + if( pos==string::npos ) return false; + + string ext=pFile.substr( pos+1 ); + if( ext.size()!=3 ) return false; + + return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D'); +} + +// ------------------------------------------------------------------------------------------------ +// Loader meta information +const aiImporterDesc* B3DImporter::GetInfo () const +{ + return &desc; +} + +#ifdef DEBUG_B3D + extern "C"{ void _stdcall AllocConsole(); } +#endif +// ------------------------------------------------------------------------------------------------ +void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){ + +#ifdef DEBUG_B3D + AllocConsole(); + freopen( "conin$","r",stdin ); + freopen( "conout$","w",stdout ); + freopen( "conout$","w",stderr ); + cout<<"Hello world from the B3DImporter!"< file( pIOHandler->Open( pFile)); + + // Check whether we can read from the file + if( file.get() == NULL) + throw DeadlyImportError( "Failed to open B3D file " + pFile + "."); + + // check whether the .b3d file is large enough to contain + // at least one chunk. + size_t fileSize = file->FileSize(); + if( fileSize<8 ) throw DeadlyImportError( "B3D File is too small."); + + _pos=0; + _buf.resize( fileSize ); + file->Read( &_buf[0],1,fileSize ); + _stack.clear(); + + ReadBB3D( pScene ); +} + +// ------------------------------------------------------------------------------------------------ +AI_WONT_RETURN void B3DImporter::Oops(){ + throw DeadlyImportError( "B3D Importer - INTERNAL ERROR" ); +} + +// ------------------------------------------------------------------------------------------------ +AI_WONT_RETURN void B3DImporter::Fail( string str ){ +#ifdef DEBUG_B3D + cout<<"Error in B3D file data: "< +T *B3DImporter::to_array( const vector &v ){ + if( v.empty() ) { + return 0; + } + T *p=new T[ v.size() ]; + for( size_t i=0;i8 ){ + Fail( "Bad texture count" ); + } + while( ChunkSize() ){ + string name=ReadString(); + aiVector3D color=ReadVec3(); + float alpha=ReadFloat(); + float shiny=ReadFloat(); + /*int blend=**/ReadInt(); + int fx=ReadInt(); + + aiMaterial *mat=new aiMaterial; + _materials.push_back( mat ); + + // Name + aiString ainame( name ); + mat->AddProperty( &ainame,AI_MATKEY_NAME ); + + // Diffuse color + mat->AddProperty( &color,1,AI_MATKEY_COLOR_DIFFUSE ); + + // Opacity + mat->AddProperty( &alpha,1,AI_MATKEY_OPACITY ); + + // Specular color + aiColor3D speccolor( shiny,shiny,shiny ); + mat->AddProperty( &speccolor,1,AI_MATKEY_COLOR_SPECULAR ); + + // Specular power + float specpow=shiny*128; + mat->AddProperty( &specpow,1,AI_MATKEY_SHININESS ); + + // Double sided + if( fx & 0x10 ){ + int i=1; + mat->AddProperty( &i,1,AI_MATKEY_TWOSIDED ); + } + + //Textures + for( int i=0;i=0 && texid>=static_cast(_textures.size())) ){ + Fail( "Bad texture id" ); + } + if( i==0 && texid>=0 ){ + aiString texname( _textures[texid] ); + mat->AddProperty( &texname,AI_MATKEY_TEXTURE_DIFFUSE(0) ); + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::ReadVRTS(){ + _vflags=ReadInt(); + _tcsets=ReadInt(); + _tcsize=ReadInt(); + if( _tcsets<0 || _tcsets>4 || _tcsize<0 || _tcsize>4 ){ + Fail( "Bad texcoord data" ); + } + + int sz=12+(_vflags&1?12:0)+(_vflags&2?16:0)+(_tcsets*_tcsize*4); + int n_verts=ChunkSize()/sz; + + int v0=_vertices.size(); + _vertices.resize( v0+n_verts ); + + for( int i=0;i=(int)_materials.size() ){ +#ifdef DEBUG_B3D + cout<<"material id="<mMaterialIndex=matid; + mesh->mNumFaces=0; + mesh->mPrimitiveTypes=aiPrimitiveType_TRIANGLE; + + int n_tris=ChunkSize()/12; + aiFace *face=mesh->mFaces=new aiFace[n_tris]; + + for( int i=0;i=(int)_vertices.size() || i1<0 || i1>=(int)_vertices.size() || i2<0 || i2>=(int)_vertices.size() ){ +#ifdef DEBUG_B3D + cout<<"Bad triangle index: i0="<mNumIndices=3; + face->mIndices=new unsigned[3]; + face->mIndices[0]=i0; + face->mIndices[1]=i1; + face->mIndices[2]=i2; + ++mesh->mNumFaces; + ++face; + } +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::ReadMESH(){ + /*int matid=*/ReadInt(); + + int v0=_vertices.size(); + + while( ChunkSize() ){ + string t=ReadChunk(); + if( t=="VRTS" ){ + ReadVRTS(); + }else if( t=="TRIS" ){ + ReadTRIS( v0 ); + } + ExitChunk(); + } +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::ReadBONE( int id ){ + while( ChunkSize() ){ + int vertex=ReadInt(); + float weight=ReadFloat(); + if( vertex<0 || vertex>=(int)_vertices.size() ){ + Fail( "Bad vertex index" ); + } + + Vertex &v=_vertices[vertex]; + int i; + for( i=0;i<4;++i ){ + if( !v.weights[i] ){ + v.bones[i]=id; + v.weights[i]=weight; + break; + } + } +#ifdef DEBUG_B3D + if( i==4 ){ + cout<<"Too many bone weights"< trans,scale; + vector rot; + int flags=ReadInt(); + while( ChunkSize() ){ + int frame=ReadInt(); + if( flags & 1 ){ + trans.push_back( aiVectorKey( frame,ReadVec3() ) ); + } + if( flags & 2 ){ + scale.push_back( aiVectorKey( frame,ReadVec3() ) ); + } + if( flags & 4 ){ + rot.push_back( aiQuatKey( frame,ReadQuat() ) ); + } + } + + if( flags & 1 ){ + nodeAnim->mNumPositionKeys=trans.size(); + nodeAnim->mPositionKeys=to_array( trans ); + } + + if( flags & 2 ){ + nodeAnim->mNumScalingKeys=scale.size(); + nodeAnim->mScalingKeys=to_array( scale ); + } + + if( flags & 4 ){ + nodeAnim->mNumRotationKeys=rot.size(); + nodeAnim->mRotationKeys=to_array( rot ); + } +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::ReadANIM(){ + /*int flags=*/ReadInt(); + int frames=ReadInt(); + float fps=ReadFloat(); + + aiAnimation *anim=new aiAnimation; + _animations.push_back( anim ); + + anim->mDuration=frames; + anim->mTicksPerSecond=fps; +} + +// ------------------------------------------------------------------------------------------------ +aiNode *B3DImporter::ReadNODE( aiNode *parent ){ + + string name=ReadString(); + aiVector3D t=ReadVec3(); + aiVector3D s=ReadVec3(); + aiQuaternion r=ReadQuat(); + + aiMatrix4x4 trans,scale,rot; + + aiMatrix4x4::Translation( t,trans ); + aiMatrix4x4::Scaling( s,scale ); + rot=aiMatrix4x4( r.GetMatrix() ); + + aiMatrix4x4 tform=trans * rot * scale; + + int nodeid=_nodes.size(); + + aiNode *node=new aiNode( name ); + _nodes.push_back( node ); + + node->mParent=parent; + node->mTransformation=tform; + + aiNodeAnim *nodeAnim=0; + vector meshes; + vector children; + + while( ChunkSize() ){ + string t=ReadChunk(); + if( t=="MESH" ){ + int n=_meshes.size(); + ReadMESH(); + for( int i=n;i<(int)_meshes.size();++i ){ + meshes.push_back( i ); + } + }else if( t=="BONE" ){ + ReadBONE( nodeid ); + }else if( t=="ANIM" ){ + ReadANIM(); + }else if( t=="KEYS" ){ + if( !nodeAnim ){ + nodeAnim=new aiNodeAnim; + _nodeAnims.push_back( nodeAnim ); + nodeAnim->mNodeName=node->mName; + } + ReadKEYS( nodeAnim ); + }else if( t=="NODE" ){ + aiNode *child=ReadNODE( node ); + children.push_back( child ); + } + ExitChunk(); + } + + node->mNumMeshes=meshes.size(); + node->mMeshes=to_array( meshes ); + + node->mNumChildren=children.size(); + node->mChildren=to_array( children ); + + return node; +} + +// ------------------------------------------------------------------------------------------------ +void B3DImporter::ReadBB3D( aiScene *scene ){ + + _textures.clear(); + _materials.clear(); + + _vertices.clear(); + _meshes.clear(); + + _nodes.clear(); + _nodeAnims.clear(); + _animations.clear(); + + string t=ReadChunk(); + if( t=="BB3D" ){ + int version=ReadInt(); + + if (!DefaultLogger::isNullLogger()) { + char dmp[128]; + sprintf(dmp,"B3D file format version: %i",version); + DefaultLogger::get()->info(dmp); + } + + while( ChunkSize() ){ + string t=ReadChunk(); + if( t=="TEXS" ){ + ReadTEXS(); + }else if( t=="BRUS" ){ + ReadBRUS(); + }else if( t=="NODE" ){ + ReadNODE( 0 ); + } + ExitChunk(); + } + } + ExitChunk(); + + if( !_nodes.size() ) Fail( "No nodes" ); + + if( !_meshes.size() ) Fail( "No meshes" ); + + //Fix nodes/meshes/bones + for(size_t i=0;i<_nodes.size();++i ){ + aiNode *node=_nodes[i]; + + for( size_t j=0;jmNumMeshes;++j ){ + aiMesh *mesh=_meshes[node->mMeshes[j]]; + + int n_tris=mesh->mNumFaces; + int n_verts=mesh->mNumVertices=n_tris * 3; + + aiVector3D *mv=mesh->mVertices=new aiVector3D[ n_verts ],*mn=0,*mc=0; + if( _vflags & 1 ) mn=mesh->mNormals=new aiVector3D[ n_verts ]; + if( _tcsets ) mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ]; + + aiFace *face=mesh->mFaces; + + vector< vector > vweights( _nodes.size() ); + + for( int i=0;imIndices[j]]; + + *mv++=v.vertex; + if( mn ) *mn++=v.normal; + if( mc ) *mc++=v.texcoords; + + face->mIndices[j]=i+j; + + for( int k=0;k<4;++k ){ + if( !v.weights[k] ) break; + + int bone=v.bones[k]; + float weight=v.weights[k]; + + vweights[bone].push_back( aiVertexWeight(i+j,weight) ); + } + } + ++face; + } + + vector bones; + for(size_t i=0;i &weights=vweights[i]; + if( !weights.size() ) continue; + + aiBone *bone=new aiBone; + bones.push_back( bone ); + + aiNode *bnode=_nodes[i]; + + bone->mName=bnode->mName; + bone->mNumWeights=weights.size(); + bone->mWeights=to_array( weights ); + + aiMatrix4x4 mat=bnode->mTransformation; + while( bnode->mParent ){ + bnode=bnode->mParent; + mat=bnode->mTransformation * mat; + } + bone->mOffsetMatrix=mat.Inverse(); + } + mesh->mNumBones=bones.size(); + mesh->mBones=to_array( bones ); + } + } + + //nodes + scene->mRootNode=_nodes[0]; + + //material + if( !_materials.size() ){ + _materials.push_back( new aiMaterial ); + } + scene->mNumMaterials=_materials.size(); + scene->mMaterials=to_array( _materials ); + + //meshes + scene->mNumMeshes=_meshes.size(); + scene->mMeshes=to_array( _meshes ); + + //animations + if( _animations.size()==1 && _nodeAnims.size() ){ + + aiAnimation *anim=_animations.back(); + anim->mNumChannels=_nodeAnims.size(); + anim->mChannels=to_array( _nodeAnims ); + + scene->mNumAnimations=_animations.size(); + scene->mAnimations=to_array( _animations ); + } + + // convert to RH + MakeLeftHandedProcess makeleft; + makeleft.Execute( scene ); + + FlipWindingOrderProcess flip; + flip.Execute( scene ); +} + +#endif // !! ASSIMP_BUILD_NO_B3D_IMPORTER diff --git a/code/B3DImporter.h b/code/B3DImporter.h index 0070e8298..9581c3469 100644 --- a/code/B3DImporter.h +++ b/code/B3DImporter.h @@ -1,131 +1,130 @@ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Definition of the .b3d importer class. */ - -#ifndef AI_B3DIMPORTER_H_INC -#define AI_B3DIMPORTER_H_INC - -#include "../include/assimp/types.h" -#include "../include/assimp/mesh.h" -#include "../include/assimp/material.h" -#include "BaseImporter.h" - -#include -#include - -struct aiNodeAnim; -struct aiNode; -struct aiAnimation; - -namespace Assimp{ - -class B3DImporter : public BaseImporter{ -public: - - virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; - -protected: - - virtual const aiImporterDesc* GetInfo () const; - virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); - -private: - - int ReadByte(); - int ReadInt(); - float ReadFloat(); - aiVector2D ReadVec2(); - aiVector3D ReadVec3(); - aiQuaternion ReadQuat(); - std::string ReadString(); - std::string ReadChunk(); - void ExitChunk(); - unsigned ChunkSize(); - - template - T *to_array( const std::vector &v ); - - struct Vertex{ - aiVector3D vertex; - aiVector3D normal; - aiVector3D texcoords; - unsigned char bones[4]; - float weights[4]; - }; - - AI_WONT_RETURN void Oops() AI_WONT_RETURN_SUFFIX; - AI_WONT_RETURN void Fail( std::string str ) AI_WONT_RETURN_SUFFIX; - - void ReadTEXS(); - void ReadBRUS(); - - void ReadVRTS(); - void ReadTRIS( int v0 ); - void ReadMESH(); - void ReadBONE( int id ); - void ReadKEYS( aiNodeAnim *nodeAnim ); - void ReadANIM(); - - aiNode *ReadNODE( aiNode *parent ); - - void ReadBB3D( aiScene *scene ); - - unsigned _pos; -// unsigned _size; - std::vector _buf; - std::vector _stack; - - std::vector _textures; - std::vector _materials; - - int _vflags,_tcsets,_tcsize; - std::vector _vertices; - - std::vector _nodes; - std::vector _meshes; - std::vector _nodeAnims; - std::vector _animations; -}; - -} - -#endif +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Definition of the .b3d importer class. */ + +#ifndef AI_B3DIMPORTER_H_INC +#define AI_B3DIMPORTER_H_INC + +#include "../include/assimp/types.h" +#include "../include/assimp/mesh.h" +#include "../include/assimp/material.h" +#include "BaseImporter.h" + +#include +#include + +struct aiNodeAnim; +struct aiNode; +struct aiAnimation; + +namespace Assimp{ + +class B3DImporter : public BaseImporter{ +public: + + virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; + +protected: + + virtual const aiImporterDesc* GetInfo () const; + virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + +private: + + int ReadByte(); + int ReadInt(); + float ReadFloat(); + aiVector2D ReadVec2(); + aiVector3D ReadVec3(); + aiQuaternion ReadQuat(); + std::string ReadString(); + std::string ReadChunk(); + void ExitChunk(); + unsigned ChunkSize(); + + template + T *to_array( const std::vector &v ); + + struct Vertex{ + aiVector3D vertex; + aiVector3D normal; + aiVector3D texcoords; + unsigned char bones[4]; + float weights[4]; + }; + + AI_WONT_RETURN void Oops() AI_WONT_RETURN_SUFFIX; + AI_WONT_RETURN void Fail( std::string str ) AI_WONT_RETURN_SUFFIX; + + void ReadTEXS(); + void ReadBRUS(); + + void ReadVRTS(); + void ReadTRIS( int v0 ); + void ReadMESH(); + void ReadBONE( int id ); + void ReadKEYS( aiNodeAnim *nodeAnim ); + void ReadANIM(); + + aiNode *ReadNODE( aiNode *parent ); + + void ReadBB3D( aiScene *scene ); + + unsigned _pos; +// unsigned _size; + std::vector _buf; + std::vector _stack; + + std::vector _textures; + std::vector _materials; + + int _vflags,_tcsets,_tcsize; + std::vector _vertices; + + std::vector _nodes; + std::vector _meshes; + std::vector _nodeAnims; + std::vector _animations; +}; + +} + +#endif diff --git a/code/BVHLoader.cpp b/code/BVHLoader.cpp index d132e689e..c0aed9100 100644 --- a/code/BVHLoader.cpp +++ b/code/BVHLoader.cpp @@ -1,539 +1,539 @@ -/** Implementation of the BVH loader */ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - - -#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER - -#include "BVHLoader.h" -#include "fast_atof.h" -#include "SkeletonMeshBuilder.h" -#include "../include/assimp/Importer.hpp" -#include -#include -#include "../include/assimp/IOSystem.hpp" -#include "../include/assimp/scene.h" - -using namespace Assimp; - -static const aiImporterDesc desc = { - "BVH Importer (MoCap)", - "", - "", - "", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "bvh" -}; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -BVHLoader::BVHLoader() -: noSkeletonMesh() -{} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -BVHLoader::~BVHLoader() -{} - -// ------------------------------------------------------------------------------------------------ -// Returns whether the class can handle the format of the given file. -bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const -{ - // check file extension - const std::string extension = GetExtension(pFile); - - if( extension == "bvh") - return true; - - if ((!extension.length() || cs) && pIOHandler) { - const char* tokens[] = {"HIERARCHY"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); - } - return false; -} - -// ------------------------------------------------------------------------------------------------ -void BVHLoader::SetupProperties(const Importer* pImp) -{ - noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; -} - -// ------------------------------------------------------------------------------------------------ -// Loader meta information -const aiImporterDesc* BVHLoader::GetInfo () const -{ - return &desc; -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file into the given scene structure. -void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) -{ - mFileName = pFile; - - // read file into memory - boost::scoped_ptr file( pIOHandler->Open( pFile)); - if( file.get() == NULL) - throw DeadlyImportError( "Failed to open file " + pFile + "."); - - size_t fileSize = file->FileSize(); - if( fileSize == 0) - throw DeadlyImportError( "File is too small."); - - mBuffer.resize( fileSize); - file->Read( &mBuffer.front(), 1, fileSize); - - // start reading - mReader = mBuffer.begin(); - mLine = 1; - ReadStructure( pScene); - - if (!noSkeletonMesh) { - // build a dummy mesh for the skeleton so that we see something at least - SkeletonMeshBuilder meshBuilder( pScene); - } - - // construct an animation from all the motion data we read - CreateAnimation( pScene); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the file -void BVHLoader::ReadStructure( aiScene* pScene) -{ - // first comes hierarchy - std::string header = GetNextToken(); - if( header != "HIERARCHY") - ThrowException( "Expected header string \"HIERARCHY\"."); - ReadHierarchy( pScene); - - // then comes the motion data - std::string motion = GetNextToken(); - if( motion != "MOTION") - ThrowException( "Expected beginning of motion data \"MOTION\"."); - ReadMotion( pScene); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the hierarchy -void BVHLoader::ReadHierarchy( aiScene* pScene) -{ - std::string root = GetNextToken(); - if( root != "ROOT") - ThrowException( "Expected root node \"ROOT\"."); - - // Go read the hierarchy from here - pScene->mRootNode = ReadNode(); -} - -// ------------------------------------------------------------------------------------------------ -// Reads a node and recursively its childs and returns the created node; -aiNode* BVHLoader::ReadNode() -{ - // first token is name - std::string nodeName = GetNextToken(); - if( nodeName.empty() || nodeName == "{") - ThrowException( boost::str( boost::format( "Expected node name, but found \"%s\".") % nodeName)); - - // then an opening brace should follow - std::string openBrace = GetNextToken(); - if( openBrace != "{") - ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace)); - - // Create a node - aiNode* node = new aiNode( nodeName); - std::vector childNodes; - - // and create an bone entry for it - mNodes.push_back( Node( node)); - Node& internNode = mNodes.back(); - - // now read the node's contents - while( 1) - { - std::string token = GetNextToken(); - - // node offset to parent node - if( token == "OFFSET") - ReadNodeOffset( node); - else if( token == "CHANNELS") - ReadNodeChannels( internNode); - else if( token == "JOINT") - { - // child node follows - aiNode* child = ReadNode(); - child->mParent = node; - childNodes.push_back( child); - } - else if( token == "End") - { - // The real symbol is "End Site". Second part comes in a separate token - std::string siteToken = GetNextToken(); - if( siteToken != "Site") - ThrowException( boost::str( boost::format( "Expected \"End Site\" keyword, but found \"%s %s\".") % token % siteToken)); - - aiNode* child = ReadEndSite( nodeName); - child->mParent = node; - childNodes.push_back( child); - } - else if( token == "}") - { - // we're done with that part of the hierarchy - break; - } else - { - // everything else is a parse error - ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token)); - } - } - - // add the child nodes if there are any - if( childNodes.size() > 0) - { - node->mNumChildren = childNodes.size(); - node->mChildren = new aiNode*[node->mNumChildren]; - std::copy( childNodes.begin(), childNodes.end(), node->mChildren); - } - - // and return the sub-hierarchy we built here - return node; -} - -// ------------------------------------------------------------------------------------------------ -// Reads an end node and returns the created node. -aiNode* BVHLoader::ReadEndSite( const std::string& pParentName) -{ - // check opening brace - std::string openBrace = GetNextToken(); - if( openBrace != "{") - ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace)); - - // Create a node - aiNode* node = new aiNode( "EndSite_" + pParentName); - - // now read the node's contents. Only possible entry is "OFFSET" - while( 1) - { - std::string token = GetNextToken(); - - // end node's offset - if( token == "OFFSET") - { - ReadNodeOffset( node); - } - else if( token == "}") - { - // we're done with the end node - break; - } else - { - // everything else is a parse error - ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token)); - } - } - - // and return the sub-hierarchy we built here - return node; -} -// ------------------------------------------------------------------------------------------------ -// Reads a node offset for the given node -void BVHLoader::ReadNodeOffset( aiNode* pNode) -{ - // Offset consists of three floats to read - aiVector3D offset; - offset.x = GetNextTokenAsFloat(); - offset.y = GetNextTokenAsFloat(); - offset.z = GetNextTokenAsFloat(); - - // build a transformation matrix from it - pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, 0.0f, 1.0f, 0.0f, offset.y, - 0.0f, 0.0f, 1.0f, offset.z, 0.0f, 0.0f, 0.0f, 1.0f); -} - -// ------------------------------------------------------------------------------------------------ -// Reads the animation channels for the given node -void BVHLoader::ReadNodeChannels( BVHLoader::Node& pNode) -{ - // number of channels. Use the float reader because we're lazy - float numChannelsFloat = GetNextTokenAsFloat(); - unsigned int numChannels = (unsigned int) numChannelsFloat; - - for( unsigned int a = 0; a < numChannels; a++) - { - std::string channelToken = GetNextToken(); - - if( channelToken == "Xposition") - pNode.mChannels.push_back( Channel_PositionX); - else if( channelToken == "Yposition") - pNode.mChannels.push_back( Channel_PositionY); - else if( channelToken == "Zposition") - pNode.mChannels.push_back( Channel_PositionZ); - else if( channelToken == "Xrotation") - pNode.mChannels.push_back( Channel_RotationX); - else if( channelToken == "Yrotation") - pNode.mChannels.push_back( Channel_RotationY); - else if( channelToken == "Zrotation") - pNode.mChannels.push_back( Channel_RotationZ); - else - ThrowException( boost::str( boost::format( "Invalid channel specifier \"%s\".") % channelToken)); - } -} - -// ------------------------------------------------------------------------------------------------ -// Reads the motion data -void BVHLoader::ReadMotion( aiScene* /*pScene*/) -{ - // Read number of frames - std::string tokenFrames = GetNextToken(); - if( tokenFrames != "Frames:") - ThrowException( boost::str( boost::format( "Expected frame count \"Frames:\", but found \"%s\".") % tokenFrames)); - - float numFramesFloat = GetNextTokenAsFloat(); - mAnimNumFrames = (unsigned int) numFramesFloat; - - // Read frame duration - std::string tokenDuration1 = GetNextToken(); - std::string tokenDuration2 = GetNextToken(); - if( tokenDuration1 != "Frame" || tokenDuration2 != "Time:") - ThrowException( boost::str( boost::format( "Expected frame duration \"Frame Time:\", but found \"%s %s\".") % tokenDuration1 % tokenDuration2)); - - mAnimTickDuration = GetNextTokenAsFloat(); - - // resize value vectors for each node - for( std::vector::iterator it = mNodes.begin(); it != mNodes.end(); ++it) - it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames); - - // now read all the data and store it in the corresponding node's value vector - for( unsigned int frame = 0; frame < mAnimNumFrames; ++frame) - { - // on each line read the values for all nodes - for( std::vector::iterator it = mNodes.begin(); it != mNodes.end(); ++it) - { - // get as many values as the node has channels - for( unsigned int c = 0; c < it->mChannels.size(); ++c) - it->mChannelValues.push_back( GetNextTokenAsFloat()); - } - - // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it - } -} - -// ------------------------------------------------------------------------------------------------ -// Retrieves the next token -std::string BVHLoader::GetNextToken() -{ - // skip any preceeding whitespace - while( mReader != mBuffer.end()) - { - if( !isspace( *mReader)) - break; - - // count lines - if( *mReader == '\n') - mLine++; - - ++mReader; - } - - // collect all chars till the next whitespace. BVH is easy in respect to that. - std::string token; - while( mReader != mBuffer.end()) - { - if( isspace( *mReader)) - break; - - token.push_back( *mReader); - ++mReader; - - // little extra logic to make sure braces are counted correctly - if( token == "{" || token == "}") - break; - } - - // empty token means end of file, which is just fine - return token; -} - -// ------------------------------------------------------------------------------------------------ -// Reads the next token as a float -float BVHLoader::GetNextTokenAsFloat() -{ - std::string token = GetNextToken(); - if( token.empty()) - ThrowException( "Unexpected end of file while trying to read a float"); - - // check if the float is valid by testing if the atof() function consumed every char of the token - const char* ctoken = token.c_str(); - float result = 0.0f; - ctoken = fast_atoreal_move( ctoken, result); - - if( ctoken != token.c_str() + token.length()) - ThrowException( boost::str( boost::format( "Expected a floating point number, but found \"%s\".") % token)); - - return result; -} - -// ------------------------------------------------------------------------------------------------ -// Aborts the file reading with an exception -AI_WONT_RETURN void BVHLoader::ThrowException( const std::string& pError) -{ - throw DeadlyImportError( boost::str( boost::format( "%s:%d - %s") % mFileName % mLine % pError)); -} - -// ------------------------------------------------------------------------------------------------ -// Constructs an animation for the motion data and stores it in the given scene -void BVHLoader::CreateAnimation( aiScene* pScene) -{ - // create the animation - pScene->mNumAnimations = 1; - pScene->mAnimations = new aiAnimation*[1]; - aiAnimation* anim = new aiAnimation; - pScene->mAnimations[0] = anim; - - // put down the basic parameters - anim->mName.Set( "Motion"); - anim->mTicksPerSecond = 1.0 / double( mAnimTickDuration); - anim->mDuration = double( mAnimNumFrames - 1); - - // now generate the tracks for all nodes - anim->mNumChannels = mNodes.size(); - anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; - - // FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown - for (unsigned int i = 0; i < anim->mNumChannels;++i) - anim->mChannels[i] = NULL; - - for( unsigned int a = 0; a < anim->mNumChannels; a++) - { - const Node& node = mNodes[a]; - const std::string nodeName = std::string( node.mNode->mName.data ); - aiNodeAnim* nodeAnim = new aiNodeAnim; - anim->mChannels[a] = nodeAnim; - nodeAnim->mNodeName.Set( nodeName); - - // translational part, if given - if( node.mChannels.size() == 6) - { - nodeAnim->mNumPositionKeys = mAnimNumFrames; - nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames]; - aiVectorKey* poskey = nodeAnim->mPositionKeys; - for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) - { - poskey->mTime = double( fr); - - // Now compute all translations in the right order - for( unsigned int channel = 0; channel < 3; ++channel) - { - switch( node.mChannels[channel]) - { - case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break; - case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break; - case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break; - default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); - } - } - ++poskey; - } - } else - { - // if no translation part is given, put a default sequence - aiVector3D nodePos( node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4); - nodeAnim->mNumPositionKeys = 1; - nodeAnim->mPositionKeys = new aiVectorKey[1]; - nodeAnim->mPositionKeys[0].mTime = 0.0; - nodeAnim->mPositionKeys[0].mValue = nodePos; - } - - // rotation part. Always present. First find value offsets - { - unsigned int rotOffset = 0; - if( node.mChannels.size() == 6) - { - // Offset all further calculations - rotOffset = 3; - } - - // Then create the number of rotation keys - nodeAnim->mNumRotationKeys = mAnimNumFrames; - nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames]; - aiQuatKey* rotkey = nodeAnim->mRotationKeys; - for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) - { - aiMatrix4x4 temp; - aiMatrix3x3 rotMatrix; - - for( unsigned int channel = 0; channel < 3; ++channel) - { - // translate ZXY euler angels into a quaternion - const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f; - - // Compute rotation transformations in the right order - switch (node.mChannels[rotOffset+channel]) - { - case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; - case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; - case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; - default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); - } - } - - rotkey->mTime = double( fr); - rotkey->mValue = aiQuaternion( rotMatrix); - ++rotkey; - } - } - - // scaling part. Always just a default track - { - nodeAnim->mNumScalingKeys = 1; - nodeAnim->mScalingKeys = new aiVectorKey[1]; - nodeAnim->mScalingKeys[0].mTime = 0.0; - nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f); - } - } -} - -#endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER +/** Implementation of the BVH loader */ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + + +#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER + +#include "BVHLoader.h" +#include "fast_atof.h" +#include "SkeletonMeshBuilder.h" +#include "../include/assimp/Importer.hpp" +#include +#include +#include "../include/assimp/IOSystem.hpp" +#include "../include/assimp/scene.h" + +using namespace Assimp; + +static const aiImporterDesc desc = { + "BVH Importer (MoCap)", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "bvh" +}; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BVHLoader::BVHLoader() +: noSkeletonMesh() +{} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BVHLoader::~BVHLoader() +{} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool BVHLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const +{ + // check file extension + const std::string extension = GetExtension(pFile); + + if( extension == "bvh") + return true; + + if ((!extension.length() || cs) && pIOHandler) { + const char* tokens[] = {"HIERARCHY"}; + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +void BVHLoader::SetupProperties(const Importer* pImp) +{ + noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Loader meta information +const aiImporterDesc* BVHLoader::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) +{ + mFileName = pFile; + + // read file into memory + boost::scoped_ptr file( pIOHandler->Open( pFile)); + if( file.get() == NULL) + throw DeadlyImportError( "Failed to open file " + pFile + "."); + + size_t fileSize = file->FileSize(); + if( fileSize == 0) + throw DeadlyImportError( "File is too small."); + + mBuffer.resize( fileSize); + file->Read( &mBuffer.front(), 1, fileSize); + + // start reading + mReader = mBuffer.begin(); + mLine = 1; + ReadStructure( pScene); + + if (!noSkeletonMesh) { + // build a dummy mesh for the skeleton so that we see something at least + SkeletonMeshBuilder meshBuilder( pScene); + } + + // construct an animation from all the motion data we read + CreateAnimation( pScene); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the file +void BVHLoader::ReadStructure( aiScene* pScene) +{ + // first comes hierarchy + std::string header = GetNextToken(); + if( header != "HIERARCHY") + ThrowException( "Expected header string \"HIERARCHY\"."); + ReadHierarchy( pScene); + + // then comes the motion data + std::string motion = GetNextToken(); + if( motion != "MOTION") + ThrowException( "Expected beginning of motion data \"MOTION\"."); + ReadMotion( pScene); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the hierarchy +void BVHLoader::ReadHierarchy( aiScene* pScene) +{ + std::string root = GetNextToken(); + if( root != "ROOT") + ThrowException( "Expected root node \"ROOT\"."); + + // Go read the hierarchy from here + pScene->mRootNode = ReadNode(); +} + +// ------------------------------------------------------------------------------------------------ +// Reads a node and recursively its childs and returns the created node; +aiNode* BVHLoader::ReadNode() +{ + // first token is name + std::string nodeName = GetNextToken(); + if( nodeName.empty() || nodeName == "{") + ThrowException( boost::str( boost::format( "Expected node name, but found \"%s\".") % nodeName)); + + // then an opening brace should follow + std::string openBrace = GetNextToken(); + if( openBrace != "{") + ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace)); + + // Create a node + aiNode* node = new aiNode( nodeName); + std::vector childNodes; + + // and create an bone entry for it + mNodes.push_back( Node( node)); + Node& internNode = mNodes.back(); + + // now read the node's contents + while( 1) + { + std::string token = GetNextToken(); + + // node offset to parent node + if( token == "OFFSET") + ReadNodeOffset( node); + else if( token == "CHANNELS") + ReadNodeChannels( internNode); + else if( token == "JOINT") + { + // child node follows + aiNode* child = ReadNode(); + child->mParent = node; + childNodes.push_back( child); + } + else if( token == "End") + { + // The real symbol is "End Site". Second part comes in a separate token + std::string siteToken = GetNextToken(); + if( siteToken != "Site") + ThrowException( boost::str( boost::format( "Expected \"End Site\" keyword, but found \"%s %s\".") % token % siteToken)); + + aiNode* child = ReadEndSite( nodeName); + child->mParent = node; + childNodes.push_back( child); + } + else if( token == "}") + { + // we're done with that part of the hierarchy + break; + } else + { + // everything else is a parse error + ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token)); + } + } + + // add the child nodes if there are any + if( childNodes.size() > 0) + { + node->mNumChildren = childNodes.size(); + node->mChildren = new aiNode*[node->mNumChildren]; + std::copy( childNodes.begin(), childNodes.end(), node->mChildren); + } + + // and return the sub-hierarchy we built here + return node; +} + +// ------------------------------------------------------------------------------------------------ +// Reads an end node and returns the created node. +aiNode* BVHLoader::ReadEndSite( const std::string& pParentName) +{ + // check opening brace + std::string openBrace = GetNextToken(); + if( openBrace != "{") + ThrowException( boost::str( boost::format( "Expected opening brace \"{\", but found \"%s\".") % openBrace)); + + // Create a node + aiNode* node = new aiNode( "EndSite_" + pParentName); + + // now read the node's contents. Only possible entry is "OFFSET" + while( 1) + { + std::string token = GetNextToken(); + + // end node's offset + if( token == "OFFSET") + { + ReadNodeOffset( node); + } + else if( token == "}") + { + // we're done with the end node + break; + } else + { + // everything else is a parse error + ThrowException( boost::str( boost::format( "Unknown keyword \"%s\".") % token)); + } + } + + // and return the sub-hierarchy we built here + return node; +} +// ------------------------------------------------------------------------------------------------ +// Reads a node offset for the given node +void BVHLoader::ReadNodeOffset( aiNode* pNode) +{ + // Offset consists of three floats to read + aiVector3D offset; + offset.x = GetNextTokenAsFloat(); + offset.y = GetNextTokenAsFloat(); + offset.z = GetNextTokenAsFloat(); + + // build a transformation matrix from it + pNode->mTransformation = aiMatrix4x4( 1.0f, 0.0f, 0.0f, offset.x, 0.0f, 1.0f, 0.0f, offset.y, + 0.0f, 0.0f, 1.0f, offset.z, 0.0f, 0.0f, 0.0f, 1.0f); +} + +// ------------------------------------------------------------------------------------------------ +// Reads the animation channels for the given node +void BVHLoader::ReadNodeChannels( BVHLoader::Node& pNode) +{ + // number of channels. Use the float reader because we're lazy + float numChannelsFloat = GetNextTokenAsFloat(); + unsigned int numChannels = (unsigned int) numChannelsFloat; + + for( unsigned int a = 0; a < numChannels; a++) + { + std::string channelToken = GetNextToken(); + + if( channelToken == "Xposition") + pNode.mChannels.push_back( Channel_PositionX); + else if( channelToken == "Yposition") + pNode.mChannels.push_back( Channel_PositionY); + else if( channelToken == "Zposition") + pNode.mChannels.push_back( Channel_PositionZ); + else if( channelToken == "Xrotation") + pNode.mChannels.push_back( Channel_RotationX); + else if( channelToken == "Yrotation") + pNode.mChannels.push_back( Channel_RotationY); + else if( channelToken == "Zrotation") + pNode.mChannels.push_back( Channel_RotationZ); + else + ThrowException( boost::str( boost::format( "Invalid channel specifier \"%s\".") % channelToken)); + } +} + +// ------------------------------------------------------------------------------------------------ +// Reads the motion data +void BVHLoader::ReadMotion( aiScene* /*pScene*/) +{ + // Read number of frames + std::string tokenFrames = GetNextToken(); + if( tokenFrames != "Frames:") + ThrowException( boost::str( boost::format( "Expected frame count \"Frames:\", but found \"%s\".") % tokenFrames)); + + float numFramesFloat = GetNextTokenAsFloat(); + mAnimNumFrames = (unsigned int) numFramesFloat; + + // Read frame duration + std::string tokenDuration1 = GetNextToken(); + std::string tokenDuration2 = GetNextToken(); + if( tokenDuration1 != "Frame" || tokenDuration2 != "Time:") + ThrowException( boost::str( boost::format( "Expected frame duration \"Frame Time:\", but found \"%s %s\".") % tokenDuration1 % tokenDuration2)); + + mAnimTickDuration = GetNextTokenAsFloat(); + + // resize value vectors for each node + for( std::vector::iterator it = mNodes.begin(); it != mNodes.end(); ++it) + it->mChannelValues.reserve( it->mChannels.size() * mAnimNumFrames); + + // now read all the data and store it in the corresponding node's value vector + for( unsigned int frame = 0; frame < mAnimNumFrames; ++frame) + { + // on each line read the values for all nodes + for( std::vector::iterator it = mNodes.begin(); it != mNodes.end(); ++it) + { + // get as many values as the node has channels + for( unsigned int c = 0; c < it->mChannels.size(); ++c) + it->mChannelValues.push_back( GetNextTokenAsFloat()); + } + + // after one frame worth of values for all nodes there should be a newline, but we better don't rely on it + } +} + +// ------------------------------------------------------------------------------------------------ +// Retrieves the next token +std::string BVHLoader::GetNextToken() +{ + // skip any preceeding whitespace + while( mReader != mBuffer.end()) + { + if( !isspace( *mReader)) + break; + + // count lines + if( *mReader == '\n') + mLine++; + + ++mReader; + } + + // collect all chars till the next whitespace. BVH is easy in respect to that. + std::string token; + while( mReader != mBuffer.end()) + { + if( isspace( *mReader)) + break; + + token.push_back( *mReader); + ++mReader; + + // little extra logic to make sure braces are counted correctly + if( token == "{" || token == "}") + break; + } + + // empty token means end of file, which is just fine + return token; +} + +// ------------------------------------------------------------------------------------------------ +// Reads the next token as a float +float BVHLoader::GetNextTokenAsFloat() +{ + std::string token = GetNextToken(); + if( token.empty()) + ThrowException( "Unexpected end of file while trying to read a float"); + + // check if the float is valid by testing if the atof() function consumed every char of the token + const char* ctoken = token.c_str(); + float result = 0.0f; + ctoken = fast_atoreal_move( ctoken, result); + + if( ctoken != token.c_str() + token.length()) + ThrowException( boost::str( boost::format( "Expected a floating point number, but found \"%s\".") % token)); + + return result; +} + +// ------------------------------------------------------------------------------------------------ +// Aborts the file reading with an exception +AI_WONT_RETURN void BVHLoader::ThrowException( const std::string& pError) +{ + throw DeadlyImportError( boost::str( boost::format( "%s:%d - %s") % mFileName % mLine % pError)); +} + +// ------------------------------------------------------------------------------------------------ +// Constructs an animation for the motion data and stores it in the given scene +void BVHLoader::CreateAnimation( aiScene* pScene) +{ + // create the animation + pScene->mNumAnimations = 1; + pScene->mAnimations = new aiAnimation*[1]; + aiAnimation* anim = new aiAnimation; + pScene->mAnimations[0] = anim; + + // put down the basic parameters + anim->mName.Set( "Motion"); + anim->mTicksPerSecond = 1.0 / double( mAnimTickDuration); + anim->mDuration = double( mAnimNumFrames - 1); + + // now generate the tracks for all nodes + anim->mNumChannels = mNodes.size(); + anim->mChannels = new aiNodeAnim*[anim->mNumChannels]; + + // FIX: set the array elements to NULL to ensure proper deletion if an exception is thrown + for (unsigned int i = 0; i < anim->mNumChannels;++i) + anim->mChannels[i] = NULL; + + for( unsigned int a = 0; a < anim->mNumChannels; a++) + { + const Node& node = mNodes[a]; + const std::string nodeName = std::string( node.mNode->mName.data ); + aiNodeAnim* nodeAnim = new aiNodeAnim; + anim->mChannels[a] = nodeAnim; + nodeAnim->mNodeName.Set( nodeName); + + // translational part, if given + if( node.mChannels.size() == 6) + { + nodeAnim->mNumPositionKeys = mAnimNumFrames; + nodeAnim->mPositionKeys = new aiVectorKey[mAnimNumFrames]; + aiVectorKey* poskey = nodeAnim->mPositionKeys; + for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) + { + poskey->mTime = double( fr); + + // Now compute all translations in the right order + for( unsigned int channel = 0; channel < 3; ++channel) + { + switch( node.mChannels[channel]) + { + case Channel_PositionX: poskey->mValue.x = node.mChannelValues[fr * node.mChannels.size() + channel]; break; + case Channel_PositionY: poskey->mValue.y = node.mChannelValues[fr * node.mChannels.size() + channel]; break; + case Channel_PositionZ: poskey->mValue.z = node.mChannelValues[fr * node.mChannels.size() + channel]; break; + default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); + } + } + ++poskey; + } + } else + { + // if no translation part is given, put a default sequence + aiVector3D nodePos( node.mNode->mTransformation.a4, node.mNode->mTransformation.b4, node.mNode->mTransformation.c4); + nodeAnim->mNumPositionKeys = 1; + nodeAnim->mPositionKeys = new aiVectorKey[1]; + nodeAnim->mPositionKeys[0].mTime = 0.0; + nodeAnim->mPositionKeys[0].mValue = nodePos; + } + + // rotation part. Always present. First find value offsets + { + unsigned int rotOffset = 0; + if( node.mChannels.size() == 6) + { + // Offset all further calculations + rotOffset = 3; + } + + // Then create the number of rotation keys + nodeAnim->mNumRotationKeys = mAnimNumFrames; + nodeAnim->mRotationKeys = new aiQuatKey[mAnimNumFrames]; + aiQuatKey* rotkey = nodeAnim->mRotationKeys; + for( unsigned int fr = 0; fr < mAnimNumFrames; ++fr) + { + aiMatrix4x4 temp; + aiMatrix3x3 rotMatrix; + + for( unsigned int channel = 0; channel < 3; ++channel) + { + // translate ZXY euler angels into a quaternion + const float angle = node.mChannelValues[fr * node.mChannels.size() + rotOffset + channel] * float( AI_MATH_PI) / 180.0f; + + // Compute rotation transformations in the right order + switch (node.mChannels[rotOffset+channel]) + { + case Channel_RotationX: aiMatrix4x4::RotationX( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; + case Channel_RotationY: aiMatrix4x4::RotationY( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; + case Channel_RotationZ: aiMatrix4x4::RotationZ( angle, temp); rotMatrix *= aiMatrix3x3( temp); break; + default: throw DeadlyImportError( "Unexpected animation channel setup at node " + nodeName ); + } + } + + rotkey->mTime = double( fr); + rotkey->mValue = aiQuaternion( rotMatrix); + ++rotkey; + } + } + + // scaling part. Always just a default track + { + nodeAnim->mNumScalingKeys = 1; + nodeAnim->mScalingKeys = new aiVectorKey[1]; + nodeAnim->mScalingKeys[0].mTime = 0.0; + nodeAnim->mScalingKeys[0].mValue.Set( 1.0f, 1.0f, 1.0f); + } + } +} + +#endif // !! ASSIMP_BUILD_NO_BVH_IMPORTER diff --git a/code/BVHLoader.h b/code/BVHLoader.h index 0a21867c9..8c88484f6 100644 --- a/code/BVHLoader.h +++ b/code/BVHLoader.h @@ -1,171 +1,171 @@ -/** Defines the BHV motion capturing loader class */ - -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the -following disclaimer. - -* Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the -following disclaimer in the documentation and/or other -materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its -contributors may be used to endorse or promote products -derived from this software without specific prior -written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file BVHLoader.h - * @brief Biovision BVH import - */ - -#ifndef AI_BVHLOADER_H_INC -#define AI_BVHLOADER_H_INC - -#include "BaseImporter.h" - -struct aiNode; - -namespace Assimp -{ - -// -------------------------------------------------------------------------------- -/** Loader class to read Motion Capturing data from a .bvh file. - * - * This format only contains a hierarchy of joints and a series of keyframes for - * the hierarchy. It contains no actual mesh data, but we generate a dummy mesh - * inside the loader just to be able to see something. -*/ -class BVHLoader : public BaseImporter -{ - - /** Possible animation channels for which the motion data holds the values */ - enum ChannelType - { - Channel_PositionX, - Channel_PositionY, - Channel_PositionZ, - Channel_RotationX, - Channel_RotationY, - Channel_RotationZ - }; - - /** Collected list of node. Will be bones of the dummy mesh some day, addressed by their array index */ - struct Node - { - const aiNode* mNode; - std::vector mChannels; - std::vector mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames - - Node() { } - Node( const aiNode* pNode) : mNode( pNode) { } - }; - -public: - - BVHLoader(); - ~BVHLoader(); - -public: - /** Returns whether the class can handle the format of the given file. - * See BaseImporter::CanRead() for details. */ - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const; - - void SetupProperties(const Importer* pImp); - const aiImporterDesc* GetInfo () const; - -protected: - - - /** Imports the given file into the given scene structure. - * See BaseImporter::InternReadFile() for details - */ - void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); - -protected: - /** Reads the file */ - void ReadStructure( aiScene* pScene); - - /** Reads the hierarchy */ - void ReadHierarchy( aiScene* pScene); - - /** Reads a node and recursively its childs and returns the created node. */ - aiNode* ReadNode(); - - /** Reads an end node and returns the created node. */ - aiNode* ReadEndSite( const std::string& pParentName); - - /** Reads a node offset for the given node */ - void ReadNodeOffset( aiNode* pNode); - - /** Reads the animation channels into the given node */ - void ReadNodeChannels( BVHLoader::Node& pNode); - - /** Reads the motion data */ - void ReadMotion( aiScene* pScene); - - /** Retrieves the next token */ - std::string GetNextToken(); - - /** Reads the next token as a float */ - float GetNextTokenAsFloat(); - - /** Aborts the file reading with an exception */ - AI_WONT_RETURN void ThrowException( const std::string& pError) AI_WONT_RETURN_SUFFIX; - - /** Constructs an animation for the motion data and stores it in the given scene */ - void CreateAnimation( aiScene* pScene); - -protected: - /** Filename, for a verbose error message */ - std::string mFileName; - - /** Buffer to hold the loaded file */ - std::vector mBuffer; - - /** Next char to read from the buffer */ - std::vector::const_iterator mReader; - - /** Current line, for error messages */ - unsigned int mLine; - - /** Collected list of nodes. Will be bones of the dummy mesh some day, addressed by their array index. - * Also contain the motion data for the node's channels - */ - std::vector mNodes; - - /** basic Animation parameters */ - float mAnimTickDuration; - unsigned int mAnimNumFrames; - - bool noSkeletonMesh; -}; - -} // end of namespace Assimp - -#endif // AI_BVHLOADER_H_INC +/** Defines the BHV motion capturing loader class */ + +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file BVHLoader.h + * @brief Biovision BVH import + */ + +#ifndef AI_BVHLOADER_H_INC +#define AI_BVHLOADER_H_INC + +#include "BaseImporter.h" + +struct aiNode; + +namespace Assimp +{ + +// -------------------------------------------------------------------------------- +/** Loader class to read Motion Capturing data from a .bvh file. + * + * This format only contains a hierarchy of joints and a series of keyframes for + * the hierarchy. It contains no actual mesh data, but we generate a dummy mesh + * inside the loader just to be able to see something. +*/ +class BVHLoader : public BaseImporter +{ + + /** Possible animation channels for which the motion data holds the values */ + enum ChannelType + { + Channel_PositionX, + Channel_PositionY, + Channel_PositionZ, + Channel_RotationX, + Channel_RotationY, + Channel_RotationZ + }; + + /** Collected list of node. Will be bones of the dummy mesh some day, addressed by their array index */ + struct Node + { + const aiNode* mNode; + std::vector mChannels; + std::vector mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames + + Node() { } + Node( const aiNode* pNode) : mNode( pNode) { } + }; + +public: + + BVHLoader(); + ~BVHLoader(); + +public: + /** Returns whether the class can handle the format of the given file. + * See BaseImporter::CanRead() for details. */ + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const; + + void SetupProperties(const Importer* pImp); + const aiImporterDesc* GetInfo () const; + +protected: + + + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + +protected: + /** Reads the file */ + void ReadStructure( aiScene* pScene); + + /** Reads the hierarchy */ + void ReadHierarchy( aiScene* pScene); + + /** Reads a node and recursively its childs and returns the created node. */ + aiNode* ReadNode(); + + /** Reads an end node and returns the created node. */ + aiNode* ReadEndSite( const std::string& pParentName); + + /** Reads a node offset for the given node */ + void ReadNodeOffset( aiNode* pNode); + + /** Reads the animation channels into the given node */ + void ReadNodeChannels( BVHLoader::Node& pNode); + + /** Reads the motion data */ + void ReadMotion( aiScene* pScene); + + /** Retrieves the next token */ + std::string GetNextToken(); + + /** Reads the next token as a float */ + float GetNextTokenAsFloat(); + + /** Aborts the file reading with an exception */ + AI_WONT_RETURN void ThrowException( const std::string& pError) AI_WONT_RETURN_SUFFIX; + + /** Constructs an animation for the motion data and stores it in the given scene */ + void CreateAnimation( aiScene* pScene); + +protected: + /** Filename, for a verbose error message */ + std::string mFileName; + + /** Buffer to hold the loaded file */ + std::vector mBuffer; + + /** Next char to read from the buffer */ + std::vector::const_iterator mReader; + + /** Current line, for error messages */ + unsigned int mLine; + + /** Collected list of nodes. Will be bones of the dummy mesh some day, addressed by their array index. + * Also contain the motion data for the node's channels + */ + std::vector mNodes; + + /** basic Animation parameters */ + float mAnimTickDuration; + unsigned int mAnimNumFrames; + + bool noSkeletonMesh; +}; + +} // end of namespace Assimp + +#endif // AI_BVHLOADER_H_INC diff --git a/code/BaseImporter.cpp b/code/BaseImporter.cpp index db1713c80..3c8d9e39b 100644 --- a/code/BaseImporter.cpp +++ b/code/BaseImporter.cpp @@ -1,617 +1,613 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file BaseImporter.cpp - * @brief Implementation of BaseImporter - */ - -#include "BaseImporter.h" -#include "FileSystemFilter.h" +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file BaseImporter.cpp + * @brief Implementation of BaseImporter + */ + +#include "BaseImporter.h" +#include "FileSystemFilter.h" #include "Importer.h" #include "ByteSwapper.h" #include "../include/assimp/scene.h" -#include "../include/assimp/Importer.hpp" -#include "../include/assimp/postprocess.h" +#include "../include/assimp/Importer.hpp" +#include "../include/assimp/postprocess.h" #include -#include -#include +#include +#include #include -#include +#include #include - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -BaseImporter::BaseImporter() -: progress() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -BaseImporter::~BaseImporter() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -// Imports the given file and returns the imported data. -aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) -{ - progress = pImp->GetProgressHandler(); - ai_assert(progress); - - // Gather configuration properties for this run - SetupProperties( pImp ); - - // Construct a file system filter to improve our success ratio at reading external files - FileSystemFilter filter(pFile,pIOHandler); - - // create a scene object to hold the data - ScopeGuard sc(new aiScene()); - - // dispatch importing - try - { - InternReadFile( pFile, sc, &filter); - - } catch( const std::exception& err ) { - // extract error description - mErrorText = err.what(); - DefaultLogger::get()->error(mErrorText); - return NULL; - } - - // return what we gathered from the import. - sc.dismiss(); - return sc; -} - -// ------------------------------------------------------------------------------------------------ -void BaseImporter::SetupProperties(const Importer* /*pImp*/) -{ - // the default implementation does nothing -} - -// ------------------------------------------------------------------------------------------------ -void BaseImporter::GetExtensionList(std::set& extensions) -{ - const aiImporterDesc* desc = GetInfo(); - ai_assert(desc != NULL); - - const char* ext = desc->mFileExtensions; - ai_assert(ext != NULL); - - const char* last = ext; - do { - if (!*ext || *ext == ' ') { - extensions.insert(std::string(last,ext-last)); - ai_assert(ext-last > 0); - last = ext; - while(*last == ' ') { - ++last; - } - } - } - while(*ext++); -} - -// ------------------------------------------------------------------------------------------------ -/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler, - const std::string& pFile, - const char** tokens, - unsigned int numTokens, - unsigned int searchBytes /* = 200 */, - bool tokensSol /* false */) -{ - ai_assert(NULL != tokens && 0 != numTokens && 0 != searchBytes); - if (!pIOHandler) - return false; - - boost::scoped_ptr pStream (pIOHandler->Open(pFile)); - if (pStream.get() ) { - // read 200 characters from the file - boost::scoped_array _buffer (new char[searchBytes+1 /* for the '\0' */]); - char* buffer = _buffer.get(); - if( NULL == buffer ) { - return false; - } - - const size_t read = pStream->Read(buffer,1,searchBytes); - if( !read ) { - return false; - } - - for( size_t i = 0; i < read; ++i ) { - buffer[ i ] = ::tolower( buffer[ i ] ); - } - - // It is not a proper handling of unicode files here ... - // ehm ... but it works in most cases. - char* cur = buffer,*cur2 = buffer,*end = &buffer[read]; - while (cur != end) { - if( *cur ) { - *cur2++ = *cur; - } - ++cur; - } - *cur2 = '\0'; - - for (unsigned int i = 0; i < numTokens;++i) { - ai_assert(NULL != tokens[i]); - - - const char* r = strstr(buffer,tokens[i]); - if( !r ) { - continue; - } - // We got a match, either we don't care where it is, or it happens to - // be in the beginning of the file / line - if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') { - DefaultLogger::get()->debug(std::string("Found positive match for header keyword: ") + tokens[i]); - return true; - } - } - } - - return false; -} - -// ------------------------------------------------------------------------------------------------ -// Simple check for file extension -/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile, - const char* ext0, - const char* ext1, - const char* ext2) -{ - std::string::size_type pos = pFile.find_last_of('.'); - - // no file extension - can't read - if( pos == std::string::npos) - return false; - - const char* ext_real = & pFile[ pos+1 ]; - if( !ASSIMP_stricmp(ext_real,ext0) ) - return true; - - // check for other, optional, file extensions - if (ext1 && !ASSIMP_stricmp(ext_real,ext1)) - return true; - - if (ext2 && !ASSIMP_stricmp(ext_real,ext2)) - return true; - - return false; -} - -// ------------------------------------------------------------------------------------------------ -// Get file extension from path -/*static*/ std::string BaseImporter::GetExtension (const std::string& pFile) -{ - std::string::size_type pos = pFile.find_last_of('.'); - - // no file extension at all - if( pos == std::string::npos) - return ""; - - std::string ret = pFile.substr(pos+1); - std::transform(ret.begin(),ret.end(),ret.begin(),::tolower); // thanks to Andy Maloney for the hint - return ret; -} - -// ------------------------------------------------------------------------------------------------ -// Check for magic bytes at the beginning of the file. -/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile, - const void* _magic, unsigned int num, unsigned int offset, unsigned int size) -{ - ai_assert(size <= 16 && _magic); - - if (!pIOHandler) { - return false; - } - union { - const char* magic; - const uint16_t* magic_u16; - const uint32_t* magic_u32; - }; - magic = reinterpret_cast(_magic); - boost::scoped_ptr pStream (pIOHandler->Open(pFile)); - if (pStream.get() ) { - - // skip to offset - pStream->Seek(offset,aiOrigin_SET); - - // read 'size' characters from the file - union { - char data[16]; - uint16_t data_u16[8]; - uint32_t data_u32[4]; - }; - if(size != pStream->Read(data,1,size)) { - return false; - } - - for (unsigned int i = 0; i < num; ++i) { - // also check against big endian versions of tokens with size 2,4 - // that's just for convinience, the chance that we cause conflicts - // is quite low and it can save some lines and prevent nasty bugs - if (2 == size) { - uint16_t rev = *magic_u16; - ByteSwap::Swap(&rev); - if (data_u16[0] == *magic_u16 || data_u16[0] == rev) { - return true; - } - } - else if (4 == size) { - uint32_t rev = *magic_u32; - ByteSwap::Swap(&rev); - if (data_u32[0] == *magic_u32 || data_u32[0] == rev) { - return true; - } - } - else { - // any length ... just compare - if(!memcmp(magic,data,size)) { - return true; - } - } - magic += size; - } - } - return false; -} - -#include "../contrib/ConvertUTF/ConvertUTF.h" - -// ------------------------------------------------------------------------------------------------ -void ReportResult(ConversionResult res) -{ - if(res == sourceExhausted) { - DefaultLogger::get()->error("Source ends with incomplete character sequence, transformation to UTF-8 fails"); - } - else if(res == sourceIllegal) { - DefaultLogger::get()->error("Source contains illegal character sequence, transformation to UTF-8 fails"); - } -} - -// ------------------------------------------------------------------------------------------------ -// Convert to UTF8 data -void BaseImporter::ConvertToUTF8(std::vector& data) -{ - ConversionResult result; - if(data.size() < 8) { - throw DeadlyImportError("File is too small"); - } - - // UTF 8 with BOM - if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) { - DefaultLogger::get()->debug("Found UTF-8 BOM ..."); - - std::copy(data.begin()+3,data.end(),data.begin()); - data.resize(data.size()-3); - return; - } - - // UTF 32 BE with BOM - if(*((uint32_t*)&data.front()) == 0xFFFE0000) { - - // swap the endianess .. - for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) { - AI_SWAP4P(p); - } - } - - // UTF 32 LE with BOM - if(*((uint32_t*)&data.front()) == 0x0000FFFE) { - DefaultLogger::get()->debug("Found UTF-32 BOM ..."); - - const uint32_t* sstart = (uint32_t*)&data.front()+1, *send = (uint32_t*)&data.back()+1; - char* dstart,*dend; - std::vector output; - do { - output.resize(output.size()?output.size()*3/2:data.size()/2); - dstart = &output.front(),dend = &output.back()+1; - - result = ConvertUTF32toUTF8((const UTF32**)&sstart,(const UTF32*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion); - } while(result == targetExhausted); - - ReportResult(result); - - // copy to output buffer. - const size_t outlen = (size_t)(dstart-&output.front()); - data.assign(output.begin(),output.begin()+outlen); - return; - } - - // UTF 16 BE with BOM - if(*((uint16_t*)&data.front()) == 0xFFFE) { - - // swap the endianess .. - for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) { - ByteSwap::Swap2(p); - } - } - - // UTF 16 LE with BOM - if(*((uint16_t*)&data.front()) == 0xFEFF) { - DefaultLogger::get()->debug("Found UTF-16 BOM ..."); - - const uint16_t* sstart = (uint16_t*)&data.front()+1, *send = (uint16_t*)(&data.back()+1); - char* dstart,*dend; - std::vector output; - do { - output.resize(output.size()?output.size()*3/2:data.size()*3/4); - dstart = &output.front(),dend = &output.back()+1; - - result = ConvertUTF16toUTF8((const UTF16**)&sstart,(const UTF16*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion); - } while(result == targetExhausted); - - ReportResult(result); - - // copy to output buffer. - const size_t outlen = (size_t)(dstart-&output.front()); - data.assign(output.begin(),output.begin()+outlen); - return; - } -} - -// ------------------------------------------------------------------------------------------------ -// Convert to UTF8 data to ISO-8859-1 -void BaseImporter::ConvertUTF8toISO8859_1(std::string& data) -{ - size_t size = data.size(); - size_t i = 0, j = 0; - - while(i < size) { - if ((unsigned char) data[i] < (size_t) 0x80) { - data[j] = data[i]; - } else if(i < size - 1) { - if((unsigned char) data[i] == 0xC2) { - data[j] = data[++i]; - } else if((unsigned char) data[i] == 0xC3) { - data[j] = ((unsigned char) data[++i] + 0x40); - } else { - std::stringstream stream; - - stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1."; - - DefaultLogger::get()->error(stream.str()); - - data[j++] = data[i++]; - data[j] = data[i]; - } - } else { - DefaultLogger::get()->error("UTF8 code but only one character remaining"); - - data[j] = data[i]; - } - - i++; j++; - } - - data.resize(j); -} - -// ------------------------------------------------------------------------------------------------ -void BaseImporter::TextFileToBuffer(IOStream* stream, - std::vector& data) -{ - ai_assert(NULL != stream); - - const size_t fileSize = stream->FileSize(); - if(!fileSize) { - throw DeadlyImportError("File is empty"); - } - - data.reserve(fileSize+1); - data.resize(fileSize); - if(fileSize != stream->Read( &data[0], 1, fileSize)) { - throw DeadlyImportError("File read error"); - } - - ConvertToUTF8(data); - - // append a binary zero to simplify string parsing - data.push_back(0); -} - -// ------------------------------------------------------------------------------------------------ -namespace Assimp -{ - // Represents an import request - struct LoadRequest - { - LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id) - : file(_file), flags(_flags), refCnt(1),scene(NULL), loaded(false), id(_id) - { - if (_map) - map = *_map; - } - - const std::string file; - unsigned int flags; - unsigned int refCnt; - aiScene* scene; - bool loaded; - BatchLoader::PropertyMap map; - unsigned int id; - - bool operator== (const std::string& f) { - return file == f; - } - }; -} - -// ------------------------------------------------------------------------------------------------ -// BatchLoader::pimpl data structure -struct Assimp::BatchData -{ - BatchData() - : pIOSystem() - , pImporter() - , next_id(0xffff) - {} - - // IO system to be used for all imports - IOSystem* pIOSystem; - - // Importer used to load all meshes - Importer* pImporter; - - // List of all imports - std::list requests; - - // Base path - std::string pathBase; - - // Id for next item - unsigned int next_id; -}; - -// ------------------------------------------------------------------------------------------------ -BatchLoader::BatchLoader(IOSystem* pIO) -{ - ai_assert(NULL != pIO); - - data = new BatchData(); - data->pIOSystem = pIO; - - data->pImporter = new Importer(); - data->pImporter->SetIOHandler(data->pIOSystem); -} - -// ------------------------------------------------------------------------------------------------ -BatchLoader::~BatchLoader() -{ - // delete all scenes wthat have not been polled by the user - for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { - - delete (*it).scene; - } - data->pImporter->SetIOHandler(NULL); /* get pointer back into our posession */ - delete data->pImporter; - delete data; -} - - -// ------------------------------------------------------------------------------------------------ -unsigned int BatchLoader::AddLoadRequest (const std::string& file, - unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/) -{ - ai_assert(!file.empty()); - - // check whether we have this loading request already - std::list::iterator it; - for (it = data->requests.begin();it != data->requests.end(); ++it) { - - // Call IOSystem's path comparison function here - if (data->pIOSystem->ComparePaths((*it).file,file)) { - - if (map) { - if (!((*it).map == *map)) - continue; - } - else if (!(*it).map.empty()) - continue; - - (*it).refCnt++; - return (*it).id; - } - } - - // no, we don't have it. So add it to the queue ... - data->requests.push_back(LoadRequest(file,steps,map,data->next_id)); - return data->next_id++; -} - -// ------------------------------------------------------------------------------------------------ -aiScene* BatchLoader::GetImport (unsigned int which) -{ - for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { - - if ((*it).id == which && (*it).loaded) { - - aiScene* sc = (*it).scene; - if (!(--(*it).refCnt)) { - data->requests.erase(it); - } - return sc; - } - } - return NULL; -} - -// ------------------------------------------------------------------------------------------------ -void BatchLoader::LoadAll() -{ - // no threaded implementation for the moment - for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { - // force validation in debug builds - unsigned int pp = (*it).flags; -#ifdef ASSIMP_BUILD_DEBUG - pp |= aiProcess_ValidateDataStructure; -#endif - // setup config properties if necessary - ImporterPimpl* pimpl = data->pImporter->Pimpl(); - pimpl->mFloatProperties = (*it).map.floats; - pimpl->mIntProperties = (*it).map.ints; - pimpl->mStringProperties = (*it).map.strings; - pimpl->mMatrixProperties = (*it).map.matrices; - - if (!DefaultLogger::isNullLogger()) - { - DefaultLogger::get()->info("%%% BEGIN EXTERNAL FILE %%%"); - DefaultLogger::get()->info("File: " + (*it).file); - } - data->pImporter->ReadFile((*it).file,pp); - (*it).scene = data->pImporter->GetOrphanedScene(); - (*it).loaded = true; - - DefaultLogger::get()->info("%%% END EXTERNAL FILE %%%"); - } -} - - - - + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BaseImporter::BaseImporter() +: progress() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BaseImporter::~BaseImporter() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Imports the given file and returns the imported data. +aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) +{ + progress = pImp->GetProgressHandler(); + ai_assert(progress); + + // Gather configuration properties for this run + SetupProperties( pImp ); + + // Construct a file system filter to improve our success ratio at reading external files + FileSystemFilter filter(pFile,pIOHandler); + + // create a scene object to hold the data + ScopeGuard sc(new aiScene()); + + // dispatch importing + try + { + InternReadFile( pFile, sc, &filter); + + } catch( const std::exception& err ) { + // extract error description + mErrorText = err.what(); + DefaultLogger::get()->error(mErrorText); + return NULL; + } + + // return what we gathered from the import. + sc.dismiss(); + return sc; +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::SetupProperties(const Importer* /*pImp*/) +{ + // the default implementation does nothing +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::GetExtensionList(std::set& extensions) +{ + const aiImporterDesc* desc = GetInfo(); + ai_assert(desc != NULL); + + const char* ext = desc->mFileExtensions; + ai_assert(ext != NULL); + + const char* last = ext; + do { + if (!*ext || *ext == ' ') { + extensions.insert(std::string(last,ext-last)); + ai_assert(ext-last > 0); + last = ext; + while(*last == ' ') { + ++last; + } + } + } + while(*ext++); +} + +// ------------------------------------------------------------------------------------------------ +/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem* pIOHandler, + const std::string& pFile, + const char** tokens, + unsigned int numTokens, + unsigned int searchBytes /* = 200 */, + bool tokensSol /* false */) +{ + ai_assert(NULL != tokens && 0 != numTokens && 0 != searchBytes); + if (!pIOHandler) + return false; + + boost::scoped_ptr pStream (pIOHandler->Open(pFile)); + if (pStream.get() ) { + // read 200 characters from the file + boost::scoped_array _buffer (new char[searchBytes+1 /* for the '\0' */]); + char* buffer = _buffer.get(); + if( NULL == buffer ) { + return false; + } + + const size_t read = pStream->Read(buffer,1,searchBytes); + if( !read ) { + return false; + } + + for( size_t i = 0; i < read; ++i ) { + buffer[ i ] = ::tolower( buffer[ i ] ); + } + + // It is not a proper handling of unicode files here ... + // ehm ... but it works in most cases. + char* cur = buffer,*cur2 = buffer,*end = &buffer[read]; + while (cur != end) { + if( *cur ) { + *cur2++ = *cur; + } + ++cur; + } + *cur2 = '\0'; + + for (unsigned int i = 0; i < numTokens;++i) { + ai_assert(NULL != tokens[i]); + + + const char* r = strstr(buffer,tokens[i]); + if( !r ) { + continue; + } + // We got a match, either we don't care where it is, or it happens to + // be in the beginning of the file / line + if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') { + DefaultLogger::get()->debug(std::string("Found positive match for header keyword: ") + tokens[i]); + return true; + } + } + } + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Simple check for file extension +/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile, + const char* ext0, + const char* ext1, + const char* ext2) +{ + std::string::size_type pos = pFile.find_last_of('.'); + + // no file extension - can't read + if( pos == std::string::npos) + return false; + + const char* ext_real = & pFile[ pos+1 ]; + if( !ASSIMP_stricmp(ext_real,ext0) ) + return true; + + // check for other, optional, file extensions + if (ext1 && !ASSIMP_stricmp(ext_real,ext1)) + return true; + + if (ext2 && !ASSIMP_stricmp(ext_real,ext2)) + return true; + + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Get file extension from path +/*static*/ std::string BaseImporter::GetExtension (const std::string& pFile) +{ + std::string::size_type pos = pFile.find_last_of('.'); + + // no file extension at all + if( pos == std::string::npos) + return ""; + + std::string ret = pFile.substr(pos+1); + std::transform(ret.begin(),ret.end(),ret.begin(),::tolower); // thanks to Andy Maloney for the hint + return ret; +} + +// ------------------------------------------------------------------------------------------------ +// Check for magic bytes at the beginning of the file. +/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile, + const void* _magic, unsigned int num, unsigned int offset, unsigned int size) +{ + ai_assert(size <= 16 && _magic); + + if (!pIOHandler) { + return false; + } + union { + const char* magic; + const uint16_t* magic_u16; + const uint32_t* magic_u32; + }; + magic = reinterpret_cast(_magic); + boost::scoped_ptr pStream (pIOHandler->Open(pFile)); + if (pStream.get() ) { + + // skip to offset + pStream->Seek(offset,aiOrigin_SET); + + // read 'size' characters from the file + union { + char data[16]; + uint16_t data_u16[8]; + uint32_t data_u32[4]; + }; + if(size != pStream->Read(data,1,size)) { + return false; + } + + for (unsigned int i = 0; i < num; ++i) { + // also check against big endian versions of tokens with size 2,4 + // that's just for convinience, the chance that we cause conflicts + // is quite low and it can save some lines and prevent nasty bugs + if (2 == size) { + uint16_t rev = *magic_u16; + ByteSwap::Swap(&rev); + if (data_u16[0] == *magic_u16 || data_u16[0] == rev) { + return true; + } + } + else if (4 == size) { + uint32_t rev = *magic_u32; + ByteSwap::Swap(&rev); + if (data_u32[0] == *magic_u32 || data_u32[0] == rev) { + return true; + } + } + else { + // any length ... just compare + if(!memcmp(magic,data,size)) { + return true; + } + } + magic += size; + } + } + return false; +} + +#include "../contrib/ConvertUTF/ConvertUTF.h" + +// ------------------------------------------------------------------------------------------------ +void ReportResult(ConversionResult res) +{ + if(res == sourceExhausted) { + DefaultLogger::get()->error("Source ends with incomplete character sequence, transformation to UTF-8 fails"); + } + else if(res == sourceIllegal) { + DefaultLogger::get()->error("Source contains illegal character sequence, transformation to UTF-8 fails"); + } +} + +// ------------------------------------------------------------------------------------------------ +// Convert to UTF8 data +void BaseImporter::ConvertToUTF8(std::vector& data) +{ + ConversionResult result; + if(data.size() < 8) { + throw DeadlyImportError("File is too small"); + } + + // UTF 8 with BOM + if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) { + DefaultLogger::get()->debug("Found UTF-8 BOM ..."); + + std::copy(data.begin()+3,data.end(),data.begin()); + data.resize(data.size()-3); + return; + } + + // UTF 32 BE with BOM + if(*((uint32_t*)&data.front()) == 0xFFFE0000) { + + // swap the endianess .. + for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) { + AI_SWAP4P(p); + } + } + + // UTF 32 LE with BOM + if(*((uint32_t*)&data.front()) == 0x0000FFFE) { + DefaultLogger::get()->debug("Found UTF-32 BOM ..."); + + const uint32_t* sstart = (uint32_t*)&data.front()+1, *send = (uint32_t*)&data.back()+1; + char* dstart,*dend; + std::vector output; + do { + output.resize(output.size()?output.size()*3/2:data.size()/2); + dstart = &output.front(),dend = &output.back()+1; + + result = ConvertUTF32toUTF8((const UTF32**)&sstart,(const UTF32*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion); + } while(result == targetExhausted); + + ReportResult(result); + + // copy to output buffer. + const size_t outlen = (size_t)(dstart-&output.front()); + data.assign(output.begin(),output.begin()+outlen); + return; + } + + // UTF 16 BE with BOM + if(*((uint16_t*)&data.front()) == 0xFFFE) { + + // swap the endianess .. + for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) { + ByteSwap::Swap2(p); + } + } + + // UTF 16 LE with BOM + if(*((uint16_t*)&data.front()) == 0xFEFF) { + DefaultLogger::get()->debug("Found UTF-16 BOM ..."); + + const uint16_t* sstart = (uint16_t*)&data.front()+1, *send = (uint16_t*)(&data.back()+1); + char* dstart,*dend; + std::vector output; + do { + output.resize(output.size()?output.size()*3/2:data.size()*3/4); + dstart = &output.front(),dend = &output.back()+1; + + result = ConvertUTF16toUTF8((const UTF16**)&sstart,(const UTF16*)send,(UTF8**)&dstart,(UTF8*)dend,lenientConversion); + } while(result == targetExhausted); + + ReportResult(result); + + // copy to output buffer. + const size_t outlen = (size_t)(dstart-&output.front()); + data.assign(output.begin(),output.begin()+outlen); + return; + } +} + +// ------------------------------------------------------------------------------------------------ +// Convert to UTF8 data to ISO-8859-1 +void BaseImporter::ConvertUTF8toISO8859_1(std::string& data) +{ + size_t size = data.size(); + size_t i = 0, j = 0; + + while(i < size) { + if ((unsigned char) data[i] < (size_t) 0x80) { + data[j] = data[i]; + } else if(i < size - 1) { + if((unsigned char) data[i] == 0xC2) { + data[j] = data[++i]; + } else if((unsigned char) data[i] == 0xC3) { + data[j] = ((unsigned char) data[++i] + 0x40); + } else { + std::stringstream stream; + + stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1."; + + DefaultLogger::get()->error(stream.str()); + + data[j++] = data[i++]; + data[j] = data[i]; + } + } else { + DefaultLogger::get()->error("UTF8 code but only one character remaining"); + + data[j] = data[i]; + } + + i++; j++; + } + + data.resize(j); +} + +// ------------------------------------------------------------------------------------------------ +void BaseImporter::TextFileToBuffer(IOStream* stream, + std::vector& data) +{ + ai_assert(NULL != stream); + + const size_t fileSize = stream->FileSize(); + if(!fileSize) { + throw DeadlyImportError("File is empty"); + } + + data.reserve(fileSize+1); + data.resize(fileSize); + if(fileSize != stream->Read( &data[0], 1, fileSize)) { + throw DeadlyImportError("File read error"); + } + + ConvertToUTF8(data); + + // append a binary zero to simplify string parsing + data.push_back(0); +} + +// ------------------------------------------------------------------------------------------------ +namespace Assimp +{ + // Represents an import request + struct LoadRequest + { + LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id) + : file(_file), flags(_flags), refCnt(1),scene(NULL), loaded(false), id(_id) + { + if (_map) + map = *_map; + } + + const std::string file; + unsigned int flags; + unsigned int refCnt; + aiScene* scene; + bool loaded; + BatchLoader::PropertyMap map; + unsigned int id; + + bool operator== (const std::string& f) { + return file == f; + } + }; +} + +// ------------------------------------------------------------------------------------------------ +// BatchLoader::pimpl data structure +struct Assimp::BatchData +{ + BatchData() + : pIOSystem() + , pImporter() + , next_id(0xffff) + {} + + // IO system to be used for all imports + IOSystem* pIOSystem; + + // Importer used to load all meshes + Importer* pImporter; + + // List of all imports + std::list requests; + + // Base path + std::string pathBase; + + // Id for next item + unsigned int next_id; +}; + +// ------------------------------------------------------------------------------------------------ +BatchLoader::BatchLoader(IOSystem* pIO) +{ + ai_assert(NULL != pIO); + + data = new BatchData(); + data->pIOSystem = pIO; + + data->pImporter = new Importer(); + data->pImporter->SetIOHandler(data->pIOSystem); +} + +// ------------------------------------------------------------------------------------------------ +BatchLoader::~BatchLoader() +{ + // delete all scenes wthat have not been polled by the user + for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { + + delete (*it).scene; + } + data->pImporter->SetIOHandler(NULL); /* get pointer back into our posession */ + delete data->pImporter; + delete data; +} + + +// ------------------------------------------------------------------------------------------------ +unsigned int BatchLoader::AddLoadRequest (const std::string& file, + unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/) +{ + ai_assert(!file.empty()); + + // check whether we have this loading request already + std::list::iterator it; + for (it = data->requests.begin();it != data->requests.end(); ++it) { + + // Call IOSystem's path comparison function here + if (data->pIOSystem->ComparePaths((*it).file,file)) { + + if (map) { + if (!((*it).map == *map)) + continue; + } + else if (!(*it).map.empty()) + continue; + + (*it).refCnt++; + return (*it).id; + } + } + + // no, we don't have it. So add it to the queue ... + data->requests.push_back(LoadRequest(file,steps,map,data->next_id)); + return data->next_id++; +} + +// ------------------------------------------------------------------------------------------------ +aiScene* BatchLoader::GetImport (unsigned int which) +{ + for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { + + if ((*it).id == which && (*it).loaded) { + + aiScene* sc = (*it).scene; + if (!(--(*it).refCnt)) { + data->requests.erase(it); + } + return sc; + } + } + return NULL; +} + +// ------------------------------------------------------------------------------------------------ +void BatchLoader::LoadAll() +{ + // no threaded implementation for the moment + for (std::list::iterator it = data->requests.begin();it != data->requests.end(); ++it) { + // force validation in debug builds + unsigned int pp = (*it).flags; +#ifdef ASSIMP_BUILD_DEBUG + pp |= aiProcess_ValidateDataStructure; +#endif + // setup config properties if necessary + ImporterPimpl* pimpl = data->pImporter->Pimpl(); + pimpl->mFloatProperties = (*it).map.floats; + pimpl->mIntProperties = (*it).map.ints; + pimpl->mStringProperties = (*it).map.strings; + pimpl->mMatrixProperties = (*it).map.matrices; + + if (!DefaultLogger::isNullLogger()) + { + DefaultLogger::get()->info("%%% BEGIN EXTERNAL FILE %%%"); + DefaultLogger::get()->info("File: " + (*it).file); + } + data->pImporter->ReadFile((*it).file,pp); + (*it).scene = data->pImporter->GetOrphanedScene(); + (*it).loaded = true; + + DefaultLogger::get()->info("%%% END EXTERNAL FILE %%%"); + } +} diff --git a/code/BaseImporter.h b/code/BaseImporter.h index 4f3f6269d..2234d7116 100644 --- a/code/BaseImporter.h +++ b/code/BaseImporter.h @@ -1,375 +1,375 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Definition of the base class for all importer worker classes. */ -#ifndef INCLUDED_AI_BASEIMPORTER_H -#define INCLUDED_AI_BASEIMPORTER_H - -#include "Exceptional.h" - -#include -#include -#include -#include -#include "../include/assimp/types.h" -#include "../include/assimp/ProgressHandler.hpp" - -struct aiScene; - -namespace Assimp { - -class Importer; -class IOSystem; -class BaseProcess; -class SharedPostProcessInfo; -class IOStream; - - -// utility to do char4 to uint32 in a portable manner -#define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \ - (string[1] << 16) + (string[2] << 8) + string[3])) - -// --------------------------------------------------------------------------- -template -struct ScopeGuard -{ - ScopeGuard(T* obj) : obj(obj), mdismiss() {} - ~ScopeGuard () throw() { - if (!mdismiss) { - delete obj; - } - obj = NULL; - } - - T* dismiss() { - mdismiss=true; - return obj; - } - - operator T*() { - return obj; - } - - T* operator -> () { - return obj; - } - -private: - // no copying allowed. - ScopeGuard(); - ScopeGuard( const ScopeGuard & ); - ScopeGuard &operator = ( const ScopeGuard & ); - - T* obj; - bool mdismiss; -}; - - - -// --------------------------------------------------------------------------- -/** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface - * for all importer worker classes. - * - * The interface defines two functions: CanRead() is used to check if the - * importer can handle the format of the given file. If an implementation of - * this function returns true, the importer then calls ReadFile() which - * imports the given file. ReadFile is not overridable, it just calls - * InternReadFile() and catches any ImportErrorException that might occur. - */ -class ASSIMP_API BaseImporter -{ - friend class Importer; - -public: - - /** Constructor to be privately used by #Importer */ - BaseImporter(); - - /** Destructor, private as well */ - virtual ~BaseImporter(); - -public: - // ------------------------------------------------------------------- - /** Returns whether the class can handle the format of the given file. - * - * The implementation should be as quick as possible. A check for - * the file extension is enough. If no suitable loader is found with - * this strategy, CanRead() is called again, the 'checkSig' parameter - * set to true this time. Now the implementation is expected to - * perform a full check of the file structure, possibly searching the - * first bytes of the file for magic identifiers or keywords. - * - * @param pFile Path and file name of the file to be examined. - * @param pIOHandler The IO handler to use for accessing any file. - * @param checkSig Set to true if this method is called a second time. - * This time, the implementation may take more time to examine the - * contents of the file to be loaded for magic bytes, keywords, etc - * to be able to load files with unknown/not existent file extensions. - * @return true if the class can read this file, false if not. - */ - virtual bool CanRead( - const std::string& pFile, - IOSystem* pIOHandler, - bool checkSig - ) const = 0; - - // ------------------------------------------------------------------- - /** Imports the given file and returns the imported data. - * If the import succeeds, ownership of the data is transferred to - * the caller. If the import fails, NULL is returned. The function - * takes care that any partially constructed data is destroyed - * beforehand. - * - * @param pImp #Importer object hosting this loader. - * @param pFile Path of the file to be imported. - * @param pIOHandler IO-Handler used to open this and possible other files. - * @return The imported data or NULL if failed. If it failed a - * human-readable error description can be retrieved by calling - * GetErrorText() - * - * @note This function is not intended to be overridden. Implement - * InternReadFile() to do the import. If an exception is thrown somewhere - * in InternReadFile(), this function will catch it and transform it into - * a suitable response to the caller. - */ - aiScene* ReadFile( - const Importer* pImp, - const std::string& pFile, - IOSystem* pIOHandler - ); - - // ------------------------------------------------------------------- - /** Returns the error description of the last error that occured. - * @return A description of the last error that occured. An empty - * string if there was no error. - */ - const std::string& GetErrorText() const { - return mErrorText; - } - - // ------------------------------------------------------------------- - /** Called prior to ReadFile(). - * The function is a request to the importer to update its configuration - * basing on the Importer's configuration property list. - * @param pImp Importer instance - */ - virtual void SetupProperties( - const Importer* pImp - ); - - - // ------------------------------------------------------------------- - /** Called by #Importer::GetImporterInfo to get a description of - * some loader features. Importers must provide this information. */ - virtual const aiImporterDesc* GetInfo() const = 0; - - - - // ------------------------------------------------------------------- - /** Called by #Importer::GetExtensionList for each loaded importer. - * Take the extension list contained in the structure returned by - * #GetInfo and insert all file extensions into the given set. - * @param extension set to collect file extensions in*/ - void GetExtensionList(std::set& extensions); - -protected: - - // ------------------------------------------------------------------- - /** Imports the given file into the given scene structure. The - * function is expected to throw an ImportErrorException if there is - * an error. If it terminates normally, the data in aiScene is - * expected to be correct. Override this function to implement the - * actual importing. - *
- * The output scene must meet the following requirements:
- *
    - *
  • At least a root node must be there, even if its only purpose - * is to reference one mesh.
  • - *
  • aiMesh::mPrimitiveTypes may be 0. The types of primitives - * in the mesh are determined automatically in this case.
  • - *
  • the vertex data is stored in a pseudo-indexed "verbose" format. - * In fact this means that every vertex that is referenced by - * a face is unique. Or the other way round: a vertex index may - * not occur twice in a single aiMesh.
  • - *
  • aiAnimation::mDuration may be -1. Assimp determines the length - * of the animation automatically in this case as the length of - * the longest animation channel.
  • - *
  • aiMesh::mBitangents may be NULL if tangents and normals are - * given. In this case bitangents are computed as the cross product - * between normal and tangent.
  • - *
  • There needn't be a material. If none is there a default material - * is generated. However, it is recommended practice for loaders - * to generate a default material for yourself that matches the - * default material setting for the file format better than Assimp's - * generic default material. Note that default materials *should* - * be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded - * or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy) - * texture.
  • - *
- * If the AI_SCENE_FLAGS_INCOMPLETE-Flag is not set:
    - *
  • at least one mesh must be there
  • - *
  • there may be no meshes with 0 vertices or faces
  • - *
- * This won't be checked (except by the validation step): Assimp will - * crash if one of the conditions is not met! - * - * @param pFile Path of the file to be imported. - * @param pScene The scene object to hold the imported data. - * NULL is not a valid parameter. - * @param pIOHandler The IO handler to use for any file access. - * NULL is not a valid parameter. */ - virtual void InternReadFile( - const std::string& pFile, - aiScene* pScene, - IOSystem* pIOHandler - ) = 0; - -public: // static utilities - - // ------------------------------------------------------------------- - /** A utility for CanRead(). - * - * The function searches the header of a file for a specific token - * and returns true if this token is found. This works for text - * files only. There is a rudimentary handling of UNICODE files. - * The comparison is case independent. - * - * @param pIOSystem IO System to work with - * @param file File name of the file - * @param tokens List of tokens to search for - * @param numTokens Size of the token array - * @param searchBytes Number of bytes to be searched for the tokens. - */ - static bool SearchFileHeaderForToken( - IOSystem* pIOSystem, - const std::string& file, - const char** tokens, - unsigned int numTokens, - unsigned int searchBytes = 200, - bool tokensSol = false); - - // ------------------------------------------------------------------- - /** @brief Check whether a file has a specific file extension - * @param pFile Input file - * @param ext0 Extension to check for. Lowercase characters only, no dot! - * @param ext1 Optional second extension - * @param ext2 Optional third extension - * @note Case-insensitive - */ - static bool SimpleExtensionCheck ( - const std::string& pFile, - const char* ext0, - const char* ext1 = NULL, - const char* ext2 = NULL); - - // ------------------------------------------------------------------- - /** @brief Extract file extension from a string - * @param pFile Input file - * @return Extension without trailing dot, all lowercase - */ - static std::string GetExtension ( - const std::string& pFile); - - // ------------------------------------------------------------------- - /** @brief Check whether a file starts with one or more magic tokens - * @param pFile Input file - * @param pIOHandler IO system to be used - * @param magic n magic tokens - * @params num Size of magic - * @param offset Offset from file start where tokens are located - * @param Size of one token, in bytes. Maximally 16 bytes. - * @return true if one of the given tokens was found - * - * @note For convinence, the check is also performed for the - * byte-swapped variant of all tokens (big endian). Only for - * tokens of size 2,4. - */ - static bool CheckMagicToken( - IOSystem* pIOHandler, - const std::string& pFile, - const void* magic, - unsigned int num, - unsigned int offset = 0, - unsigned int size = 4); - - // ------------------------------------------------------------------- - /** An utility for all text file loaders. It converts a file to our - * UTF8 character set. Errors are reported, but ignored. - * - * @param data File buffer to be converted to UTF8 data. The buffer - * is resized as appropriate. */ - static void ConvertToUTF8( - std::vector& data); - - // ------------------------------------------------------------------- - /** An utility for all text file loaders. It converts a file from our - * UTF8 character set back to ISO-8859-1. Errors are reported, but ignored. - * - * @param data File buffer to be converted from UTF8 to ISO-8859-1. The buffer - * is resized as appropriate. */ - static void ConvertUTF8toISO8859_1( - std::string& data); - - // ------------------------------------------------------------------- - /** Utility for text file loaders which copies the contents of the - * file into a memory buffer and converts it to our UTF8 - * representation. - * @param stream Stream to read from. - * @param data Output buffer to be resized and filled with the - * converted text file data. The buffer is terminated with - * a binary 0. */ - static void TextFileToBuffer( - IOStream* stream, - std::vector& data); - -protected: - - /** Error description in case there was one. */ - std::string mErrorText; - - /** Currently set progress handler */ - ProgressHandler* progress; -}; - - - -} // end of namespace Assimp - -#endif // AI_BASEIMPORTER_H_INC +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Definition of the base class for all importer worker classes. */ +#ifndef INCLUDED_AI_BASEIMPORTER_H +#define INCLUDED_AI_BASEIMPORTER_H + +#include "Exceptional.h" + +#include +#include +#include +#include +#include "../include/assimp/types.h" +#include "../include/assimp/ProgressHandler.hpp" + +struct aiScene; + +namespace Assimp { + +class Importer; +class IOSystem; +class BaseProcess; +class SharedPostProcessInfo; +class IOStream; + + +// utility to do char4 to uint32 in a portable manner +#define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \ + (string[1] << 16) + (string[2] << 8) + string[3])) + +// --------------------------------------------------------------------------- +template +struct ScopeGuard +{ + ScopeGuard(T* obj) : obj(obj), mdismiss() {} + ~ScopeGuard () throw() { + if (!mdismiss) { + delete obj; + } + obj = NULL; + } + + T* dismiss() { + mdismiss=true; + return obj; + } + + operator T*() { + return obj; + } + + T* operator -> () { + return obj; + } + +private: + // no copying allowed. + ScopeGuard(); + ScopeGuard( const ScopeGuard & ); + ScopeGuard &operator = ( const ScopeGuard & ); + + T* obj; + bool mdismiss; +}; + + + +// --------------------------------------------------------------------------- +/** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface + * for all importer worker classes. + * + * The interface defines two functions: CanRead() is used to check if the + * importer can handle the format of the given file. If an implementation of + * this function returns true, the importer then calls ReadFile() which + * imports the given file. ReadFile is not overridable, it just calls + * InternReadFile() and catches any ImportErrorException that might occur. + */ +class ASSIMP_API BaseImporter +{ + friend class Importer; + +public: + + /** Constructor to be privately used by #Importer */ + BaseImporter(); + + /** Destructor, private as well */ + virtual ~BaseImporter(); + +public: + // ------------------------------------------------------------------- + /** Returns whether the class can handle the format of the given file. + * + * The implementation should be as quick as possible. A check for + * the file extension is enough. If no suitable loader is found with + * this strategy, CanRead() is called again, the 'checkSig' parameter + * set to true this time. Now the implementation is expected to + * perform a full check of the file structure, possibly searching the + * first bytes of the file for magic identifiers or keywords. + * + * @param pFile Path and file name of the file to be examined. + * @param pIOHandler The IO handler to use for accessing any file. + * @param checkSig Set to true if this method is called a second time. + * This time, the implementation may take more time to examine the + * contents of the file to be loaded for magic bytes, keywords, etc + * to be able to load files with unknown/not existent file extensions. + * @return true if the class can read this file, false if not. + */ + virtual bool CanRead( + const std::string& pFile, + IOSystem* pIOHandler, + bool checkSig + ) const = 0; + + // ------------------------------------------------------------------- + /** Imports the given file and returns the imported data. + * If the import succeeds, ownership of the data is transferred to + * the caller. If the import fails, NULL is returned. The function + * takes care that any partially constructed data is destroyed + * beforehand. + * + * @param pImp #Importer object hosting this loader. + * @param pFile Path of the file to be imported. + * @param pIOHandler IO-Handler used to open this and possible other files. + * @return The imported data or NULL if failed. If it failed a + * human-readable error description can be retrieved by calling + * GetErrorText() + * + * @note This function is not intended to be overridden. Implement + * InternReadFile() to do the import. If an exception is thrown somewhere + * in InternReadFile(), this function will catch it and transform it into + * a suitable response to the caller. + */ + aiScene* ReadFile( + const Importer* pImp, + const std::string& pFile, + IOSystem* pIOHandler + ); + + // ------------------------------------------------------------------- + /** Returns the error description of the last error that occured. + * @return A description of the last error that occured. An empty + * string if there was no error. + */ + const std::string& GetErrorText() const { + return mErrorText; + } + + // ------------------------------------------------------------------- + /** Called prior to ReadFile(). + * The function is a request to the importer to update its configuration + * basing on the Importer's configuration property list. + * @param pImp Importer instance + */ + virtual void SetupProperties( + const Importer* pImp + ); + + + // ------------------------------------------------------------------- + /** Called by #Importer::GetImporterInfo to get a description of + * some loader features. Importers must provide this information. */ + virtual const aiImporterDesc* GetInfo() const = 0; + + + + // ------------------------------------------------------------------- + /** Called by #Importer::GetExtensionList for each loaded importer. + * Take the extension list contained in the structure returned by + * #GetInfo and insert all file extensions into the given set. + * @param extension set to collect file extensions in*/ + void GetExtensionList(std::set& extensions); + +protected: + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. The + * function is expected to throw an ImportErrorException if there is + * an error. If it terminates normally, the data in aiScene is + * expected to be correct. Override this function to implement the + * actual importing. + *
+ * The output scene must meet the following requirements:
+ *
    + *
  • At least a root node must be there, even if its only purpose + * is to reference one mesh.
  • + *
  • aiMesh::mPrimitiveTypes may be 0. The types of primitives + * in the mesh are determined automatically in this case.
  • + *
  • the vertex data is stored in a pseudo-indexed "verbose" format. + * In fact this means that every vertex that is referenced by + * a face is unique. Or the other way round: a vertex index may + * not occur twice in a single aiMesh.
  • + *
  • aiAnimation::mDuration may be -1. Assimp determines the length + * of the animation automatically in this case as the length of + * the longest animation channel.
  • + *
  • aiMesh::mBitangents may be NULL if tangents and normals are + * given. In this case bitangents are computed as the cross product + * between normal and tangent.
  • + *
  • There needn't be a material. If none is there a default material + * is generated. However, it is recommended practice for loaders + * to generate a default material for yourself that matches the + * default material setting for the file format better than Assimp's + * generic default material. Note that default materials *should* + * be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded + * or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy) + * texture.
  • + *
+ * If the AI_SCENE_FLAGS_INCOMPLETE-Flag is not set:
    + *
  • at least one mesh must be there
  • + *
  • there may be no meshes with 0 vertices or faces
  • + *
+ * This won't be checked (except by the validation step): Assimp will + * crash if one of the conditions is not met! + * + * @param pFile Path of the file to be imported. + * @param pScene The scene object to hold the imported data. + * NULL is not a valid parameter. + * @param pIOHandler The IO handler to use for any file access. + * NULL is not a valid parameter. */ + virtual void InternReadFile( + const std::string& pFile, + aiScene* pScene, + IOSystem* pIOHandler + ) = 0; + +public: // static utilities + + // ------------------------------------------------------------------- + /** A utility for CanRead(). + * + * The function searches the header of a file for a specific token + * and returns true if this token is found. This works for text + * files only. There is a rudimentary handling of UNICODE files. + * The comparison is case independent. + * + * @param pIOSystem IO System to work with + * @param file File name of the file + * @param tokens List of tokens to search for + * @param numTokens Size of the token array + * @param searchBytes Number of bytes to be searched for the tokens. + */ + static bool SearchFileHeaderForToken( + IOSystem* pIOSystem, + const std::string& file, + const char** tokens, + unsigned int numTokens, + unsigned int searchBytes = 200, + bool tokensSol = false); + + // ------------------------------------------------------------------- + /** @brief Check whether a file has a specific file extension + * @param pFile Input file + * @param ext0 Extension to check for. Lowercase characters only, no dot! + * @param ext1 Optional second extension + * @param ext2 Optional third extension + * @note Case-insensitive + */ + static bool SimpleExtensionCheck ( + const std::string& pFile, + const char* ext0, + const char* ext1 = NULL, + const char* ext2 = NULL); + + // ------------------------------------------------------------------- + /** @brief Extract file extension from a string + * @param pFile Input file + * @return Extension without trailing dot, all lowercase + */ + static std::string GetExtension ( + const std::string& pFile); + + // ------------------------------------------------------------------- + /** @brief Check whether a file starts with one or more magic tokens + * @param pFile Input file + * @param pIOHandler IO system to be used + * @param magic n magic tokens + * @params num Size of magic + * @param offset Offset from file start where tokens are located + * @param Size of one token, in bytes. Maximally 16 bytes. + * @return true if one of the given tokens was found + * + * @note For convinence, the check is also performed for the + * byte-swapped variant of all tokens (big endian). Only for + * tokens of size 2,4. + */ + static bool CheckMagicToken( + IOSystem* pIOHandler, + const std::string& pFile, + const void* magic, + unsigned int num, + unsigned int offset = 0, + unsigned int size = 4); + + // ------------------------------------------------------------------- + /** An utility for all text file loaders. It converts a file to our + * UTF8 character set. Errors are reported, but ignored. + * + * @param data File buffer to be converted to UTF8 data. The buffer + * is resized as appropriate. */ + static void ConvertToUTF8( + std::vector& data); + + // ------------------------------------------------------------------- + /** An utility for all text file loaders. It converts a file from our + * UTF8 character set back to ISO-8859-1. Errors are reported, but ignored. + * + * @param data File buffer to be converted from UTF8 to ISO-8859-1. The buffer + * is resized as appropriate. */ + static void ConvertUTF8toISO8859_1( + std::string& data); + + // ------------------------------------------------------------------- + /** Utility for text file loaders which copies the contents of the + * file into a memory buffer and converts it to our UTF8 + * representation. + * @param stream Stream to read from. + * @param data Output buffer to be resized and filled with the + * converted text file data. The buffer is terminated with + * a binary 0. */ + static void TextFileToBuffer( + IOStream* stream, + std::vector& data); + +protected: + + /** Error description in case there was one. */ + std::string mErrorText; + + /** Currently set progress handler */ + ProgressHandler* progress; +}; + + + +} // end of namespace Assimp + +#endif // AI_BASEIMPORTER_H_INC diff --git a/code/BaseProcess.cpp b/code/BaseProcess.cpp index 2c06b3883..199f13caa 100644 --- a/code/BaseProcess.cpp +++ b/code/BaseProcess.cpp @@ -1,105 +1,105 @@ -/* ---------------------------------------------------------------------------- -Open Asset Import Library (assimp) ---------------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the following -conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------------- -*/ - -/** @file Implementation of BaseProcess */ - -#include "BaseImporter.h" -#include "BaseProcess.h" -#include "../include/assimp/DefaultLogger.hpp" -#include "../include/assimp/scene.h" -#include "Importer.h" - -using namespace Assimp; - -// ------------------------------------------------------------------------------------------------ -// Constructor to be privately used by Importer -BaseProcess::BaseProcess() -: shared() -, progress() -{ -} - -// ------------------------------------------------------------------------------------------------ -// Destructor, private as well -BaseProcess::~BaseProcess() -{ - // nothing to do here -} - -// ------------------------------------------------------------------------------------------------ -void BaseProcess::ExecuteOnScene( Importer* pImp) -{ - ai_assert(NULL != pImp && NULL != pImp->Pimpl()->mScene); - - progress = pImp->GetProgressHandler(); - ai_assert(progress); - - SetupProperties( pImp ); - - // catch exceptions thrown inside the PostProcess-Step - try - { - Execute(pImp->Pimpl()->mScene); - - } catch( const std::exception& err ) { - - // extract error description - pImp->Pimpl()->mErrorString = err.what(); - DefaultLogger::get()->error(pImp->Pimpl()->mErrorString); - - // and kill the partially imported data - delete pImp->Pimpl()->mScene; - pImp->Pimpl()->mScene = NULL; - } -} - -// ------------------------------------------------------------------------------------------------ -void BaseProcess::SetupProperties(const Importer* /*pImp*/) -{ - // the default implementation does nothing -} - -// ------------------------------------------------------------------------------------------------ -bool BaseProcess::RequireVerboseFormat() const -{ - return true; -} - +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file Implementation of BaseProcess */ + +#include "BaseImporter.h" +#include "BaseProcess.h" +#include "../include/assimp/DefaultLogger.hpp" +#include "../include/assimp/scene.h" +#include "Importer.h" + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +BaseProcess::BaseProcess() +: shared() +, progress() +{ +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +BaseProcess::~BaseProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +void BaseProcess::ExecuteOnScene( Importer* pImp) +{ + ai_assert(NULL != pImp && NULL != pImp->Pimpl()->mScene); + + progress = pImp->GetProgressHandler(); + ai_assert(progress); + + SetupProperties( pImp ); + + // catch exceptions thrown inside the PostProcess-Step + try + { + Execute(pImp->Pimpl()->mScene); + + } catch( const std::exception& err ) { + + // extract error description + pImp->Pimpl()->mErrorString = err.what(); + DefaultLogger::get()->error(pImp->Pimpl()->mErrorString); + + // and kill the partially imported data + delete pImp->Pimpl()->mScene; + pImp->Pimpl()->mScene = NULL; + } +} + +// ------------------------------------------------------------------------------------------------ +void BaseProcess::SetupProperties(const Importer* /*pImp*/) +{ + // the default implementation does nothing +} + +// ------------------------------------------------------------------------------------------------ +bool BaseProcess::RequireVerboseFormat() const +{ + return true; +} + diff --git a/code/BaseProcess.h b/code/BaseProcess.h index 7ec3e2d7c..0e7530f16 100644 --- a/code/BaseProcess.h +++ b/code/BaseProcess.h @@ -1,294 +1,294 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file Base class of all import post processing steps */ -#ifndef INCLUDED_AI_BASEPROCESS_H -#define INCLUDED_AI_BASEPROCESS_H - -#include - -#include "../include/assimp/types.h" -#include "GenericProperty.h" - -struct aiScene; - -namespace Assimp { - -class Importer; - -// --------------------------------------------------------------------------- -/** Helper class to allow post-processing steps to interact with each other. - * - * The class maintains a simple property list that can be used by pp-steps - * to provide additional information to other steps. This is primarily - * intended for cross-step optimizations. - */ -class SharedPostProcessInfo -{ -public: - - struct Base - { - virtual ~Base() - {} - }; - - //! Represents data that is allocated on the heap, thus needs to be deleted - template - struct THeapData : public Base - { - THeapData(T* in) - : data (in) - {} - - ~THeapData() - { - delete data; - } - T* data; - }; - - //! Represents static, by-value data not allocated on the heap - template - struct TStaticData : public Base - { - TStaticData(T in) - : data (in) - {} - - ~TStaticData() - {} - - T data; - }; - - // some typedefs for cleaner code - typedef unsigned int KeyType; - typedef std::map PropertyMap; - -public: - - //! Destructor - ~SharedPostProcessInfo() - { - Clean(); - } - - //! Remove all stored properties from the table - void Clean() - { - // invoke the virtual destructor for all stored properties - for (PropertyMap::iterator it = pmap.begin(), end = pmap.end(); - it != end; ++it) - { - delete (*it).second; - } - pmap.clear(); - } - - //! Add a heap property to the list - template - void AddProperty( const char* name, T* in ){ - AddProperty(name,(Base*)new THeapData(in)); - } - - //! Add a static by-value property to the list - template - void AddProperty( const char* name, T in ){ - AddProperty(name,(Base*)new TStaticData(in)); - } - - - //! Get a heap property - template - bool GetProperty( const char* name, T*& out ) const - { - THeapData* t = (THeapData*)GetPropertyInternal(name); - if(!t) - { - out = NULL; - return false; - } - out = t->data; - return true; - } - - //! Get a static, by-value property - template - bool GetProperty( const char* name, T& out ) const - { - TStaticData* t = (TStaticData*)GetPropertyInternal(name); - if(!t)return false; - out = t->data; - return true; - } - - //! Remove a property of a specific type - void RemoveProperty( const char* name) { - SetGenericPropertyPtr(pmap,name,NULL); - } - -private: - - void AddProperty( const char* name, Base* data) { - SetGenericPropertyPtr(pmap,name,data); - } - - Base* GetPropertyInternal( const char* name) const { - return GetGenericProperty(pmap,name,NULL); - } - -private: - - //! Map of all stored properties - PropertyMap pmap; -}; - -#if 0 - -// --------------------------------------------------------------------------- -/** @brief Represents a dependency table for a postprocessing steps. - * - * For future use. - */ - struct PPDependencyTable - { - unsigned int execute_me_before_these; - unsigned int execute_me_after_these; - unsigned int only_if_these_are_not_specified; - unsigned int mutually_exclusive_with; - }; - -#endif - - -#define AI_SPP_SPATIAL_SORT "$Spat" - -// --------------------------------------------------------------------------- -/** The BaseProcess defines a common interface for all post processing steps. - * A post processing step is run after a successful import if the caller - * specified the corresponding flag when calling ReadFile(). - * Enum #aiPostProcessSteps defines which flags are available. - * After a successful import the Importer iterates over its internal array - * of processes and calls IsActive() on each process to evaluate if the step - * should be executed. If the function returns true, the class' Execute() - * function is called subsequently. - */ -class ASSIMP_API_WINONLY BaseProcess -{ - friend class Importer; - -public: - - /** Constructor to be privately used by Importer */ - BaseProcess(); - - /** Destructor, private as well */ - virtual ~BaseProcess(); - -public: - - // ------------------------------------------------------------------- - /** Returns whether the processing step is present in the given flag. - * @param pFlags The processing flags the importer was called with. A - * bitwise combination of #aiPostProcessSteps. - * @return true if the process is present in this flag fields, - * false if not. - */ - virtual bool IsActive( unsigned int pFlags) const = 0; - - // ------------------------------------------------------------------- - /** Check whether this step expects its input vertex data to be - * in verbose format. */ - virtual bool RequireVerboseFormat() const; - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * The function deletes the scene if the postprocess step fails ( - * the object pointer will be set to NULL). - * @param pImp Importer instance (pImp->mScene must be valid) - */ - void ExecuteOnScene( Importer* pImp); - - // ------------------------------------------------------------------- - /** Called prior to ExecuteOnScene(). - * The function is a request to the process to update its configuration - * basing on the Importer's configuration property list. - */ - virtual void SetupProperties(const Importer* pImp); - - // ------------------------------------------------------------------- - /** Executes the post processing step on the given imported data. - * A process should throw an ImportErrorException* if it fails. - * This method must be implemented by deriving classes. - * @param pScene The imported data to work at. - */ - virtual void Execute( aiScene* pScene) = 0; - - - // ------------------------------------------------------------------- - /** Assign a new SharedPostProcessInfo to the step. This object - * allows multiple postprocess steps to share data. - * @param sh May be NULL - */ - inline void SetSharedData(SharedPostProcessInfo* sh) { - shared = sh; - } - - // ------------------------------------------------------------------- - /** Get the shared data that is assigned to the step. - */ - inline SharedPostProcessInfo* GetSharedData() { - return shared; - } - -protected: - - /** See the doc of #SharedPostProcessInfo for more details */ - SharedPostProcessInfo* shared; - - /** Currently active progress handler */ - ProgressHandler* progress; -}; - - -} // end of namespace Assimp - -#endif // AI_BASEPROCESS_H_INC +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Base class of all import post processing steps */ +#ifndef INCLUDED_AI_BASEPROCESS_H +#define INCLUDED_AI_BASEPROCESS_H + +#include + +#include "../include/assimp/types.h" +#include "GenericProperty.h" + +struct aiScene; + +namespace Assimp { + +class Importer; + +// --------------------------------------------------------------------------- +/** Helper class to allow post-processing steps to interact with each other. + * + * The class maintains a simple property list that can be used by pp-steps + * to provide additional information to other steps. This is primarily + * intended for cross-step optimizations. + */ +class SharedPostProcessInfo +{ +public: + + struct Base + { + virtual ~Base() + {} + }; + + //! Represents data that is allocated on the heap, thus needs to be deleted + template + struct THeapData : public Base + { + THeapData(T* in) + : data (in) + {} + + ~THeapData() + { + delete data; + } + T* data; + }; + + //! Represents static, by-value data not allocated on the heap + template + struct TStaticData : public Base + { + TStaticData(T in) + : data (in) + {} + + ~TStaticData() + {} + + T data; + }; + + // some typedefs for cleaner code + typedef unsigned int KeyType; + typedef std::map PropertyMap; + +public: + + //! Destructor + ~SharedPostProcessInfo() + { + Clean(); + } + + //! Remove all stored properties from the table + void Clean() + { + // invoke the virtual destructor for all stored properties + for (PropertyMap::iterator it = pmap.begin(), end = pmap.end(); + it != end; ++it) + { + delete (*it).second; + } + pmap.clear(); + } + + //! Add a heap property to the list + template + void AddProperty( const char* name, T* in ){ + AddProperty(name,(Base*)new THeapData(in)); + } + + //! Add a static by-value property to the list + template + void AddProperty( const char* name, T in ){ + AddProperty(name,(Base*)new TStaticData(in)); + } + + + //! Get a heap property + template + bool GetProperty( const char* name, T*& out ) const + { + THeapData* t = (THeapData*)GetPropertyInternal(name); + if(!t) + { + out = NULL; + return false; + } + out = t->data; + return true; + } + + //! Get a static, by-value property + template + bool GetProperty( const char* name, T& out ) const + { + TStaticData* t = (TStaticData*)GetPropertyInternal(name); + if(!t)return false; + out = t->data; + return true; + } + + //! Remove a property of a specific type + void RemoveProperty( const char* name) { + SetGenericPropertyPtr(pmap,name,NULL); + } + +private: + + void AddProperty( const char* name, Base* data) { + SetGenericPropertyPtr(pmap,name,data); + } + + Base* GetPropertyInternal( const char* name) const { + return GetGenericProperty(pmap,name,NULL); + } + +private: + + //! Map of all stored properties + PropertyMap pmap; +}; + +#if 0 + +// --------------------------------------------------------------------------- +/** @brief Represents a dependency table for a postprocessing steps. + * + * For future use. + */ + struct PPDependencyTable + { + unsigned int execute_me_before_these; + unsigned int execute_me_after_these; + unsigned int only_if_these_are_not_specified; + unsigned int mutually_exclusive_with; + }; + +#endif + + +#define AI_SPP_SPATIAL_SORT "$Spat" + +// --------------------------------------------------------------------------- +/** The BaseProcess defines a common interface for all post processing steps. + * A post processing step is run after a successful import if the caller + * specified the corresponding flag when calling ReadFile(). + * Enum #aiPostProcessSteps defines which flags are available. + * After a successful import the Importer iterates over its internal array + * of processes and calls IsActive() on each process to evaluate if the step + * should be executed. If the function returns true, the class' Execute() + * function is called subsequently. + */ +class ASSIMP_API_WINONLY BaseProcess +{ + friend class Importer; + +public: + + /** Constructor to be privately used by Importer */ + BaseProcess(); + + /** Destructor, private as well */ + virtual ~BaseProcess(); + +public: + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. A + * bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + virtual bool IsActive( unsigned int pFlags) const = 0; + + // ------------------------------------------------------------------- + /** Check whether this step expects its input vertex data to be + * in verbose format. */ + virtual bool RequireVerboseFormat() const; + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * The function deletes the scene if the postprocess step fails ( + * the object pointer will be set to NULL). + * @param pImp Importer instance (pImp->mScene must be valid) + */ + void ExecuteOnScene( Importer* pImp); + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + virtual void SetupProperties(const Importer* pImp); + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * A process should throw an ImportErrorException* if it fails. + * This method must be implemented by deriving classes. + * @param pScene The imported data to work at. + */ + virtual void Execute( aiScene* pScene) = 0; + + + // ------------------------------------------------------------------- + /** Assign a new SharedPostProcessInfo to the step. This object + * allows multiple postprocess steps to share data. + * @param sh May be NULL + */ + inline void SetSharedData(SharedPostProcessInfo* sh) { + shared = sh; + } + + // ------------------------------------------------------------------- + /** Get the shared data that is assigned to the step. + */ + inline SharedPostProcessInfo* GetSharedData() { + return shared; + } + +protected: + + /** See the doc of #SharedPostProcessInfo for more details */ + SharedPostProcessInfo* shared; + + /** Currently active progress handler */ + ProgressHandler* progress; +}; + + +} // end of namespace Assimp + +#endif // AI_BASEPROCESS_H_INC diff --git a/code/Bitmap.cpp b/code/Bitmap.cpp index cd3567578..13ec37204 100644 --- a/code/Bitmap.cpp +++ b/code/Bitmap.cpp @@ -53,95 +53,95 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { - void Bitmap::Save(aiTexture* texture, IOStream* file) { - if(file != NULL) { - Header header; - DIB dib; + void Bitmap::Save(aiTexture* texture, IOStream* file) { + if(file != NULL) { + Header header; + DIB dib; - dib.size = DIB::dib_size; - dib.width = texture->mWidth; - dib.height = texture->mHeight; - dib.planes = 1; - dib.bits_per_pixel = 8 * mBytesPerPixel; - dib.compression = 0; - dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height; - dib.x_resolution = 0; - dib.y_resolution = 0; - dib.nb_colors = 0; - dib.nb_important_colors = 0; + dib.size = DIB::dib_size; + dib.width = texture->mWidth; + dib.height = texture->mHeight; + dib.planes = 1; + dib.bits_per_pixel = 8 * mBytesPerPixel; + dib.compression = 0; + dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height; + dib.x_resolution = 0; + dib.y_resolution = 0; + dib.nb_colors = 0; + dib.nb_important_colors = 0; - header.type = 0x4D42; // 'BM' - header.offset = Header::header_size + DIB::dib_size; - header.size = header.offset + dib.image_size; - header.reserved1 = 0; - header.reserved2 = 0; + header.type = 0x4D42; // 'BM' + header.offset = Header::header_size + DIB::dib_size; + header.size = header.offset + dib.image_size; + header.reserved1 = 0; + header.reserved2 = 0; - WriteHeader(header, file); - WriteDIB(dib, file); - WriteData(texture, file); - } - } + WriteHeader(header, file); + WriteDIB(dib, file); + WriteData(texture, file); + } + } - template - inline std::size_t Copy(uint8_t* data, T& field) { - std::memcpy(data, &AI_BE(field), sizeof(field)); return sizeof(field); - } + template + inline std::size_t Copy(uint8_t* data, T& field) { + std::memcpy(data, &AI_BE(field), sizeof(field)); return sizeof(field); + } - void Bitmap::WriteHeader(Header& header, IOStream* file) { - uint8_t data[Header::header_size]; + void Bitmap::WriteHeader(Header& header, IOStream* file) { + uint8_t data[Header::header_size]; - std::size_t offset = 0; + std::size_t offset = 0; - offset += Copy(&data[offset], header.type); - offset += Copy(&data[offset], header.size); - offset += Copy(&data[offset], header.reserved1); - offset += Copy(&data[offset], header.reserved2); - offset += Copy(&data[offset], header.offset); + offset += Copy(&data[offset], header.type); + offset += Copy(&data[offset], header.size); + offset += Copy(&data[offset], header.reserved1); + offset += Copy(&data[offset], header.reserved2); + offset += Copy(&data[offset], header.offset); - file->Write(data, Header::header_size, 1); - } + file->Write(data, Header::header_size, 1); + } - void Bitmap::WriteDIB(DIB& dib, IOStream* file) { - uint8_t data[DIB::dib_size]; + void Bitmap::WriteDIB(DIB& dib, IOStream* file) { + uint8_t data[DIB::dib_size]; - std::size_t offset = 0; + std::size_t offset = 0; - offset += Copy(&data[offset], dib.size); - offset += Copy(&data[offset], dib.width); - offset += Copy(&data[offset], dib.height); - offset += Copy(&data[offset], dib.planes); - offset += Copy(&data[offset], dib.bits_per_pixel); - offset += Copy(&data[offset], dib.compression); - offset += Copy(&data[offset], dib.image_size); - offset += Copy(&data[offset], dib.x_resolution); - offset += Copy(&data[offset], dib.y_resolution); - offset += Copy(&data[offset], dib.nb_colors); - offset += Copy(&data[offset], dib.nb_important_colors); + offset += Copy(&data[offset], dib.size); + offset += Copy(&data[offset], dib.width); + offset += Copy(&data[offset], dib.height); + offset += Copy(&data[offset], dib.planes); + offset += Copy(&data[offset], dib.bits_per_pixel); + offset += Copy(&data[offset], dib.compression); + offset += Copy(&data[offset], dib.image_size); + offset += Copy(&data[offset], dib.x_resolution); + offset += Copy(&data[offset], dib.y_resolution); + offset += Copy(&data[offset], dib.nb_colors); + offset += Copy(&data[offset], dib.nb_important_colors); - file->Write(data, DIB::dib_size, 1); - } + file->Write(data, DIB::dib_size, 1); + } - void Bitmap::WriteData(aiTexture* texture, IOStream* file) { - static const std::size_t padding_offset = 4; - static const uint8_t padding_data[padding_offset] = {0x0, 0x0, 0x0, 0x0}; + void Bitmap::WriteData(aiTexture* texture, IOStream* file) { + static const std::size_t padding_offset = 4; + static const uint8_t padding_data[padding_offset] = {0x0, 0x0, 0x0, 0x0}; - unsigned int padding = (padding_offset - ((mBytesPerPixel * texture->mWidth) % padding_offset)) % padding_offset; - uint8_t pixel[mBytesPerPixel]; + unsigned int padding = (padding_offset - ((mBytesPerPixel * texture->mWidth) % padding_offset)) % padding_offset; + uint8_t pixel[mBytesPerPixel]; - for(std::size_t i = 0; i < texture->mHeight; ++i) { - for(std::size_t j = 0; j < texture->mWidth; ++j) { - const aiTexel& texel = texture->pcData[(texture->mHeight - i - 1) * texture->mWidth + j]; // Bitmap files are stored in bottom-up format + for(std::size_t i = 0; i < texture->mHeight; ++i) { + for(std::size_t j = 0; j < texture->mWidth; ++j) { + const aiTexel& texel = texture->pcData[(texture->mHeight - i - 1) * texture->mWidth + j]; // Bitmap files are stored in bottom-up format - pixel[0] = texel.r; - pixel[1] = texel.g; - pixel[2] = texel.b; - pixel[3] = texel.a; + pixel[0] = texel.r; + pixel[1] = texel.g; + pixel[2] = texel.b; + pixel[3] = texel.a; - file->Write(pixel, mBytesPerPixel, 1); - } + file->Write(pixel, mBytesPerPixel, 1); + } - file->Write(padding_data, padding, 1); - } - } + file->Write(padding_data, padding, 1); + } + } } diff --git a/code/Bitmap.h b/code/Bitmap.h index 118a6e1ab..6fe275cc1 100644 --- a/code/Bitmap.h +++ b/code/Bitmap.h @@ -57,85 +57,85 @@ namespace Assimp { class IOStream; class Bitmap { - protected: + protected: - struct Header { + struct Header { - uint16_t type; + uint16_t type; - uint32_t size; + uint32_t size; - uint16_t reserved1; + uint16_t reserved1; - uint16_t reserved2; + uint16_t reserved2; - uint32_t offset; + uint32_t offset; - // We define the struct size because sizeof(Header) might return a wrong result because of structure padding. - // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field). - static const std::size_t header_size = - sizeof(uint16_t) + // type - sizeof(uint32_t) + // size - sizeof(uint16_t) + // reserved1 - sizeof(uint16_t) + // reserved2 - sizeof(uint32_t); // offset + // We define the struct size because sizeof(Header) might return a wrong result because of structure padding. + // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field). + static const std::size_t header_size = + sizeof(uint16_t) + // type + sizeof(uint32_t) + // size + sizeof(uint16_t) + // reserved1 + sizeof(uint16_t) + // reserved2 + sizeof(uint32_t); // offset - }; + }; - struct DIB { + struct DIB { - uint32_t size; + uint32_t size; - int32_t width; + int32_t width; - int32_t height; + int32_t height; - uint16_t planes; + uint16_t planes; - uint16_t bits_per_pixel; + uint16_t bits_per_pixel; - uint32_t compression; + uint32_t compression; - uint32_t image_size; + uint32_t image_size; - int32_t x_resolution; + int32_t x_resolution; - int32_t y_resolution; + int32_t y_resolution; - uint32_t nb_colors; + uint32_t nb_colors; - uint32_t nb_important_colors; + uint32_t nb_important_colors; - // We define the struct size because sizeof(DIB) might return a wrong result because of structure padding. - // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field). - static const std::size_t dib_size = - sizeof(uint32_t) + // size - sizeof(int32_t) + // width - sizeof(int32_t) + // height - sizeof(uint16_t) + // planes - sizeof(uint16_t) + // bits_per_pixel - sizeof(uint32_t) + // compression - sizeof(uint32_t) + // image_size - sizeof(int32_t) + // x_resolution - sizeof(int32_t) + // y_resolution - sizeof(uint32_t) + // nb_colors - sizeof(uint32_t); // nb_important_colors + // We define the struct size because sizeof(DIB) might return a wrong result because of structure padding. + // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field). + static const std::size_t dib_size = + sizeof(uint32_t) + // size + sizeof(int32_t) + // width + sizeof(int32_t) + // height + sizeof(uint16_t) + // planes + sizeof(uint16_t) + // bits_per_pixel + sizeof(uint32_t) + // compression + sizeof(uint32_t) + // image_size + sizeof(int32_t) + // x_resolution + sizeof(int32_t) + // y_resolution + sizeof(uint32_t) + // nb_colors + sizeof(uint32_t); // nb_important_colors - }; + }; - static const std::size_t mBytesPerPixel = 4; + static const std::size_t mBytesPerPixel = 4; - public: + public: - static void Save(aiTexture* texture, IOStream* file); + static void Save(aiTexture* texture, IOStream* file); - protected: + protected: - static void WriteHeader(Header& header, IOStream* file); + static void WriteHeader(Header& header, IOStream* file); - static void WriteDIB(DIB& dib, IOStream* file); + static void WriteDIB(DIB& dib, IOStream* file); - static void WriteData(aiTexture* texture, IOStream* file); + static void WriteData(aiTexture* texture, IOStream* file); }; diff --git a/code/BlenderBMesh.cpp b/code/BlenderBMesh.cpp index c41b46301..bf27a9396 100644 --- a/code/BlenderBMesh.cpp +++ b/code/BlenderBMesh.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2013, assimp team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -23,16 +23,16 @@ following conditions are met: 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 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- @@ -52,7 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { - template< > const std::string LogFunctions< BlenderBMeshConverter >::log_prefix = "BLEND_BMESH: "; + template< > const std::string LogFunctions< BlenderBMeshConverter >::log_prefix = "BLEND_BMESH: "; } using namespace Assimp; @@ -61,142 +61,142 @@ using namespace Assimp::Formatter; // ------------------------------------------------------------------------------------------------ BlenderBMeshConverter::BlenderBMeshConverter( const Mesh* mesh ): - BMesh( mesh ), - triMesh( NULL ) + BMesh( mesh ), + triMesh( NULL ) { } // ------------------------------------------------------------------------------------------------ BlenderBMeshConverter::~BlenderBMeshConverter( ) { - DestroyTriMesh( ); + DestroyTriMesh( ); } // ------------------------------------------------------------------------------------------------ bool BlenderBMeshConverter::ContainsBMesh( ) const { - // TODO - Should probably do some additional verification here - return BMesh->totpoly && BMesh->totloop && BMesh->totvert; + // TODO - Should probably do some additional verification here + return BMesh->totpoly && BMesh->totloop && BMesh->totvert; } // ------------------------------------------------------------------------------------------------ const Mesh* BlenderBMeshConverter::TriangulateBMesh( ) { - AssertValidMesh( ); - AssertValidSizes( ); - PrepareTriMesh( ); + AssertValidMesh( ); + AssertValidSizes( ); + PrepareTriMesh( ); - for ( int i = 0; i < BMesh->totpoly; ++i ) - { - const MPoly& poly = BMesh->mpoly[ i ]; - ConvertPolyToFaces( poly ); - } + for ( int i = 0; i < BMesh->totpoly; ++i ) + { + const MPoly& poly = BMesh->mpoly[ i ]; + ConvertPolyToFaces( poly ); + } - return triMesh; + return triMesh; } // ------------------------------------------------------------------------------------------------ void BlenderBMeshConverter::AssertValidMesh( ) { - if ( !ContainsBMesh( ) ) - { - ThrowException( "BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first" ); - } + if ( !ContainsBMesh( ) ) + { + ThrowException( "BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first" ); + } } // ------------------------------------------------------------------------------------------------ void BlenderBMeshConverter::AssertValidSizes( ) { - if ( BMesh->totpoly != static_cast( BMesh->mpoly.size( ) ) ) - { - ThrowException( "BMesh poly array has incorrect size" ); - } - if ( BMesh->totloop != static_cast( BMesh->mloop.size( ) ) ) - { - ThrowException( "BMesh loop array has incorrect size" ); - } + if ( BMesh->totpoly != static_cast( BMesh->mpoly.size( ) ) ) + { + ThrowException( "BMesh poly array has incorrect size" ); + } + if ( BMesh->totloop != static_cast( BMesh->mloop.size( ) ) ) + { + ThrowException( "BMesh loop array has incorrect size" ); + } } // ------------------------------------------------------------------------------------------------ void BlenderBMeshConverter::PrepareTriMesh( ) { - if ( triMesh ) - { - DestroyTriMesh( ); - } + if ( triMesh ) + { + DestroyTriMesh( ); + } - triMesh = new Mesh( *BMesh ); - triMesh->totface = 0; - triMesh->mface.clear( ); + triMesh = new Mesh( *BMesh ); + triMesh->totface = 0; + triMesh->mface.clear( ); } // ------------------------------------------------------------------------------------------------ void BlenderBMeshConverter::DestroyTriMesh( ) { - delete triMesh; - triMesh = NULL; + delete triMesh; + triMesh = NULL; } // ------------------------------------------------------------------------------------------------ void BlenderBMeshConverter::ConvertPolyToFaces( const MPoly& poly ) { - const MLoop* polyLoop = &BMesh->mloop[ poly.loopstart ]; + 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 ); + 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( 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 ) - { + // UVs are optional, so only convert when present. + if ( BMesh->mloopuv.size() ) + { + if ( (poly.loopstart + poly.totloop ) > static_cast( 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 ) + { #if ASSIMP_BLEND_WITH_GLU_TESSELLATE - BlenderTessellatorGL tessGL( *this ); - tessGL.Tessellate( polyLoop, poly.totloop, triMesh->mvert ); + BlenderTessellatorGL tessGL( *this ); + tessGL.Tessellate( polyLoop, poly.totloop, triMesh->mvert ); #elif ASSIMP_BLEND_WITH_POLY_2_TRI - BlenderTessellatorP2T tessP2T( *this ); - tessP2T.Tessellate( polyLoop, poly.totloop, triMesh->mvert ); + BlenderTessellatorP2T tessP2T( *this ); + tessP2T.Tessellate( polyLoop, poly.totloop, triMesh->mvert ); #endif - } + } } // ------------------------------------------------------------------------------------------------ void BlenderBMeshConverter::AddFace( int v1, int v2, int v3, int v4 ) { - MFace face; - face.v1 = v1; - face.v2 = v2; - face.v3 = v3; - face.v4 = v4; - // TODO - Work out how materials work - face.mat_nr = 0; - triMesh->mface.push_back( face ); - triMesh->totface = triMesh->mface.size( ); + MFace face; + face.v1 = v1; + face.v2 = v2; + face.v3 = v3; + face.v4 = v4; + // TODO - Work out how materials work + face.mat_nr = 0; + triMesh->mface.push_back( face ); + triMesh->totface = triMesh->mface.size( ); } // ------------------------------------------------------------------------------------------------ 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 ); + 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 diff --git a/code/BlenderBMesh.h b/code/BlenderBMesh.h index f26a2a043..0d58b818c 100644 --- a/code/BlenderBMesh.h +++ b/code/BlenderBMesh.h @@ -5,8 +5,8 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2013, assimp team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above @@ -23,16 +23,16 @@ following conditions are met: 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 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- @@ -48,46 +48,46 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { - // TinyFormatter.h - namespace Formatter - { - template < typename T,typename TR, typename A > class basic_formatter; - typedef class basic_formatter< char, std::char_traits< char >, std::allocator< char > > format; - } + // TinyFormatter.h + namespace Formatter + { + template < typename T,typename TR, typename A > class basic_formatter; + typedef class basic_formatter< char, std::char_traits< char >, std::allocator< char > > format; + } - // BlenderScene.h - namespace Blender - { - struct Mesh; - struct MPoly; - struct MLoop; - } + // BlenderScene.h + namespace Blender + { + struct Mesh; + struct MPoly; + struct MLoop; + } - class BlenderBMeshConverter: public LogFunctions< BlenderBMeshConverter > - { - public: - BlenderBMeshConverter( const Blender::Mesh* mesh ); - ~BlenderBMeshConverter( ); + class BlenderBMeshConverter: public LogFunctions< BlenderBMeshConverter > + { + public: + BlenderBMeshConverter( const Blender::Mesh* mesh ); + ~BlenderBMeshConverter( ); - bool ContainsBMesh( ) const; + bool ContainsBMesh( ) const; - const Blender::Mesh* TriangulateBMesh( ); + const Blender::Mesh* TriangulateBMesh( ); - private: - void AssertValidMesh( ); - void AssertValidSizes( ); - void PrepareTriMesh( ); - void DestroyTriMesh( ); - void ConvertPolyToFaces( const Blender::MPoly& poly ); - void AddFace( int v1, int v2, int v3, int v4 = 0 ); - void AddTFace( const float* uv1, const float* uv2, const float *uv3, const float* uv4 = 0 ); + private: + void AssertValidMesh( ); + void AssertValidSizes( ); + void PrepareTriMesh( ); + void DestroyTriMesh( ); + void ConvertPolyToFaces( const Blender::MPoly& poly ); + void AddFace( int v1, int v2, int v3, int v4 = 0 ); + void AddTFace( const float* uv1, const float* uv2, const float *uv3, const float* uv4 = 0 ); - const Blender::Mesh* BMesh; - Blender::Mesh* triMesh; + const Blender::Mesh* BMesh; + Blender::Mesh* triMesh; - friend class BlenderTessellatorGL; - friend class BlenderTessellatorP2T; - }; + friend class BlenderTessellatorGL; + friend class BlenderTessellatorP2T; + }; } // end of namespace Assimp diff --git a/code/BlenderDNA.cpp b/code/BlenderDNA.cpp index 39ecb3aec..45797b3ef 100644 --- a/code/BlenderDNA.cpp +++ b/code/BlenderDNA.cpp @@ -1,373 +1,373 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file BlenderDNA.cpp - * @brief Implementation of the Blender `DNA`, that is its own - * serialized set of data structures. - */ - - -#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER -#include "BlenderDNA.h" -#include "StreamReader.h" -#include "fast_atof.h" -#include - -using namespace Assimp; -using namespace Assimp::Blender; -using namespace Assimp::Formatter; - -#define for_each BOOST_FOREACH -bool match4(StreamReaderAny& stream, const char* string) { - char tmp[] = { - (stream).GetI1(), - (stream).GetI1(), - (stream).GetI1(), - (stream).GetI1() - }; - return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]); -} - -struct Type { - size_t size; - std::string name; -}; - -// ------------------------------------------------------------------------------------------------ -void DNAParser :: Parse () -{ - StreamReaderAny& stream = *db.reader.get(); - DNA& dna = db.dna; - - if(!match4(stream,"SDNA")) { - throw DeadlyImportError("BlenderDNA: Expected SDNA chunk"); - } - - // name dictionary - if(!match4(stream,"NAME")) { - throw DeadlyImportError("BlenderDNA: Expected NAME field"); - } - - std::vector names (stream.GetI4()); - for_each(std::string& s, names) { - while (char c = stream.GetI1()) { - s += c; - } - } - - // type dictionary - for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); - if(!match4(stream,"TYPE")) { - throw DeadlyImportError("BlenderDNA: Expected TYPE field"); - } - - std::vector types (stream.GetI4()); - for_each(Type& s, types) { - while (char c = stream.GetI1()) { - s.name += c; - } - } - - // type length dictionary - for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); - if(!match4(stream,"TLEN")) { - throw DeadlyImportError("BlenderDNA: Expected TLEN field"); - } - - for_each(Type& s, types) { - s.size = stream.GetI2(); - } - - // structures dictionary - for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); - if(!match4(stream,"STRC")) { - throw DeadlyImportError("BlenderDNA: Expected STRC field"); - } - - size_t end = stream.GetI4(), fields = 0; - - dna.structures.reserve(end); - for(size_t i = 0; i != end; ++i) { - - uint16_t n = stream.GetI2(); - if (n >= types.size()) { - throw DeadlyImportError((format(), - "BlenderDNA: Invalid type index in structure name" ,n, - " (there are only ", types.size(), " entries)" - )); - } - - // maintain separate indexes - dna.indices[types[n].name] = dna.structures.size(); - - dna.structures.push_back(Structure()); - Structure& s = dna.structures.back(); - s.name = types[n].name; - //s.index = dna.structures.size()-1; - - n = stream.GetI2(); - s.fields.reserve(n); - - size_t offset = 0; - for (size_t m = 0; m < n; ++m, ++fields) { - - uint16_t j = stream.GetI2(); - if (j >= types.size()) { - throw DeadlyImportError((format(), - "BlenderDNA: Invalid type index in structure field ", j, - " (there are only ", types.size(), " entries)" - )); - } - s.fields.push_back(Field()); - Field& f = s.fields.back(); - f.offset = offset; - - f.type = types[j].name; - f.size = types[j].size; - - j = stream.GetI2(); - if (j >= names.size()) { - throw DeadlyImportError((format(), - "BlenderDNA: Invalid name index in structure field ", j, - " (there are only ", names.size(), " entries)" - )); - } - - f.name = names[j]; - f.flags = 0u; - - // pointers always specify the size of the pointee instead of their own. - // The pointer asterisk remains a property of the lookup name. - if (f.name[0] == '*') { - f.size = db.i64bit ? 8 : 4; - f.flags |= FieldFlag_Pointer; - } - - // arrays, however, specify the size of a single element so we - // need to parse the (possibly multi-dimensional) array declaration - // in order to obtain the actual size of the array in the file. - // Also we need to alter the lookup name to include no array - // brackets anymore or size fixup won't work (if our size does - // not match the size read from the DNA). - if (*f.name.rbegin() == ']') { - const std::string::size_type rb = f.name.find('['); - if (rb == std::string::npos) { - throw DeadlyImportError((format(), - "BlenderDNA: Encountered invalid array declaration ", - f.name - )); - } - - f.flags |= FieldFlag_Array; - DNA::ExtractArraySize(f.name,f.array_sizes); - f.name = f.name.substr(0,rb); - - f.size *= f.array_sizes[0] * f.array_sizes[1]; - } - - // maintain separate indexes - s.indices[f.name] = s.fields.size()-1; - offset += f.size; - } - s.size = offset; - } - - DefaultLogger::get()->debug((format(),"BlenderDNA: Got ",dna.structures.size(), - " structures with totally ",fields," fields")); - -#ifdef ASSIMP_BUILD_BLENDER_DEBUG - dna.DumpToFile(); -#endif - - dna.AddPrimitiveStructures(); - dna.RegisterConverters(); -} - - -#ifdef ASSIMP_BUILD_BLENDER_DEBUG - -#include -// ------------------------------------------------------------------------------------------------ -void DNA :: DumpToFile() -{ - // we dont't bother using the VFS here for this is only for debugging. - // (and all your bases are belong to us). - - std::ofstream f("dna.txt"); - if (f.fail()) { - DefaultLogger::get()->error("Could not dump dna to dna.txt"); - return; - } - f << "Field format: type name offset size" << "\n"; - f << "Structure format: name size" << "\n"; - - for_each(const Structure& s, structures) { - f << s.name << " " << s.size << "\n\n"; - for_each(const Field& ff, s.fields) { - f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << std::endl; - } - f << std::endl; - } - DefaultLogger::get()->info("BlenderDNA: Dumped dna to dna.txt"); -} -#endif - -// ------------------------------------------------------------------------------------------------ -/*static*/ void DNA :: ExtractArraySize( - const std::string& out, - size_t array_sizes[2] -) -{ - array_sizes[0] = array_sizes[1] = 1; - std::string::size_type pos = out.find('['); - if (pos++ == std::string::npos) { - return; - } - array_sizes[0] = strtoul10(&out[pos]); - - pos = out.find('[',pos); - if (pos++ == std::string::npos) { - return; - } - array_sizes[1] = strtoul10(&out[pos]); -} - -// ------------------------------------------------------------------------------------------------ -boost::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure( - const Structure& structure, - const FileDatabase& db -) const -{ - std::map::const_iterator it = converters.find(structure.name); - if (it == converters.end()) { - return boost::shared_ptr< ElemBase >(); - } - - boost::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))(); - (structure.*((*it).second.second))(ret,db); - - return ret; -} - -// ------------------------------------------------------------------------------------------------ -DNA::FactoryPair DNA :: GetBlobToStructureConverter( - const Structure& structure, - const FileDatabase& /*db*/ -) const -{ - std::map::const_iterator it = converters.find(structure.name); - return it == converters.end() ? FactoryPair() : (*it).second; -} - -// basing on http://www.blender.org/development/architecture/notes-on-sdna/ -// ------------------------------------------------------------------------------------------------ -void DNA :: AddPrimitiveStructures() -{ - // NOTE: these are just dummies. Their presence enforces - // Structure::Convert to be called on these - // empty structures. These converters are special - // overloads which scan the name of the structure and - // perform the required data type conversion if one - // of these special names is found in the structure - // in question. - - indices["int"] = structures.size(); - structures.push_back( Structure() ); - structures.back().name = "int"; - structures.back().size = 4; - - indices["short"] = structures.size(); - structures.push_back( Structure() ); - structures.back().name = "short"; - structures.back().size = 2; - - - indices["char"] = structures.size(); - structures.push_back( Structure() ); - structures.back().name = "char"; - structures.back().size = 1; - - - indices["float"] = structures.size(); - structures.push_back( Structure() ); - structures.back().name = "float"; - structures.back().size = 4; - - - indices["double"] = structures.size(); - structures.push_back( Structure() ); - structures.back().name = "double"; - structures.back().size = 8; - - // no long, seemingly. -} - -// ------------------------------------------------------------------------------------------------ -void SectionParser :: Next() -{ - stream.SetCurrentPos(current.start + current.size); - - const char tmp[] = { - stream.GetI1(), - stream.GetI1(), - stream.GetI1(), - stream.GetI1() - }; - current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1); - - current.size = stream.GetI4(); - current.address.val = ptr64 ? stream.GetU8() : stream.GetU4(); - - current.dna_index = stream.GetI4(); - current.num = stream.GetI4(); - - current.start = stream.GetCurrentPos(); - if (stream.GetRemainingSizeToLimit() < current.size) { - throw DeadlyImportError("BLEND: invalid size of file block"); - } - -#ifdef ASSIMP_BUILD_BLENDER_DEBUG - DefaultLogger::get()->debug(current.id); -#endif -} - - - -#endif +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2015, assimp team +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file BlenderDNA.cpp + * @brief Implementation of the Blender `DNA`, that is its own + * serialized set of data structures. + */ + + +#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER +#include "BlenderDNA.h" +#include "StreamReader.h" +#include "fast_atof.h" +#include + +using namespace Assimp; +using namespace Assimp::Blender; +using namespace Assimp::Formatter; + +#define for_each BOOST_FOREACH +bool match4(StreamReaderAny& stream, const char* string) { + char tmp[] = { + (stream).GetI1(), + (stream).GetI1(), + (stream).GetI1(), + (stream).GetI1() + }; + return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]); +} + +struct Type { + size_t size; + std::string name; +}; + +// ------------------------------------------------------------------------------------------------ +void DNAParser :: Parse () +{ + StreamReaderAny& stream = *db.reader.get(); + DNA& dna = db.dna; + + if(!match4(stream,"SDNA")) { + throw DeadlyImportError("BlenderDNA: Expected SDNA chunk"); + } + + // name dictionary + if(!match4(stream,"NAME")) { + throw DeadlyImportError("BlenderDNA: Expected NAME field"); + } + + std::vector names (stream.GetI4()); + for_each(std::string& s, names) { + while (char c = stream.GetI1()) { + s += c; + } + } + + // type dictionary + for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); + if(!match4(stream,"TYPE")) { + throw DeadlyImportError("BlenderDNA: Expected TYPE field"); + } + + std::vector types (stream.GetI4()); + for_each(Type& s, types) { + while (char c = stream.GetI1()) { + s.name += c; + } + } + + // type length dictionary + for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); + if(!match4(stream,"TLEN")) { + throw DeadlyImportError("BlenderDNA: Expected TLEN field"); + } + + for_each(Type& s, types) { + s.size = stream.GetI2(); + } + + // structures dictionary + for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); + if(!match4(stream,"STRC")) { + throw DeadlyImportError("BlenderDNA: Expected STRC field"); + } + + size_t end = stream.GetI4(), fields = 0; + + dna.structures.reserve(end); + for(size_t i = 0; i != end; ++i) { + + uint16_t n = stream.GetI2(); + if (n >= types.size()) { + throw DeadlyImportError((format(), + "BlenderDNA: Invalid type index in structure name" ,n, + " (there are only ", types.size(), " entries)" + )); + } + + // maintain separate indexes + dna.indices[types[n].name] = dna.structures.size(); + + dna.structures.push_back(Structure()); + Structure& s = dna.structures.back(); + s.name = types[n].name; + //s.index = dna.structures.size()-1; + + n = stream.GetI2(); + s.fields.reserve(n); + + size_t offset = 0; + for (size_t m = 0; m < n; ++m, ++fields) { + + uint16_t j = stream.GetI2(); + if (j >= types.size()) { + throw DeadlyImportError((format(), + "BlenderDNA: Invalid type index in structure field ", j, + " (there are only ", types.size(), " entries)" + )); + } + s.fields.push_back(Field()); + Field& f = s.fields.back(); + f.offset = offset; + + f.type = types[j].name; + f.size = types[j].size; + + j = stream.GetI2(); + if (j >= names.size()) { + throw DeadlyImportError((format(), + "BlenderDNA: Invalid name index in structure field ", j, + " (there are only ", names.size(), " entries)" + )); + } + + f.name = names[j]; + f.flags = 0u; + + // pointers always specify the size of the pointee instead of their own. + // The pointer asterisk remains a property of the lookup name. + if (f.name[0] == '*') { + f.size = db.i64bit ? 8 : 4; + f.flags |= FieldFlag_Pointer; + } + + // arrays, however, specify the size of a single element so we + // need to parse the (possibly multi-dimensional) array declaration + // in order to obtain the actual size of the array in the file. + // Also we need to alter the lookup name to include no array + // brackets anymore or size fixup won't work (if our size does + // not match the size read from the DNA). + if (*f.name.rbegin() == ']') { + const std::string::size_type rb = f.name.find('['); + if (rb == std::string::npos) { + throw DeadlyImportError((format(), + "BlenderDNA: Encountered invalid array declaration ", + f.name + )); + } + + f.flags |= FieldFlag_Array; + DNA::ExtractArraySize(f.name,f.array_sizes); + f.name = f.name.substr(0,rb); + + f.size *= f.array_sizes[0] * f.array_sizes[1]; + } + + // maintain separate indexes + s.indices[f.name] = s.fields.size()-1; + offset += f.size; + } + s.size = offset; + } + + DefaultLogger::get()->debug((format(),"BlenderDNA: Got ",dna.structures.size(), + " structures with totally ",fields," fields")); + +#ifdef ASSIMP_BUILD_BLENDER_DEBUG + dna.DumpToFile(); +#endif + + dna.AddPrimitiveStructures(); + dna.RegisterConverters(); +} + + +#ifdef ASSIMP_BUILD_BLENDER_DEBUG + +#include +// ------------------------------------------------------------------------------------------------ +void DNA :: DumpToFile() +{ + // we dont't bother using the VFS here for this is only for debugging. + // (and all your bases are belong to us). + + std::ofstream f("dna.txt"); + if (f.fail()) { + DefaultLogger::get()->error("Could not dump dna to dna.txt"); + return; + } + f << "Field format: type name offset size" << "\n"; + f << "Structure format: name size" << "\n"; + + for_each(const Structure& s, structures) { + f << s.name << " " << s.size << "\n\n"; + for_each(const Field& ff, s.fields) { + f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << std::endl; + } + f << std::endl; + } + DefaultLogger::get()->info("BlenderDNA: Dumped dna to dna.txt"); +} +#endif + +// ------------------------------------------------------------------------------------------------ +/*static*/ void DNA :: ExtractArraySize( + const std::string& out, + size_t array_sizes[2] +) +{ + array_sizes[0] = array_sizes[1] = 1; + std::string::size_type pos = out.find('['); + if (pos++ == std::string::npos) { + return; + } + array_sizes[0] = strtoul10(&out[pos]); + + pos = out.find('[',pos); + if (pos++ == std::string::npos) { + return; + } + array_sizes[1] = strtoul10(&out[pos]); +} + +// ------------------------------------------------------------------------------------------------ +boost::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure( + const Structure& structure, + const FileDatabase& db +) const +{ + std::map::const_iterator it = converters.find(structure.name); + if (it == converters.end()) { + return boost::shared_ptr< ElemBase >(); + } + + boost::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))(); + (structure.*((*it).second.second))(ret,db); + + return ret; +} + +// ------------------------------------------------------------------------------------------------ +DNA::FactoryPair DNA :: GetBlobToStructureConverter( + const Structure& structure, + const FileDatabase& /*db*/ +) const +{ + std::map::const_iterator it = converters.find(structure.name); + return it == converters.end() ? FactoryPair() : (*it).second; +} + +// basing on http://www.blender.org/development/architecture/notes-on-sdna/ +// ------------------------------------------------------------------------------------------------ +void DNA :: AddPrimitiveStructures() +{ + // NOTE: these are just dummies. Their presence enforces + // Structure::Convert to be called on these + // empty structures. These converters are special + // overloads which scan the name of the structure and + // perform the required data type conversion if one + // of these special names is found in the structure + // in question. + + indices["int"] = structures.size(); + structures.push_back( Structure() ); + structures.back().name = "int"; + structures.back().size = 4; + + indices["short"] = structures.size(); + structures.push_back( Structure() ); + structures.back().name = "short"; + structures.back().size = 2; + + + indices["char"] = structures.size(); + structures.push_back( Structure() ); + structures.back().name = "char"; + structures.back().size = 1; + + + indices["float"] = structures.size(); + structures.push_back( Structure() ); + structures.back().name = "float"; + structures.back().size = 4; + + + indices["double"] = structures.size(); + structures.push_back( Structure() ); + structures.back().name = "double"; + structures.back().size = 8; + + // no long, seemingly. +} + +// ------------------------------------------------------------------------------------------------ +void SectionParser :: Next() +{ + stream.SetCurrentPos(current.start + current.size); + + const char tmp[] = { + stream.GetI1(), + stream.GetI1(), + stream.GetI1(), + stream.GetI1() + }; + current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1); + + current.size = stream.GetI4(); + current.address.val = ptr64 ? stream.GetU8() : stream.GetU4(); + + current.dna_index = stream.GetI4(); + current.num = stream.GetI4(); + + current.start = stream.GetCurrentPos(); + if (stream.GetRemainingSizeToLimit() < current.size) { + throw DeadlyImportError("BLEND: invalid size of file block"); + } + +#ifdef ASSIMP_BUILD_BLENDER_DEBUG + DefaultLogger::get()->debug(current.id); +#endif +} + + + +#endif diff --git a/code/BlenderDNA.h b/code/BlenderDNA.h index c7b331c03..55eafdc77 100644 --- a/code/BlenderDNA.h +++ b/code/BlenderDNA.h @@ -1,809 +1,809 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2015, assimp team -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -/** @file BlenderDNA.h - * @brief Blender `DNA` (file format specification embedded in - * blend file itself) loader. - */ -#ifndef INCLUDED_AI_BLEND_DNA_H -#define INCLUDED_AI_BLEND_DNA_H - -#include "BaseImporter.h" -#include "TinyFormatter.h" -#include "StreamReader.h" -#include "../include/assimp/DefaultLogger.hpp" -#include -#include - - -// enable verbose log output. really verbose, so be careful. -#ifdef ASSIMP_BUILD_DEBUG -# define ASSIMP_BUILD_BLENDER_DEBUG -#endif - -// #define ASSIMP_BUILD_BLENDER_NO_STATS - -namespace Assimp { - template class StreamReader; - typedef StreamReader StreamReaderAny; - - namespace Blender { - class FileDatabase; - struct FileBlockHead; - - template