diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d8972b1d..83e1be081 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,7 +110,6 @@ if (WIN32) endif() IF(MSVC) - SET (CMAKE_PREFIX_PATH "D:\\libs\\devil") OPTION( ASSIMP_INSTALL_PDB "Install MSVC debug files." ON @@ -139,8 +138,7 @@ SET (PROJECT_VERSION "${ASSIMP_VERSION}") SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" ) -# Needed for openddl_parser config, no use of c++11 at this moment -ADD_DEFINITIONS( -DOPENDDL_NO_USE_CPP11 ) +# Enable C++1 globally set_property( GLOBAL PROPERTY CXX_STANDARD 11 ) # Get the current working branch @@ -162,7 +160,7 @@ EXECUTE_PROCESS( ) IF(NOT GIT_COMMIT_HASH) - SET(GIT_COMMIT_HASH 0) + SET(GIT_COMMIT_HASH 0) ENDIF(NOT GIT_COMMIT_HASH) IF(ASSIMP_DOUBLE_PRECISION) @@ -180,10 +178,10 @@ CONFIGURE_FILE( ) INCLUDE_DIRECTORIES( - ./ - include - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_BINARY_DIR}/include + ./ + include + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_BINARY_DIR}/include ) LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules" ) @@ -193,14 +191,6 @@ SET(CPACK_COMPONENTS_ALL assimp-bin ${LIBASSIMP_COMPONENT} ${LIBASSIMP-DEV_COMPO SET(ASSIMP_LIBRARY_SUFFIX "" CACHE STRING "Suffix to append to library names") IF( UNIX ) - # Ensure that we do not run into issues like http://www.tcm.phy.cam.ac.uk/sw/inodes64.html on 32 bit linux - IF( ${OPERATING_SYSTEM} MATCHES "Android") - ELSE() - IF ( CMAKE_SIZEOF_VOID_P EQUAL 4) # only necessary for 32-bit linux - #ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 ) - ENDIF() - ENDIF() - # Use GNUInstallDirs for Unix predefined directories INCLUDE(GNUInstallDirs) ENDIF( UNIX ) @@ -213,13 +203,13 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT CMAKE_COMPILER_IS_MINGW) SET(LIBSTDC++_LIBRARIES -lstdc++) ELSEIF(MSVC) # enable multi-core compilation with MSVC - add_compile_options(/MP) - if("${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)") - add_compile_options( /bigobj ) - endif() + ADD_COMPILE_OPTIONS(/MP) + IF("${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)") + ADD_COMPILE_OPTIONS( /bigobj ) + ENDIF() # disable "elements of array '' will be default initialized" warning on MSVC2013 IF(MSVC12) - add_compile_options(/wd4351) + ADD_COMPILE_OPTIONS(/wd4351) ENDIF() ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fvisibility=hidden -fPIC -Wall -Wno-long-long -std=c++11" ) @@ -230,34 +220,39 @@ ELSEIF( CMAKE_COMPILER_IS_MINGW ) ADD_DEFINITIONS( -U__STRICT_ANSI__ ) ENDIF() -if (ASSIMP_COVERALLS) - MESSAGE(STATUS "Coveralls enabled") - INCLUDE(Coveralls) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") -endif() +IF (IOS) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3") +ENDIF() -if (ASSIMP_WERROR) +IF (ASSIMP_COVERALLS) + MESSAGE(STATUS "Coveralls enabled") + INCLUDE(Coveralls) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") +ENDIF() + +IF (ASSIMP_WERROR) MESSAGE(STATUS "Treating warnings as errors") IF (MSVC) - add_compile_options(/WX) + ADD_COMPILE_OPTIONS(/WX) ELSE() SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") ENDIF() -endif() +ENDIF() -if (ASSIMP_ASAN) - MESSAGE(STATUS "AddressSanitizer enabled") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") -endif() +IF (ASSIMP_ASAN) + MESSAGE(STATUS "AddressSanitizer enabled") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") +ENDIF() -if (ASSIMP_UBSAN) - MESSAGE(STATUS "Undefined Behavior sanitizer enabled") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") -endif() +IF (ASSIMP_UBSAN) + MESSAGE(STATUS "Undefined Behavior sanitizer enabled") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") +ENDIF() INCLUDE (FindPkgMacros) INCLUDE (PrecompiledHeader) @@ -290,42 +285,42 @@ ENDIF() 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_CUSTOM_TARGET(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") ENDIF() # cmake configuration files CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config-version.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" @ONLY IMMEDIATE) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" DESTINATION "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}" COMPONENT ${LIBASSIMP-DEV_COMPONENT}) +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}) FIND_PACKAGE( DirectX ) IF( BUILD_DOCS ) - add_subdirectory(doc) + ADD_SUBDIRECTORY(doc) ENDIF( BUILD_DOCS ) # Look for system installed irrXML IF ( SYSTEM_IRRXML ) - find_package( IrrXML REQUIRED ) + FIND_PACKAGE( IrrXML REQUIRED ) ENDIF( SYSTEM_IRRXML ) # Search for external dependencies, and build them from source if not found # Search for zlib IF ( NOT ASSIMP_BUILD_ZLIB ) - find_package(ZLIB) + FIND_PACKAGE(ZLIB) ENDIF( NOT ASSIMP_BUILD_ZLIB ) IF( NOT ZLIB_FOUND ) - message(STATUS "compiling zlib from souces") + MESSAGE(STATUS "compiling zlib from souces") INCLUDE(CheckIncludeFile) INCLUDE(CheckTypeSize) INCLUDE(CheckFunctionExists) # compile from sources - add_subdirectory(contrib/zlib) + 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) +ELSE(NOT ZLIB_FOUND) ADD_DEFINITIONS(-DASSIMP_BUILD_NO_OWN_ZLIB) SET(ZLIB_LIBRARIES_LINKED -lz) ENDIF(NOT ZLIB_FOUND) @@ -367,7 +362,9 @@ IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) SET(C4D_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/includes") # pick the correct prebuilt library - IF(MSVC14) + IF(MSVC15) + SET(C4D_LIB_POSTFIX "_2017") + ELSEIF(MSVC14) SET(C4D_LIB_POSTFIX "_2015") ELSEIF(MSVC12) SET(C4D_LIB_POSTFIX "_2013") @@ -408,7 +405,7 @@ ADD_SUBDIRECTORY(contrib) ADD_SUBDIRECTORY( code/ ) IF ( ASSIMP_BUILD_ASSIMP_TOOLS ) IF ( WIN32 AND DirectX_D3DX9_LIBRARY ) - option ( ASSIMP_BUILD_ASSIMP_VIEW "If the Assimp view tool is built. (requires DirectX)" ${DirectX_FOUND} ) + OPTION ( ASSIMP_BUILD_ASSIMP_VIEW "If the Assimp view tool is built. (requires DirectX)" ${DirectX_FOUND} ) IF ( ASSIMP_BUILD_ASSIMP_VIEW ) ADD_SUBDIRECTORY( tools/assimp_view/ ) ENDIF ( ASSIMP_BUILD_ASSIMP_VIEW ) @@ -472,8 +469,8 @@ IF(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES) 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}" ) @@ -503,8 +500,8 @@ IF(CMAKE_CPACK_COMMAND AND UNIX AND ASSIMP_OPT_BUILD_PACKAGES) 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) + INCLUDE(CPack) + INCLUDE(DebSourcePPA) ENDIF() if(WIN32) @@ -516,14 +513,16 @@ if(WIN32) SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib32/") ENDIF() - if(MSVC12) + IF(MSVC12) SET(ASSIMP_MSVC_VERSION "vc120") - elseif(MSVC14) + ELSEIF(MSVC14) SET(ASSIMP_MSVC_VERSION "vc140") + ELSEIF(MSVC15) + SET(ASSIMP_MSVC_VERSION "vc141") ENDIF(MSVC12) - if(MSVC12 OR MSVC14) - add_custom_target(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM) + IF(MSVC12 OR MSVC14 OR MSVC15 ) + ADD_CUSTOM_TARGET(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM) IF(CMAKE_GENERATOR MATCHES "^Visual Studio") ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.dll ${BIN_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.dll VERBATIM) ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/Release/assimp-${ASSIMP_MSVC_VERSION}-mt.exp ${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mt.exp VERBATIM) @@ -544,5 +543,5 @@ if(WIN32) ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb ${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM) ADD_CUSTOM_COMMAND(TARGET UpdateAssimpLibsDebugSymbolsAndDLLs COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/code/assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb ${LIB_DIR}assimp-${ASSIMP_MSVC_VERSION}-mtd.pdb VERBATIM) ENDIF() - ENDIF(MSVC12 OR MSVC14) + ENDIF(MSVC12 OR MSVC14 OR MSVC15 ) ENDIF (WIN32) diff --git a/CREDITS b/CREDITS index df41c482d..26e21d2f4 100644 --- a/CREDITS +++ b/CREDITS @@ -157,12 +157,27 @@ Contributed ExportProperties interface Contributed X File exporter Contributed Step (stp) exporter +- Thomas Iorns (mesilliac) +Initial FBX Export support + For a more detailed list just check: https://github.com/assimp/assimp/network/members -Patreons: + +======== +Patreons +======== + +Huge thanks to our Patreons! + - migenius - Marcus - Cort - elect - Steffen + +=================== +Commercial Sponsors +=================== + +- MyDidimo (mydidimo.com): Sponsored development of FBX Export support diff --git a/Readme.md b/Readme.md index ca6c9ba4d..c78a0c1c8 100644 --- a/Readme.md +++ b/Readme.md @@ -32,32 +32,32 @@ Please check our Wiki as well: https://github.com/assimp/assimp/wiki #### Supported file formats #### -A full list [is here](http://assimp.org/main_features_formats.html). __Importers__: + - 3D -- 3DS -- 3MF +- [3DS](https://en.wikipedia.org/wiki/.3ds) +- [3MF](https://en.wikipedia.org/wiki/3D_Manufacturing_Format) - AC -- AC3D +- [AC3D](https://en.wikipedia.org/wiki/AC3D) - ACC - AMJ - ASE - ASK - B3D -- BLEND (Blender) -- BVH -- COB +- [BLEND](https://en.wikipedia.org/wiki/.blend_(file_format)) +- [BVH](https://en.wikipedia.org/wiki/Biovision_Hierarchy) - CMS -- DAE/Collada -- DXF +- COB +- [DAE/Collada](https://en.wikipedia.org/wiki/COLLADA) +- [DXF](https://en.wikipedia.org/wiki/AutoCAD_DXF) - ENFF -- FBX -- glTF 1.0 + GLB -- glTF 2.0 +- [FBX](https://en.wikipedia.org/wiki/FBX) +- [glTF 1.0](https://en.wikipedia.org/wiki/GlTF#glTF_1.0) + GLB +- [glTF 2.0](https://en.wikipedia.org/wiki/GlTF#glTF_2.0) - HMB - IFC-STEP - IRR / IRRMESH -- LWO +- [LWO](https://en.wikipedia.org/wiki/LightWave_3D) - LWS - LXO - MD2 @@ -70,10 +70,10 @@ __Importers__: - MS3D - NDO - NFF -- OBJ -- OFF -- OGEX -- PLY +- [OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file) +- [OFF](https://en.wikipedia.org/wiki/OFF_(file_format)) +- [OGEX](https://en.wikipedia.org/wiki/Open_Game_Engine_Exchange) +- [PLY](https://en.wikipedia.org/wiki/PLY_(file_format)) - PMX - PRJ - Q3O @@ -82,19 +82,19 @@ __Importers__: - SCN - SIB - SMD -- STL -- STP +- [STP](https://en.wikipedia.org/wiki/ISO_10303-21) +- [STL](https://en.wikipedia.org/wiki/STL_(file_format)) - TER - UC - VTA - X -- X3D +- [X3D](https://en.wikipedia.org/wiki/X3D) - XGL - ZGL Additionally, some formats are supported by dependency on non-free code or external SDKs (not built by default): -- C4D (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange) +- [C4D](https://en.wikipedia.org/wiki/Cinema_4D) (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange) __Exporters__: @@ -109,6 +109,8 @@ __Exporters__: - STEP - glTF 1.0 (partial) - glTF 2.0 (partial) +- 3MF ( experimental ) +- FBX ( experimental ) ### Building ### Take a look into the `INSTALL` file. Our build system is CMake, if you used CMake before there is a good chance you know what to do. @@ -120,7 +122,7 @@ Take a look into the `INSTALL` file. Our build system is CMake, if you used CMak * [Pascal](port/AssimpPascal/Readme.md) * [Javascript (Alpha)](https://github.com/makc/assimp2json) * [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777) -* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (currently supported obj, ply, stl, collada, md2) +* [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status)) ### Other tools ### [open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities. diff --git a/assimp-config.cmake.in b/assimp-config.cmake.in index e06cc10ee..5031c8f8d 100644 --- a/assimp-config.cmake.in +++ b/assimp-config.cmake.in @@ -34,36 +34,18 @@ if( MSVC ) else() set(MSVC_PREFIX "vc150") endif() - set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" FORCE) + set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" ) else() - set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the openrave libraries" FORCE) + set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the openrave libraries" ) endif() set( ASSIMP_CXX_FLAGS ) # dynamically linked library -if( WIN32 ) - # for visual studio linking, most of the time boost dlls will be used - set( ASSIMP_CXX_FLAGS " -DBOOST_ALL_DYN_LINK -DBOOST_ALL_NO_LIB") -endif() set( ASSIMP_LINK_FLAGS "" ) set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@") set( ASSIMP_INCLUDE_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_INCLUDE_INSTALL_DIR@") set( ASSIMP_LIBRARIES assimp${ASSIMP_LIBRARY_SUFFIX}) set( ASSIMP_LIBRARIES ${ASSIMP_LIBRARIES}@CMAKE_DEBUG_POSTFIX@) -# search for the boost version assimp was compiled with -#set(Boost_USE_MULTITHREAD ON) -#set(Boost_USE_STATIC_LIBS OFF) -#set(Boost_USE_STATIC_RUNTIME OFF) -#find_package(Boost ${ASSIMP_Boost_VERSION} EXACT COMPONENTS thread date_time) -#if(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") -# set( ASSIMP_INCLUDE_DIRS "${ASSIMP_INCLUDE_DIRS}" ${Boost_INCLUDE_DIRS}) -#else(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") -# message(WARNING "Failed to find Boost ${ASSIMP_Boost_VERSION} necessary for assimp") -#endif(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") - -# the boost version assimp was compiled with -set( ASSIMP_Boost_VERSION "@Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@") - # for compatibility with pkg-config set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}") set(ASSIMP_LDFLAGS_OTHER "${ASSIMP_LINK_FLAGS}") @@ -74,7 +56,6 @@ MARK_AS_ADVANCED( ASSIMP_LINK_FLAGS ASSIMP_INCLUDE_DIRS ASSIMP_LIBRARIES - ASSIMP_Boost_VERSION ASSIMP_CFLAGS_OTHER ASSIMP_LDFLAGS_OTHER ASSIMP_LIBRARY_SUFFIX diff --git a/code/3DSConverter.cpp b/code/3DSConverter.cpp index cbc6d31d2..081da1bcf 100644 --- a/code/3DSConverter.cpp +++ b/code/3DSConverter.cpp @@ -72,7 +72,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() unsigned int idx( NotSet ); for (unsigned int i = 0; i < mScene->mMaterials.size();++i) { - std::string s = mScene->mMaterials[i].mName; + std::string &s = mScene->mMaterials[i].mName; for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) { *it = static_cast< char >( ::tolower( *it ) ); } diff --git a/code/3DSExporter.cpp b/code/3DSExporter.cpp index 65f4bf8d8..fcd24d8aa 100644 --- a/code/3DSExporter.cpp +++ b/code/3DSExporter.cpp @@ -184,7 +184,7 @@ void ExportScene3DS(const char* pFile, IOSystem* pIOSystem, const aiScene* pScen } // end of namespace Assimp // ------------------------------------------------------------------------------------------------ -Discreet3DSExporter:: Discreet3DSExporter(std::shared_ptr outfile, const aiScene* scene) +Discreet3DSExporter:: Discreet3DSExporter(std::shared_ptr &outfile, const aiScene* scene) : scene(scene) , writer(outfile) { diff --git a/code/3DSExporter.h b/code/3DSExporter.h index 5e138e92d..5db58a4cb 100644 --- a/code/3DSExporter.h +++ b/code/3DSExporter.h @@ -67,7 +67,7 @@ namespace Assimp // ------------------------------------------------------------------------------------------------ class Discreet3DSExporter { public: - Discreet3DSExporter(std::shared_ptr outfile, const aiScene* pScene); + Discreet3DSExporter(std::shared_ptr &outfile, const aiScene* pScene); ~Discreet3DSExporter(); private: diff --git a/code/3DSLoader.cpp b/code/3DSLoader.cpp index 7a5e2b6e5..bfe2bf6e5 100644 --- a/code/3DSLoader.cpp +++ b/code/3DSLoader.cpp @@ -71,7 +71,7 @@ static const aiImporterDesc desc = { 0, 0, 0, - "3ds prj" + "3ds prj 3DS PRJ" }; @@ -113,22 +113,24 @@ Discreet3DSImporter::Discreet3DSImporter() , mScene() , mMasterScale() , bHasBG() -, bIsPrj() -{} +, bIsPrj() { + // empty +} // ------------------------------------------------------------------------------------------------ // Destructor, private as well -Discreet3DSImporter::~Discreet3DSImporter() -{} +Discreet3DSImporter::~Discreet3DSImporter() { + // empty +} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ +bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { std::string extension = GetExtension(pFile); - if(extension == "3ds" || extension == "prj" ) { + if(extension == "3ds" || extension == "3DS" || extension == "prj"|| extension == "PRJ" ) { return true; } + if (!extension.length() || checkSig) { uint16_t token[3]; token[0] = 0x4d4d; @@ -210,7 +212,7 @@ void Discreet3DSImporter::InternReadFile( const std::string& pFile, 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 + // tricky since we'll need to split some meshes into sub-meshes GenerateNodeGraph(pScene); // Now apply the master scaling factor to the scene @@ -347,7 +349,7 @@ void Discreet3DSImporter::ParseObjectChunk() case Discreet3DS::CHUNK_MAT_MATERIAL: // Add a new material to the list - mScene->mMaterials.push_back(D3DS::Material(std::string("UNNAMED_" + std::to_string(mScene->mMaterials.size())))); + mScene->mMaterials.push_back(D3DS::Material(std::string("UNNAMED_" + to_string(mScene->mMaterials.size())))); ParseMaterialChunk(); break; diff --git a/code/3MFXmlTags.h b/code/3MFXmlTags.h index 30aed0e95..e869c33c1 100644 --- a/code/3MFXmlTags.h +++ b/code/3MFXmlTags.h @@ -45,6 +45,11 @@ namespace Assimp { namespace D3MF { namespace XmlTag { + // Meta-data + static const std::string meta = "metadata"; + static const std::string meta_name = "name"; + + // Model-data specific tags static const std::string model = "model"; static const std::string model_unit = "unit"; static const std::string metadata = "metadata"; @@ -62,6 +67,8 @@ namespace XmlTag { static const std::string v2 = "v2"; static const std::string v3 = "v3"; static const std::string id = "id"; + static const std::string pid = "pid"; + static const std::string p1 = "p1"; static const std::string name = "name"; static const std::string type = "type"; static const std::string build = "build"; @@ -69,6 +76,14 @@ namespace XmlTag { static const std::string objectid = "objectid"; static const std::string transform = "transform"; + // Material definitions + static const std::string basematerials = "basematerials"; + static const std::string basematerials_id = "id"; + static const std::string basematerials_base = "base"; + static const std::string basematerials_name = "name"; + static const std::string basematerials_displaycolor = "displaycolor"; + + // Meta info tags static const std::string CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; static const std::string ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels"; static const std::string SCHEMA_CONTENTTYPES = "http://schemas.openxmlformats.org/package/2006/content-types"; @@ -83,7 +98,6 @@ namespace XmlTag { static const std::string PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; static const std::string PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; static const std::string PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; - } } // Namespace D3MF diff --git a/code/AMFImporter_Postprocess.cpp b/code/AMFImporter_Postprocess.cpp index f048e1178..192544fcb 100644 --- a/code/AMFImporter_Postprocess.cpp +++ b/code/AMFImporter_Postprocess.cpp @@ -156,10 +156,11 @@ size_t AMFImporter::PostprocessHelper_GetTextureID_Or_Create(const std::string& TextureConverted_Index = 0; for(const SPP_Texture& tex_convd: mTexture_Converted) { - if(tex_convd.ID == TextureConverted_ID) - return TextureConverted_Index; - else - TextureConverted_Index++; + if ( tex_convd.ID == TextureConverted_ID ) { + return TextureConverted_Index; + } else { + ++TextureConverted_Index; + } } // diff --git a/code/ASELoader.cpp b/code/ASELoader.cpp index 122b226f4..32b0eae2d 100644 --- a/code/ASELoader.cpp +++ b/code/ASELoader.cpp @@ -583,7 +583,7 @@ void ASEImporter::AddNodes (const std::vector& nodes, node->mTransformation = mParentAdjust*snode->mTransform; // Add sub nodes - prevent stack overflow due to recursive parenting - if (node->mName != node->mParent->mName) { + if (node->mName != node->mParent->mName && node->mName != node->mParent->mParent->mName ) { AddNodes(nodes,node,node->mName.data,snode->mTransform); } diff --git a/code/ASEParser.cpp b/code/ASEParser.cpp index 59af24a10..b953f8d96 100644 --- a/code/ASEParser.cpp +++ b/code/ASEParser.cpp @@ -267,7 +267,9 @@ void Parser::Parse() // at the file extension (ASE, ASK, ASC) // ************************************************************* - if (fmt)iFileFormat = fmt; + if ( fmt ) { + iFileFormat = fmt; + } continue; } // main scene information @@ -427,28 +429,25 @@ void Parser::ParseLV1SoftSkinBlock() // Reserve enough storage vert.mBoneWeights.reserve(numWeights); - for (unsigned int w = 0; w < numWeights;++w) - { - std::string bone; + std::string bone; + for (unsigned int w = 0; w < numWeights;++w) { + bone.clear(); 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) - { + for (unsigned int n = 0; n < curMesh->mBones.size();++n) { + if (curMesh->mBones[n].mName == bone) { me.first = n; break; } } - if (-1 == me.first) - { + 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)); + me.first = static_cast( curMesh->mBones.size() ); + curMesh->mBones.push_back( ASE::Bone( bone ) ); } ParseLV4MeshFloat( me.second ); @@ -745,6 +744,7 @@ void Parser::ParseLV3MapBlock(Texture& map) // empty the texture won't be used later. // *********************************************************** bool parsePath = true; + std::string temp; while (true) { if ('*' == *filePtr) @@ -753,7 +753,7 @@ void Parser::ParseLV3MapBlock(Texture& map) // type of map if (TokenMatch(filePtr,"MAP_CLASS" ,9)) { - std::string temp; + temp.clear(); if(!ParseString(temp,"*MAP_CLASS")) SkipToNextToken(); if (temp != "Bitmap" && temp != "Normal Bump") diff --git a/code/BVHLoader.cpp b/code/BVHLoader.cpp index dfeeb60ac..0b2a818ae 100644 --- a/code/BVHLoader.cpp +++ b/code/BVHLoader.cpp @@ -199,6 +199,7 @@ aiNode* BVHLoader::ReadNode() Node& internNode = mNodes.back(); // now read the node's contents + std::string siteToken; while( 1) { std::string token = GetNextToken(); @@ -218,7 +219,8 @@ aiNode* BVHLoader::ReadNode() else if( token == "End") { // The real symbol is "End Site". Second part comes in a separate token - std::string siteToken = GetNextToken(); + siteToken.clear(); + siteToken = GetNextToken(); if( siteToken != "Site") ThrowException( format() << "Expected \"End Site\" keyword, but found \"" << token << " " << siteToken << "\"." ); @@ -262,21 +264,18 @@ aiNode* BVHLoader::ReadEndSite( const std::string& pParentName) aiNode* node = new aiNode( "EndSite_" + pParentName); // now read the node's contents. Only possible entry is "OFFSET" - while( 1) - { - std::string token = GetNextToken(); + std::string token; + while( 1) { + token.clear(); + token = GetNextToken(); // end node's offset - if( token == "OFFSET") - { + if( token == "OFFSET") { ReadNodeOffset( node); - } - else if( token == "}") - { + } else if( token == "}") { // we're done with the end node break; - } else - { + } else { // everything else is a parse error ThrowException( format() << "Unknown keyword \"" << token << "\"." ); } @@ -296,8 +295,10 @@ void BVHLoader::ReadNodeOffset( aiNode* pNode) 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); + 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); } // ------------------------------------------------------------------------------------------------ diff --git a/code/BVHLoader.h b/code/BVHLoader.h index 0836488ff..a18ad81d9 100644 --- a/code/BVHLoader.h +++ b/code/BVHLoader.h @@ -84,7 +84,10 @@ class BVHLoader : public BaseImporter std::vector mChannels; std::vector mChannelValues; // motion data values for that node. Of size NumChannels * NumFrames - Node() { } + Node() + : mNode(nullptr) + { } + explicit Node( const aiNode* pNode) : mNode( pNode) { } }; diff --git a/code/BaseImporter.cpp b/code/BaseImporter.cpp index f653d8d81..4782e9e2d 100644 --- a/code/BaseImporter.cpp +++ b/code/BaseImporter.cpp @@ -115,13 +115,12 @@ void BaseImporter::SetupProperties(const Importer* /*pImp*/) } // ------------------------------------------------------------------------------------------------ -void BaseImporter::GetExtensionList(std::set& extensions) -{ +void BaseImporter::GetExtensionList(std::set& extensions) { const aiImporterDesc* desc = GetInfo(); - ai_assert(desc != NULL); + ai_assert(desc != nullptr); const char* ext = desc->mFileExtensions; - ai_assert(ext != NULL); + ai_assert(ext != nullptr ); const char* last = ext; do { @@ -145,21 +144,19 @@ void BaseImporter::GetExtensionList(std::set& extensions) unsigned int searchBytes /* = 200 */, bool tokensSol /* false */) { - ai_assert( NULL != tokens ); + ai_assert( nullptr != tokens ); ai_assert( 0 != numTokens ); ai_assert( 0 != searchBytes); - if (!pIOHandler) + if ( nullptr == pIOHandler ) { return false; + } std::unique_ptr pStream (pIOHandler->Open(pFile)); if (pStream.get() ) { // read 200 characters from the file std::unique_ptr _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 ) { @@ -181,9 +178,17 @@ void BaseImporter::GetExtensionList(std::set& extensions) } *cur2 = '\0'; - for (unsigned int i = 0; i < numTokens;++i) { - ai_assert(NULL != tokens[i]); - const char* r = strstr(buffer,tokens[i]); + std::string token; + for (unsigned int i = 0; i < numTokens; ++i ) { + ai_assert( nullptr != tokens[i] ); + const size_t len( strlen( tokens[ i ] ) ); + token.clear(); + const char *ptr( tokens[ i ] ); + for ( size_t tokIdx = 0; tokIdx < len; ++tokIdx ) { + token.push_back( tolower( *ptr ) ); + ++ptr; + } + const char* r = strstr( buffer, token.c_str() ); if( !r ) { continue; } @@ -246,7 +251,8 @@ void BaseImporter::GetExtensionList(std::set& extensions) /* 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); + ai_assert( size <= 16 ); + ai_assert( _magic ); if (!pIOHandler) { return false; diff --git a/code/BlenderDNA.cpp b/code/BlenderDNA.cpp index 285cb05b8..1d88f8fa6 100644 --- a/code/BlenderDNA.cpp +++ b/code/BlenderDNA.cpp @@ -58,12 +58,11 @@ using namespace Assimp::Formatter; static bool match4(StreamReaderAny& stream, const char* string) { ai_assert( nullptr != string ); - char tmp[] = { - (const char)(stream).GetI1(), - (const char)(stream).GetI1(), - (const char)(stream).GetI1(), - (const char)(stream).GetI1() - }; + char tmp[4]; + tmp[ 0 ] = ( stream ).GetI1(); + tmp[ 1 ] = ( stream ).GetI1(); + tmp[ 2 ] = ( stream ).GetI1(); + tmp[ 3 ] = ( stream ).GetI1(); return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]); } diff --git a/code/BlenderDNA.h b/code/BlenderDNA.h index 6f4c69c04..3a2455275 100644 --- a/code/BlenderDNA.h +++ b/code/BlenderDNA.h @@ -205,7 +205,7 @@ enum ErrorPolicy { // ------------------------------------------------------------------------------- /** Represents a data structure in a BLEND file. A Structure defines n fields - * and their locatios and encodings the input stream. Usually, every + * and their locations and encodings the input stream. Usually, every * Structure instance pertains to one equally-named data structure in the * BlenderScene.h header. This class defines various utilities to map a * binary `blob` read from the file to such a structure instance with diff --git a/code/BlenderDNA.inl b/code/BlenderDNA.inl index 185531d1a..163798a40 100644 --- a/code/BlenderDNA.inl +++ b/code/BlenderDNA.inl @@ -502,7 +502,7 @@ const FileBlockHead* Structure :: LocateFileBlockForAddress(const Pointer & ptrv { // the file blocks appear in list sorted by // with ascending base addresses so we can run a - // binary search to locate the pointee quickly. + // binary search to locate the pointer quickly. // NOTE: Blender seems to distinguish between side-by-side // data (stored in the same data block) and far pointers, diff --git a/code/BlenderIntermediate.h b/code/BlenderIntermediate.h index 74184a1f1..f3d34d1b2 100644 --- a/code/BlenderIntermediate.h +++ b/code/BlenderIntermediate.h @@ -122,9 +122,11 @@ namespace Blender { # pragma warning(disable:4351) #endif + // As counter-intuitive as it may seem, a comparator must return false for equal values. + // The C++ standard defines and expects this behavior: true if lhs < rhs, false otherwise. struct ObjectCompare { bool operator() (const Object* left, const Object* right) const { - return ::strncmp(left->id.name, right->id.name, strlen( left->id.name ) ) == 0; + return ::strncmp(left->id.name, right->id.name, strlen( left->id.name ) ) < 0; } }; @@ -143,9 +145,11 @@ namespace Blender { , db(db) {} + // As counter-intuitive as it may seem, a comparator must return false for equal values. + // The C++ standard defines and expects this behavior: true if lhs < rhs, false otherwise. struct ObjectCompare { bool operator() (const Object* left, const Object* right) const { - return ::strncmp( left->id.name, right->id.name, strlen( left->id.name ) ) == 0; + return ::strncmp( left->id.name, right->id.name, strlen( left->id.name ) ) < 0; } }; diff --git a/code/BlenderLoader.cpp b/code/BlenderLoader.cpp index 482f9ae5c..d4d6473c5 100644 --- a/code/BlenderLoader.cpp +++ b/code/BlenderLoader.cpp @@ -154,14 +154,6 @@ void BlenderImporter::SetupProperties(const Importer* /*pImp*/) // nothing to be done for the moment } -struct free_it { - free_it(void* free) : free(free) {} - ~free_it() { - ::free(this->free); - } - - void* free; -}; // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. @@ -169,8 +161,7 @@ void BlenderImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND - Bytef* dest = NULL; - free_it free_it_really(dest); + std::vector uncompressed; #endif @@ -218,6 +209,7 @@ void BlenderImporter::InternReadFile( const std::string& pFile, size_t total = 0l; + // TODO: be smarter about this, decompress directly into heap buffer // and decompress the data .... do 1k chunks in the hope that we won't kill the stack #define MYBLOCK 1024 Bytef block[MYBLOCK]; @@ -232,8 +224,8 @@ void BlenderImporter::InternReadFile( const std::string& pFile, } const size_t have = MYBLOCK - zstream.avail_out; total += have; - dest = reinterpret_cast( realloc(dest,total) ); - memcpy(dest + total - have,block,have); + uncompressed.resize(total); + memcpy(uncompressed.data() + total - have,block,have); } while (ret != Z_STREAM_END); @@ -241,7 +233,7 @@ void BlenderImporter::InternReadFile( const std::string& pFile, inflateEnd(&zstream); // replace the input stream with a memory stream - stream.reset(new MemoryIOStream(reinterpret_cast(dest),total)); + stream.reset(new MemoryIOStream(reinterpret_cast(uncompressed.data()),total)); // .. and retry stream->Read(magic,7,1); diff --git a/code/BlenderScene.cpp b/code/BlenderScene.cpp index 0e1dec7f3..e4373909e 100644 --- a/code/BlenderScene.cpp +++ b/code/BlenderScene.cpp @@ -116,7 +116,7 @@ template <> void Structure :: Convert ( ReadField(temp,"projy",db); dest.projy = static_cast(temp); ReadField(temp,"projz",db); - dest.projx = static_cast(temp); + dest.projz = static_cast(temp); ReadField(dest.mapping,"mapping",db); ReadFieldArray(dest.ofs,"ofs",db); ReadFieldArray(dest.size,"size",db); diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 376725e7e..d5d05db18 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -72,6 +72,7 @@ SET( PUBLIC_HEADERS ${HEADER_PATH}/matrix4x4.h ${HEADER_PATH}/matrix4x4.inl ${HEADER_PATH}/mesh.h + ${HEADER_PATH}/pbrmaterial.h ${HEADER_PATH}/postprocess.h ${HEADER_PATH}/quaternion.h ${HEADER_PATH}/quaternion.inl @@ -176,8 +177,6 @@ SET( Common_SRCS SkeletonMeshBuilder.cpp SplitByBoneCountProcess.cpp SplitByBoneCountProcess.h - ScaleProcess.cpp - ScaleProcess.h StandardShapes.cpp TargetAnimation.cpp TargetAnimation.h @@ -187,6 +186,8 @@ SET( Common_SRCS Bitmap.cpp Version.cpp CreateAnimMesh.cpp + simd.h + simd.cpp ) SOURCE_GROUP(Common FILES ${Common_SRCS}) @@ -484,9 +485,9 @@ ADD_ASSIMP_IMPORTER( IFC ) if (ASSIMP_BUILD_IFC_IMPORTER) if (MSVC) - set_source_files_properties(IFCReaderGen1.cpp IFCReaderGen2.cpp PROPERTIES COMPILE_FLAGS "/bigobj") + set_source_files_properties(Importer/IFC/IFCReaderGen1_2x3.cpp Importer/IFC/IFCReaderGen2_2x3.cpp PROPERTIES COMPILE_FLAGS "/bigobj") elseif(CMAKE_COMPILER_IS_MINGW) - set_source_files_properties(IFCReaderGen1.cpp IFCReaderGen2.cpp PROPERTIES COMPILE_FLAGS "-O2 -Wa,-mbig-obj") + set_source_files_properties(Importer/IFC/IFCReaderGen1_2x3.cpp Importer/IFC/IFCReaderGen2_2x3.cpp PROPERTIES COMPILE_FLAGS "-O2 -Wa,-mbig-obj") endif() endif (ASSIMP_BUILD_IFC_IMPORTER) @@ -522,6 +523,13 @@ ADD_ASSIMP_IMPORTER( FBX FBXDeformer.cpp FBXBinaryTokenizer.cpp FBXDocumentUtil.cpp + FBXExporter.h + FBXExporter.cpp + FBXExportNode.h + FBXExportNode.cpp + FBXExportProperty.h + FBXExportProperty.cpp + FBXCommon.h ) SET( PostProcessing_SRCS @@ -578,6 +586,8 @@ SET( PostProcessing_SRCS PolyTools.h MakeVerboseFormat.cpp MakeVerboseFormat.h + ScaleProcess.cpp + ScaleProcess.h ) SOURCE_GROUP( PostProcessing FILES ${PostProcessing_SRCS}) @@ -641,7 +651,7 @@ ADD_ASSIMP_IMPORTER( X XFileExporter.cpp ) -ADD_ASSIMP_IMPORTER(X3D +ADD_ASSIMP_IMPORTER( X3D X3DExporter.cpp X3DExporter.hpp X3DImporter.cpp diff --git a/code/COBLoader.cpp b/code/COBLoader.cpp index 793cbfe75..222088cbd 100644 --- a/code/COBLoader.cpp +++ b/code/COBLoader.cpp @@ -47,11 +47,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_COB_IMPORTER #include "COBLoader.h" #include "COBScene.h" - +#include "ConvertToLHProcess.h" #include #include #include - #include #include #include @@ -105,7 +104,7 @@ COBImporter::~COBImporter() bool COBImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { const std::string& extension = GetExtension(pFile); - if (extension == "cob" || extension == "scn") { + if (extension == "cob" || extension == "scn" || extension == "COB" || extension == "SCN") { return true; } @@ -225,6 +224,9 @@ void COBImporter::InternReadFile( const std::string& pFile, } pScene->mRootNode = BuildNodes(*root.get(),scene,pScene); + //flip normals after import + FlipWindingOrderProcess flip; + flip.Execute( pScene ); } // ------------------------------------------------------------------------------------------------ @@ -1299,3 +1301,4 @@ void COBImporter::ReadUnit_Binary(COB::Scene& out, StreamReaderLE& reader, const #endif + diff --git a/code/CSMLoader.cpp b/code/CSMLoader.cpp index 7b0064164..7160740c1 100644 --- a/code/CSMLoader.cpp +++ b/code/CSMLoader.cpp @@ -136,7 +136,7 @@ void CSMImporter::InternReadFile( const std::string& pFile, TextFileToBuffer(file.get(),mBuffer2); const char* buffer = &mBuffer2[0]; - aiAnimation* anim = new aiAnimation(); + std::unique_ptr anim(new aiAnimation()); int first = 0, last = 0x00ffffff; // now process the file and look out for '$' sections @@ -294,8 +294,8 @@ void CSMImporter::InternReadFile( const std::string& pFile, // Store the one and only animation in the scene pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations=1]; - pScene->mAnimations[0] = anim; anim->mName.Set("$CSM_MasterAnim"); + pScene->mAnimations[0] = anim.release(); // mark the scene as incomplete and run SkeletonMeshBuilder on it pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; diff --git a/code/CalcTangentsProcess.cpp b/code/CalcTangentsProcess.cpp index cb0117aef..d77d70a5b 100644 --- a/code/CalcTangentsProcess.cpp +++ b/code/CalcTangentsProcess.cpp @@ -190,7 +190,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) float tx = meshTex[p2].x - meshTex[p0].x, ty = meshTex[p2].y - meshTex[p0].y; float dirCorrection = (tx * sy - ty * sx) < 0.0f ? -1.0f : 1.0f; // when t1, t2, t3 in same position in UV space, just use default UV direction. - if ( 0 == sx && 0 ==sy && 0 == tx && 0 == ty ) { + if ( sx * ty == sy * tx ) { sx = 0.0; sy = 1.0; tx = 1.0; ty = 0.0; } diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index dc6b15ec4..96421a532 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -1269,7 +1269,8 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) mOutput << startstr << "" << endstr; PushTag(); - + + std::string node_idstr; for (size_t a = 0; a < anim->mNumChannels; ++a) { const aiNodeAnim * nodeAnim = anim->mChannels[a]; @@ -1277,7 +1278,9 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) if ( nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys || nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys ) continue; { - const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-input"); + node_idstr.clear(); + node_idstr += nodeAnim->mNodeName.data; + node_idstr += std::string( "_matrix-input" ); std::vector frames; for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { @@ -1289,12 +1292,14 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) } { - const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-output"); + node_idstr.clear(); + + node_idstr += nodeAnim->mNodeName.data; + node_idstr += std::string("_matrix-output"); std::vector keyframes; keyframes.reserve(nodeAnim->mNumPositionKeys * 16); for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { - aiVector3D Scaling = nodeAnim->mScalingKeys[i].mValue; aiMatrix4x4 ScalingM; // identity ScalingM[0][0] = Scaling.x; ScalingM[1][1] = Scaling.y; ScalingM[2][2] = Scaling.z; @@ -1361,7 +1366,6 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex) PopTag(); mOutput << startstr << "" << endstr; } - } for (size_t a = 0; a < anim->mNumChannels; ++a) { diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index a3516f9c4..01ba1c400 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -131,6 +131,7 @@ void ColladaLoader::SetupProperties(const Importer* pImp) { noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION,0) != 0; + useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES,0) != 0; } // ------------------------------------------------------------------------------------------------ @@ -1120,6 +1121,7 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars continue; // now check all channels if they affect the current node + std::string targetID, subElement; for( std::vector::const_iterator cit = pSrcAnim->mChannels.begin(); cit != pSrcAnim->mChannels.end(); ++cit) { @@ -1146,7 +1148,9 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars } if( srcChannel.mTarget.find( '/', slashPos+1) != std::string::npos) continue; - std::string targetID = srcChannel.mTarget.substr( 0, slashPos); + + targetID.clear(); + targetID = srcChannel.mTarget.substr( 0, slashPos); if( targetID != srcNode->mID) continue; @@ -1159,7 +1163,8 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars entry.mTransformId = srcChannel.mTarget.substr( slashPos+1, dotPos - slashPos - 1); - std::string subElement = srcChannel.mTarget.substr( dotPos+1); + subElement.clear(); + subElement = srcChannel.mTarget.substr( dotPos+1); if( subElement == "ANGLE") entry.mSubElement = 3; // last number in an Axis-Angle-Transform is the angle else if( subElement == "X") @@ -1180,7 +1185,8 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars if (bracketPos != std::string::npos) { entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1, bracketPos - slashPos - 1); - std::string subElement = srcChannel.mTarget.substr(bracketPos); + subElement.clear(); + subElement = srcChannel.mTarget.substr(bracketPos); if (subElement == "(0)(0)") entry.mSubElement = 0; @@ -1214,7 +1220,6 @@ void ColladaLoader::CreateAnimation( aiScene* pScene, const ColladaParser& pPars entry.mSubElement = 14; else if (subElement == "(3)(3)") entry.mSubElement = 15; - } // determine which transform step is affected by this channel @@ -1913,6 +1918,11 @@ const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, c // The name must be unique for proper node-bone association. std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode) { + // If explicitly requested, just use the collada name. + if (useColladaName) { + return pNode->mName; + } + // Now setup the name of the assimp node. The collada name might not be // unique, so we use the collada ID. if (!pNode->mID.empty()) diff --git a/code/ColladaLoader.h b/code/ColladaLoader.h index c63bf2945..d61845b24 100644 --- a/code/ColladaLoader.h +++ b/code/ColladaLoader.h @@ -248,6 +248,7 @@ protected: bool noSkeletonMesh; bool ignoreUpDirection; + bool useColladaName; /** Used by FindNameForNode() to generate unique node names */ unsigned int mNodeNameCounter; diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index d62c5cafc..d96ac0d39 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -222,6 +222,7 @@ void ColladaParser::ReadStructure() } PostProcessRootAnimations(); + PostProcessControllers(); } // ------------------------------------------------------------------------------------------------ @@ -360,6 +361,21 @@ void ColladaParser::ReadAnimationClipLibrary() } } +void ColladaParser::PostProcessControllers() +{ + for (ControllerLibrary::iterator it = mControllerLibrary.begin(); it != mControllerLibrary.end(); ++it) + { + std::string meshId = it->second.mMeshId; + ControllerLibrary::iterator findItr = mControllerLibrary.find(meshId); + while(findItr != mControllerLibrary.end()) { + meshId = findItr->second.mMeshId; + findItr = mControllerLibrary.find(meshId); + } + + it->second.mMeshId = meshId; + } +} + // ------------------------------------------------------------------------------------------------ // Re-build animations from animation clip library, if present, otherwise combine single-channel animations void ColladaParser::PostProcessRootAnimations() diff --git a/code/ColladaParser.h b/code/ColladaParser.h index 566386840..21f741551 100644 --- a/code/ColladaParser.h +++ b/code/ColladaParser.h @@ -87,6 +87,9 @@ namespace Assimp /** Reads the animation clip library */ void ReadAnimationClipLibrary(); + /** Unwrap controllers dependency hierarchy */ + void PostProcessControllers(); + /** Re-build animations from animation clip library, if present, otherwise combine single-channel animations */ void PostProcessRootAnimations(); diff --git a/code/ConvertToLHProcess.cpp b/code/ConvertToLHProcess.cpp index 47e2fb949..ba8371439 100644 --- a/code/ConvertToLHProcess.cpp +++ b/code/ConvertToLHProcess.cpp @@ -91,12 +91,14 @@ void MakeLeftHandedProcess::Execute( aiScene* pScene) ProcessNode( pScene->mRootNode, aiMatrix4x4()); // process the meshes accordingly - for( unsigned int a = 0; a < pScene->mNumMeshes; ++a) - ProcessMesh( pScene->mMeshes[a]); + for ( unsigned int a = 0; a < pScene->mNumMeshes; ++a ) { + ProcessMesh( pScene->mMeshes[ a ] ); + } // process the materials accordingly - for( unsigned int a = 0; a < pScene->mNumMaterials; ++a) - ProcessMaterial( pScene->mMaterials[a]); + for ( unsigned int a = 0; a < pScene->mNumMaterials; ++a ) { + ProcessMaterial( pScene->mMaterials[ a ] ); + } // transform all animation channels as well for( unsigned int a = 0; a < pScene->mNumAnimations; a++) @@ -136,8 +138,11 @@ void MakeLeftHandedProcess::ProcessNode( aiNode* pNode, const aiMatrix4x4& pPare // ------------------------------------------------------------------------------------------------ // Converts a single mesh to left handed coordinates. -void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) -{ +void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) { + if ( nullptr == pMesh ) { + DefaultLogger::get()->error( "Nullptr to mesh found." ); + return; + } // mirror positions, normals and stuff along the Z axis for( size_t a = 0; a < pMesh->mNumVertices; ++a) { @@ -173,8 +178,12 @@ void MakeLeftHandedProcess::ProcessMesh( aiMesh* pMesh) // ------------------------------------------------------------------------------------------------ // Converts a single material to left handed coordinates. -void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) -{ +void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) { + if ( nullptr == _mat ) { + DefaultLogger::get()->error( "Nullptr to aiMaterial found." ); + return; + } + aiMaterial* mat = (aiMaterial*)_mat; for (unsigned int a = 0; a < mat->mNumProperties;++a) { aiMaterialProperty* prop = mat->mProperties[a]; @@ -183,7 +192,6 @@ void MakeLeftHandedProcess::ProcessMaterial( aiMaterial* _mat) if (!::strcmp( prop->mKey.data, "$tex.mapaxis")) { ai_assert( prop->mDataLength >= sizeof(aiVector3D)); /* something is wrong with the validation if we end up here */ aiVector3D* pff = (aiVector3D*)prop->mData; - pff->z *= -1.f; } } diff --git a/code/D3MFExporter.cpp b/code/D3MFExporter.cpp index 91f06fc87..204e6f77b 100644 --- a/code/D3MFExporter.cpp +++ b/code/D3MFExporter.cpp @@ -49,8 +49,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include - +#include #include + #include "3MFXmlTags.h" #include "D3MFOpcPackage.h" @@ -116,6 +117,7 @@ bool D3MFExporter::exportArchive( const char *file ) { if ( nullptr == m_zipArchive ) { return false; } + ok |= exportContentTypes(); ok |= export3DModel(); ok |= exportRelations(); @@ -126,7 +128,6 @@ bool D3MFExporter::exportArchive( const char *file ) { return ok; } - bool D3MFExporter::exportContentTypes() { mContentOutput.clear(); @@ -153,7 +154,11 @@ bool D3MFExporter::exportRelations() { mRelOutput << ""; for ( size_t i = 0; i < mRelations.size(); ++i ) { - mRelOutput << "target << "\" "; + if ( mRelations[ i ]->target[ 0 ] == '/' ) { + mRelOutput << "target << "\" "; + } else { + mRelOutput << "target << "\" "; + } mRelOutput << "Id=\"" << mRelations[i]->id << "\" "; mRelOutput << "Type=\"" << mRelations[ i ]->type << "\" />"; mRelOutput << std::endl; @@ -177,6 +182,10 @@ bool D3MFExporter::export3DModel() { mModelOutput << "<" << XmlTag::resources << ">"; mModelOutput << std::endl; + writeMetaData(); + + writeBaseMaterials(); + writeObjects(); @@ -203,6 +212,63 @@ void D3MFExporter::writeHeader() { mModelOutput << std::endl; } +void D3MFExporter::writeMetaData() { + if ( nullptr == mScene->mMetaData ) { + return; + } + + const unsigned int numMetaEntries( mScene->mMetaData->mNumProperties ); + if ( 0 == numMetaEntries ) { + return; + } + + const aiString *key; + const aiMetadataEntry *entry(nullptr); + for ( size_t i = 0; i < numMetaEntries; ++i ) { + mScene->mMetaData->Get( i, key, entry ); + std::string k( key->C_Str() ); + aiString value; + mScene->mMetaData->Get( k, value ); + mModelOutput << "<" << XmlTag::meta << " " << XmlTag::meta_name << "=\"" << key->C_Str() << "\">"; + mModelOutput << value.C_Str(); + mModelOutput << "" << std::endl; + } +} + +void D3MFExporter::writeBaseMaterials() { + mModelOutput << "\n"; + std::string strName, hexDiffuseColor , tmp; + for ( size_t i = 0; i < mScene->mNumMaterials; ++i ) { + aiMaterial *mat = mScene->mMaterials[ i ]; + aiString name; + if ( mat->Get( AI_MATKEY_NAME, name ) != aiReturn_SUCCESS ) { + strName = "basemat_" + to_string( i ); + } else { + strName = name.C_Str(); + } + aiColor4D color; + if ( mat->Get( AI_MATKEY_COLOR_DIFFUSE, color ) == aiReturn_SUCCESS ) { + hexDiffuseColor.clear(); + tmp.clear(); + hexDiffuseColor = "#"; + + tmp = DecimalToHexa( color.r ); + hexDiffuseColor += tmp; + tmp = DecimalToHexa( color.g ); + hexDiffuseColor += tmp; + tmp = DecimalToHexa( color.b ); + hexDiffuseColor += tmp; + tmp = DecimalToHexa( color.a ); + hexDiffuseColor += tmp; + } else { + hexDiffuseColor = "#FFFFFFFF"; + } + + mModelOutput << "\n"; + } + mModelOutput << "\n"; +} + void D3MFExporter::writeObjects() { if ( nullptr == mScene->mRootNode ) { return; @@ -242,7 +308,9 @@ void D3MFExporter::writeMesh( aiMesh *mesh ) { } mModelOutput << "" << std::endl; - writeFaces( mesh ); + const unsigned int matIdx( mesh->mMaterialIndex ); + + writeFaces( mesh, matIdx ); mModelOutput << "" << std::endl; } @@ -252,7 +320,7 @@ void D3MFExporter::writeVertex( const aiVector3D &pos ) { mModelOutput << std::endl; } -void D3MFExporter::writeFaces( aiMesh *mesh ) { +void D3MFExporter::writeFaces( aiMesh *mesh, unsigned int matIdx ) { if ( nullptr == mesh ) { return; } @@ -264,7 +332,8 @@ void D3MFExporter::writeFaces( aiMesh *mesh ) { for ( unsigned int i = 0; i < mesh->mNumFaces; ++i ) { aiFace ¤tFace = mesh->mFaces[ i ]; mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[ 0 ] << "\" v2=\"" - << currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ] << "\"/>"; + << currentFace.mIndices[ 1 ] << "\" v3=\"" << currentFace.mIndices[ 2 ] + << "\" pid=\"1\" p1=\""+to_string(matIdx)+"\" />"; mModelOutput << std::endl; } mModelOutput << ""; diff --git a/code/D3MFExporter.h b/code/D3MFExporter.h index 64967f68b..110862b99 100644 --- a/code/D3MFExporter.h +++ b/code/D3MFExporter.h @@ -76,10 +76,12 @@ public: protected: void writeHeader(); + void writeMetaData(); + void writeBaseMaterials(); void writeObjects(); void writeMesh( aiMesh *mesh ); void writeVertex( const aiVector3D &pos ); - void writeFaces( aiMesh *mesh ); + void writeFaces( aiMesh *mesh, unsigned int matIdx ); void writeBuild(); void exportContentTyp( const std::string &filename ); void writeModelToArchive( const std::string &folder, const std::string &modelName ); diff --git a/code/D3MFImporter.cpp b/code/D3MFImporter.cpp index 0777a55fa..2732c73c4 100644 --- a/code/D3MFImporter.cpp +++ b/code/D3MFImporter.cpp @@ -61,14 +61,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include "3MFXmlTags.h" +#include + +#include namespace Assimp { namespace D3MF { class XmlSerializer { public: + using MatArray = std::vector; + using MatId2MatArray = std::map>; + XmlSerializer(XmlReader* xmlReader) - : xmlReader(xmlReader) { + : mMeshes() + , mMatArray() + , mActiveMatGroup( 99999999 ) + , mMatId2MatArray() + , xmlReader(xmlReader){ // empty } @@ -77,14 +87,24 @@ public: } void ImportXml(aiScene* scene) { + if ( nullptr == scene ) { + return; + } + scene->mRootNode = new aiNode(); std::vector children; + std::string nodeName; while(ReadToEndElement(D3MF::XmlTag::model)) { - if(xmlReader->getNodeName() == D3MF::XmlTag::object) { + nodeName = xmlReader->getNodeName(); + if( nodeName == D3MF::XmlTag::object) { children.push_back(ReadObject(scene)); - } else if(xmlReader->getNodeName() == D3MF::XmlTag::build) { - + } else if( nodeName == D3MF::XmlTag::build) { + // + } else if ( nodeName == D3MF::XmlTag::basematerials ) { + ReadBaseMaterials(); + } else if ( nodeName == D3MF::XmlTag::meta ) { + ReadMetadata(); } } @@ -92,31 +112,47 @@ public: scene->mRootNode->mName.Set( "3MF" ); } - scene->mNumMeshes = static_cast(meshes.size()); + // import the metadata + if ( !mMetaData.empty() ) { + const size_t numMeta( mMetaData.size() ); + scene->mMetaData = aiMetadata::Alloc( numMeta ); + for ( size_t i = 0; i < numMeta; ++i ) { + aiString val( mMetaData[ i ].value ); + scene->mMetaData->Set( i, mMetaData[ i ].name, val ); + } + } + + // import the meshes + scene->mNumMeshes = static_cast( mMeshes.size()); scene->mMeshes = new aiMesh*[scene->mNumMeshes](); + std::copy( mMeshes.begin(), mMeshes.end(), scene->mMeshes); - std::copy(meshes.begin(), meshes.end(), scene->mMeshes); + // import the materials + scene->mNumMaterials = static_cast( mMatArray.size() ); + if ( 0 != scene->mNumMaterials ) { + scene->mMaterials = new aiMaterial*[ scene->mNumMaterials ]; + std::copy( mMatArray.begin(), mMatArray.end(), scene->mMaterials ); + } + // create the scenegraph scene->mRootNode->mNumChildren = static_cast(children.size()); scene->mRootNode->mChildren = new aiNode*[scene->mRootNode->mNumChildren](); - std::copy(children.begin(), children.end(), scene->mRootNode->mChildren); } private: - aiNode* ReadObject(aiScene* scene) - { + aiNode* ReadObject(aiScene* scene) { std::unique_ptr node(new aiNode()); std::vector meshIds; const char *attrib( nullptr ); std::string name, type; - attrib = xmlReader->getAttributeValue( D3MF::XmlTag::name.c_str() ); + attrib = xmlReader->getAttributeValue( D3MF::XmlTag::id.c_str() ); if ( nullptr != attrib ) { name = attrib; } - attrib = xmlReader->getAttributeValue( D3MF::XmlTag::name.c_str() ); + attrib = xmlReader->getAttributeValue( D3MF::XmlTag::type.c_str() ); if ( nullptr != attrib ) { type = attrib; } @@ -124,19 +160,16 @@ private: node->mParent = scene->mRootNode; node->mName.Set(name); - size_t meshIdx = meshes.size(); + size_t meshIdx = mMeshes.size(); - while(ReadToEndElement(D3MF::XmlTag::object)) - { - if(xmlReader->getNodeName() == D3MF::XmlTag::mesh) - { + while(ReadToEndElement(D3MF::XmlTag::object)) { + if(xmlReader->getNodeName() == D3MF::XmlTag::mesh) { auto mesh = ReadMesh(); mesh->mName.Set(name); - meshes.push_back(mesh); + mMeshes.push_back(mesh); meshIds.push_back(static_cast(meshIdx)); - meshIdx++; - + ++meshIdx; } } @@ -147,19 +180,14 @@ private: std::copy(meshIds.begin(), meshIds.end(), node->mMeshes); return node.release(); - } - aiMesh* ReadMesh() { + aiMesh *ReadMesh() { aiMesh* mesh = new aiMesh(); - while(ReadToEndElement(D3MF::XmlTag::mesh)) - { - if(xmlReader->getNodeName() == D3MF::XmlTag::vertices) - { + while(ReadToEndElement(D3MF::XmlTag::mesh)) { + if(xmlReader->getNodeName() == D3MF::XmlTag::vertices) { ImportVertices(mesh); - } - else if(xmlReader->getNodeName() == D3MF::XmlTag::triangles) - { + } else if(xmlReader->getNodeName() == D3MF::XmlTag::triangles) { ImportTriangles(mesh); } } @@ -167,14 +195,25 @@ private: return mesh; } - void ImportVertices(aiMesh* mesh) - { - std::vector vertices; + void ReadMetadata() { + const std::string name = xmlReader->getAttributeValue( D3MF::XmlTag::meta_name.c_str() ); + xmlReader->read(); + const std::string value = xmlReader->getNodeData(); - while(ReadToEndElement(D3MF::XmlTag::vertices)) - { - if(xmlReader->getNodeName() == D3MF::XmlTag::vertex) - { + if ( name.empty() ) { + return; + } + + MetaEntry entry; + entry.name = name; + entry.value = value; + mMetaData.push_back( entry ); + } + + void ImportVertices(aiMesh* mesh) { + std::vector vertices; + while(ReadToEndElement(D3MF::XmlTag::vertices)) { + if(xmlReader->getNodeName() == D3MF::XmlTag::vertex) { vertices.push_back(ReadVertex()); } } @@ -182,11 +221,9 @@ private: mesh->mVertices = new aiVector3D[mesh->mNumVertices]; std::copy(vertices.begin(), vertices.end(), mesh->mVertices); - } - aiVector3D ReadVertex() - { + aiVector3D ReadVertex() { aiVector3D vertex; vertex.x = ai_strtof(xmlReader->getAttributeValue(D3MF::XmlTag::x.c_str()), nullptr); @@ -196,16 +233,18 @@ private: return vertex; } - void ImportTriangles(aiMesh* mesh) - { + void ImportTriangles(aiMesh* mesh) { std::vector faces; - - while(ReadToEndElement(D3MF::XmlTag::triangles)) - { - if(xmlReader->getNodeName() == D3MF::XmlTag::triangle) - { + while(ReadToEndElement(D3MF::XmlTag::triangles)) { + const std::string nodeName( xmlReader->getNodeName() ); + if(xmlReader->getNodeName() == D3MF::XmlTag::triangle) { faces.push_back(ReadTriangle()); + const char *pidToken( xmlReader->getAttributeValue( D3MF::XmlTag::p1.c_str() ) ); + if ( nullptr != pidToken ) { + int matIdx( std::atoi( pidToken ) ); + mesh->mMaterialIndex = matIdx; + } } } @@ -216,8 +255,7 @@ private: std::copy(faces.begin(), faces.end(), mesh->mFaces); } - aiFace ReadTriangle() - { + aiFace ReadTriangle() { aiFace face; face.mNumIndices = 3; @@ -229,45 +267,150 @@ private: return face; } -private: - bool ReadToStartElement(const std::string& startTag) - { - while(xmlReader->read()) - { - if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT && xmlReader->getNodeName() == startTag) - { - return true; + void ReadBaseMaterials() { + std::vector MatIdArray; + const char *baseMaterialId( xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_id.c_str() ) ); + if ( nullptr != baseMaterialId ) { + unsigned int id = std::atoi( baseMaterialId ); + const size_t newMatIdx( mMatArray.size() ); + if ( id != mActiveMatGroup ) { + mActiveMatGroup = id; + MatId2MatArray::const_iterator it( mMatId2MatArray.find( id ) ); + if ( mMatId2MatArray.end() == it ) { + MatIdArray.clear(); + mMatId2MatArray[ id ] = MatIdArray; + } else { + MatIdArray = it->second; + } } - else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && - xmlReader->getNodeName() == startTag) - { + MatIdArray.push_back( static_cast( newMatIdx ) ); + mMatId2MatArray[ mActiveMatGroup ] = MatIdArray; + } + + while ( ReadToEndElement( D3MF::XmlTag::basematerials ) ) { + mMatArray.push_back( readMaterialDef() ); + } + } + + bool parseColor( const char *color, aiColor4D &diffuse ) { + if ( nullptr == color ) { + return false; + } + + const size_t len( strlen( color ) ); + if ( 9 != len ) { + return false; + } + + const char *buf( color ); + if ( '#' != *buf ) { + return false; + } + ++buf; + char comp[ 3 ] = { 0,0,'\0' }; + + comp[ 0 ] = *buf; + ++buf; + comp[ 1 ] = *buf; + ++buf; + diffuse.r = static_cast( strtol( comp, NULL, 16 ) ); + + + comp[ 0 ] = *buf; + ++buf; + comp[ 1 ] = *buf; + ++buf; + diffuse.g = static_cast< ai_real >( strtol( comp, NULL, 16 ) ); + + comp[ 0 ] = *buf; + ++buf; + comp[ 1 ] = *buf; + ++buf; + diffuse.b = static_cast< ai_real >( strtol( comp, NULL, 16 ) ); + + comp[ 0 ] = *buf; + ++buf; + comp[ 1 ] = *buf; + ++buf; + diffuse.a = static_cast< ai_real >( strtol( comp, NULL, 16 ) ); + + return true; + } + + void assignDiffuseColor( aiMaterial *mat ) { + const char *color = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_displaycolor.c_str() ); + aiColor4D diffuse; + if ( parseColor( color, diffuse ) ) { + mat->AddProperty( &diffuse, 1, AI_MATKEY_COLOR_DIFFUSE ); + } + + } + aiMaterial *readMaterialDef() { + aiMaterial *mat( nullptr ); + const char *name( nullptr ); + const std::string nodeName( xmlReader->getNodeName() ); + if ( nodeName == D3MF::XmlTag::basematerials_base ) { + name = xmlReader->getAttributeValue( D3MF::XmlTag::basematerials_name.c_str() ); + std::string stdMatName; + aiString matName; + std::string strId( to_string( mActiveMatGroup ) ); + stdMatName += "id"; + stdMatName += strId; + stdMatName += "_"; + if ( nullptr != name ) { + stdMatName += std::string( name ); + } else { + stdMatName += "basemat"; + } + matName.Set( stdMatName ); + + mat = new aiMaterial; + mat->AddProperty( &matName, AI_MATKEY_NAME ); + + assignDiffuseColor( mat ); + } + + return mat; + } + +private: + bool ReadToStartElement(const std::string& startTag) { + while(xmlReader->read()) { + const std::string &nodeName( xmlReader->getNodeName() ); + if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT && nodeName == startTag) { + return true; + } else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == startTag) { return false; } } - //DefaultLogger::get()->error("unexpected EOF, expected closing <" + closeTag + "> tag"); + return false; } - bool ReadToEndElement(const std::string& closeTag) - { - while(xmlReader->read()) - { + bool ReadToEndElement(const std::string& closeTag) { + while(xmlReader->read()) { + const std::string &nodeName( xmlReader->getNodeName() ); if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT) { return true; - } - else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END - && xmlReader->getNodeName() == closeTag) - { + } else if (xmlReader->getNodeType() == irr::io::EXN_ELEMENT_END && nodeName == closeTag) { return false; } } DefaultLogger::get()->error("unexpected EOF, expected closing <" + closeTag + "> tag"); + return false; } - private: - std::vector meshes; + struct MetaEntry { + std::string name; + std::string value; + }; + std::vector mMetaData; + std::vector mMeshes; + MatArray mMatArray; + unsigned int mActiveMatGroup; + MatId2MatArray mMatId2MatArray; XmlReader* xmlReader; }; @@ -288,7 +431,6 @@ static const aiImporterDesc desc = { Extension.c_str() }; - D3MFImporter::D3MFImporter() : BaseImporter() { // empty @@ -298,14 +440,19 @@ D3MFImporter::~D3MFImporter() { // empty } -bool D3MFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); +bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const { + const std::string extension( GetExtension( filename ) ); if(extension == Extension ) { return true; } else if ( !extension.length() || checkSig ) { - if (nullptr == pIOHandler ) { - return true; + if ( nullptr == pIOHandler ) { + return false; } + if ( !D3MF::D3MFOpcPackage::isZipArchive( pIOHandler, filename ) ) { + return false; + } + D3MF::D3MFOpcPackage opcPackage( pIOHandler, filename ); + return opcPackage.validate(); } return false; @@ -319,8 +466,8 @@ const aiImporterDesc *D3MFImporter::GetInfo() const { return &desc; } -void D3MFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { - D3MF::D3MFOpcPackage opcPackage(pIOHandler, pFile); +void D3MFImporter::InternReadFile( const std::string &filename, aiScene *pScene, IOSystem *pIOHandler ) { + D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename); std::unique_ptr xmlStream(new CIrrXML_IOStreamReader(opcPackage.RootStream())); std::unique_ptr xmlReader(irr::io::createIrrXMLReader(xmlStream.get())); diff --git a/code/D3MFOpcPackage.cpp b/code/D3MFOpcPackage.cpp index a7039d34e..3bf545813 100644 --- a/code/D3MFOpcPackage.cpp +++ b/code/D3MFOpcPackage.cpp @@ -247,13 +247,13 @@ private: // ------------------------------------------------------------------------------------------------ // Constructor. D3MFZipArchive::D3MFZipArchive(IOSystem* pIOHandler, const std::string& rFile) -: m_ZipFileHandle(NULL) +: m_ZipFileHandle( nullptr ) , m_ArchiveMap() { if (! rFile.empty()) { zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler); m_ZipFileHandle = unzOpen2(rFile.c_str(), &mapping); - if(m_ZipFileHandle != NULL) { + if(m_ZipFileHandle != nullptr ) { mapArchive(); } } @@ -267,32 +267,32 @@ D3MFZipArchive::~D3MFZipArchive() { } m_ArchiveMap.clear(); - if(m_ZipFileHandle != NULL) { + if(m_ZipFileHandle != nullptr) { unzClose(m_ZipFileHandle); - m_ZipFileHandle = NULL; + m_ZipFileHandle = nullptr; } } // ------------------------------------------------------------------------------------------------ // Returns true, if the archive is already open. bool D3MFZipArchive::isOpen() const { - return (m_ZipFileHandle != NULL); + return (m_ZipFileHandle != nullptr ); } // ------------------------------------------------------------------------------------------------ // Returns true, if the filename is part of the archive. bool D3MFZipArchive::Exists(const char* pFile) const { - ai_assert(pFile != NULL); + ai_assert(pFile != nullptr ); - bool exist = false; + if ( pFile == nullptr ) { + return false; + } - if (pFile != NULL) { - std::string rFile(pFile); - std::map::const_iterator it = m_ArchiveMap.find(rFile); - - if(it != m_ArchiveMap.end()) { - exist = true; - } + std::string filename(pFile); + std::map::const_iterator it = m_ArchiveMap.find(filename); + bool exist( false ); + if(it != m_ArchiveMap.end()) { + exist = true; } return exist; @@ -434,8 +434,8 @@ public: std::vector m_relationShips; }; -// ------------------------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------------------------ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile) : mRootStream(nullptr) , mZipArchive() { @@ -460,7 +460,7 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile) if ( rootFile.size() > 0 && rootFile[ 0 ] == '/' ) { rootFile = rootFile.substr( 1 ); if ( rootFile[ 0 ] == '/' ) { - // deal with zipbug + // deal with zip-bug rootFile = rootFile.substr( 1 ); } } @@ -470,18 +470,9 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile) mRootStream = mZipArchive->Open(rootFile.c_str()); ai_assert( mRootStream != nullptr ); if ( nullptr == mRootStream ) { - throw DeadlyExportError( "Cannot open rootfile in archive : " + rootFile ); + throw DeadlyExportError( "Cannot open root-file in archive : " + rootFile ); } - // const size_t size = zipArchive->FileSize(); - // m_Data.resize( size ); - - // const size_t readSize = pMapFile->Read( &m_Data[0], sizeof( char ), size ); - // if ( readSize != size ) - // { - // m_Data.clear(); - // return false; - // } mZipArchive->Close( fileStream ); } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) { @@ -498,6 +489,25 @@ IOStream* D3MFOpcPackage::RootStream() const { return mRootStream; } +static const std::string ModelRef = "3D/3dmodel.model"; + +bool D3MFOpcPackage::validate() { + if ( nullptr == mRootStream || nullptr == mZipArchive ) { + return false; + } + + return mZipArchive->Exists( ModelRef.c_str() ); +} + +bool D3MFOpcPackage::isZipArchive( IOSystem* pIOHandler, const std::string& rFile ) { + D3MF::D3MFZipArchive ar( pIOHandler, rFile ); + if ( !ar.isOpen() ) { + return false; + } + + return true; +} + std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) { std::unique_ptr xmlStream(new CIrrXML_IOStreamReader(stream)); std::unique_ptr xml(irr::io::createIrrXMLReader(xmlStream.get())); @@ -508,14 +518,14 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) { return rel->type == XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE; }); - if(itr == reader.m_relationShips.end()) - throw DeadlyImportError("Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE); + if ( itr == reader.m_relationShips.end() ) { + throw DeadlyImportError( "Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE ); + } return (*itr)->target; } } // Namespace D3MF - } // Namespace Assimp #endif //ASSIMP_BUILD_NO_3MF_IMPORTER diff --git a/code/D3MFOpcPackage.h b/code/D3MFOpcPackage.h index 8cf08d092..6d7b3d478 100644 --- a/code/D3MFOpcPackage.h +++ b/code/D3MFOpcPackage.h @@ -51,8 +51,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace D3MF { -typedef irr::io::IrrXMLReader XmlReader; -typedef std::shared_ptr XmlReaderPtr; +using XmlReader = irr::io::IrrXMLReader ; +using XmlReaderPtr = std::shared_ptr ; struct OpcPackageRelationship { std::string id; @@ -64,9 +64,11 @@ class D3MFZipArchive; class D3MFOpcPackage { public: - D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile); + D3MFOpcPackage( IOSystem* pIOHandler, const std::string& rFile ); ~D3MFOpcPackage(); IOStream* RootStream() const; + bool validate(); + static bool isZipArchive( IOSystem* pIOHandler, const std::string& rFile ); protected: std::string ReadPackageRootRelationship(IOStream* stream); @@ -76,7 +78,7 @@ private: std::unique_ptr mZipArchive; }; -} -} +} // Namespace D3MF +} // Namespace Assimp #endif // D3MFOPCPACKAGE_H diff --git a/code/DXFHelper.h b/code/DXFHelper.h index 5aaf4844f..49ca2814d 100644 --- a/code/DXFHelper.h +++ b/code/DXFHelper.h @@ -169,7 +169,6 @@ public: } private: - LineSplitter splitter; int groupcode; std::string value; diff --git a/code/DefaultIOSystem.cpp b/code/DefaultIOSystem.cpp index 5e50be8bd..36c14e41d 100644 --- a/code/DefaultIOSystem.cpp +++ b/code/DefaultIOSystem.cpp @@ -76,7 +76,7 @@ bool DefaultIOSystem::Exists( const char* pFile) const #ifdef _WIN32 wchar_t fileName16[PATHLIMIT]; - bool isUnicode = IsTextUnicode(pFile, strlen(pFile), NULL); + bool isUnicode = IsTextUnicode(pFile, static_cast(strlen(pFile)), NULL); if (isUnicode) { MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT); @@ -110,7 +110,7 @@ IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode) FILE* file; #ifdef _WIN32 wchar_t fileName16[PATHLIMIT]; - bool isUnicode = IsTextUnicode(strFile, strlen(strFile), NULL ); + bool isUnicode = IsTextUnicode(strFile, static_cast(strlen(strFile)), NULL ); if (isUnicode) { MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT); std::string mode8(strMode); @@ -158,7 +158,7 @@ inline static void MakeAbsolutePath (const char* in, char* _out) { ai_assert(in && _out); #if defined( _MSC_VER ) || defined( __MINGW32__ ) - bool isUnicode = IsTextUnicode(in, strlen(in), NULL); + bool isUnicode = IsTextUnicode(in, static_cast(strlen(in)), NULL); if (isUnicode) { wchar_t out16[PATHLIMIT]; wchar_t in16[PATHLIMIT]; diff --git a/code/DefaultLogger.cpp b/code/DefaultLogger.cpp index ad101174c..2871a4131 100644 --- a/code/DefaultLogger.cpp +++ b/code/DefaultLogger.cpp @@ -45,7 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Implementation of DefaultLogger (and Logger) */ - // Default log streams #include "Win32DebugLogStream.h" #include "StdOStreamLogStream.h" @@ -62,8 +61,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_SINGLETHREADED # include # include - -std::mutex loggerMutex; + std::mutex loggerMutex; #endif namespace Assimp { @@ -76,22 +74,19 @@ static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::War // ---------------------------------------------------------------------------------- // Represents a log-stream + its error severity -struct LogStreamInfo -{ - unsigned int m_uiErrorSeverity; - LogStream *m_pStream; +struct LogStreamInfo { + unsigned int m_uiErrorSeverity; + LogStream *m_pStream; // Constructor LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) : m_uiErrorSeverity( uiErrorSev ), - m_pStream( pStream ) - { + m_pStream( pStream ) { // empty } // Destructor - ~LogStreamInfo() - { + ~LogStreamInfo() { delete m_pStream; } }; @@ -109,7 +104,7 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams, #ifdef WIN32 return new Win32DebugLogStream(); #else - return NULL; + return nullptr; #endif // Platform-independent default streams @@ -118,7 +113,7 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams, case aiDefaultLogStream_STDOUT: return new StdOStreamLogStream(std::cout); case aiDefaultLogStream_FILE: - return (name && *name ? new FileLogStream(name,io) : NULL); + return (name && *name ? new FileLogStream(name,io) : nullptr ); default: // We don't know this default log stream, so raise an assertion ai_assert(false); @@ -134,34 +129,38 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams, Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/, LogSeverity severity /*= NORMAL*/, unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/, - IOSystem* io /*= NULL*/) -{ + IOSystem* io /*= NULL*/) { // enter the mutex here to avoid concurrency problems #ifndef ASSIMP_BUILD_SINGLETHREADED std::lock_guard lock(loggerMutex); #endif - if (m_pLogger && !isNullLogger() ) + if ( m_pLogger && !isNullLogger() ) { delete m_pLogger; + } m_pLogger = new DefaultLogger( severity ); // Attach default log streams // Stream the log to the MSVC debugger? - if (defStreams & aiDefaultLogStream_DEBUGGER) - m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_DEBUGGER)); + if ( defStreams & aiDefaultLogStream_DEBUGGER ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_DEBUGGER ) ); + } // Stream the log to COUT? - if (defStreams & aiDefaultLogStream_STDOUT) - m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_STDOUT)); + if ( defStreams & aiDefaultLogStream_STDOUT ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDOUT ) ); + } // Stream the log to CERR? - if (defStreams & aiDefaultLogStream_STDERR) - m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_STDERR)); + if ( defStreams & aiDefaultLogStream_STDERR ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDERR ) ); + } // Stream the log to a file - if (defStreams & aiDefaultLogStream_FILE && name && *name) - m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_FILE,name,io)); + if ( defStreams & aiDefaultLogStream_FILE && name && *name ) { + m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_FILE, name, io ) ); + } return m_pLogger; } @@ -200,7 +199,6 @@ void Logger::warn(const char* message) { // ---------------------------------------------------------------------------------- void Logger::error(const char* message) { - // SECURITY FIX: see above if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { return; @@ -209,23 +207,24 @@ void Logger::error(const char* message) { } // ---------------------------------------------------------------------------------- -void DefaultLogger::set( Logger *logger ) -{ +void DefaultLogger::set( Logger *logger ) { // enter the mutex here to avoid concurrency problems #ifndef ASSIMP_BUILD_SINGLETHREADED std::lock_guard lock(loggerMutex); #endif - if (!logger)logger = &s_pNullLogger; - if (m_pLogger && !isNullLogger() ) + if ( nullptr == logger ) { + logger = &s_pNullLogger; + } + if ( nullptr != m_pLogger && !isNullLogger() ) { delete m_pLogger; + } DefaultLogger::m_pLogger = logger; } // ---------------------------------------------------------------------------------- -bool DefaultLogger::isNullLogger() -{ +bool DefaultLogger::isNullLogger() { return m_pLogger == &s_pNullLogger; } @@ -236,8 +235,7 @@ Logger *DefaultLogger::get() { // ---------------------------------------------------------------------------------- // Kills the only instance -void DefaultLogger::kill() -{ +void DefaultLogger::kill() { // enter the mutex here to avoid concurrency problems #ifndef ASSIMP_BUILD_SINGLETHREADED std::lock_guard lock(loggerMutex); @@ -252,10 +250,10 @@ void DefaultLogger::kill() // ---------------------------------------------------------------------------------- // Debug message -void DefaultLogger::OnDebug( const char* message ) -{ - if ( m_Severity == Logger::NORMAL ) - return; +void DefaultLogger::OnDebug( const char* message ) { + if ( m_Severity == Logger::NORMAL ) { + return; + } static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; char msg[Size]; @@ -266,8 +264,7 @@ void DefaultLogger::OnDebug( const char* message ) // ---------------------------------------------------------------------------------- // Logs an info -void DefaultLogger::OnInfo( const char* message ) -{ +void DefaultLogger::OnInfo( const char* message ){ static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; char msg[Size]; ai_snprintf(msg, Size, "Info, T%u: %s", GetThreadID(), message ); @@ -277,8 +274,7 @@ void DefaultLogger::OnInfo( const char* message ) // ---------------------------------------------------------------------------------- // Logs a warning -void DefaultLogger::OnWarn( const char* message ) -{ +void DefaultLogger::OnWarn( const char* message ) { static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; char msg[Size]; ai_snprintf(msg, Size, "Warn, T%u: %s", GetThreadID(), message ); @@ -288,8 +284,7 @@ void DefaultLogger::OnWarn( const char* message ) // ---------------------------------------------------------------------------------- // Logs an error -void DefaultLogger::OnError( const char* message ) -{ +void DefaultLogger::OnError( const char* message ) { static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16; char msg[ Size ]; ai_snprintf(msg, Size, "Error, T%u: %s", GetThreadID(), message ); @@ -299,10 +294,10 @@ void DefaultLogger::OnError( const char* message ) // ---------------------------------------------------------------------------------- // Will attach a new stream -bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) -{ - if (!pStream) +bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) { + if ( nullptr == pStream ) { return false; + } if (0 == severity) { severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; @@ -312,8 +307,7 @@ bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) it != m_StreamArray.end(); ++it ) { - if ( (*it)->m_pStream == pStream ) - { + if ( (*it)->m_pStream == pStream ) { (*it)->m_uiErrorSeverity |= severity; return true; } @@ -326,34 +320,31 @@ bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) // ---------------------------------------------------------------------------------- // Detach a stream -bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) -{ - if (!pStream) +bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) { + if ( nullptr == pStream ) { return false; + } if (0 == severity) { severity = SeverityAll; } - for ( StreamIt it = m_StreamArray.begin(); - it != m_StreamArray.end(); - ++it ) - { - if ( (*it)->m_pStream == pStream ) - { + bool res( false ); + for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) { + if ( (*it)->m_pStream == pStream ) { (*it)->m_uiErrorSeverity &= ~severity; - if ( (*it)->m_uiErrorSeverity == 0 ) - { + if ( (*it)->m_uiErrorSeverity == 0 ) { // don't delete the underlying stream 'cause the caller gains ownership again - (**it).m_pStream = NULL; + (**it).m_pStream = nullptr; delete *it; m_StreamArray.erase( it ); + res = true; break; } return true; } } - return false; + return res; } // ---------------------------------------------------------------------------------- @@ -361,15 +352,13 @@ bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) DefaultLogger::DefaultLogger(LogSeverity severity) : Logger ( severity ) , noRepeatMsg (false) - , lastLen( 0 ) -{ + , lastLen( 0 ) { lastMsg[0] = '\0'; } // ---------------------------------------------------------------------------------- // Destructor -DefaultLogger::~DefaultLogger() -{ +DefaultLogger::~DefaultLogger() { for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) { // also frees the underlying stream, we are its owner. delete *it; @@ -378,9 +367,8 @@ DefaultLogger::~DefaultLogger() // ---------------------------------------------------------------------------------- // Writes message to stream -void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) -{ - ai_assert(NULL != message); +void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) { + ai_assert(nullptr != message); // Check whether this is a repeated message if (! ::strncmp( message,lastMsg, lastLen-1)) diff --git a/code/EmbedTexturesProcess.cpp b/code/EmbedTexturesProcess.cpp index addfb9746..076339468 100644 --- a/code/EmbedTexturesProcess.cpp +++ b/code/EmbedTexturesProcess.cpp @@ -99,22 +99,22 @@ void EmbedTexturesProcess::Execute(aiScene* pScene) { } bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { - uint32_t imageSize = 0; - std::string imagePath = path; + std::streampos imageSize = 0; + std::string imagePath = path; // Test path directly std::ifstream file(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == -1u) { + if ((imageSize = file.tellg()) == std::streampos(-1)) { DefaultLogger::get()->warn("EmbedTexturesProcess: Cannot find image: " + imagePath + ". Will try to find it in root folder."); // Test path in root path imagePath = mRootPath + path; file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == -1u) { + if ((imageSize = file.tellg()) == std::streampos(-1)) { // Test path basename in root path imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u); file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == -1u) { + if ((imageSize = file.tellg()) == std::streampos(-1)) { DefaultLogger::get()->error("EmbedTexturesProcess: Unable to embed texture: " + path + "."); return false; } @@ -134,7 +134,7 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { // Add the new texture auto pTexture = new aiTexture(); pTexture->mHeight = 0; // Means that this is still compressed - pTexture->mWidth = imageSize; + pTexture->mWidth = static_cast(imageSize); pTexture->pcData = imageContent; auto extension = path.substr(path.find_last_of('.') + 1u); diff --git a/code/Exporter.cpp b/code/Exporter.cpp index 51c639cfd..b7478c0be 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -97,6 +97,8 @@ void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportPropert void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*); +void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* ); // ------------------------------------------------------------------------------------------------ @@ -169,6 +171,11 @@ Exporter::ExportFormatEntry gExporters[] = Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ), #endif +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ), + Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ), +#endif + #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ) #endif @@ -301,7 +308,8 @@ bool IsVerboseFormat(const aiScene* pScene) { } // ------------------------------------------------------------------------------------------------ -aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, unsigned int pPreprocessing, const ExportProperties* pProperties) { +aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, + unsigned int pPreprocessing, const ExportProperties* pProperties) { ASSIMP_BEGIN_EXCEPTION_REGION(); // when they create scenes from scratch, users will likely create them not in verbose diff --git a/code/FBXCommon.h b/code/FBXCommon.h new file mode 100644 index 000000000..60b040552 --- /dev/null +++ b/code/FBXCommon.h @@ -0,0 +1,86 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, 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 FBXCommon.h +* Some useful constants and enums for dealing with FBX files. +*/ +#ifndef AI_FBXCOMMON_H_INC +#define AI_FBXCOMMON_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + + +namespace FBX +{ + const std::string NULL_RECORD = { // 13 null bytes + '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0' + }; // who knows why + const std::string SEPARATOR = {'\x00', '\x01'}; // for use inside strings + const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import + const int64_t SECOND = 46186158000; // FBX's kTime unit + + // rotation order. We'll probably use EulerXYZ for everything + enum RotOrder { + RotOrder_EulerXYZ = 0, + RotOrder_EulerXZY, + RotOrder_EulerYZX, + RotOrder_EulerYXZ, + RotOrder_EulerZXY, + RotOrder_EulerZYX, + + RotOrder_SphericXYZ, + + RotOrder_MAX // end-of-enum sentinel + }; + + // transformation inheritance method. Most of the time RSrs + enum TransformInheritance { + TransformInheritance_RrSs = 0, + TransformInheritance_RSrs, + TransformInheritance_Rrs, + + TransformInheritance_MAX // end-of-enum sentinel + }; +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXCOMMON_H_INC diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index 6929e5c33..2c9818a16 100644 --- a/code/FBXConverter.cpp +++ b/code/FBXConverter.cpp @@ -61,6 +61,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include +#include namespace Assimp { namespace FBX { @@ -133,15 +135,14 @@ void Converter::ConvertRootNode() { ConvertNodes( 0L, *out->mRootNode ); } - -void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform ) -{ +void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform ) { const std::vector& conns = doc.GetConnectionsByDestinationSequenced( id, "Model" ); std::vector nodes; nodes.reserve( conns.size() ); std::vector nodes_chain; + std::vector post_nodes_chain; try { for( const Connection* con : conns ) { @@ -152,15 +153,16 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa } const Object* const object = con->SourceObject(); - if ( !object ) { + if ( nullptr == object ) { FBXImporter::LogWarn( "failed to convert source object for Model link" ); continue; } const Model* const model = dynamic_cast( object ); - if ( model ) { + if ( nullptr != model ) { nodes_chain.clear(); + post_nodes_chain.clear(); aiMatrix4x4 new_abs_transform = parent_transform; @@ -168,11 +170,11 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa // assimp (or rather: the complicated transformation chain that // is employed by fbx) means that we may need multiple aiNode's // to represent a fbx node's transformation. - GenerateTransformationNodeChain( *model, nodes_chain ); + GenerateTransformationNodeChain( *model, nodes_chain, post_nodes_chain ); ai_assert( nodes_chain.size() ); - const std::string& original_name = FixNodeName( model->Name() ); + std::string original_name = FixNodeName( model->Name() ); // check if any of the nodes in the chain has the name the fbx node // is supposed to have. If there is none, add another node to @@ -187,7 +189,15 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa } if ( !name_carrier ) { + NodeNameCache::const_iterator it( std::find( mNodeNames.begin(), mNodeNames.end(), original_name ) ); + if ( it != mNodeNames.end() ) { + original_name = original_name + std::string( "001" ); + } + + mNodeNames.push_back( original_name ); nodes_chain.push_back( new aiNode( original_name ) ); + } else { + original_name = nodes_chain.back()->mName.C_Str(); } //setup metadata on newest node @@ -213,15 +223,46 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa // attach geometry ConvertModel( *model, *nodes_chain.back(), new_abs_transform ); - // attach sub-nodes - ConvertNodes( model->ID(), *nodes_chain.back(), new_abs_transform ); + // check if there will be any child nodes + const std::vector& child_conns + = doc.GetConnectionsByDestinationSequenced( model->ID(), "Model" ); + + // if so, link the geometric transform inverse nodes + // before we attach any child nodes + if (child_conns.size()) { + for( aiNode* postnode : post_nodes_chain ) { + ai_assert( postnode ); + + if ( last_parent != &parent ) { + last_parent->mNumChildren = 1; + last_parent->mChildren = new aiNode*[ 1 ]; + last_parent->mChildren[ 0 ] = postnode; + } + + postnode->mParent = last_parent; + last_parent = postnode; + + new_abs_transform *= postnode->mTransformation; + } + } else { + // free the nodes we allocated as we don't need them + Util::delete_fun deleter; + std::for_each( + post_nodes_chain.begin(), + post_nodes_chain.end(), + deleter + ); + } + + // attach sub-nodes (if any) + ConvertNodes( model->ID(), *last_parent, new_abs_transform ); if ( doc.Settings().readLights ) { - ConvertLights( *model ); + ConvertLights( *model, original_name ); } if ( doc.Settings().readCameras ) { - ConvertCameras( *model ); + ConvertCameras( *model, original_name ); } nodes.push_back( nodes_chain.front() ); @@ -240,38 +281,36 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa Util::delete_fun deleter; std::for_each( nodes.begin(), nodes.end(), deleter ); std::for_each( nodes_chain.begin(), nodes_chain.end(), deleter ); + std::for_each( post_nodes_chain.begin(), post_nodes_chain.end(), deleter ); } } -void Converter::ConvertLights( const Model& model ) -{ +void Converter::ConvertLights( const Model& model, const std::string &orig_name ) { const std::vector& node_attrs = model.GetAttributes(); for( const NodeAttribute* attr : node_attrs ) { const Light* const light = dynamic_cast( attr ); if ( light ) { - ConvertLight( model, *light ); + ConvertLight( *light, orig_name ); } } } -void Converter::ConvertCameras( const Model& model ) -{ +void Converter::ConvertCameras( const Model& model, const std::string &orig_name ) { const std::vector& node_attrs = model.GetAttributes(); for( const NodeAttribute* attr : node_attrs ) { const Camera* const cam = dynamic_cast( attr ); if ( cam ) { - ConvertCamera( model, *cam ); + ConvertCamera( *cam, orig_name ); } } } -void Converter::ConvertLight( const Model& model, const Light& light ) -{ +void Converter::ConvertLight( const Light& light, const std::string &orig_name ) { lights.push_back( new aiLight() ); aiLight* const out_light = lights.back(); - out_light->mName.Set( FixNodeName( model.Name() ) ); + out_light->mName.Set( orig_name ); const float intensity = light.Intensity() / 100.0f; const aiVector3D& col = light.Color(); @@ -344,12 +383,12 @@ void Converter::ConvertLight( const Model& model, const Light& light ) } } -void Converter::ConvertCamera( const Model& model, const Camera& cam ) +void Converter::ConvertCamera( const Camera& cam, const std::string &orig_name ) { cameras.push_back( new aiCamera() ); aiCamera* const out_camera = cameras.back(); - out_camera->mName.Set( FixNodeName( model.Name() ) ); + out_camera->mName.Set( orig_name ); out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight(); @@ -363,6 +402,31 @@ void Converter::ConvertCamera( const Model& model, const Camera& cam ) out_camera->mClipPlaneFar = cam.FarPlane(); } +static bool HasName( NodeNameCache &cache, const std::string &name ) { + NodeNameCache::const_iterator it( std::find( cache.begin(), cache.end(), name ) ); + return it != cache.end(); + +} +void Converter::GetUniqueName( const std::string &name, std::string uniqueName ) { + if ( !HasName( mNodeNames, name ) ) { + uniqueName = name; + return; + } + + int i( 0 ); + std::string newName; + while ( HasName( mNodeNames, newName ) ) { + ++i; + newName.clear(); + newName += name; + std::stringstream ext; + ext << std::setfill( '0' ) << std::setw( 3 ) << i; + newName += ext.str(); + } + uniqueName = newName; + mNodeNames.push_back( uniqueName ); +} + const char* Converter::NameTransformationComp( TransformationComp comp ) { @@ -396,6 +460,12 @@ const char* Converter::NameTransformationComp( TransformationComp comp ) return "GeometricRotation"; case TransformationComp_GeometricTranslation: return "GeometricTranslation"; + case TransformationComp_GeometricScalingInverse: + return "GeometricScalingInverse"; + case TransformationComp_GeometricRotationInverse: + return "GeometricRotationInverse"; + case TransformationComp_GeometricTranslationInverse: + return "GeometricTranslationInverse"; case TransformationComp_MAXIMUM: // this is to silence compiler warnings default: break; @@ -437,6 +507,12 @@ const char* Converter::NameTransformationCompProperty( TransformationComp comp ) return "GeometricRotation"; case TransformationComp_GeometricTranslation: return "GeometricTranslation"; + case TransformationComp_GeometricScalingInverse: + return "GeometricScalingInverse"; + case TransformationComp_GeometricRotationInverse: + return "GeometricRotationInverse"; + case TransformationComp_GeometricTranslationInverse: + return "GeometricTranslationInverse"; case TransformationComp_MAXIMUM: // this is to silence compiler warnings break; } @@ -548,17 +624,25 @@ bool Converter::NeedsComplexTransformationChain( const Model& model ) bool ok; const float zero_epsilon = 1e-6f; + const aiVector3D all_ones(1.0f, 1.0f, 1.0f); for ( size_t i = 0; i < TransformationComp_MAXIMUM; ++i ) { const TransformationComp comp = static_cast< TransformationComp >( i ); - if ( comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation || - comp == TransformationComp_GeometricScaling || comp == TransformationComp_GeometricRotation || comp == TransformationComp_GeometricTranslation ) { + if ( comp == TransformationComp_Rotation || comp == TransformationComp_Scaling || comp == TransformationComp_Translation ) { continue; } + bool scale_compare = ( comp == TransformationComp_GeometricScaling || comp == TransformationComp_Scaling ); + const aiVector3D& v = PropertyGet( props, NameTransformationCompProperty( comp ), ok ); - if ( ok && v.SquareLength() > zero_epsilon ) { - return true; + if ( ok && scale_compare ) { + if ( (v - all_ones).SquareLength() > zero_epsilon ) { + return true; + } + } else if ( ok ) { + if ( v.SquareLength() > zero_epsilon ) { + return true; + } } } @@ -570,7 +654,7 @@ std::string Converter::NameTransformationChainNode( const std::string& name, Tra return name + std::string( MAGIC_NODE_TAG ) + "_" + NameTransformationComp( comp ); } -void Converter::GenerateTransformationNodeChain( const Model& model, std::vector& output_nodes ) +void Converter::GenerateTransformationNodeChain( const Model& model, std::vector& output_nodes, std::vector& post_output_nodes ) { const PropertyTable& props = model.Props(); const Model::RotOrder rot = model.RotationOrder(); @@ -582,20 +666,21 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector // generate transformation matrices for all the different transformation components const float zero_epsilon = 1e-6f; + const aiVector3D all_ones(1.0f, 1.0f, 1.0f); bool is_complex = false; const aiVector3D& PreRotation = PropertyGet( props, "PreRotation", ok ); if ( ok && PreRotation.SquareLength() > zero_epsilon ) { is_complex = true; - GetRotationMatrix( rot, PreRotation, chain[ TransformationComp_PreRotation ] ); + GetRotationMatrix( Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[ TransformationComp_PreRotation ] ); } const aiVector3D& PostRotation = PropertyGet( props, "PostRotation", ok ); if ( ok && PostRotation.SquareLength() > zero_epsilon ) { is_complex = true; - GetRotationMatrix( rot, PostRotation, chain[ TransformationComp_PostRotation ] ); + GetRotationMatrix( Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[ TransformationComp_PostRotation ] ); } const aiVector3D& RotationPivot = PropertyGet( props, "RotationPivot", ok ); @@ -634,7 +719,7 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector } const aiVector3D& Scaling = PropertyGet( props, "Lcl Scaling", ok ); - if ( ok && std::fabs( Scaling.SquareLength() - 1.0f ) > zero_epsilon ) { + if ( ok && (Scaling - all_ones).SquareLength() > zero_epsilon ) { aiMatrix4x4::Scaling( Scaling, chain[ TransformationComp_Scaling ] ); } @@ -644,18 +729,38 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector } const aiVector3D& GeometricScaling = PropertyGet( props, "GeometricScaling", ok ); - if ( ok && std::fabs( GeometricScaling.SquareLength() - 1.0f ) > zero_epsilon ) { + if ( ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon ) { + is_complex = true; aiMatrix4x4::Scaling( GeometricScaling, chain[ TransformationComp_GeometricScaling ] ); + aiVector3D GeometricScalingInverse = GeometricScaling; + bool canscale = true; + for (unsigned int i = 0; i < 3; ++i) { + if ( std::fabs( GeometricScalingInverse[i] ) > zero_epsilon ) { + GeometricScalingInverse[i] = 1.0f / GeometricScaling[i]; + } else { + FBXImporter::LogError( "cannot invert geometric scaling matrix with a 0.0 scale component" ); + canscale = false; + break; + } + } + if (canscale) { + aiMatrix4x4::Scaling( GeometricScalingInverse, chain[ TransformationComp_GeometricScalingInverse ] ); + } } const aiVector3D& GeometricRotation = PropertyGet( props, "GeometricRotation", ok ); if ( ok && GeometricRotation.SquareLength() > zero_epsilon ) { + is_complex = true; GetRotationMatrix( rot, GeometricRotation, chain[ TransformationComp_GeometricRotation ] ); + GetRotationMatrix( rot, GeometricRotation, chain[ TransformationComp_GeometricRotationInverse ] ); + chain[ TransformationComp_GeometricRotationInverse ].Inverse(); } const aiVector3D& GeometricTranslation = PropertyGet( props, "GeometricTranslation", ok ); if ( ok && GeometricTranslation.SquareLength() > zero_epsilon ) { + is_complex = true; aiMatrix4x4::Translation( GeometricTranslation, chain[ TransformationComp_GeometricTranslation ] ); + aiMatrix4x4::Translation( -GeometricTranslation, chain[ TransformationComp_GeometricTranslationInverse ] ); } // is_complex needs to be consistent with NeedsComplexTransformationChain() @@ -663,7 +768,7 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector // not be guaranteed. ai_assert( NeedsComplexTransformationChain( model ) == is_complex ); - const std::string& name = FixNodeName( model.Name() ); + std::string name = FixNodeName( model.Name() ); // now, if we have more than just Translation, Scaling and Rotation, // we need to generate a full node chain to accommodate for assimp's @@ -690,10 +795,18 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector } aiNode* nd = new aiNode(); - output_nodes.push_back( nd ); - nd->mName.Set( NameTransformationChainNode( name, comp ) ); nd->mTransformation = chain[ i ]; + + // geometric inverses go in a post-node chain + if ( comp == TransformationComp_GeometricScalingInverse || + comp == TransformationComp_GeometricRotationInverse || + comp == TransformationComp_GeometricTranslationInverse + ) { + post_output_nodes.push_back( nd ); + } else { + output_nodes.push_back( nd ); + } } ai_assert( output_nodes.size() ); @@ -703,8 +816,10 @@ void Converter::GenerateTransformationNodeChain( const Model& model, std::vector // else, we can just multiply the matrices together aiNode* nd = new aiNode(); output_nodes.push_back( nd ); + std::string uniqueName; + GetUniqueName( name, uniqueName ); - nd->mName.Set( name ); + nd->mName.Set( uniqueName ); for (const auto &transform : chain) { nd->mTransformation = nd->mTransformation * transform; @@ -1559,9 +1674,8 @@ void Converter::TrySetTextureProperties( aiMaterial* out_mat, const TextureMap& } void Converter::TrySetTextureProperties( aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, - const std::string& propName, - aiTextureType target, const MeshGeometry* const mesh ) -{ + const std::string& propName, + aiTextureType target, const MeshGeometry* const mesh ) { LayeredTextureMap::const_iterator it = layeredTextures.find( propName ); if ( it == layeredTextures.end() ) { return; @@ -1805,11 +1919,11 @@ void Converter::SetShadingPropertiesCommon( aiMaterial* out_mat, const PropertyT // TransparentColor / TransparencyFactor... gee thanks FBX :rolleyes: const aiColor3D& Transparent = GetColorPropertyFactored( props, "TransparentColor", "TransparencyFactor", ok ); - float CalculatedOpacity = 1.0; + float CalculatedOpacity = 1.0f; if ( ok ) { out_mat->AddProperty( &Transparent, 1, AI_MATKEY_COLOR_TRANSPARENT ); // as calculated by FBX SDK 2017: - CalculatedOpacity = 1.0 - ((Transparent.r + Transparent.g + Transparent.b) / 3.0); + CalculatedOpacity = 1.0f - ((Transparent.r + Transparent.g + Transparent.b) / 3.0f); } // use of TransparencyFactor is inconsistent. @@ -1923,81 +2037,17 @@ void Converter::ConvertAnimations() } } -void Converter::RenameNode( const std::string& fixed_name, const std::string& new_name ) { - if ( node_names.find( fixed_name ) == node_names.end() ) { - FBXImporter::LogError( "Cannot rename node " + fixed_name + ", not existing."); - return; - } - - if ( node_names.find( new_name ) != node_names.end() ) { - FBXImporter::LogError( "Cannot rename node " + fixed_name + " to " + new_name +", name already existing." ); - return; - } - - ai_assert( node_names.find( fixed_name ) != node_names.end() ); - ai_assert( node_names.find( new_name ) == node_names.end() ); - - renamed_nodes[ fixed_name ] = new_name; - - const aiString fn( fixed_name ); - - for( aiCamera* cam : cameras ) { - if ( cam->mName == fn ) { - cam->mName.Set( new_name ); - break; - } - } - - for( aiLight* light : lights ) { - if ( light->mName == fn ) { - light->mName.Set( new_name ); - break; - } - } - - for( aiAnimation* anim : animations ) { - for ( unsigned int i = 0; i < anim->mNumChannels; ++i ) { - aiNodeAnim* const na = anim->mChannels[ i ]; - if ( na->mNodeName == fn ) { - na->mNodeName.Set( new_name ); - break; - } - } - } -} - - -std::string Converter::FixNodeName( const std::string& name ) -{ +std::string Converter::FixNodeName( const std::string& name ) { // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if // this causes ambiguities, well possible between empty identifiers, // such as "Model::" and ""). Make sure the behaviour is consistent // across multiple calls to FixNodeName(). if ( name.substr( 0, 7 ) == "Model::" ) { std::string temp = name.substr( 7 ); - - const NodeNameMap::const_iterator it = node_names.find( temp ); - if ( it != node_names.end() ) { - if ( !( *it ).second ) { - return FixNodeName( name + "_" ); - } - } - node_names[ temp ] = true; - - const NameNameMap::const_iterator rit = renamed_nodes.find( temp ); - return rit == renamed_nodes.end() ? temp : ( *rit ).second; + return temp; } - const NodeNameMap::const_iterator it = node_names.find( name ); - if ( it != node_names.end() ) { - if ( ( *it ).second ) { - return FixNodeName( name + "_" ); - } - } - node_names[ name ] = false; - - const NameNameMap::const_iterator rit = renamed_nodes.find( name ); - return rit == renamed_nodes.end() ? name : ( *rit ).second; + return name; } void Converter::ConvertAnimationStack( const AnimationStack& st ) @@ -2209,8 +2259,7 @@ void Converter::GenerateNodeAnimations( std::vector& node_anims, has_any = true; - if ( comp != TransformationComp_Rotation && comp != TransformationComp_Scaling && comp != TransformationComp_Translation && - comp != TransformationComp_GeometricScaling && comp != TransformationComp_GeometricRotation && comp != TransformationComp_GeometricTranslation ) + if ( comp != TransformationComp_Rotation && comp != TransformationComp_Scaling && comp != TransformationComp_Translation ) { has_complex = true; } diff --git a/code/FBXConverter.h b/code/FBXConverter.h index c882e9326..60fd04e2c 100644 --- a/code/FBXConverter.h +++ b/code/FBXConverter.h @@ -68,6 +68,8 @@ namespace FBX { class Document; +using NodeNameCache = std::vector; + /** * Convert a FBX #Document to #aiScene * @param out Empty scene to be populated @@ -82,7 +84,10 @@ public: * The different parts that make up the final local transformation of a fbx-node */ enum TransformationComp { - TransformationComp_Translation = 0, + TransformationComp_GeometricScalingInverse = 0, + TransformationComp_GeometricRotationInverse, + TransformationComp_GeometricTranslationInverse, + TransformationComp_Translation, TransformationComp_RotationOffset, TransformationComp_RotationPivot, TransformationComp_PreRotation, @@ -114,16 +119,19 @@ private: void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4()); // ------------------------------------------------------------------------------------------------ - void ConvertLights(const Model& model); + void ConvertLights(const Model& model, const std::string &orig_name ); // ------------------------------------------------------------------------------------------------ - void ConvertCameras(const Model& model); + void ConvertCameras(const Model& model, const std::string &orig_name ); // ------------------------------------------------------------------------------------------------ - void ConvertLight(const Model& model, const Light& light); + void ConvertLight( const Light& light, const std::string &orig_name ); // ------------------------------------------------------------------------------------------------ - void ConvertCamera(const Model& model, const Camera& cam); + void ConvertCamera( const Camera& cam, const std::string &orig_name ); + + // ------------------------------------------------------------------------------------------------ + void GetUniqueName( const std::string &name, std::string uniqueName ); // ------------------------------------------------------------------------------------------------ // this returns unified names usable within assimp identifiers (i.e. no space characters - @@ -153,7 +161,7 @@ private: /** * note: memory for output_nodes will be managed by the caller */ - void GenerateTransformationNodeChain(const Model& model, std::vector& output_nodes); + void GenerateTransformationNodeChain(const Model& model, std::vector& output_nodes, std::vector& post_output_nodes); // ------------------------------------------------------------------------------------------------ void SetupNodeMetadata(const Model& model, aiNode& nd); @@ -255,18 +263,6 @@ private: // convert animation data to aiAnimation et al void ConvertAnimations(); - // ------------------------------------------------------------------------------------------------ - // rename a node already partially converted. fixed_name is a string previously returned by - // FixNodeName, new_name specifies the string FixNodeName should return on all further invocations - // which would previously have returned the old value. - // - // this also updates names in node animations, cameras and light sources and is thus slow. - // - // NOTE: the caller is responsible for ensuring that the new name is unique and does - // not collide with any other identifiers. The best way to ensure this is to only - // append to the old name, which is guaranteed to match these requirements. - void RenameNode(const std::string& fixed_name, const std::string& new_name); - // ------------------------------------------------------------------------------------------------ // takes a fbx node name and returns the identifier to be used in the assimp output scene. // the function is guaranteed to provide consistent results over multiple invocations @@ -278,7 +274,6 @@ private: // XXX: better use multi_map .. typedef std::map > NodeMap; - // ------------------------------------------------------------------------------------------------ void ConvertAnimationStack(const AnimationStack& st); @@ -429,13 +424,7 @@ private: typedef std::map NodeAnimBitMap; NodeAnimBitMap node_anim_chain_bits; - // name -> has had its prefix_stripped? - typedef std::map NodeNameMap; - NodeNameMap node_names; - - typedef std::map NameNameMap; - NameNameMap renamed_nodes; - + NodeNameCache mNodeNames; double anim_fps; aiScene* const out; diff --git a/code/FBXDocument.cpp b/code/FBXDocument.cpp index 3714cd617..a1fbd2cf0 100644 --- a/code/FBXDocument.cpp +++ b/code/FBXDocument.cpp @@ -351,7 +351,9 @@ void Document::ReadGlobalSettings() return; } - std::shared_ptr props = GetPropertyTable(*this, "", *ehead, *ehead->Compound(), true); + std::shared_ptr props = GetPropertyTable( *this, "", *ehead, *ehead->Compound(), true ); + + //double v = PropertyGet( *props.get(), std::string("UnitScaleFactor"), 1.0 ); if(!props) { DOMError("GlobalSettings dictionary contains no property table"); diff --git a/code/FBXDocument.h b/code/FBXDocument.h index 386a660d9..654f5bfa8 100644 --- a/code/FBXDocument.h +++ b/code/FBXDocument.h @@ -1022,8 +1022,8 @@ public: fbx_simple_property(CoordAxisSign, int, 1) fbx_simple_property(OriginalUpAxis, int, 0) fbx_simple_property(OriginalUpAxisSign, int, 1) - fbx_simple_property(UnitScaleFactor, double, 1) - fbx_simple_property(OriginalUnitScaleFactor, double, 1) + fbx_simple_property(UnitScaleFactor, float, 1) + fbx_simple_property(OriginalUnitScaleFactor, float, 1) fbx_simple_property(AmbientColor, aiVector3D, aiVector3D(0,0,0)) fbx_simple_property(DefaultCamera, std::string, "") diff --git a/code/FBXExportNode.cpp b/code/FBXExportNode.cpp new file mode 100644 index 000000000..a1171eca8 --- /dev/null +++ b/code/FBXExportNode.cpp @@ -0,0 +1,568 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, 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_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportNode.h" +#include "FBXCommon.h" + +#include // StreamWriterLE +#include // DeadlyExportError +#include +#include // ai_snprintf + +#include +#include +#include // ostringstream +#include // shared_ptr + +// AddP70 helpers... there's no usable pattern here, +// so all are defined as separate functions. +// Even "animatable" properties are often completely different +// from the standard (nonanimated) property definition, +// so they are specified with an 'A' suffix. + +void FBX::Node::AddP70int( + const std::string& name, int32_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "int", "Integer", "", value); + AddChild(n); +} + +void FBX::Node::AddP70bool( + const std::string& name, bool value +) { + FBX::Node n("P"); + n.AddProperties(name, "bool", "", "", int32_t(value)); + AddChild(n); +} + +void FBX::Node::AddP70double( + const std::string& name, double value +) { + FBX::Node n("P"); + n.AddProperties(name, "double", "Number", "", value); + AddChild(n); +} + +void FBX::Node::AddP70numberA( + const std::string& name, double value +) { + FBX::Node n("P"); + n.AddProperties(name, "Number", "", "A", value); + AddChild(n); +} + +void FBX::Node::AddP70color( + const std::string& name, double r, double g, double b +) { + FBX::Node n("P"); + n.AddProperties(name, "ColorRGB", "Color", "", r, g, b); + AddChild(n); +} + +void FBX::Node::AddP70colorA( + const std::string& name, double r, double g, double b +) { + FBX::Node n("P"); + n.AddProperties(name, "Color", "", "A", r, g, b); + AddChild(n); +} + +void FBX::Node::AddP70vector( + const std::string& name, double x, double y, double z +) { + FBX::Node n("P"); + n.AddProperties(name, "Vector3D", "Vector", "", x, y, z); + AddChild(n); +} + +void FBX::Node::AddP70vectorA( + const std::string& name, double x, double y, double z +) { + FBX::Node n("P"); + n.AddProperties(name, "Vector", "", "A", x, y, z); + AddChild(n); +} + +void FBX::Node::AddP70string( + const std::string& name, const std::string& value +) { + FBX::Node n("P"); + n.AddProperties(name, "KString", "", "", value); + AddChild(n); +} + +void FBX::Node::AddP70enum( + const std::string& name, int32_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "enum", "", "", value); + AddChild(n); +} + +void FBX::Node::AddP70time( + const std::string& name, int64_t value +) { + FBX::Node n("P"); + n.AddProperties(name, "KTime", "Time", "", value); + AddChild(n); +} + + +// public member functions for writing nodes to stream + +void FBX::Node::Dump( + std::shared_ptr outfile, + bool binary, int indent +) { + if (binary) { + Assimp::StreamWriterLE outstream(outfile); + DumpBinary(outstream); + } else { + std::ostringstream ss; + DumpAscii(ss, indent); + std::string s = ss.str(); + outfile->Write(s.c_str(), s.size(), 1); + } +} + +void FBX::Node::Dump( + Assimp::StreamWriterLE &outstream, + bool binary, int indent +) { + if (binary) { + DumpBinary(outstream); + } else { + std::ostringstream ss; + DumpAscii(ss, indent); + outstream.PutString(ss.str()); + } +} + + +// public member functions for low-level writing + +void FBX::Node::Begin( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + if (binary) { + BeginBinary(s); + } else { + // assume we're at the correct place to start already + (void)indent; + std::ostringstream ss; + BeginAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::DumpProperties( + Assimp::StreamWriterLE& s, + bool binary, int indent +) { + if (binary) { + DumpPropertiesBinary(s); + } else { + std::ostringstream ss; + DumpPropertiesAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::EndProperties( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + EndProperties(s, binary, indent, properties.size()); +} + +void FBX::Node::EndProperties( + Assimp::StreamWriterLE &s, + bool binary, int indent, + size_t num_properties +) { + if (binary) { + EndPropertiesBinary(s, num_properties); + } else { + // nothing to do + (void)indent; + } +} + +void FBX::Node::BeginChildren( + Assimp::StreamWriterLE &s, + bool binary, int indent +) { + if (binary) { + // nothing to do + } else { + std::ostringstream ss; + BeginChildrenAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::DumpChildren( + Assimp::StreamWriterLE& s, + bool binary, int indent +) { + if (binary) { + DumpChildrenBinary(s); + } else { + std::ostringstream ss; + DumpChildrenAscii(ss, indent); + s.PutString(ss.str()); + } +} + +void FBX::Node::End( + Assimp::StreamWriterLE &s, + bool binary, int indent, + bool has_children +) { + if (binary) { + EndBinary(s, has_children); + } else { + std::ostringstream ss; + EndAscii(ss, indent, has_children); + s.PutString(ss.str()); + } +} + + +// public member functions for writing to binary fbx + +void FBX::Node::DumpBinary(Assimp::StreamWriterLE &s) +{ + // write header section (with placeholders for some things) + BeginBinary(s); + + // write properties + DumpPropertiesBinary(s); + + // go back and fill in property related placeholders + EndPropertiesBinary(s, properties.size()); + + // write children + DumpChildrenBinary(s); + + // finish, filling in end offset placeholder + EndBinary(s, force_has_children || !children.empty()); +} + + +// public member functions for writing to ascii fbx + +void FBX::Node::DumpAscii(std::ostream &s, int indent) +{ + // write name + BeginAscii(s, indent); + + // write properties + DumpPropertiesAscii(s, indent); + + if (force_has_children || !children.empty()) { + // begin children (with a '{') + BeginChildrenAscii(s, indent + 1); + // write children + DumpChildrenAscii(s, indent + 1); + } + + // finish (also closing the children bracket '}') + EndAscii(s, indent, force_has_children || !children.empty()); +} + + +// private member functions for low-level writing to fbx + +void FBX::Node::BeginBinary(Assimp::StreamWriterLE &s) +{ + // remember start pos so we can come back and write the end pos + this->start_pos = s.Tell(); + + // placeholders for end pos and property section info + s.PutU4(0); // end pos + s.PutU4(0); // number of properties + s.PutU4(0); // total property section length + + // node name + s.PutU1(uint8_t(name.size())); // length of node name + s.PutString(name); // node name as raw bytes + + // property data comes after here + this->property_start = s.Tell(); +} + +void FBX::Node::DumpPropertiesBinary(Assimp::StreamWriterLE& s) +{ + for (auto &p : properties) { + p.DumpBinary(s); + } +} + +void FBX::Node::EndPropertiesBinary( + Assimp::StreamWriterLE &s, + size_t num_properties +) { + if (num_properties == 0) { return; } + size_t pos = s.Tell(); + ai_assert(pos > property_start); + size_t property_section_size = pos - property_start; + s.Seek(start_pos + 4); + s.PutU4(uint32_t(num_properties)); + s.PutU4(uint32_t(property_section_size)); + s.Seek(pos); +} + +void FBX::Node::DumpChildrenBinary(Assimp::StreamWriterLE& s) +{ + for (FBX::Node& child : children) { + child.DumpBinary(s); + } +} + +void FBX::Node::EndBinary( + Assimp::StreamWriterLE &s, + bool has_children +) { + // if there were children, add a null record + if (has_children) { s.PutString(FBX::NULL_RECORD); } + + // now go back and write initial pos + this->end_pos = s.Tell(); + s.Seek(start_pos); + s.PutU4(uint32_t(end_pos)); + s.Seek(end_pos); +} + + +void FBX::Node::BeginAscii(std::ostream& s, int indent) +{ + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << name << ": "; +} + +void FBX::Node::DumpPropertiesAscii(std::ostream &s, int indent) +{ + for (size_t i = 0; i < properties.size(); ++i) { + if (i > 0) { s << ", "; } + properties[i].DumpAscii(s, indent); + } +} + +void FBX::Node::BeginChildrenAscii(std::ostream& s, int indent) +{ + // only call this if there are actually children + s << " {"; + (void)indent; +} + +void FBX::Node::DumpChildrenAscii(std::ostream& s, int indent) +{ + // children will need a lot of padding and corralling + if (children.size() || force_has_children) { + for (size_t i = 0; i < children.size(); ++i) { + // no compression in ascii files, so skip this node if it exists + if (children[i].name == "EncryptionType") { continue; } + // the child can dump itself + children[i].DumpAscii(s, indent); + } + } +} + +void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children) +{ + if (!has_children) { return; } // nothing to do + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "}"; +} + +// private helpers for static member functions + +// ascii property node from vector of doubles +void FBX::Node::WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent +){ + char buffer[32]; + FBX::Node node(name); + node.Begin(s, false, indent); + std::string vsize = std::to_string(v.size()); + // * { + s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); + // indent + 1 + for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); } + // a: value,value,value,... + s.PutString("a: "); + int count = 0; + for (size_t i = 0; i < v.size(); ++i) { + if (i > 0) { s.PutChar(','); } + int len = ai_snprintf(buffer, sizeof(buffer), "%f", v[i]); + count += len; + if (count > 2048) { s.PutChar('\n'); count = 0; } + if (len < 0 || len > 31) { + // this should never happen + throw DeadlyExportError("failed to convert double to string"); + } + for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); } + } + // } + s.PutChar('\n'); + for (int i = 0; i < indent; ++i) { s.PutChar('\t'); } + s.PutChar('}'); s.PutChar(' '); + node.End(s, false, indent, false); +} + +// ascii property node from vector of int32_t +void FBX::Node::WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent +){ + char buffer[32]; + FBX::Node node(name); + node.Begin(s, false, indent); + std::string vsize = std::to_string(v.size()); + // * { + s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); + // indent + 1 + for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); } + // a: value,value,value,... + s.PutString("a: "); + int count = 0; + for (size_t i = 0; i < v.size(); ++i) { + if (i > 0) { s.PutChar(','); } + int len = ai_snprintf(buffer, sizeof(buffer), "%d", v[i]); + count += len; + if (count > 2048) { s.PutChar('\n'); count = 0; } + if (len < 0 || len > 31) { + // this should never happen + throw DeadlyExportError("failed to convert double to string"); + } + for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); } + } + // } + s.PutChar('\n'); + for (int i = 0; i < indent; ++i) { s.PutChar('\t'); } + s.PutChar('}'); s.PutChar(' '); + node.End(s, false, indent, false); +} + +// binary property node from vector of doubles +// TODO: optional zip compression! +void FBX::Node::WritePropertyNodeBinary( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s +){ + FBX::Node node(name); + node.BeginBinary(s); + s.PutU1('d'); + s.PutU4(uint32_t(v.size())); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + s.PutU4(uint32_t(v.size()) * 8); // data size + for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); } + node.EndPropertiesBinary(s, 1); + node.EndBinary(s, false); +} + +// binary property node from vector of int32_t +// TODO: optional zip compression! +void FBX::Node::WritePropertyNodeBinary( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s +){ + FBX::Node node(name); + node.BeginBinary(s); + s.PutU1('i'); + s.PutU4(uint32_t(v.size())); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + s.PutU4(uint32_t(v.size()) * 4); // data size + for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); } + node.EndPropertiesBinary(s, 1); + node.EndBinary(s, false); +} + +// public static member functions + +// convenience function to create and write a property node, +// holding a single property which is an array of values. +// does not copy the data, so is efficient for large arrays. +void FBX::Node::WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + bool binary, int indent +){ + if (binary) { + FBX::Node::WritePropertyNodeBinary(name, v, s); + } else { + FBX::Node::WritePropertyNodeAscii(name, v, s, indent); + } +} + +// convenience function to create and write a property node, +// holding a single property which is an array of values. +// does not copy the data, so is efficient for large arrays. +void FBX::Node::WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + bool binary, int indent +){ + if (binary) { + FBX::Node::WritePropertyNodeBinary(name, v, s); + } else { + FBX::Node::WritePropertyNodeAscii(name, v, s, indent); + } +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/FBXExportNode.h b/code/FBXExportNode.h new file mode 100644 index 000000000..5ddd8c77b --- /dev/null +++ b/code/FBXExportNode.h @@ -0,0 +1,258 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, 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 FBXExportNode.h +* Declares the FBX::Node helper class for fbx export. +*/ +#ifndef AI_FBXEXPORTNODE_H_INC +#define AI_FBXEXPORTNODE_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportProperty.h" + +#include // StreamWriterLE + +#include +#include + +namespace FBX { + class Node; +} + +class FBX::Node +{ +public: // public data members + // TODO: accessors + std::string name; // node name + std::vector properties; // node properties + std::vector children; // child nodes + + // some nodes always pretend they have children... + bool force_has_children = false; + +public: // constructors + Node() = default; + Node(const std::string& n) : name(n) {} + + // convenience template to construct with properties directly + template + Node(const std::string& n, const More... more) + : name(n) + { AddProperties(more...); } + +public: // functions to add properties or children + // add a single property to the node + template + void AddProperty(T value) { + properties.emplace_back(value); + } + + // convenience function to add multiple properties at once + template + void AddProperties(T value, More... more) { + properties.emplace_back(value); + AddProperties(more...); + } + void AddProperties() {} + + // add a child node directly + void AddChild(const Node& node) { children.push_back(node); } + + // convenience function to add a child node with a single property + template + void AddChild( + const std::string& name, + More... more + ) { + FBX::Node c(name); + c.AddProperties(more...); + children.push_back(c); + } + +public: // support specifically for dealing with Properties70 nodes + + // it really is simpler to make these all separate functions. + // the versions with 'A' suffixes are for animatable properties. + // those often follow a completely different format internally in FBX. + void AddP70int(const std::string& name, int32_t value); + void AddP70bool(const std::string& name, bool value); + void AddP70double(const std::string& name, double value); + void AddP70numberA(const std::string& name, double value); + void AddP70color(const std::string& name, double r, double g, double b); + void AddP70colorA(const std::string& name, double r, double g, double b); + void AddP70vector(const std::string& name, double x, double y, double z); + void AddP70vectorA(const std::string& name, double x, double y, double z); + void AddP70string(const std::string& name, const std::string& value); + void AddP70enum(const std::string& name, int32_t value); + void AddP70time(const std::string& name, int64_t value); + + // template for custom P70 nodes. + // anything that doesn't fit in the above can be created manually. + template + void AddP70( + const std::string& name, + const std::string& type, + const std::string& type2, + const std::string& flags, + More... more + ) { + Node n("P"); + n.AddProperties(name, type, type2, flags, more...); + AddChild(n); + } + +public: // member functions for writing data to a file or stream + + // write the full node to the given file or stream + void Dump( + std::shared_ptr outfile, + bool binary, int indent + ); + void Dump(Assimp::StreamWriterLE &s, bool binary, int indent); + + // these other functions are for writing data piece by piece. + // they must be used carefully. + // for usage examples see FBXExporter.cpp. + void Begin(Assimp::StreamWriterLE &s, bool binary, int indent); + void DumpProperties(Assimp::StreamWriterLE& s, bool binary, int indent); + void EndProperties(Assimp::StreamWriterLE &s, bool binary, int indent); + void EndProperties( + Assimp::StreamWriterLE &s, bool binary, int indent, + size_t num_properties + ); + void BeginChildren(Assimp::StreamWriterLE &s, bool binary, int indent); + void DumpChildren(Assimp::StreamWriterLE& s, bool binary, int indent); + void End( + Assimp::StreamWriterLE &s, bool binary, int indent, + bool has_children + ); + +private: // internal functions used for writing + + void DumpBinary(Assimp::StreamWriterLE &s); + void DumpAscii(Assimp::StreamWriterLE &s, int indent); + void DumpAscii(std::ostream &s, int indent); + + void BeginBinary(Assimp::StreamWriterLE &s); + void DumpPropertiesBinary(Assimp::StreamWriterLE& s); + void EndPropertiesBinary(Assimp::StreamWriterLE &s); + void EndPropertiesBinary(Assimp::StreamWriterLE &s, size_t num_properties); + void DumpChildrenBinary(Assimp::StreamWriterLE& s); + void EndBinary(Assimp::StreamWriterLE &s, bool has_children); + + void BeginAscii(std::ostream &s, int indent); + void DumpPropertiesAscii(std::ostream &s, int indent); + void BeginChildrenAscii(std::ostream &s, int indent); + void DumpChildrenAscii(std::ostream &s, int indent); + void EndAscii(std::ostream &s, int indent, bool has_children); + +private: // data used for binary dumps + size_t start_pos; // starting position in stream + size_t end_pos; // ending position in stream + size_t property_start; // starting position of property section + +public: // static member functions + + // convenience function to create a node with a single property, + // and write it to the stream. + template + static void WritePropertyNode( + const std::string& name, + const T value, + Assimp::StreamWriterLE& s, + bool binary, int indent + ) { + FBX::Property p(value); + FBX::Node node(name, p); + node.Dump(s, binary, indent); + } + + // convenience function to create and write a property node, + // holding a single property which is an array of values. + // does not copy the data, so is efficient for large arrays. + static void WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + bool binary, int indent + ); + + // convenience function to create and write a property node, + // holding a single property which is an array of values. + // does not copy the data, so is efficient for large arrays. + static void WritePropertyNode( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + bool binary, int indent + ); + +private: // static helper functions + static void WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent + ); + static void WritePropertyNodeAscii( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s, + int indent + ); + static void WritePropertyNodeBinary( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s + ); + static void WritePropertyNodeBinary( + const std::string& name, + const std::vector& v, + Assimp::StreamWriterLE& s + ); + +}; + + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTNODE_H_INC diff --git a/code/FBXExportProperty.cpp b/code/FBXExportProperty.cpp new file mode 100644 index 000000000..431750274 --- /dev/null +++ b/code/FBXExportProperty.cpp @@ -0,0 +1,364 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, 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_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportProperty.h" + +#include // StreamWriterLE +#include // DeadlyExportError + +#include +#include +#include +#include +#include // ostringstream + + +// constructors for single element properties + +FBX::Property::Property(bool v) + : type('C'), data(1) +{ + data = {uint8_t(v)}; +} + +FBX::Property::Property(int16_t v) : type('Y'), data(2) +{ + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBX::Property::Property(int32_t v) : type('I'), data(4) +{ + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBX::Property::Property(float v) : type('F'), data(4) +{ + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBX::Property::Property(double v) : type('D'), data(8) +{ + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + +FBX::Property::Property(int64_t v) : type('L'), data(8) +{ + uint8_t* d = data.data(); + (reinterpret_cast(d))[0] = v; +} + + +// constructors for array-type properties + +FBX::Property::Property(const char* c, bool raw) + : Property(std::string(c), raw) +{} + +// strings can either be saved as "raw" (R) data, or "string" (S) data +FBX::Property::Property(const std::string& s, bool raw) + : type(raw ? 'R' : 'S'), data(s.size()) +{ + for (size_t i = 0; i < s.size(); ++i) { + data[i] = uint8_t(s[i]); + } +} + +FBX::Property::Property(const std::vector& r) + : type('R'), data(r) +{} + +FBX::Property::Property(const std::vector& va) + : type('i'), data(4*va.size()) +{ + int32_t* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + +FBX::Property::Property(const std::vector& va) + : type('l'), data(8*va.size()) +{ + int64_t* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + +FBX::Property::Property(const std::vector& va) + : type('f'), data(4*va.size()) +{ + float* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + +FBX::Property::Property(const std::vector& va) + : type('d'), data(8*va.size()) +{ + double* d = reinterpret_cast(data.data()); + for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; } +} + +FBX::Property::Property(const aiMatrix4x4& vm) + : type('d'), data(8*16) +{ + double* d = reinterpret_cast(data.data()); + for (unsigned int c = 0; c < 4; ++c) { + for (unsigned int r = 0; r < 4; ++r) { + d[4*c+r] = vm[r][c]; + } + } +} + +// public member functions + +size_t FBX::Property::size() +{ + switch (type) { + case 'C': case 'Y': case 'I': case 'F': case 'D': case 'L': + return data.size() + 1; + case 'S': case 'R': + return data.size() + 5; + case 'i': case 'd': + return data.size() + 13; + default: + throw DeadlyExportError("Requested size on property of unknown type"); + } +} + +void FBX::Property::DumpBinary(Assimp::StreamWriterLE &s) +{ + s.PutU1(type); + uint8_t* d = data.data(); + size_t N; + switch (type) { + case 'C': s.PutU1(*(reinterpret_cast(d))); return; + case 'Y': s.PutI2(*(reinterpret_cast(d))); return; + case 'I': s.PutI4(*(reinterpret_cast(d))); return; + case 'F': s.PutF4(*(reinterpret_cast(d))); return; + case 'D': s.PutF8(*(reinterpret_cast(d))); return; + case 'L': s.PutI8(*(reinterpret_cast(d))); return; + case 'S': + case 'R': + s.PutU4(uint32_t(data.size())); + for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); } + return; + case 'i': + N = data.size() / 4; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutI4((reinterpret_cast(d))[i]); + } + return; + case 'l': + N = data.size() / 8; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutI8((reinterpret_cast(d))[i]); + } + return; + case 'f': + N = data.size() / 4; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutF4((reinterpret_cast(d))[i]); + } + return; + case 'd': + N = data.size() / 8; + s.PutU4(uint32_t(N)); // number of elements + s.PutU4(0); // no encoding (1 would be zip-compressed) + // TODO: compress if large? + s.PutU4(uint32_t(data.size())); // data size + for (size_t i = 0; i < N; ++i) { + s.PutF8((reinterpret_cast(d))[i]); + } + return; + default: + std::ostringstream err; + err << "Tried to dump property with invalid type '"; + err << type << "'!"; + throw DeadlyExportError(err.str()); + } +} + +void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent) +{ + std::ostringstream ss; + ss.imbue(std::locale::classic()); + ss.precision(15); // this seems to match official FBX SDK exports + DumpAscii(ss, indent); + outstream.PutString(ss.str()); +} + +void FBX::Property::DumpAscii(std::ostream& s, int indent) +{ + // no writing type... or anything. just shove it into the stream. + uint8_t* d = data.data(); + size_t N; + size_t swap = data.size(); + size_t count = 0; + switch (type) { + case 'C': + if (*(reinterpret_cast(d))) { s << 'T'; } + else { s << 'F'; } + return; + case 'Y': s << *(reinterpret_cast(d)); return; + case 'I': s << *(reinterpret_cast(d)); return; + case 'F': s << *(reinterpret_cast(d)); return; + case 'D': s << *(reinterpret_cast(d)); return; + case 'L': s << *(reinterpret_cast(d)); return; + case 'S': + // first search to see if it has "\x00\x01" in it - + // which separates fields which are reversed in the ascii version. + // yeah. + // FBX, yeah. + for (size_t i = 0; i < data.size(); ++i) { + if (data[i] == '\0') { + swap = i; + break; + } + } + case 'R': + s << '"'; + // we might as well check this now, + // probably it will never happen + for (size_t i = 0; i < data.size(); ++i) { + char c = data[i]; + if (c == '"') { + throw runtime_error("can't handle quotes in property string"); + } + } + // first write the SWAPPED member (if any) + for (size_t i = swap + 2; i < data.size(); ++i) { + char c = data[i]; + s << c; + } + // then a separator + if (swap != data.size()) { + s << "::"; + } + // then the initial member + for (size_t i = 0; i < swap; ++i) { + char c = data[i]; + s << c; + } + s << '"'; + return; + case 'i': + N = data.size() / 4; // number of elements + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'l': + N = data.size() / 8; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'f': + N = data.size() / 4; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + case 'd': + N = data.size() / 8; + s << '*' << N << " {\n"; + for (int i = 0; i < indent + 1; ++i) { s << '\t'; } + s << "a: "; + // set precision to something that can handle doubles + s.precision(15); + for (size_t i = 0; i < N; ++i) { + if (i > 0) { s << ','; } + if (count++ > 120) { s << '\n'; count = 0; } + s << (reinterpret_cast(d))[i]; + } + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << "} "; + return; + default: + std::ostringstream err; + err << "Tried to dump property with invalid type '"; + err << type << "'!"; + throw runtime_error(err.str()); + } +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/FBXExportProperty.h b/code/FBXExportProperty.h new file mode 100644 index 000000000..cb3b0113f --- /dev/null +++ b/code/FBXExportProperty.h @@ -0,0 +1,129 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, 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 FBXExportProperty.h +* Declares the FBX::Property helper class for fbx export. +*/ +#ifndef AI_FBXEXPORTPROPERTY_H_INC +#define AI_FBXEXPORTPROPERTY_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + + +#include // aiMatrix4x4 +#include // StreamWriterLE + +#include +#include +#include +#include // is_void + +namespace FBX { + class Property; +} + +/** FBX::Property + * + * Holds a value of any of FBX's recognized types, + * each represented by a particular one-character code. + * C : 1-byte uint8, usually 0x00 or 0x01 to represent boolean false and true + * Y : 2-byte int16 + * I : 4-byte int32 + * F : 4-byte float + * D : 8-byte double + * L : 8-byte int64 + * i : array of int32 + * f : array of float + * d : array of double + * l : array of int64 + * b : array of 1-byte booleans (0x00 or 0x01) + * S : string (array of 1-byte char) + * R : raw data (array of bytes) + */ +class FBX::Property +{ +public: + // constructors for basic types. + // all explicit to avoid accidental typecasting + explicit Property(bool v); + // TODO: determine if there is actually a byte type, + // or if this always means . 'C' seems to imply , + // so possibly the above was intended to represent both. + explicit Property(int16_t v); + explicit Property(int32_t v); + explicit Property(float v); + explicit Property(double v); + explicit Property(int64_t v); + // strings can either be stored as 'R' (raw) or 'S' (string) type + explicit Property(const char* c, bool raw=false); + explicit Property(const std::string& s, bool raw=false); + explicit Property(const std::vector& r); + explicit Property(const std::vector& va); + explicit Property(const std::vector& va); + explicit Property(const std::vector& va); + explicit Property(const std::vector& va); + explicit Property(const aiMatrix4x4& vm); + + // this will catch any type not defined above, + // so that we don't accidentally convert something we don't want. + // for example (const char*) --> (bool)... seriously wtf C++ + template + explicit Property(T v) : type('X') { + static_assert(std::is_void::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION"); + } // note: no line wrap so it appears verbatim on the compiler error + + // the size of this property node in a binary file, in bytes + size_t size(); + + // write this property node as binary data to the given stream + void DumpBinary(Assimp::StreamWriterLE &s); + void DumpAscii(Assimp::StreamWriterLE &s, int indent=0); + void DumpAscii(std::ostream &s, int indent=0); + // note: make sure the ostream is in classic "C" locale + +private: + char type; + std::vector data; +}; + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTPROPERTY_H_INC diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp new file mode 100644 index 000000000..c524e3911 --- /dev/null +++ b/code/FBXExporter.cpp @@ -0,0 +1,2475 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, 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_EXPORT +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExporter.h" +#include "FBXExportNode.h" +#include "FBXExportProperty.h" +#include "FBXCommon.h" + +#include // aiGetVersion +#include +#include +#include +#include // StreamWriterLE +#include // DeadlyExportError +#include // aiTextureType +#include +#include + +// Header files, standard library. +#include // shared_ptr +#include +#include // stringstream +#include // localtime, tm_* +#include +#include +#include +#include +#include + +// RESOURCES: +// https://code.blender.org/2013/08/fbx-binary-file-format-specification/ +// https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure + +const double DEG = 57.29577951308232087679815481; // degrees per radian + +// some constants that we'll use for writing metadata +namespace FBX { + const std::string EXPORT_VERSION_STR = "7.4.0"; + const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015 + // FBX files have some hashed values that depend on the creation time field, + // but for now we don't actually know how to generate these. + // what we can do is set them to a known-working version. + // this is the data that Blender uses in their FBX export process. + const std::string GENERIC_CTIME = "1970-01-01 10:00:00:000"; + const std::string GENERIC_FILEID = + "\x28\xb3\x2a\xeb\xb6\x24\xcc\xc2\xbf\xc8\xb0\x2a\xa9\x2b\xfc\xf1"; + const std::string GENERIC_FOOTID = + "\xfa\xbc\xab\x09\xd0\xc8\xd4\x66\xb1\x76\xfb\x83\x1c\xf7\x26\x7e"; + const std::string FOOT_MAGIC = + "\xf8\x5a\x8c\x6a\xde\xf5\xd9\x7e\xec\xe9\x0c\xe3\x75\x8f\x29\x0b"; + const std::string COMMENT_UNDERLINE = + ";------------------------------------------------------------------"; +} + +using namespace Assimp; +using namespace FBX; + +namespace Assimp { + + // --------------------------------------------------------------------- + // Worker function for exporting a scene to binary FBX. + // Prototyped and registered in Exporter.cpp + void ExportSceneFBX ( + const char* pFile, + IOSystem* pIOSystem, + const aiScene* pScene, + const ExportProperties* pProperties + ){ + // initialize the exporter + FBXExporter exporter(pScene, pProperties); + + // perform binary export + exporter.ExportBinary(pFile, pIOSystem); + } + + // --------------------------------------------------------------------- + // Worker function for exporting a scene to ASCII FBX. + // Prototyped and registered in Exporter.cpp + void ExportSceneFBXA ( + const char* pFile, + IOSystem* pIOSystem, + const aiScene* pScene, + const ExportProperties* pProperties + ){ + // initialize the exporter + FBXExporter exporter(pScene, pProperties); + + // perform ascii export + exporter.ExportAscii(pFile, pIOSystem); + } + +} // end of namespace Assimp + +FBXExporter::FBXExporter ( + const aiScene* pScene, + const ExportProperties* pProperties +) + : mScene(pScene) + , mProperties(pProperties) +{ + // will probably need to determine UIDs, connections, etc here. + // basically anything that needs to be known + // before we start writing sections to the stream. +} + +void FBXExporter::ExportBinary ( + const char* pFile, + IOSystem* pIOSystem +){ + // remember that we're exporting in binary mode + binary = true; + + // we're not currently using these preferences, + // but clang will cry about it if we never touch it. + // TODO: some of these might be relevant to export + (void)mProperties; + + // open the indicated file for writing (in binary mode) + outfile.reset(pIOSystem->Open(pFile,"wb")); + if (!outfile) { + throw DeadlyExportError( + "could not open output .fbx file: " + std::string(pFile) + ); + } + + // first a binary-specific file header + WriteBinaryHeader(); + + // the rest of the file is in node entries. + // we have to serialize each entry before we write to the output, + // as the first thing we write is the byte offset of the _next_ entry. + // Either that or we can skip back to write the offset when we finish. + WriteAllNodes(); + + // finally we have a binary footer to the file + WriteBinaryFooter(); + + // explicitly release file pointer, + // so we don't have to rely on class destruction. + outfile.reset(); +} + +void FBXExporter::ExportAscii ( + const char* pFile, + IOSystem* pIOSystem +){ + // remember that we're exporting in ascii mode + binary = false; + + // open the indicated file for writing in text mode + outfile.reset(pIOSystem->Open(pFile,"wt")); + if (!outfile) { + throw DeadlyExportError( + "could not open output .fbx file: " + std::string(pFile) + ); + } + + // write the ascii header + WriteAsciiHeader(); + + // write all the sections + WriteAllNodes(); + + // make sure the file ends with a newline. + // note: if the file is opened in text mode, + // this should do the right cross-platform thing. + outfile->Write("\n", 1, 1); + + // explicitly release file pointer, + // so we don't have to rely on class destruction. + outfile.reset(); +} + +void FBXExporter::WriteAsciiHeader() +{ + // basically just a comment at the top of the file + std::stringstream head; + head << "; FBX " << EXPORT_VERSION_STR << " project file\n"; + head << "; Created by the Open Asset Import Library (Assimp)\n"; + head << "; http://assimp.org\n"; + head << "; -------------------------------------------------\n"; + const std::string ascii_header = head.str(); + outfile->Write(ascii_header.c_str(), ascii_header.size(), 1); +} + +void FBXExporter::WriteAsciiSectionHeader(const std::string& title) +{ + StreamWriterLE outstream(outfile); + std::stringstream s; + s << "\n\n; " << title << '\n'; + s << FBX::COMMENT_UNDERLINE << "\n"; + outstream.PutString(s.str()); +} + +void FBXExporter::WriteBinaryHeader() +{ + // first a specific sequence of 23 bytes, always the same + const char binary_header[24] = "Kaydara FBX Binary\x20\x20\x00\x1a\x00"; + outfile->Write(binary_header, 1, 23); + + // then FBX version number, "multiplied" by 1000, as little-endian uint32. + // so 7.3 becomes 7300 == 0x841C0000, 7.4 becomes 7400 == 0xE81C0000, etc + { + StreamWriterLE outstream(outfile); + outstream.PutU4(EXPORT_VERSION_INT); + } // StreamWriter destructor writes the data to the file + + // after this the node data starts immediately + // (probably with the FBXHEaderExtension node) +} + +void FBXExporter::WriteBinaryFooter() +{ + outfile->Write(NULL_RECORD.c_str(), NULL_RECORD.size(), 1); + + outfile->Write(GENERIC_FOOTID.c_str(), GENERIC_FOOTID.size(), 1); + + // here some padding is added for alignment to 16 bytes. + // if already aligned, the full 16 bytes is added. + size_t pos = outfile->Tell(); + size_t pad = 16 - (pos % 16); + for (size_t i = 0; i < pad; ++i) { + outfile->Write("\x00", 1, 1); + } + + // not sure what this is, but it seems to always be 0 in modern files + for (size_t i = 0; i < 4; ++i) { + outfile->Write("\x00", 1, 1); + } + + // now the file version again + { + StreamWriterLE outstream(outfile); + outstream.PutU4(EXPORT_VERSION_INT); + } // StreamWriter destructor writes the data to the file + + // and finally some binary footer added to all files + for (size_t i = 0; i < 120; ++i) { + outfile->Write("\x00", 1, 1); + } + outfile->Write(FOOT_MAGIC.c_str(), FOOT_MAGIC.size(), 1); +} + +void FBXExporter::WriteAllNodes () +{ + // header + // (and fileid, creation time, creator, if binary) + WriteHeaderExtension(); + + // global settings + WriteGlobalSettings(); + + // documents + WriteDocuments(); + + // references + WriteReferences(); + + // definitions + WriteDefinitions(); + + // objects + WriteObjects(); + + // connections + WriteConnections(); + + // WriteTakes? (deprecated since at least 2015 (fbx 7.4)) +} + +//FBXHeaderExtension top-level node +void FBXExporter::WriteHeaderExtension () +{ + if (!binary) { + // no title, follows directly from the top comment + } + FBX::Node n("FBXHeaderExtension"); + StreamWriterLE outstream(outfile); + int indent = 0; + + // begin node + n.Begin(outstream, binary, indent); + + // write properties + // (none) + + // finish properties + n.EndProperties(outstream, binary, indent, 0); + + // begin children + n.BeginChildren(outstream, binary, indent); + + indent = 1; + + // write child nodes + FBX::Node::WritePropertyNode( + "FBXHeaderVersion", int32_t(1003), outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "FBXVersion", int32_t(EXPORT_VERSION_INT), outstream, binary, indent + ); + if (binary) { + FBX::Node::WritePropertyNode( + "EncryptionType", int32_t(0), outstream, binary, indent + ); + } + + FBX::Node CreationTimeStamp("CreationTimeStamp"); + time_t rawtime; + time(&rawtime); + struct tm * now = localtime(&rawtime); + CreationTimeStamp.AddChild("Version", int32_t(1000)); + CreationTimeStamp.AddChild("Year", int32_t(now->tm_year + 1900)); + CreationTimeStamp.AddChild("Month", int32_t(now->tm_mon + 1)); + CreationTimeStamp.AddChild("Day", int32_t(now->tm_mday)); + CreationTimeStamp.AddChild("Hour", int32_t(now->tm_hour)); + CreationTimeStamp.AddChild("Minute", int32_t(now->tm_min)); + CreationTimeStamp.AddChild("Second", int32_t(now->tm_sec)); + CreationTimeStamp.AddChild("Millisecond", int32_t(0)); + CreationTimeStamp.Dump(outstream, binary, indent); + + std::stringstream creator; + creator << "Open Asset Import Library (Assimp) " << aiGetVersionMajor() + << "." << aiGetVersionMinor() << "." << aiGetVersionRevision(); + FBX::Node::WritePropertyNode( + "Creator", creator.str(), outstream, binary, indent + ); + + //FBX::Node sceneinfo("SceneInfo"); + //sceneinfo.AddProperty("GlobalInfo" + FBX::SEPARATOR + "SceneInfo"); + // not sure if any of this is actually needed, + // so just write an empty node for now. + //sceneinfo.Dump(outstream, binary, indent); + + indent = 0; + + // finish node + n.End(outstream, binary, indent, true); + + // that's it for FBXHeaderExtension... + if (!binary) { return; } + + // but binary files also need top-level FileID, CreationTime, Creator: + std::vector raw(GENERIC_FILEID.size()); + for (size_t i = 0; i < GENERIC_FILEID.size(); ++i) { + raw[i] = uint8_t(GENERIC_FILEID[i]); + } + FBX::Node::WritePropertyNode( + "FileId", raw, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "CreationTime", GENERIC_CTIME, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "Creator", creator.str(), outstream, binary, indent + ); +} + +void FBXExporter::WriteGlobalSettings () +{ + if (!binary) { + // no title, follows directly from the header extension + } + FBX::Node gs("GlobalSettings"); + gs.AddChild("Version", int32_t(1000)); + + FBX::Node p("Properties70"); + p.AddP70int("UpAxis", 1); + p.AddP70int("UpAxisSign", 1); + p.AddP70int("FrontAxis", 2); + p.AddP70int("FrontAxisSign", 1); + p.AddP70int("CoordAxis", 0); + p.AddP70int("CoordAxisSign", 1); + p.AddP70int("OriginalUpAxis", 1); + p.AddP70int("OriginalUpAxisSign", 1); + p.AddP70double("UnitScaleFactor", 1.0); + p.AddP70double("OriginalUnitScaleFactor", 1.0); + p.AddP70color("AmbientColor", 0.0, 0.0, 0.0); + p.AddP70string("DefaultCamera", "Producer Perspective"); + p.AddP70enum("TimeMode", 11); + p.AddP70enum("TimeProtocol", 2); + p.AddP70enum("SnapOnFrameMode", 0); + p.AddP70time("TimeSpanStart", 0); // TODO: animation support + p.AddP70time("TimeSpanStop", FBX::SECOND); // TODO: animation support + p.AddP70double("CustomFrameRate", -1.0); + p.AddP70("TimeMarker", "Compound", "", ""); // not sure what this is + p.AddP70int("CurrentTimeMarker", -1); + gs.AddChild(p); + + gs.Dump(outfile, binary, 0); +} + +void FBXExporter::WriteDocuments () +{ + if (!binary) { + WriteAsciiSectionHeader("Documents Description"); + } + + // not sure what the use of multiple documents would be, + // or whether any end-application supports it + FBX::Node docs("Documents"); + docs.AddChild("Count", int32_t(1)); + FBX::Node doc("Document"); + + // generate uid + int64_t uid = generate_uid(); + doc.AddProperties(uid, "", "Scene"); + FBX::Node p("Properties70"); + p.AddP70("SourceObject", "object", "", ""); // what is this even for? + p.AddP70string("ActiveAnimStackName", ""); // should do this properly? + doc.AddChild(p); + + // UID for root node in scene heirarchy. + // always set to 0 in the case of a single document. + // not sure what happens if more than one document exists, + // but that won't matter to us as we're exporting a single scene. + doc.AddChild("RootNode", int64_t(0)); + + docs.AddChild(doc); + docs.Dump(outfile, binary, 0); +} + +void FBXExporter::WriteReferences () +{ + if (!binary) { + WriteAsciiSectionHeader("Document References"); + } + // always empty for now. + // not really sure what this is for. + FBX::Node n("References"); + n.force_has_children = true; + n.Dump(outfile, binary, 0); +} + + +// --------------------------------------------------------------- +// some internal helper functions used for writing the definitions +// (before any actual data is written) +// --------------------------------------------------------------- + +size_t count_nodes(const aiNode* n) { + size_t count = 1; + for (size_t i = 0; i < n->mNumChildren; ++i) { + count += count_nodes(n->mChildren[i]); + } + return count; +} + +bool has_phong_mat(const aiScene* scene) +{ + // just search for any material with a shininess exponent + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + float shininess = 0; + mat->Get(AI_MATKEY_SHININESS, shininess); + if (shininess > 0) { + return true; + } + } + return false; +} + +size_t count_images(const aiScene* scene) { + std::unordered_set images; + aiString texpath; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + const aiTextureType textype = static_cast(tt); + const size_t texcount = mat->GetTextureCount(textype); + for (unsigned int j = 0; j < texcount; ++j) { + mat->GetTexture(textype, j, &texpath); + images.insert(std::string(texpath.C_Str())); + } + } + } + return images.size(); +} + +size_t count_textures(const aiScene* scene) { + size_t count = 0; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + aiMaterial* mat = scene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + // TODO: handle layered textures + if (mat->GetTextureCount(static_cast(tt)) > 0) { + count += 1; + } + } + } + return count; +} + +size_t count_deformers(const aiScene* scene) { + size_t count = 0; + for (size_t i = 0; i < scene->mNumMeshes; ++i) { + const size_t n = scene->mMeshes[i]->mNumBones; + if (n) { + // 1 main deformer, 1 subdeformer per bone + count += n + 1; + } + } + return count; +} + +void FBXExporter::WriteDefinitions () +{ + // basically this is just bookkeeping: + // determining how many of each type of object there are + // and specifying the base properties to use when otherwise unspecified. + + // ascii section header + if (!binary) { + WriteAsciiSectionHeader("Object definitions"); + } + + // we need to count the objects + int32_t count; + int32_t total_count = 0; + + // and store them + std::vector object_nodes; + FBX::Node n, pt, p; + + // GlobalSettings + // this seems to always be here in Maya exports + n = FBX::Node("ObjectType", "GlobalSettings"); + count = 1; + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + + // AnimationStack / FbxAnimStack + // this seems to always be here in Maya exports, + // but no harm seems to come of leaving it out. + count = mScene->mNumAnimations; + if (count) { + n = FBX::Node("ObjectType", "AnimationStack"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxAnimStack"); + p = FBX::Node("Properties70"); + p.AddP70string("Description", ""); + p.AddP70time("LocalStart", 0); + p.AddP70time("LocalStop", 0); + p.AddP70time("ReferenceStart", 0); + p.AddP70time("ReferenceStop", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // AnimationLayer / FbxAnimLayer + // this seems to always be here in Maya exports, + // but no harm seems to come of leaving it out. + // Assimp doesn't support animation layers, + // so there will be one per aiAnimation + count = mScene->mNumAnimations; + if (count) { + n = FBX::Node("ObjectType", "AnimationLayer"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FBXAnimLayer"); + p = FBX::Node("Properties70"); + p.AddP70("Weight", "Number", "", "A", double(100)); + p.AddP70bool("Mute", 0); + p.AddP70bool("Solo", 0); + p.AddP70bool("Lock", 0); + p.AddP70color("Color", 0.8, 0.8, 0.8); + p.AddP70("BlendMode", "enum", "", "", int32_t(0)); + p.AddP70("RotationAccumulationMode", "enum", "", "", int32_t(0)); + p.AddP70("ScaleAccumulationMode", "enum", "", "", int32_t(0)); + p.AddP70("BlendModeBypass", "ULongLong", "", "", int64_t(0)); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // NodeAttribute + // this is completely absurd. + // there can only be one "NodeAttribute" template, + // but FbxSkeleton, FbxCamera, FbxLight all are "NodeAttributes". + // so if only one exists we should set the template for that, + // otherwise... we just pick one :/. + // the others have to set all their properties every instance, + // because there's no template. + count = 1; // TODO: select properly + if (count) { + // FbxSkeleton + n = FBX::Node("ObjectType", "NodeAttribute"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxSkeleton"); + p = FBX::Node("Properties70"); + p.AddP70color("Color", 0.8, 0.8, 0.8); + p.AddP70double("Size", 33.333333333333); + p.AddP70("LimbLength", "double", "Number", "H", double(1)); + // note: not sure what the "H" flag is for - hidden? + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Model / FbxNode + // <~~ node heirarchy + count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) + if (count) { + n = FBX::Node("ObjectType", "Model"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxNode"); + p = FBX::Node("Properties70"); + p.AddP70enum("QuaternionInterpolate", 0); + p.AddP70vector("RotationOffset", 0.0, 0.0, 0.0); + p.AddP70vector("RotationPivot", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingOffset", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingPivot", 0.0, 0.0, 0.0); + p.AddP70bool("TranslationActive", 0); + p.AddP70vector("TranslationMin", 0.0, 0.0, 0.0); + p.AddP70vector("TranslationMax", 0.0, 0.0, 0.0); + p.AddP70bool("TranslationMinX", 0); + p.AddP70bool("TranslationMinY", 0); + p.AddP70bool("TranslationMinZ", 0); + p.AddP70bool("TranslationMaxX", 0); + p.AddP70bool("TranslationMaxY", 0); + p.AddP70bool("TranslationMaxZ", 0); + p.AddP70enum("RotationOrder", 0); + p.AddP70bool("RotationSpaceForLimitOnly", 0); + p.AddP70double("RotationStiffnessX", 0.0); + p.AddP70double("RotationStiffnessY", 0.0); + p.AddP70double("RotationStiffnessZ", 0.0); + p.AddP70double("AxisLen", 10.0); + p.AddP70vector("PreRotation", 0.0, 0.0, 0.0); + p.AddP70vector("PostRotation", 0.0, 0.0, 0.0); + p.AddP70bool("RotationActive", 0); + p.AddP70vector("RotationMin", 0.0, 0.0, 0.0); + p.AddP70vector("RotationMax", 0.0, 0.0, 0.0); + p.AddP70bool("RotationMinX", 0); + p.AddP70bool("RotationMinY", 0); + p.AddP70bool("RotationMinZ", 0); + p.AddP70bool("RotationMaxX", 0); + p.AddP70bool("RotationMaxY", 0); + p.AddP70bool("RotationMaxZ", 0); + p.AddP70enum("InheritType", 0); + p.AddP70bool("ScalingActive", 0); + p.AddP70vector("ScalingMin", 0.0, 0.0, 0.0); + p.AddP70vector("ScalingMax", 1.0, 1.0, 1.0); + p.AddP70bool("ScalingMinX", 0); + p.AddP70bool("ScalingMinY", 0); + p.AddP70bool("ScalingMinZ", 0); + p.AddP70bool("ScalingMaxX", 0); + p.AddP70bool("ScalingMaxY", 0); + p.AddP70bool("ScalingMaxZ", 0); + p.AddP70vector("GeometricTranslation", 0.0, 0.0, 0.0); + p.AddP70vector("GeometricRotation", 0.0, 0.0, 0.0); + p.AddP70vector("GeometricScaling", 1.0, 1.0, 1.0); + p.AddP70double("MinDampRangeX", 0.0); + p.AddP70double("MinDampRangeY", 0.0); + p.AddP70double("MinDampRangeZ", 0.0); + p.AddP70double("MaxDampRangeX", 0.0); + p.AddP70double("MaxDampRangeY", 0.0); + p.AddP70double("MaxDampRangeZ", 0.0); + p.AddP70double("MinDampStrengthX", 0.0); + p.AddP70double("MinDampStrengthY", 0.0); + p.AddP70double("MinDampStrengthZ", 0.0); + p.AddP70double("MaxDampStrengthX", 0.0); + p.AddP70double("MaxDampStrengthY", 0.0); + p.AddP70double("MaxDampStrengthZ", 0.0); + p.AddP70double("PreferedAngleX", 0.0); + p.AddP70double("PreferedAngleY", 0.0); + p.AddP70double("PreferedAngleZ", 0.0); + p.AddP70("LookAtProperty", "object", "", ""); + p.AddP70("UpVectorProperty", "object", "", ""); + p.AddP70bool("Show", 1); + p.AddP70bool("NegativePercentShapeSupport", 1); + p.AddP70int("DefaultAttributeIndex", -1); + p.AddP70bool("Freeze", 0); + p.AddP70bool("LODBox", 0); + p.AddP70( + "Lcl Translation", "Lcl Translation", "", "A", + double(0), double(0), double(0) + ); + p.AddP70( + "Lcl Rotation", "Lcl Rotation", "", "A", + double(0), double(0), double(0) + ); + p.AddP70( + "Lcl Scaling", "Lcl Scaling", "", "A", + double(1), double(1), double(1) + ); + p.AddP70("Visibility", "Visibility", "", "A", double(1)); + p.AddP70( + "Visibility Inheritance", "Visibility Inheritance", "", "", + int32_t(1) + ); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Geometry / FbxMesh + // <~~ aiMesh + count = mScene->mNumMeshes; + if (count) { + n = FBX::Node("ObjectType", "Geometry"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxMesh"); + p = FBX::Node("Properties70"); + p.AddP70color("Color", 0, 0, 0); + p.AddP70vector("BBoxMin", 0, 0, 0); + p.AddP70vector("BBoxMax", 0, 0, 0); + p.AddP70bool("Primary Visibility", 1); + p.AddP70bool("Casts Shadows", 1); + p.AddP70bool("Receive Shadows", 1); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Material / FbxSurfacePhong, FbxSurfaceLambert, FbxSurfaceMaterial + // <~~ aiMaterial + // basically if there's any phong material this is defined as phong, + // and otherwise lambert. + // More complex materials cause a bare-bones FbxSurfaceMaterial definition + // and are treated specially, as they're not really supported by FBX. + // TODO: support Maya's Stingray PBS material + count = mScene->mNumMaterials; + if (count) { + bool has_phong = has_phong_mat(mScene); + n = FBX::Node("ObjectType", "Material"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate"); + if (has_phong) { + pt.AddProperty("FbxSurfacePhong"); + } else { + pt.AddProperty("FbxSurfaceLambert"); + } + p = FBX::Node("Properties70"); + if (has_phong) { + p.AddP70string("ShadingModel", "Phong"); + } else { + p.AddP70string("ShadingModel", "Lambert"); + } + p.AddP70bool("MultiLayer", 0); + p.AddP70colorA("EmissiveColor", 0.0, 0.0, 0.0); + p.AddP70numberA("EmissiveFactor", 1.0); + p.AddP70colorA("AmbientColor", 0.2, 0.2, 0.2); + p.AddP70numberA("AmbientFactor", 1.0); + p.AddP70colorA("DiffuseColor", 0.8, 0.8, 0.8); + p.AddP70numberA("DiffuseFactor", 1.0); + p.AddP70vector("Bump", 0.0, 0.0, 0.0); + p.AddP70vector("NormalMap", 0.0, 0.0, 0.0); + p.AddP70double("BumpFactor", 1.0); + p.AddP70colorA("TransparentColor", 0.0, 0.0, 0.0); + p.AddP70numberA("TransparencyFactor", 0.0); + p.AddP70color("DisplacementColor", 0.0, 0.0, 0.0); + p.AddP70double("DisplacementFactor", 1.0); + p.AddP70color("VectorDisplacementColor", 0.0, 0.0, 0.0); + p.AddP70double("VectorDisplacementFactor", 1.0); + if (has_phong) { + p.AddP70colorA("SpecularColor", 0.2, 0.2, 0.2); + p.AddP70numberA("SpecularFactor", 1.0); + p.AddP70numberA("ShininessExponent", 20.0); + p.AddP70colorA("ReflectionColor", 0.0, 0.0, 0.0); + p.AddP70numberA("ReflectionFactor", 1.0); + } + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Video / FbxVideo + // one for each image file. + count = int32_t(count_images(mScene)); + if (count) { + n = FBX::Node("ObjectType", "Video"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxVideo"); + p = FBX::Node("Properties70"); + p.AddP70bool("ImageSequence", 0); + p.AddP70int("ImageSequenceOffset", 0); + p.AddP70double("FrameRate", 0.0); + p.AddP70int("LastFrame", 0); + p.AddP70int("Width", 0); + p.AddP70int("Height", 0); + p.AddP70("Path", "KString", "XRefUrl", "", ""); + p.AddP70int("StartFrame", 0); + p.AddP70int("StopFrame", 0); + p.AddP70double("PlaySpeed", 0.0); + p.AddP70time("Offset", 0); + p.AddP70enum("InterlaceMode", 0); + p.AddP70bool("FreeRunning", 0); + p.AddP70bool("Loop", 0); + p.AddP70enum("AccessMode", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // Texture / FbxFileTexture + // <~~ aiTexture + count = int32_t(count_textures(mScene)); + if (count) { + n = FBX::Node("ObjectType", "Texture"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxFileTexture"); + p = FBX::Node("Properties70"); + p.AddP70enum("TextureTypeUse", 0); + p.AddP70numberA("Texture alpha", 1.0); + p.AddP70enum("CurrentMappingType", 0); + p.AddP70enum("WrapModeU", 0); + p.AddP70enum("WrapModeV", 0); + p.AddP70bool("UVSwap", 0); + p.AddP70bool("PremultiplyAlpha", 1); + p.AddP70vectorA("Translation", 0.0, 0.0, 0.0); + p.AddP70vectorA("Rotation", 0.0, 0.0, 0.0); + p.AddP70vectorA("Scaling", 1.0, 1.0, 1.0); + p.AddP70vector("TextureRotationPivot", 0.0, 0.0, 0.0); + p.AddP70vector("TextureScalingPivot", 0.0, 0.0, 0.0); + p.AddP70enum("CurrentTextureBlendMode", 1); + p.AddP70string("UVSet", "default"); + p.AddP70bool("UseMaterial", 0); + p.AddP70bool("UseMipMap", 0); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // AnimationCurveNode / FbxAnimCurveNode + count = mScene->mNumAnimations * 3; + if (count) { + n = FBX::Node("ObjectType", "AnimationCurveNode"); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", "FbxAnimCurveNode"); + p = FBX::Node("Properties70"); + p.AddP70("d", "Compound", "", ""); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // AnimationCurve / FbxAnimCurve + count = mScene->mNumAnimations * 9; + if (count) { + n = FBX::Node("ObjectType", "AnimationCurve"); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + + // Pose + count = 0; + for (size_t i = 0; i < mScene->mNumMeshes; ++i) { + aiMesh* mesh = mScene->mMeshes[i]; + if (mesh->HasBones()) { ++count; } + } + if (count) { + n = FBX::Node("ObjectType", "Pose"); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + + // Deformer + count = int32_t(count_deformers(mScene)); + if (count) { + n = FBX::Node("ObjectType", "Deformer"); + n.AddChild("Count", count); + object_nodes.push_back(n); + total_count += count; + } + + // (template) + count = 0; + if (count) { + n = FBX::Node("ObjectType", ""); + n.AddChild("Count", count); + pt = FBX::Node("PropertyTemplate", ""); + p = FBX::Node("Properties70"); + pt.AddChild(p); + n.AddChild(pt); + object_nodes.push_back(n); + total_count += count; + } + + // now write it all + FBX::Node defs("Definitions"); + defs.AddChild("Version", int32_t(100)); + defs.AddChild("Count", int32_t(total_count)); + for (auto &n : object_nodes) { defs.AddChild(n); } + defs.Dump(outfile, binary, 0); +} + + +// ------------------------------------------------------------------- +// some internal helper functions used for writing the objects section +// (which holds the actual data) +// ------------------------------------------------------------------- + +aiNode* get_node_for_mesh(unsigned int meshIndex, aiNode* node) +{ + for (size_t i = 0; i < node->mNumMeshes; ++i) { + if (node->mMeshes[i] == meshIndex) { + return node; + } + } + for (size_t i = 0; i < node->mNumChildren; ++i) { + aiNode* ret = get_node_for_mesh(meshIndex, node->mChildren[i]); + if (ret) { return ret; } + } + return nullptr; +} + +aiMatrix4x4 get_world_transform(const aiNode* node, const aiScene* scene) +{ + std::vector node_chain; + while (node != scene->mRootNode) { + node_chain.push_back(node); + node = node->mParent; + } + aiMatrix4x4 transform; + for (auto n = node_chain.rbegin(); n != node_chain.rend(); ++n) { + transform *= (*n)->mTransformation; + } + return transform; +} + +int64_t to_ktime(double ticks, const aiAnimation* anim) { + if (anim->mTicksPerSecond <= 0) { + return ticks * FBX::SECOND; + } + return (ticks / anim->mTicksPerSecond) * FBX::SECOND; +} + +void FBXExporter::WriteObjects () +{ + if (!binary) { + WriteAsciiSectionHeader("Object properties"); + } + // numbers should match those given in definitions! make sure to check + StreamWriterLE outstream(outfile); + FBX::Node object_node("Objects"); + int indent = 0; + object_node.Begin(outstream, binary, indent); + object_node.EndProperties(outstream, binary, indent); + object_node.BeginChildren(outstream, binary, indent); + + // geometry (aiMesh) + mesh_uids.clear(); + indent = 1; + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + // it's all about this mesh + aiMesh* m = mScene->mMeshes[mi]; + + // start the node record + FBX::Node n("Geometry"); + int64_t uid = generate_uid(); + mesh_uids.push_back(uid); + n.AddProperty(uid); + n.AddProperty(FBX::SEPARATOR + "Geometry"); + n.AddProperty("Mesh"); + n.Begin(outstream, binary, indent); + n.DumpProperties(outstream, binary, indent); + n.EndProperties(outstream, binary, indent); + n.BeginChildren(outstream, binary, indent); + indent = 2; + + // output vertex data - each vertex should be unique (probably) + std::vector flattened_vertices; + // index of original vertex in vertex data vector + std::vector vertex_indices; + // map of vertex value to its index in the data vector + std::map index_by_vertex_value; + int32_t index = 0; + for (size_t vi = 0; vi < m->mNumVertices; ++vi) { + aiVector3D vtx = m->mVertices[vi]; + auto elem = index_by_vertex_value.find(vtx); + if (elem == index_by_vertex_value.end()) { + vertex_indices.push_back(index); + index_by_vertex_value[vtx] = index; + flattened_vertices.push_back(vtx[0]); + flattened_vertices.push_back(vtx[1]); + flattened_vertices.push_back(vtx[2]); + ++index; + } else { + vertex_indices.push_back(int32_t(elem->second)); + } + } + FBX::Node::WritePropertyNode( + "Vertices", flattened_vertices, outstream, binary, indent + ); + + // output polygon data as a flattened array of vertex indices. + // the last vertex index of each polygon is negated and - 1 + std::vector polygon_data; + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices - 1; ++pvi) { + polygon_data.push_back(vertex_indices[f.mIndices[pvi]]); + } + polygon_data.push_back( + -1 - vertex_indices[f.mIndices[f.mNumIndices-1]] + ); + } + FBX::Node::WritePropertyNode( + "PolygonVertexIndex", polygon_data, outstream, binary, indent + ); + + // here could be edges but they're insane. + // it's optional anyway, so let's ignore it. + + FBX::Node::WritePropertyNode( + "GeometryVersion", int32_t(124), outstream, binary, indent + ); + + // normals, if any + if (m->HasNormals()) { + FBX::Node normals("LayerElementNormal", int32_t(0)); + normals.Begin(outstream, binary, indent); + normals.DumpProperties(outstream, binary, indent); + normals.EndProperties(outstream, binary, indent); + normals.BeginChildren(outstream, binary, indent); + indent = 3; + FBX::Node::WritePropertyNode( + "Version", int32_t(101), outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "Name", "", outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "MappingInformationType", "ByPolygonVertex", + outstream, binary, indent + ); + // TODO: vertex-normals or indexed normals when appropriate + FBX::Node::WritePropertyNode( + "ReferenceInformationType", "Direct", + outstream, binary, indent + ); + std::vector normal_data; + normal_data.reserve(3 * polygon_data.size()); + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { + const aiVector3D &n = m->mNormals[f.mIndices[pvi]]; + normal_data.push_back(n.x); + normal_data.push_back(n.y); + normal_data.push_back(n.z); + } + } + FBX::Node::WritePropertyNode( + "Normals", normal_data, outstream, binary, indent + ); + // note: version 102 has a NormalsW also... not sure what it is, + // so we can stick with version 101 for now. + indent = 2; + normals.End(outstream, binary, indent, true); + } + + // uvs, if any + for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) { + if (m->mNumUVComponents[uvi] > 2) { + // FBX only supports 2-channel UV maps... + // or at least i'm not sure how to indicate a different number + std::stringstream err; + err << "Only 2-channel UV maps supported by FBX,"; + err << " but mesh " << mi; + if (m->mName.length) { + err << " (" << m->mName.C_Str() << ")"; + } + err << " UV map " << uvi; + err << " has " << m->mNumUVComponents[uvi]; + err << " components! Data will be preserved,"; + err << " but may be incorrectly interpreted on load."; + DefaultLogger::get()->warn(err.str()); + } + FBX::Node uv("LayerElementUV", int32_t(uvi)); + uv.Begin(outstream, binary, indent); + uv.DumpProperties(outstream, binary, indent); + uv.EndProperties(outstream, binary, indent); + uv.BeginChildren(outstream, binary, indent); + indent = 3; + FBX::Node::WritePropertyNode( + "Version", int32_t(101), outstream, binary, indent + ); + // it doesn't seem like assimp keeps the uv map name, + // so just leave it blank. + FBX::Node::WritePropertyNode( + "Name", "", outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "MappingInformationType", "ByPolygonVertex", + outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "ReferenceInformationType", "IndexToDirect", + outstream, binary, indent + ); + + std::vector uv_data; + std::vector uv_indices; + std::map index_by_uv; + int32_t index = 0; + for (size_t fi = 0; fi < m->mNumFaces; ++fi) { + const aiFace &f = m->mFaces[fi]; + for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) { + const aiVector3D &uv = + m->mTextureCoords[uvi][f.mIndices[pvi]]; + auto elem = index_by_uv.find(uv); + if (elem == index_by_uv.end()) { + index_by_uv[uv] = index; + uv_indices.push_back(index); + for (unsigned int x = 0; x < m->mNumUVComponents[uvi]; ++x) { + uv_data.push_back(uv[x]); + } + ++index; + } else { + uv_indices.push_back(elem->second); + } + } + } + FBX::Node::WritePropertyNode( + "UV", uv_data, outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "UVIndex", uv_indices, outstream, binary, indent + ); + indent = 2; + uv.End(outstream, binary, indent, true); + } + + // i'm not really sure why this material section exists, + // as the material is linked via "Connections". + // it seems to always have the same "0" value. + FBX::Node mat("LayerElementMaterial", int32_t(0)); + mat.AddChild("Version", int32_t(101)); + mat.AddChild("Name", ""); + mat.AddChild("MappingInformationType", "AllSame"); + mat.AddChild("ReferenceInformationType", "IndexToDirect"); + std::vector mat_indices = {0}; + mat.AddChild("Materials", mat_indices); + mat.Dump(outstream, binary, indent); + + // finally we have the layer specifications, + // which select the normals / UV set / etc to use. + // TODO: handle multiple uv sets correctly? + FBX::Node layer("Layer", int32_t(0)); + layer.AddChild("Version", int32_t(100)); + FBX::Node le("LayerElement"); + le.AddChild("Type", "LayerElementNormal"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + le = FBX::Node("LayerElement"); + le.AddChild("Type", "LayerElementMaterial"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + le = FBX::Node("LayerElement"); + le.AddChild("Type", "LayerElementUV"); + le.AddChild("TypedIndex", int32_t(0)); + layer.AddChild(le); + layer.Dump(outstream, binary, indent); + + // finish the node record + indent = 1; + n.End(outstream, binary, indent, true); + } + + // aiMaterial + material_uids.clear(); + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + // it's all about this material + aiMaterial* m = mScene->mMaterials[i]; + + // these are used to recieve material data + float f; aiColor3D c; + + // start the node record + FBX::Node n("Material"); + + int64_t uid = generate_uid(); + material_uids.push_back(uid); + n.AddProperty(uid); + + aiString name; + m->Get(AI_MATKEY_NAME, name); + n.AddProperty(name.C_Str() + FBX::SEPARATOR + "Material"); + + n.AddProperty(""); + + n.AddChild("Version", int32_t(102)); + f = 0; + m->Get(AI_MATKEY_SHININESS, f); + bool phong = (f > 0); + if (phong) { + n.AddChild("ShadingModel", "phong"); + } else { + n.AddChild("ShadingModel", "lambert"); + } + n.AddChild("MultiLayer", int32_t(0)); + + FBX::Node p("Properties70"); + + // materials exported using the FBX SDK have two sets of fields. + // there are the properties specified in the PropertyTemplate, + // which are those supported by the modernFBX SDK, + // and an extra set of properties with simpler names. + // The extra properties are a legacy material system from pre-2009. + // + // In the modern system, each property has "color" and "factor". + // Generally the interpretation of these seems to be + // that the colour is multiplied by the factor before use, + // but this is not always clear-cut. + // + // Usually assimp only stores the colour, + // so we can just leave the factors at the default "1.0". + + // first we can export the "standard" properties + if (m->Get(AI_MATKEY_COLOR_AMBIENT, c) == aiReturn_SUCCESS) { + p.AddP70colorA("AmbientColor", c.r, c.g, c.b); + //p.AddP70numberA("AmbientFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_DIFFUSE, c) == aiReturn_SUCCESS) { + p.AddP70colorA("DiffuseColor", c.r, c.g, c.b); + //p.AddP70numberA("DiffuseFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) { + // "TransparentColor" / "TransparencyFactor"... + // thanks FBX, for your insightful interpretation of consistency + p.AddP70colorA("TransparentColor", c.r, c.g, c.b); + // TransparencyFactor defaults to 0.0, so set it to 1.0. + // note: Maya always sets this to 1.0, + // so we can't use it sensibly as "Opacity". + // In stead we rely on the legacy "Opacity" value, below. + // Blender also relies on "Opacity" not "TransparencyFactor", + // probably for a similar reason. + p.AddP70numberA("TransparencyFactor", 1.0); + } + if (m->Get(AI_MATKEY_COLOR_REFLECTIVE, c) == aiReturn_SUCCESS) { + p.AddP70colorA("ReflectionColor", c.r, c.g, c.b); + } + if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ReflectionFactor", f); + } + if (phong) { + if (m->Get(AI_MATKEY_COLOR_SPECULAR, c) == aiReturn_SUCCESS) { + p.AddP70colorA("SpecularColor", c.r, c.g, c.b); + } + if (m->Get(AI_MATKEY_SHININESS_STRENGTH, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ShininessFactor", f); + } + if (m->Get(AI_MATKEY_SHININESS, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ShininessExponent", f); + } + if (m->Get(AI_MATKEY_REFLECTIVITY, f) == aiReturn_SUCCESS) { + p.AddP70numberA("ReflectionFactor", f); + } + } + + // Now the legacy system. + // For safety let's include it. + // thrse values don't exist in the property template, + // and usualy are completely ignored when loading. + // One notable exception is the "Opacity" property, + // which Blender uses as (1.0 - alpha). + c.r = 0.0f; c.g = 0.0f; c.b = 0.0f; + m->Get(AI_MATKEY_COLOR_EMISSIVE, c); + p.AddP70vector("Emissive", c.r, c.g, c.b); + c.r = 0.2f; c.g = 0.2f; c.b = 0.2f; + m->Get(AI_MATKEY_COLOR_AMBIENT, c); + p.AddP70vector("Ambient", c.r, c.g, c.b); + c.r = 0.8f; c.g = 0.8f; c.b = 0.8f; + m->Get(AI_MATKEY_COLOR_DIFFUSE, c); + p.AddP70vector("Diffuse", c.r, c.g, c.b); + // The FBX SDK determines "Opacity" from transparency colour (RGB) + // and factor (F) as: O = (1.0 - F * ((R + G + B) / 3)). + // However we actually have an opacity value, + // so we should take it from AI_MATKEY_OPACITY if possible. + // It might make more sense to use TransparencyFactor, + // but Blender actually loads "Opacity" correctly, so let's use it. + f = 1.0f; + if (m->Get(AI_MATKEY_COLOR_TRANSPARENT, c) == aiReturn_SUCCESS) { + f = 1.0f - ((c.r + c.g + c.b) / 3.0f); + } + m->Get(AI_MATKEY_OPACITY, f); + p.AddP70double("Opacity", f); + if (phong) { + // specular color is multiplied by shininess_strength + c.r = 0.2f; c.g = 0.2f; c.b = 0.2f; + m->Get(AI_MATKEY_COLOR_SPECULAR, c); + f = 1.0f; + m->Get(AI_MATKEY_SHININESS_STRENGTH, f); + p.AddP70vector("Specular", f*c.r, f*c.g, f*c.b); + f = 20.0f; + m->Get(AI_MATKEY_SHININESS, f); + p.AddP70double("Shininess", f); + // Legacy "Reflectivity" is F*F*((R+G+B)/3), + // where F is the proportion of light reflected (AKA reflectivity), + // and RGB is the reflective colour of the material. + // No idea why, but we might as well set it the same way. + f = 0.0f; + m->Get(AI_MATKEY_REFLECTIVITY, f); + c.r = 1.0f, c.g = 1.0f, c.b = 1.0f; + m->Get(AI_MATKEY_COLOR_REFLECTIVE, c); + p.AddP70double("Reflectivity", f*f*((c.r+c.g+c.b)/3.0)); + } + + n.AddChild(p); + + n.Dump(outstream, binary, indent); + } + + // we need to look up all the images we're using, + // so we can generate uids, and eliminate duplicates. + std::map uid_by_image; + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + aiString texpath; + aiMaterial* mat = mScene->mMaterials[i]; + for ( + size_t tt = aiTextureType_DIFFUSE; + tt < aiTextureType_UNKNOWN; + ++tt + ){ + const aiTextureType textype = static_cast(tt); + const size_t texcount = mat->GetTextureCount(textype); + for (size_t j = 0; j < texcount; ++j) { + mat->GetTexture(textype, (unsigned int)j, &texpath); + const std::string texstring = texpath.C_Str(); + auto elem = uid_by_image.find(texstring); + if (elem == uid_by_image.end()) { + uid_by_image[texstring] = generate_uid(); + } + } + } + } + + // FbxVideo - stores images used by textures. + for (const auto &it : uid_by_image) { + if (it.first.compare(0, 1, "*") == 0) { + // TODO: embedded textures + continue; + } + FBX::Node n("Video"); + const int64_t& uid = it.second; + const std::string name = ""; // TODO: ... name??? + n.AddProperties(uid, name + FBX::SEPARATOR + "Video", "Clip"); + n.AddChild("Type", "Clip"); + FBX::Node p("Properties70"); + // TODO: get full path... relative path... etc... ugh... + // for now just use the same path for everything, + // and hopefully one of them will work out. + const std::string& path = it.first; + p.AddP70("Path", "KString", "XRefUrl", "", path); + n.AddChild(p); + n.AddChild("UseMipMap", int32_t(0)); + n.AddChild("Filename", path); + n.AddChild("RelativeFilename", path); + n.Dump(outstream, binary, indent); + } + + // Textures + // referenced by material_index/texture_type pairs. + std::map,int64_t> texture_uids; + const std::map prop_name_by_tt = { + {aiTextureType_DIFFUSE, "DiffuseColor"}, + {aiTextureType_SPECULAR, "SpecularColor"}, + {aiTextureType_AMBIENT, "AmbientColor"}, + {aiTextureType_EMISSIVE, "EmissiveColor"}, + {aiTextureType_HEIGHT, "Bump"}, + {aiTextureType_NORMALS, "NormalMap"}, + {aiTextureType_SHININESS, "ShininessExponent"}, + {aiTextureType_OPACITY, "TransparentColor"}, + {aiTextureType_DISPLACEMENT, "DisplacementColor"}, + //{aiTextureType_LIGHTMAP, "???"}, + {aiTextureType_REFLECTION, "ReflectionColor"} + //{aiTextureType_UNKNOWN, ""} + }; + for (size_t i = 0; i < mScene->mNumMaterials; ++i) { + // textures are attached to materials + aiMaterial* mat = mScene->mMaterials[i]; + int64_t material_uid = material_uids[i]; + + for ( + size_t j = aiTextureType_DIFFUSE; + j < aiTextureType_UNKNOWN; + ++j + ) { + const aiTextureType tt = static_cast(j); + size_t n = mat->GetTextureCount(tt); + + if (n < 1) { // no texture of this type + continue; + } + + if (n > 1) { + // TODO: multilayer textures + std::stringstream err; + err << "Multilayer textures not supported (for now),"; + err << " skipping texture type " << j; + err << " of material " << i; + DefaultLogger::get()->warn(err.str()); + } + + // get image path for this (single-image) texture + aiString tpath; + if (mat->GetTexture(tt, 0, &tpath) != aiReturn_SUCCESS) { + std::stringstream err; + err << "Failed to get texture 0 for texture of type " << tt; + err << " on material " << i; + err << ", however GetTextureCount returned 1."; + throw DeadlyExportError(err.str()); + } + const std::string texture_path(tpath.C_Str()); + + // get connected image uid + auto elem = uid_by_image.find(texture_path); + if (elem == uid_by_image.end()) { + // this should never happen + std::stringstream err; + err << "Failed to find video element for texture with path"; + err << " \"" << texture_path << "\""; + err << ", type " << j << ", material " << i; + throw DeadlyExportError(err.str()); + } + const int64_t image_uid = elem->second; + + // get the name of the material property to connect to + auto elem2 = prop_name_by_tt.find(tt); + if (elem2 == prop_name_by_tt.end()) { + // don't know how to handle this type of texture, + // so skip it. + std::stringstream err; + err << "Not sure how to handle texture of type " << j; + err << " on material " << i; + err << ", skipping..."; + DefaultLogger::get()->warn(err.str()); + continue; + } + const std::string& prop_name = elem2->second; + + // generate a uid for this texture + const int64_t texture_uid = generate_uid(); + + // link the texture to the material + connections.emplace_back( + "C", "OP", texture_uid, material_uid, prop_name + ); + + // link the image data to the texture + connections.emplace_back("C", "OO", image_uid, texture_uid); + + // now write the actual texture node + FBX::Node tnode("Texture"); + // TODO: some way to determine texture name? + const std::string texture_name = "" + FBX::SEPARATOR + "Texture"; + tnode.AddProperties(texture_uid, texture_name, ""); + // there really doesn't seem to be a better type than this: + tnode.AddChild("Type", "TextureVideoClip"); + tnode.AddChild("Version", int32_t(202)); + tnode.AddChild("TextureName", texture_name); + FBX::Node p("Properties70"); + p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify + //p.AddP70string("UVSet", ""); // TODO: how should this work? + p.AddP70bool("UseMaterial", 1); + tnode.AddChild(p); + // can't easily detrmine which texture path will be correct, + // so just store what we have in every field. + // these being incorrect is a common problem with FBX anyway. + tnode.AddChild("FileName", texture_path); + tnode.AddChild("RelativeFilename", texture_path); + tnode.AddChild("ModelUVTranslation", double(0.0), double(0.0)); + tnode.AddChild("ModelUVScaling", double(1.0), double(1.0)); + tnode.AddChild("Texture_Alpha_Source", "None"); + tnode.AddChild( + "Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0) + ); + tnode.Dump(outstream, binary, indent); + } + } + + // bones. + // + // output structure: + // subset of node heirarchy that are "skeleton", + // i.e. do not have meshes but only bones. + // but.. i'm not sure how anyone could guarantee that... + // + // input... + // well, for each mesh it has "bones", + // and the bone names correspond to nodes. + // of course we also need the parent nodes, + // as they give some of the transform........ + // + // well. we can assume a sane input, i suppose. + // + // so input is the bone node heirarchy, + // with an extra thing for the transformation of the MESH in BONE space. + // + // output is a set of bone nodes, + // a "bindpose" which indicates the default local transform of all bones, + // and a set of "deformers". + // each deformer is parented to a mesh geometry, + // and has one or more "subdeformer"s as children. + // each subdeformer has one bone node as a child, + // and represents the influence of that bone on the grandparent mesh. + // the subdeformer has a list of indices, and weights, + // with indices specifying vertex indices, + // and weights specifying the correspoding influence of this bone. + // it also has Transform and TransformLink elements, + // specifying the transform of the MESH in BONE space, + // and the transformation of the BONE in WORLD space, + // likely in the bindpose. + // + // the input bone structure is different but similar, + // storing the number of weights for this bone, + // and an array of (vertex index, weight) pairs. + // + // one sticky point is that the number of vertices may not match, + // because assimp splits vertices by normal, uv, etc. + + // first we should mark the skeleton for each mesh. + // the skeleton must include not only the aiBones, + // but also all their parent nodes. + // anything that affects the position of any bone node must be included. + std::vector> skeleton_by_mesh(mScene->mNumMeshes); + // at the same time we can build a list of all the skeleton nodes, + // which will be used later to mark them as type "limbNode". + std::unordered_set limbnodes; + // and a map of nodes by bone name, as finding them is annoying. + std::map node_by_bone; + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + const aiMesh* m = mScene->mMeshes[mi]; + std::set skeleton; + for (size_t bi =0; bi < m->mNumBones; ++bi) { + const aiBone* b = m->mBones[bi]; + const std::string name(b->mName.C_Str()); + auto elem = node_by_bone.find(name); + aiNode* n; + if (elem != node_by_bone.end()) { + n = elem->second; + } else { + n = mScene->mRootNode->FindNode(b->mName); + if (!n) { + // this should never happen + std::stringstream err; + err << "Failed to find node for bone: \"" << name << "\""; + throw DeadlyExportError(err.str()); + } + node_by_bone[name] = n; + limbnodes.insert(n); + } + skeleton.insert(n); + // mark all parent nodes as skeleton as well, + // up until we find the root node, + // or else the node containing the mesh, + // or else the parent of a node containig the mesh. + for ( + const aiNode* parent = n->mParent; + parent && parent != mScene->mRootNode; + parent = parent->mParent + ) { + // if we've already done this node we can skip it all + if (skeleton.count(parent)) { + break; + } + // ignore fbx transform nodes as these will be collapsed later + // TODO: cache this by aiNode* + const std::string node_name(parent->mName.C_Str()); + if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { + continue; + } + // otherwise check if this is the root of the skeleton + bool end = false; + // is the mesh part of this node? + for (size_t i = 0; i < parent->mNumMeshes; ++i) { + if (parent->mMeshes[i] == mi) { + end = true; + break; + } + } + // is the mesh in one of the children of this node? + for (size_t j = 0; j < parent->mNumChildren; ++j) { + aiNode* child = parent->mChildren[j]; + for (size_t i = 0; i < child->mNumMeshes; ++i) { + if (child->mMeshes[i] == mi) { + end = true; + break; + } + } + if (end) { break; } + } + limbnodes.insert(parent); + skeleton.insert(parent); + // if it was the skeleton root we can finish here + if (end) { break; } + } + } + skeleton_by_mesh[mi] = skeleton; + } + + // we'll need the uids for the bone nodes, so generate them now + for (size_t i = 0; i < mScene->mNumMeshes; ++i) { + auto &s = skeleton_by_mesh[i]; + for (const aiNode* n : s) { + auto elem = node_uids.find(n); + if (elem == node_uids.end()) { + node_uids[n] = generate_uid(); + } + } + } + + // now, for each aiMesh, we need to export a deformer, + // and for each aiBone a subdeformer, + // which should have all the skinning info. + // these will need to be connected properly to the mesh, + // and we can do that all now. + for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + const aiMesh* m = mScene->mMeshes[mi]; + if (!m->HasBones()) { + continue; + } + // make a deformer for this mesh + int64_t deformer_uid = generate_uid(); + FBX::Node dnode("Deformer"); + dnode.AddProperties(deformer_uid, FBX::SEPARATOR + "Deformer", "Skin"); + dnode.AddChild("Version", int32_t(101)); + // "acuracy"... this is not a typo.... + dnode.AddChild("Link_DeformAcuracy", double(50)); + dnode.AddChild("SkinningType", "Linear"); // TODO: other modes? + dnode.Dump(outstream, binary, indent); + + // connect it + connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]); + + // we will be indexing by vertex... + // but there might be a different number of "vertices" + // between assimp and our output FBX. + // this code is cut-and-pasted from the geometry section above... + // ideally this should not be so. + // --- + // index of original vertex in vertex data vector + std::vector vertex_indices; + // map of vertex value to its index in the data vector + std::map index_by_vertex_value; + int32_t index = 0; + for (size_t vi = 0; vi < m->mNumVertices; ++vi) { + aiVector3D vtx = m->mVertices[vi]; + auto elem = index_by_vertex_value.find(vtx); + if (elem == index_by_vertex_value.end()) { + vertex_indices.push_back(index); + index_by_vertex_value[vtx] = index; + ++index; + } else { + vertex_indices.push_back(int32_t(elem->second)); + } + } + + // TODO, FIXME: this won't work if anything is not in the bind pose. + // for now if such a situation is detected, we throw an exception. + std::set not_in_bind_pose; + std::set no_offset_matrix; + + // first get this mesh's position in world space, + // as we'll need it for each subdeformer. + // + // ...of course taking the position of the MESH doesn't make sense, + // as it can be instanced to many nodes. + // All we can do is assume no instancing, + // and take the first node we find that contains the mesh. + aiNode* mesh_node = get_node_for_mesh((unsigned int)mi, mScene->mRootNode); + aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene); + + // now make a subdeformer for each bone in the skeleton + const std::set &skeleton = skeleton_by_mesh[mi]; + for (const aiNode* bone_node : skeleton) { + // if there's a bone for this node, find it + const aiBone* b = nullptr; + for (size_t bi = 0; bi < m->mNumBones; ++bi) { + // TODO: this probably should index by something else + const std::string name(m->mBones[bi]->mName.C_Str()); + if (node_by_bone[name] == bone_node) { + b = m->mBones[bi]; + break; + } + } + if (!b) { + no_offset_matrix.insert(bone_node); + } + + // start the subdeformer node + const int64_t subdeformer_uid = generate_uid(); + FBX::Node sdnode("Deformer"); + sdnode.AddProperties( + subdeformer_uid, FBX::SEPARATOR + "SubDeformer", "Cluster" + ); + sdnode.AddChild("Version", int32_t(100)); + sdnode.AddChild("UserData", "", ""); + + // add indices and weights, if any + if (b) { + std::vector subdef_indices; + std::vector subdef_weights; + int32_t last_index = -1; + for (size_t wi = 0; wi < b->mNumWeights; ++wi) { + int32_t vi = vertex_indices[b->mWeights[wi].mVertexId]; + if (vi == last_index) { + // only for vertices we exported to fbx + // TODO, FIXME: this assumes identically-located vertices + // will always deform in the same way. + // as assimp doesn't store a separate list of "positions", + // there's not much that can be done about this + // other than assuming that identical position means + // identical vertex. + continue; + } + subdef_indices.push_back(vi); + subdef_weights.push_back(b->mWeights[wi].mWeight); + last_index = vi; + } + // yes, "indexes" + sdnode.AddChild("Indexes", subdef_indices); + sdnode.AddChild("Weights", subdef_weights); + } + + // transform is the transform of the mesh, but in bone space. + // if the skeleton is in the bind pose, + // we can take the inverse of the world-space bone transform + // and multiply by the world-space transform of the mesh. + aiMatrix4x4 bone_xform = get_world_transform(bone_node, mScene); + aiMatrix4x4 inverse_bone_xform = bone_xform; + inverse_bone_xform.Inverse(); + aiMatrix4x4 tr = inverse_bone_xform * mesh_xform; + + // this should be the same as the bone's mOffsetMatrix. + // if it's not the same, the skeleton isn't in the bind pose. + const float epsilon = 1e-4f; // some error is to be expected + bool bone_xform_okay = true; + if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) { + not_in_bind_pose.insert(b); + bone_xform_okay = false; + } + + // if we have a bone we should use the mOffsetMatrix, + // otherwise try to just use the calculated transform. + if (b) { + sdnode.AddChild("Transform", b->mOffsetMatrix); + } else { + sdnode.AddChild("Transform", tr); + } + // note: it doesn't matter if we mix these, + // because if they disagree we'll throw an exception later. + // it could be that the skeleton is not in the bone pose + // but all bones are still defined, + // in which case this would use the mOffsetMatrix for everything + // and a correct skeleton would still be output. + + // transformlink should be the position of the bone in world space. + // if the bone is in the bind pose (or nonexistant), + // we can just use the matrix we already calculated + if (bone_xform_okay) { + sdnode.AddChild("TransformLink", bone_xform); + // otherwise we can only work it out using the mesh position. + } else { + aiMatrix4x4 trl = b->mOffsetMatrix; + trl.Inverse(); + trl *= mesh_xform; + sdnode.AddChild("TransformLink", trl); + } + // note: this means we ALWAYS rely on the mesh node transform + // being unchanged from the time the skeleton was bound. + // there's not really any way around this at the moment. + + // done + sdnode.Dump(outstream, binary, indent); + + // lastly, connect to the parent deformer + connections.emplace_back( + "C", "OO", subdeformer_uid, deformer_uid + ); + + // we also need to connect the limb node to the subdeformer. + connections.emplace_back( + "C", "OO", node_uids[bone_node], subdeformer_uid + ); + } + + // if we cannot create a valid FBX file, simply die. + // this will both prevent unnecessary bug reports, + // and tell the user what they can do to fix the situation + // (i.e. export their model in the bind pose). + if (no_offset_matrix.size() && not_in_bind_pose.size()) { + std::stringstream err; + err << "Not enough information to construct bind pose"; + err << " for mesh " << mi << "!"; + err << " Transform matrix for bone \""; + err << (*not_in_bind_pose.begin())->mName.C_Str() << "\""; + if (not_in_bind_pose.size() > 1) { + err << " (and " << not_in_bind_pose.size() - 1 << " more)"; + } + err << " does not match mOffsetMatrix,"; + err << " and node \""; + err << (*no_offset_matrix.begin())->mName.C_Str() << "\""; + if (no_offset_matrix.size() > 1) { + err << " (and " << no_offset_matrix.size() - 1 << " more)"; + } + err << " has no offset matrix to rely on."; + err << " Please ensure bones are in the bind pose to export."; + throw DeadlyExportError(err.str()); + } + + } + + // BindPose + // + // This is a legacy system, which should be unnecessary. + // + // Somehow including it slows file loading by the official FBX SDK, + // and as it can reconstruct it from the deformers anyway, + // this is not currently included. + // + // The code is kept here in case it's useful in the future, + // but it's pretty much a hack anyway, + // as assimp doesn't store bindpose information for full skeletons. + // + /*for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { + aiMesh* mesh = mScene->mMeshes[mi]; + if (! mesh->HasBones()) { continue; } + int64_t bindpose_uid = generate_uid(); + FBX::Node bpnode("Pose"); + bpnode.AddProperty(bindpose_uid); + // note: this uid is never linked or connected to anything. + bpnode.AddProperty(FBX::SEPARATOR + "Pose"); // blank name + bpnode.AddProperty("BindPose"); + + bpnode.AddChild("Type", "BindPose"); + bpnode.AddChild("Version", int32_t(100)); + + aiNode* mesh_node = get_node_for_mesh(mi, mScene->mRootNode); + + // next get the whole skeleton for this mesh. + // we need it all to define the bindpose section. + // the FBX SDK will complain if it's missing, + // and also if parents of used bones don't have a subdeformer. + // order shouldn't matter. + std::set skeleton; + for (size_t bi = 0; bi < mesh->mNumBones; ++bi) { + // bone node should have already been indexed + const aiBone* b = mesh->mBones[bi]; + const std::string bone_name(b->mName.C_Str()); + aiNode* parent = node_by_bone[bone_name]; + // insert all nodes down to the root or mesh node + while ( + parent + && parent != mScene->mRootNode + && parent != mesh_node + ) { + skeleton.insert(parent); + parent = parent->mParent; + } + } + + // number of pose nodes. includes one for the mesh itself. + bpnode.AddChild("NbPoseNodes", int32_t(1 + skeleton.size())); + + // the first pose node is always the mesh itself + FBX::Node pose("PoseNode"); + pose.AddChild("Node", mesh_uids[mi]); + aiMatrix4x4 mesh_node_xform = get_world_transform(mesh_node, mScene); + pose.AddChild("Matrix", mesh_node_xform); + bpnode.AddChild(pose); + + for (aiNode* bonenode : skeleton) { + // does this node have a uid yet? + int64_t node_uid; + auto node_uid_iter = node_uids.find(bonenode); + if (node_uid_iter != node_uids.end()) { + node_uid = node_uid_iter->second; + } else { + node_uid = generate_uid(); + node_uids[bonenode] = node_uid; + } + + // make a pose thingy + pose = FBX::Node("PoseNode"); + pose.AddChild("Node", node_uid); + aiMatrix4x4 node_xform = get_world_transform(bonenode, mScene); + pose.AddChild("Matrix", node_xform); + bpnode.AddChild(pose); + } + + // now write it + bpnode.Dump(outstream, binary, indent); + }*/ + + // TODO: cameras, lights + + // write nodes (i.e. model heirarchy) + // start at root node + WriteModelNodes( + outstream, mScene->mRootNode, 0, limbnodes + ); + + // animations + // + // in FBX there are: + // * AnimationStack - corresponds to an aiAnimation + // * AnimationLayer - a combinable animation component + // * AnimationCurveNode - links the property to be animated + // * AnimationCurve - defines animation data for a single property value + // + // the CurveNode also provides the default value for a property, + // such as the X, Y, Z coordinates for animatable translation. + // + // the Curve only specifies values for one component of the property, + // so there will be a separate AnimationCurve for X, Y, and Z. + // + // Assimp has: + // * aiAnimation - basically corresponds to an AnimationStack + // * aiNodeAnim - defines all animation for one aiNode + // * aiVectorKey/aiQuatKey - define the keyframe data for T/R/S + // + // assimp has no equivalent for AnimationLayer, + // and these are flattened on FBX import. + // we can assume there will be one per AnimationStack. + // + // the aiNodeAnim contains all animation data for a single aiNode, + // which will correspond to three AnimationCurveNode's: + // one each for translation, rotation and scale. + // The data for each of these will be put in 9 AnimationCurve's, + // T.X, T.Y, T.Z, R.X, R.Y, R.Z, etc. + + // AnimationStack / aiAnimation + std::vector animation_stack_uids(mScene->mNumAnimations); + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + int64_t animstack_uid = generate_uid(); + animation_stack_uids[ai] = animstack_uid; + const aiAnimation* anim = mScene->mAnimations[ai]; + + FBX::Node asnode("AnimationStack"); + std::string name = anim->mName.C_Str() + FBX::SEPARATOR + "AnimStack"; + asnode.AddProperties(animstack_uid, name, ""); + FBX::Node p("Properties70"); + p.AddP70time("LocalStart", 0); // assimp doesn't store this + p.AddP70time("LocalStop", to_ktime(anim->mDuration, anim)); + p.AddP70time("ReferenceStart", 0); + p.AddP70time("ReferenceStop", to_ktime(anim->mDuration, anim)); + asnode.AddChild(p); + + // this node absurdly always pretends it has children + // (in this case it does, but just in case...) + asnode.force_has_children = true; + asnode.Dump(outstream, binary, indent); + + // note: animation stacks are not connected to anything + } + + // AnimationLayer - one per aiAnimation + std::vector animation_layer_uids(mScene->mNumAnimations); + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + int64_t animlayer_uid = generate_uid(); + animation_layer_uids[ai] = animlayer_uid; + FBX::Node alnode("AnimationLayer"); + alnode.AddProperties(animlayer_uid, FBX::SEPARATOR + "AnimLayer", ""); + + // this node absurdly always pretends it has children + alnode.force_has_children = true; + alnode.Dump(outstream, binary, indent); + + // connect to the relevant animstack + connections.emplace_back( + "C", "OO", animlayer_uid, animation_stack_uids[ai] + ); + } + + // AnimCurveNode - three per aiNodeAnim + std::vector>> curve_node_uids; + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + const aiAnimation* anim = mScene->mAnimations[ai]; + const int64_t layer_uid = animation_layer_uids[ai]; + std::vector> nodeanim_uids; + for (size_t nai = 0; nai < anim->mNumChannels; ++nai) { + const aiNodeAnim* na = anim->mChannels[nai]; + // get the corresponding aiNode + const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName); + // and its transform + const aiMatrix4x4 node_xfm = get_world_transform(node, mScene); + aiVector3D T, R, S; + node_xfm.Decompose(S, R, T); + + // AnimationCurveNode uids + std::array ids; + ids[0] = generate_uid(); // T + ids[1] = generate_uid(); // R + ids[2] = generate_uid(); // S + + // translation + WriteAnimationCurveNode(outstream, + ids[0], "T", T, "Lcl Translation", + layer_uid, node_uids[node] + ); + + // rotation + WriteAnimationCurveNode(outstream, + ids[1], "R", R, "Lcl Rotation", + layer_uid, node_uids[node] + ); + + // scale + WriteAnimationCurveNode(outstream, + ids[2], "S", S, "Lcl Scale", + layer_uid, node_uids[node] + ); + + // store the uids for later use + nodeanim_uids.push_back(ids); + } + curve_node_uids.push_back(nodeanim_uids); + } + + // AnimCurve - defines actual keyframe data. + // there's a separate curve for every component of every vector, + // for example a transform curvenode will have separate X/Y/Z AnimCurve's + for (size_t ai = 0; ai < mScene->mNumAnimations; ++ai) { + const aiAnimation* anim = mScene->mAnimations[ai]; + for (size_t nai = 0; nai < anim->mNumChannels; ++nai) { + const aiNodeAnim* na = anim->mChannels[nai]; + // get the corresponding aiNode + const aiNode* node = mScene->mRootNode->FindNode(na->mNodeName); + // and its transform + const aiMatrix4x4 node_xfm = get_world_transform(node, mScene); + aiVector3D T, R, S; + node_xfm.Decompose(S, R, T); + const std::array& ids = curve_node_uids[ai][nai]; + + std::vector times; + std::vector xval, yval, zval; + + // position/translation + for (size_t ki = 0; ki < na->mNumPositionKeys; ++ki) { + const aiVectorKey& k = na->mPositionKeys[ki]; + times.push_back(to_ktime(k.mTime, anim)); + xval.push_back(k.mValue.x); + yval.push_back(k.mValue.y); + zval.push_back(k.mValue.z); + } + // one curve each for X, Y, Z + WriteAnimationCurve(outstream, T.x, times, xval, ids[0], "d|X"); + WriteAnimationCurve(outstream, T.y, times, yval, ids[0], "d|Y"); + WriteAnimationCurve(outstream, T.z, times, zval, ids[0], "d|Z"); + + // rotation + times.clear(); xval.clear(); yval.clear(); zval.clear(); + for (size_t ki = 0; ki < na->mNumRotationKeys; ++ki) { + const aiQuatKey& k = na->mRotationKeys[ki]; + times.push_back(to_ktime(k.mTime, anim)); + // TODO: aiQuaternion method to convert to Euler... + aiMatrix4x4 m(k.mValue.GetMatrix()); + aiVector3D qs, qr, qt; + m.Decompose(qs, qr, qt); + qr *= DEG; + xval.push_back(qr.x); + yval.push_back(qr.y); + zval.push_back(qr.z); + } + WriteAnimationCurve(outstream, R.x, times, xval, ids[1], "d|X"); + WriteAnimationCurve(outstream, R.y, times, yval, ids[1], "d|Y"); + WriteAnimationCurve(outstream, R.z, times, zval, ids[1], "d|Z"); + + // scaling/scale + times.clear(); xval.clear(); yval.clear(); zval.clear(); + for (size_t ki = 0; ki < na->mNumScalingKeys; ++ki) { + const aiVectorKey& k = na->mScalingKeys[ki]; + times.push_back(to_ktime(k.mTime, anim)); + xval.push_back(k.mValue.x); + yval.push_back(k.mValue.y); + zval.push_back(k.mValue.z); + } + WriteAnimationCurve(outstream, S.x, times, xval, ids[2], "d|X"); + WriteAnimationCurve(outstream, S.y, times, yval, ids[2], "d|Y"); + WriteAnimationCurve(outstream, S.z, times, zval, ids[2], "d|Z"); + } + } + + indent = 0; + object_node.End(outstream, binary, indent, true); +} + +// convenience map of magic node name strings to FBX properties, +// including the expected type of transform. +const std::map> transform_types = { + {"Translation", {"Lcl Translation", 't'}}, + {"RotationOffset", {"RotationOffset", 't'}}, + {"RotationPivot", {"RotationPivot", 't'}}, + {"PreRotation", {"PreRotation", 'r'}}, + {"Rotation", {"Lcl Rotation", 'r'}}, + {"PostRotation", {"PostRotation", 'r'}}, + {"RotationPivotInverse", {"RotationPivotInverse", 'i'}}, + {"ScalingOffset", {"ScalingOffset", 't'}}, + {"ScalingPivot", {"ScalingPivot", 't'}}, + {"Scaling", {"Lcl Scaling", 's'}}, + {"ScalingPivotInverse", {"ScalingPivotInverse", 'i'}}, + {"GeometricScaling", {"GeometricScaling", 's'}}, + {"GeometricRotation", {"GeometricRotation", 'r'}}, + {"GeometricTranslation", {"GeometricTranslation", 't'}}, + {"GeometricTranslationInverse", {"GeometricTranslationInverse", 'i'}}, + {"GeometricRotationInverse", {"GeometricRotationInverse", 'i'}}, + {"GeometricScalingInverse", {"GeometricScalingInverse", 'i'}} +}; + +// write a single model node to the stream +void FBXExporter::WriteModelNode( + StreamWriterLE& outstream, + bool binary, + const aiNode* node, + int64_t node_uid, + const std::string& type, + const std::vector>& transform_chain, + TransformInheritance inherit_type +){ + const aiVector3D zero = {0, 0, 0}; + const aiVector3D one = {1, 1, 1}; + FBX::Node m("Model"); + std::string name = node->mName.C_Str() + FBX::SEPARATOR + "Model"; + m.AddProperties(node_uid, name, type); + m.AddChild("Version", int32_t(232)); + FBX::Node p("Properties70"); + p.AddP70bool("RotationActive", 1); + p.AddP70int("DefaultAttributeIndex", 0); + p.AddP70enum("InheritType", inherit_type); + if (transform_chain.empty()) { + // decompose 4x4 transform matrix into TRS + aiVector3D t, r, s; + node->mTransformation.Decompose(s, r, t); + if (t != zero) { + p.AddP70( + "Lcl Translation", "Lcl Translation", "", "A", + double(t.x), double(t.y), double(t.z) + ); + } + if (r != zero) { + p.AddP70( + "Lcl Rotation", "Lcl Rotation", "", "A", + double(DEG*r.x), double(DEG*r.y), double(DEG*r.z) + ); + } + if (s != one) { + p.AddP70( + "Lcl Scaling", "Lcl Scaling", "", "A", + double(s.x), double(s.y), double(s.z) + ); + } + } else { + // apply the transformation chain. + // these transformation elements are created when importing FBX, + // which has a complex transformation heirarchy for each node. + // as such we can bake the heirarchy back into the node on export. + for (auto &item : transform_chain) { + auto elem = transform_types.find(item.first); + if (elem == transform_types.end()) { + // then this is a bug + std::stringstream err; + err << "unrecognized FBX transformation type: "; + err << item.first; + throw DeadlyExportError(err.str()); + } + const std::string &name = elem->second.first; + const aiVector3D &v = item.second; + if (name.compare(0, 4, "Lcl ") == 0) { + // special handling for animatable properties + p.AddP70( + name, name, "", "A", + double(v.x), double(v.y), double(v.z) + ); + } else { + p.AddP70vector(name, v.x, v.y, v.z); + } + } + } + m.AddChild(p); + + // not sure what these are for, + // but they seem to be omnipresent + m.AddChild("Shading", Property(true)); + m.AddChild("Culling", Property("CullingOff")); + + m.Dump(outstream, binary, 1); +} + +// wrapper for WriteModelNodes to create and pass a blank transform chain +void FBXExporter::WriteModelNodes( + StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set& limbnodes +) { + std::vector> chain; + WriteModelNodes(s, node, parent_uid, limbnodes, chain); +} + +void FBXExporter::WriteModelNodes( + StreamWriterLE& outstream, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set& limbnodes, + std::vector>& transform_chain +) { + // first collapse any expanded transformation chains created by FBX import. + std::string node_name(node->mName.C_Str()); + if (node_name.find(MAGIC_NODE_TAG) != std::string::npos) { + auto pos = node_name.find(MAGIC_NODE_TAG) + MAGIC_NODE_TAG.size() + 1; + std::string type_name = node_name.substr(pos); + auto elem = transform_types.find(type_name); + if (elem == transform_types.end()) { + // then this is a bug and should be fixed + std::stringstream err; + err << "unrecognized FBX transformation node"; + err << " of type " << type_name << " in node " << node_name; + throw DeadlyExportError(err.str()); + } + aiVector3D t, r, s; + node->mTransformation.Decompose(s, r, t); + switch (elem->second.second) { + case 'i': // inverse + // we don't need to worry about the inverse matrices + break; + case 't': // translation + transform_chain.emplace_back(elem->first, t); + break; + case 'r': // rotation + r *= float(DEG); + transform_chain.emplace_back(elem->first, r); + break; + case 's': // scale + transform_chain.emplace_back(elem->first, s); + break; + default: + // this should never happen + std::stringstream err; + err << "unrecognized FBX transformation type code: "; + err << elem->second.second; + throw DeadlyExportError(err.str()); + } + // now continue on to any child nodes + for (unsigned i = 0; i < node->mNumChildren; ++i) { + WriteModelNodes( + outstream, + node->mChildren[i], + parent_uid, + limbnodes, + transform_chain + ); + } + return; + } + + int64_t node_uid = 0; + // generate uid and connect to parent, if not the root node, + if (node != mScene->mRootNode) { + auto elem = node_uids.find(node); + if (elem != node_uids.end()) { + node_uid = elem->second; + } else { + node_uid = generate_uid(); + node_uids[node] = node_uid; + } + connections.emplace_back("C", "OO", node_uid, parent_uid); + } + + // what type of node is this? + if (node == mScene->mRootNode) { + // handled later + } else if (node->mNumMeshes == 1) { + // connect to child mesh, which should have been written previously + connections.emplace_back( + "C", "OO", mesh_uids[node->mMeshes[0]], node_uid + ); + // also connect to the material for the child mesh + connections.emplace_back( + "C", "OO", + material_uids[mScene->mMeshes[node->mMeshes[0]]->mMaterialIndex], + node_uid + ); + // write model node + WriteModelNode( + outstream, binary, node, node_uid, "Mesh", transform_chain + ); + } else if (limbnodes.count(node)) { + WriteModelNode( + outstream, binary, node, node_uid, "LimbNode", transform_chain + ); + // we also need to write a nodeattribute to mark it as a skeleton + int64_t node_attribute_uid = generate_uid(); + FBX::Node na("NodeAttribute"); + na.AddProperties( + node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode" + ); + na.AddChild("TypeFlags", Property("Skeleton")); + na.Dump(outstream, binary, 1); + // and connect them + connections.emplace_back("C", "OO", node_attribute_uid, node_uid); + } else { + // generate a null node so we can add children to it + WriteModelNode( + outstream, binary, node, node_uid, "Null", transform_chain + ); + } + + // if more than one child mesh, make nodes for each mesh + if (node->mNumMeshes > 1 || node == mScene->mRootNode) { + for (size_t i = 0; i < node->mNumMeshes; ++i) { + // make a new model node + int64_t new_node_uid = generate_uid(); + // connect to parent node + connections.emplace_back("C", "OO", new_node_uid, node_uid); + // connect to child mesh, which should have been written previously + connections.emplace_back( + "C", "OO", mesh_uids[node->mMeshes[i]], new_node_uid + ); + // also connect to the material for the child mesh + connections.emplace_back( + "C", "OO", + material_uids[ + mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex + ], + new_node_uid + ); + // write model node + FBX::Node m("Model"); + // take name from mesh name, if it exists + std::string name = mScene->mMeshes[node->mMeshes[i]]->mName.C_Str(); + name += FBX::SEPARATOR + "Model"; + m.AddProperties(new_node_uid, name, "Mesh"); + m.AddChild("Version", int32_t(232)); + FBX::Node p("Properties70"); + p.AddP70enum("InheritType", 1); + m.AddChild(p); + m.Dump(outstream, binary, 1); + } + } + + // now recurse into children + for (size_t i = 0; i < node->mNumChildren; ++i) { + WriteModelNodes( + outstream, node->mChildren[i], node_uid, limbnodes + ); + } +} + + +void FBXExporter::WriteAnimationCurveNode( + StreamWriterLE& outstream, + int64_t uid, + std::string name, // "T", "R", or "S" + aiVector3D default_value, + std::string property_name, // "Lcl Translation" etc + int64_t layer_uid, + int64_t node_uid +) { + FBX::Node n("AnimationCurveNode"); + n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", ""); + FBX::Node p("Properties70"); + p.AddP70numberA("d|X", default_value.x); + p.AddP70numberA("d|Y", default_value.y); + p.AddP70numberA("d|Z", default_value.z); + n.AddChild(p); + n.Dump(outstream, binary, 1); + // connect to layer + this->connections.emplace_back("C", "OO", uid, layer_uid); + // connect to bone + this->connections.emplace_back("C", "OP", uid, node_uid, property_name); +} + + +void FBXExporter::WriteAnimationCurve( + StreamWriterLE& outstream, + double default_value, + const std::vector& times, + const std::vector& values, + int64_t curvenode_uid, + const std::string& property_link // "d|X", "d|Y", etc +) { + FBX::Node n("AnimationCurve"); + int64_t curve_uid = generate_uid(); + n.AddProperties(curve_uid, FBX::SEPARATOR + "AnimCurve", ""); + n.AddChild("Default", default_value); + n.AddChild("KeyVer", int32_t(4009)); + n.AddChild("KeyTime", times); + n.AddChild("KeyValueFloat", values); + // TODO: keyattr flags and data (STUB for now) + n.AddChild("KeyAttrFlags", std::vector{0}); + n.AddChild("KeyAttrDataFloat", std::vector{0,0,0,0}); + ai_assert(times.size() <= std::numeric_limits::max()); + n.AddChild( + "KeyAttrRefCount", + std::vector{static_cast(times.size())} + ); + n.Dump(outstream, binary, 1); + this->connections.emplace_back( + "C", "OP", curve_uid, curvenode_uid, property_link + ); +} + + +void FBXExporter::WriteConnections () +{ + // we should have completed the connection graph already, + // so basically just dump it here + if (!binary) { + WriteAsciiSectionHeader("Object connections"); + } + // TODO: comments with names in the ascii version + FBX::Node conn("Connections"); + StreamWriterLE outstream(outfile); + conn.Begin(outstream, binary, 0); + conn.BeginChildren(outstream, binary, 0); + for (auto &n : connections) { + n.Dump(outstream, binary, 1); + } + conn.End(outstream, binary, 0, !connections.empty()); + connections.clear(); +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER +#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/FBXExporter.h b/code/FBXExporter.h new file mode 100644 index 000000000..3b9de8acb --- /dev/null +++ b/code/FBXExporter.h @@ -0,0 +1,178 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2018, 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 FBXExporter.h +* Declares the exporter class to write a scene to an fbx file +*/ +#ifndef AI_FBXEXPORTER_H_INC +#define AI_FBXEXPORTER_H_INC + +#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER + +#include "FBXExportNode.h" // FBX::Node +#include "FBXCommon.h" // FBX::TransformInheritance + +#include +//#include +#include // StreamWriterLE +#include // DeadlyExportError + +#include +#include +#include +#include // shared_ptr +#include // stringstream + +struct aiScene; +struct aiNode; +//struct aiMaterial; + +namespace Assimp +{ + class IOSystem; + class IOStream; + class ExportProperties; + + // --------------------------------------------------------------------- + /** Helper class to export a given scene to an FBX file. */ + // --------------------------------------------------------------------- + class FBXExporter + { + public: + /// Constructor for a specific scene to export + FBXExporter(const aiScene* pScene, const ExportProperties* pProperties); + + // call one of these methods to export + void ExportBinary(const char* pFile, IOSystem* pIOSystem); + void ExportAscii(const char* pFile, IOSystem* pIOSystem); + + private: + bool binary; // whether current export is in binary or ascii format + const aiScene* mScene; // the scene to export + const ExportProperties* mProperties; // currently unused + std::shared_ptr outfile; // file to write to + + std::vector connections; // conection storage + + std::vector mesh_uids; + std::vector material_uids; + std::map node_uids; + + // this crude unique-ID system is actually fine + int64_t last_uid = 999999; + int64_t generate_uid() { return ++last_uid; } + + // binary files have a specific header and footer, + // in addition to the actual data + void WriteBinaryHeader(); + void WriteBinaryFooter(); + + // ascii files have a comment at the top + void WriteAsciiHeader(); + + // WriteAllNodes does the actual export. + // It just calls all the Write
methods below in order. + void WriteAllNodes(); + + // Methods to write individual sections. + // The order here matches the order inside an FBX file. + // Each method corresponds to a top-level FBX section, + // except WriteHeader which also includes some binary-only sections + // and WriteFooter which is binary data only. + void WriteHeaderExtension(); + // WriteFileId(); // binary-only, included in WriteHeader + // WriteCreationTime(); // binary-only, included in WriteHeader + // WriteCreator(); // binary-only, included in WriteHeader + void WriteGlobalSettings(); + void WriteDocuments(); + void WriteReferences(); + void WriteDefinitions(); + void WriteObjects(); + void WriteConnections(); + // WriteTakes(); // deprecated since at least 2015 (fbx 7.4) + + // helpers + void WriteAsciiSectionHeader(const std::string& title); + void WriteModelNodes( + Assimp::StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set& limbnodes + ); + void WriteModelNodes( // usually don't call this directly + StreamWriterLE& s, + const aiNode* node, + int64_t parent_uid, + const std::unordered_set& limbnodes, + std::vector>& transform_chain + ); + void WriteModelNode( // nor this + StreamWriterLE& s, + bool binary, + const aiNode* node, + int64_t node_uid, + const std::string& type, + const std::vector>& xfm_chain, + FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs + ); + void WriteAnimationCurveNode( + StreamWriterLE& outstream, + int64_t uid, + std::string name, // "T", "R", or "S" + aiVector3D default_value, + std::string property_name, // "Lcl Translation" etc + int64_t animation_layer_uid, + int64_t node_uid + ); + void WriteAnimationCurve( + StreamWriterLE& outstream, + double default_value, + const std::vector& times, + const std::vector& values, + int64_t curvenode_id, + const std::string& property_link // "d|X", "d|Y", etc + ); + }; +} + +#endif // ASSIMP_BUILD_NO_FBX_EXPORTER + +#endif // AI_FBXEXPORTER_H_INC diff --git a/code/FBXMaterial.cpp b/code/FBXMaterial.cpp index 10f3bbe6c..8bb3920de 100644 --- a/code/FBXMaterial.cpp +++ b/code/FBXMaterial.cpp @@ -54,6 +54,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXProperties.h" #include +#include // std::transform + namespace Assimp { namespace FBX { @@ -82,11 +84,12 @@ Material::Material(uint64_t id, const Element& element, const Document& doc, con std::string templateName; - const char* const sh = shading.c_str(); - if(!strcmp(sh,"phong")) { + // lower-case shading because Blender (for example) writes "Phong" + std::transform(shading.begin(), shading.end(), shading.begin(), ::tolower); + if(shading == "phong") { templateName = "Material.FbxSurfacePhong"; } - else if(!strcmp(sh,"lambert")) { + else if(shading == "lambert") { templateName = "Material.FbxSurfaceLambert"; } else { diff --git a/code/FBXMeshGeometry.cpp b/code/FBXMeshGeometry.cpp index d55a8b0bb..cc1a5a83e 100644 --- a/code/FBXMeshGeometry.cpp +++ b/code/FBXMeshGeometry.cpp @@ -79,14 +79,13 @@ Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, // ------------------------------------------------------------------------------------------------ Geometry::~Geometry() { - + // empty } const Skin* Geometry::DeformerSkin() const { return skin; } - // ------------------------------------------------------------------------------------------------ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) : Geometry(id, element,name, doc) @@ -186,9 +185,8 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin } // ------------------------------------------------------------------------------------------------ -MeshGeometry::~MeshGeometry() -{ - +MeshGeometry::~MeshGeometry() { + // empty } // ------------------------------------------------------------------------------------------------ @@ -308,7 +306,6 @@ void MeshGeometry::ReadLayerElement(const Scope& layerElement) << type << ", index: " << typedIndex); } - // ------------------------------------------------------------------------------------------------ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scope& source) { @@ -412,7 +409,6 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop } } - // ------------------------------------------------------------------------------------------------ // Lengthy utility function to read and resolve a FBX vertex data array - that is, the // output is in polygon vertex order. This logic is used for reading normals, UVs, colors, @@ -428,16 +424,19 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, const std::vector& mapping_offsets, const std::vector& mappings) { + bool isDirect = ReferenceInformationType == "Direct"; + bool isIndexToDirect = ReferenceInformationType == "IndexToDirect"; + // fall-back to direct data if there is no index data element + if ( isIndexToDirect && !HasElement( source, indexDataElementName ) ) { + isDirect = true; + isIndexToDirect = false; + } // handle permutations of Mapping and Reference type - it would be nice to // deal with this more elegantly and with less redundancy, but right // now it seems unavoidable. - if (MappingInformationType == "ByVertice" && ReferenceInformationType == "Direct") { - if ( !HasElement( source, indexDataElementName ) ) { - return; - } - + if (MappingInformationType == "ByVertice" && isDirect) { std::vector tempData; ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); @@ -450,14 +449,11 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, } } } - else if (MappingInformationType == "ByVertice" && ReferenceInformationType == "IndexToDirect") { + else if (MappingInformationType == "ByVertice" && isIndexToDirect) { std::vector tempData; ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); data_out.resize(vertex_count); - if ( !HasElement( source, indexDataElementName ) ) { - return; - } std::vector uvIndices; ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); @@ -472,7 +468,7 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, } } } - else if (MappingInformationType == "ByPolygonVertex" && ReferenceInformationType == "Direct") { + else if (MappingInformationType == "ByPolygonVertex" && isDirect) { std::vector tempData; ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); @@ -485,7 +481,7 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, data_out.swap(tempData); } - else if (MappingInformationType == "ByPolygonVertex" && ReferenceInformationType == "IndexToDirect") { + else if (MappingInformationType == "ByPolygonVertex" && isIndexToDirect) { std::vector tempData; ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); @@ -499,9 +495,14 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, return; } + const T empty; unsigned int next = 0; for(int i : uvIndices) { - if (static_cast(i) >= tempData.size()) { + if ( -1 == i ) { + data_out[ next++ ] = empty; + continue; + } + if (static_cast(i) >= tempData.size()) { DOMError("index out of range",&GetRequiredElement(source,indexDataElementName)); } @@ -528,7 +529,6 @@ void MeshGeometry::ReadVertexDataNormals(std::vector& normals_out, c m_mappings); } - // ------------------------------------------------------------------------------------------------ void MeshGeometry::ReadVertexDataUV(std::vector& uv_out, const Scope& source, const std::string& MappingInformationType, @@ -543,7 +543,6 @@ void MeshGeometry::ReadVertexDataUV(std::vector& uv_out, const Scope m_mappings); } - // ------------------------------------------------------------------------------------------------ void MeshGeometry::ReadVertexDataColors(std::vector& colors_out, const Scope& source, const std::string& MappingInformationType, diff --git a/code/FBXMeshGeometry.h b/code/FBXMeshGeometry.h index 5dbf6f491..acd44668a 100644 --- a/code/FBXMeshGeometry.h +++ b/code/FBXMeshGeometry.h @@ -68,7 +68,6 @@ private: const Skin* skin; }; - typedef std::vector MatIndexArray; @@ -95,8 +94,8 @@ public: * if no tangents are specified */ const std::vector& GetTangents() const; - /** Get a list of all vertex binormals or an empty array - * if no binormals are specified */ + /** Get a list of all vertex bi-normals or an empty array + * if no bi-normals are specified */ const std::vector& GetBinormals() const; /** Return list of faces - each entry denotes a face and specifies diff --git a/code/FileSystemFilter.h b/code/FileSystemFilter.h index 0fabb41dd..8d43c1c27 100644 --- a/code/FileSystemFilter.h +++ b/code/FileSystemFilter.h @@ -42,13 +42,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * Implements a filter system to filter calls to Exists() and Open() * in order to improve the success rate of file opening ... */ +#pragma once #ifndef AI_FILESYSTEMFILTER_H_INC #define AI_FILESYSTEMFILTER_H_INC -#include "../include/assimp/IOSystem.hpp" -#include "../include/assimp/DefaultLogger.hpp" -#include "../include/assimp/fast_atof.h" -#include "../include/assimp/ParsingUtils.h" +#include +#include +#include +#include namespace Assimp { @@ -64,90 +65,89 @@ class FileSystemFilter : public IOSystem public: /** Constructor. */ FileSystemFilter(const std::string& file, IOSystem* old) - : wrapped (old) - , src_file (file) - , sep(wrapped->getOsSeparator()) - { - ai_assert(NULL != wrapped); + : mWrapped (old) + , mSrc_file(file) + , sep(mWrapped->getOsSeparator()) { + ai_assert(nullptr != mWrapped); // Determine base directory - base = src_file; + mBase = mSrc_file; std::string::size_type ss2; - if (std::string::npos != (ss2 = base.find_last_of("\\/"))) { - base.erase(ss2,base.length()-ss2); - } - else { - base = ""; - // return; + if (std::string::npos != (ss2 = mBase.find_last_of("\\/"))) { + mBase.erase(ss2,mBase.length()-ss2); + } else { + mBase = ""; } // make sure the directory is terminated properly char s; - if (base.length() == 0) { - base = "."; - base += getOsSeparator(); - } - else if ((s = *(base.end()-1)) != '\\' && s != '/') { - base += getOsSeparator(); + if ( mBase.empty() ) { + mBase = "."; + mBase += getOsSeparator(); + } else if ((s = *(mBase.end()-1)) != '\\' && s != '/') { + mBase += getOsSeparator(); } - DefaultLogger::get()->info("Import root directory is \'" + base + "\'"); + DefaultLogger::get()->info("Import root directory is \'" + mBase + "\'"); } /** Destructor. */ - ~FileSystemFilter() - { - // haha + ~FileSystemFilter() { + // empty } // ------------------------------------------------------------------- /** Tests for the existence of a file at the given path. */ - bool Exists( const char* pFile) const - { + bool Exists( const char* pFile) const { + ai_assert( nullptr != mWrapped ); + std::string tmp = pFile; // Currently this IOSystem is also used to open THE ONE FILE. - if (tmp != src_file) { + if (tmp != mSrc_file) { BuildPath(tmp); Cleanup(tmp); } - return wrapped->Exists(tmp); + return mWrapped->Exists(tmp); } // ------------------------------------------------------------------- /** Returns the directory separator. */ - char getOsSeparator() const - { + char getOsSeparator() const { return sep; } // ------------------------------------------------------------------- /** Open a new file with a given path. */ - IOStream* Open( const char* pFile, const char* pMode = "rb") - { - ai_assert(pFile); - ai_assert(pMode); + IOStream* Open( const char* pFile, const char* pMode = "rb") { + ai_assert( nullptr != mWrapped ); + if ( nullptr == pFile || nullptr == pMode ) { + return nullptr; + } + + ai_assert( nullptr != pFile ); + ai_assert( nullptr != pMode ); // First try the unchanged path - IOStream* s = wrapped->Open(pFile,pMode); + IOStream* s = mWrapped->Open(pFile,pMode); - if (!s) { + if (nullptr == s) { std::string tmp = pFile; // Try to convert between absolute and relative paths BuildPath(tmp); - s = wrapped->Open(tmp,pMode); + s = mWrapped->Open(tmp,pMode); - if (!s) { + if (nullptr == s) { // Finally, look for typical issues with paths // and try to correct them. This is our last // resort. tmp = pFile; Cleanup(tmp); BuildPath(tmp); - s = wrapped->Open(tmp,pMode); + s = mWrapped->Open(tmp,pMode); } } @@ -156,27 +156,75 @@ public: // ------------------------------------------------------------------- /** Closes the given file and releases all resources associated with it. */ - void Close( IOStream* pFile) - { - return wrapped->Close(pFile); + void Close( IOStream* pFile) { + ai_assert( nullptr != mWrapped ); + return mWrapped->Close(pFile); } // ------------------------------------------------------------------- /** Compare two paths */ - bool ComparePaths (const char* one, const char* second) const - { - return wrapped->ComparePaths (one,second); + bool ComparePaths (const char* one, const char* second) const { + ai_assert( nullptr != mWrapped ); + return mWrapped->ComparePaths (one,second); + } + + // ------------------------------------------------------------------- + /** Pushes a new directory onto the directory stack. */ + bool PushDirectory(const std::string &path ) { + ai_assert( nullptr != mWrapped ); + return mWrapped->PushDirectory(path); + } + + // ------------------------------------------------------------------- + /** Returns the top directory from the stack. */ + const std::string &CurrentDirectory() const { + ai_assert( nullptr != mWrapped ); + return mWrapped->CurrentDirectory(); + } + + // ------------------------------------------------------------------- + /** Returns the number of directories stored on the stack. */ + size_t StackSize() const { + ai_assert( nullptr != mWrapped ); + return mWrapped->StackSize(); + } + + // ------------------------------------------------------------------- + /** Pops the top directory from the stack. */ + bool PopDirectory() { + ai_assert( nullptr != mWrapped ); + return mWrapped->PopDirectory(); + } + + // ------------------------------------------------------------------- + /** Creates an new directory at the given path. */ + bool CreateDirectory(const std::string &path) { + ai_assert( nullptr != mWrapped ); + return mWrapped->CreateDirectory(path); + } + + // ------------------------------------------------------------------- + /** Will change the current directory to the given path. */ + bool ChangeDirectory(const std::string &path) { + ai_assert( nullptr != mWrapped ); + return mWrapped->ChangeDirectory(path); + } + + // ------------------------------------------------------------------- + /** Delete file. */ + bool DeleteFile(const std::string &file) { + ai_assert( nullptr != mWrapped ); + return mWrapped->DeleteFile(file); } private: - // ------------------------------------------------------------------- /** Build a valid path from a given relative or absolute path. */ - void BuildPath (std::string& in) const - { + void BuildPath (std::string& in) const { + ai_assert( nullptr != mWrapped ); // if we can already access the file, great. - if (in.length() < 3 || wrapped->Exists(in)) { + if (in.length() < 3 || mWrapped->Exists(in)) { return; } @@ -184,8 +232,8 @@ private: if (in[1] != ':') { // append base path and try - const std::string tmp = base + in; - if (wrapped->Exists(tmp)) { + const std::string tmp = mBase + in; + if (mWrapped->Exists(tmp)) { in = tmp; return; } @@ -207,7 +255,7 @@ private: std::string::size_type last_dirsep = std::string::npos; while(true) { - tmp = base; + tmp = mBase; tmp += sep; std::string::size_type dirsep = in.rfind('/', last_dirsep); @@ -223,7 +271,7 @@ private: last_dirsep = dirsep-1; tmp += in.substr(dirsep+1, in.length()-pos); - if (wrapped->Exists(tmp)) { + if (mWrapped->Exists(tmp)) { in = tmp; return; } @@ -236,15 +284,14 @@ private: // ------------------------------------------------------------------- /** Cleanup the given path */ - void Cleanup (std::string& in) const - { - char last = 0; + void Cleanup (std::string& in) const { if(in.empty()) { return; } // Remove a very common issue when we're parsing file names: spaces at the // beginning of the path. + char last = 0; std::string::iterator it = in.begin(); while (IsSpaceOrNewLine( *it ))++it; if (it != in.begin()) { @@ -274,9 +321,7 @@ private: it = in.erase(it); --it; } - } - else if (*it == '%' && in.end() - it > 2) { - + } else if (*it == '%' && in.end() - it > 2) { // Hex sequence in URIs if( IsHex((&*it)[0]) && IsHex((&*it)[1]) ) { *it = HexOctetToDecimal(&*it); @@ -290,8 +335,8 @@ private: } private: - IOSystem* wrapped; - std::string src_file, base; + IOSystem *mWrapped; + std::string mSrc_file, mBase; char sep; }; diff --git a/code/FindInstancesProcess.h b/code/FindInstancesProcess.h index 2ef6c1ca7..fb2ac6eb6 100644 --- a/code/FindInstancesProcess.h +++ b/code/FindInstancesProcess.h @@ -60,9 +60,9 @@ namespace Assimp { * @param in Input mesh * @return Hash. */ -inline uint64_t GetMeshHash(aiMesh* in) -{ - ai_assert(NULL != in); +inline +uint64_t GetMeshHash(aiMesh* in) { + ai_assert(nullptr != in); // ... get an unique value representing the vertex format of the mesh const unsigned int fhash = GetMeshVFormatUnique(in); @@ -78,14 +78,14 @@ inline uint64_t GetMeshHash(aiMesh* in) /** @brief Perform a component-wise comparison of two arrays * * @param first First array - * @param second Second aray + * @param second Second array * @param size Size of both arrays * @param e Epsilon * @return true if the arrays are identical */ -inline bool CompareArrays(const aiVector3D* first, const aiVector3D* second, - unsigned int size, float e) -{ +inline +bool CompareArrays(const aiVector3D* first, const aiVector3D* second, + unsigned int size, float e) { for (const aiVector3D* end = first+size; first != end; ++first,++second) { if ( (*first - *second).SquareLength() >= e) return false; diff --git a/code/IRRLoader.cpp b/code/IRRLoader.cpp index fbec3da00..66d15c5c4 100644 --- a/code/IRRLoader.cpp +++ b/code/IRRLoader.cpp @@ -100,26 +100,22 @@ IRRImporter::~IRRImporter() // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool IRRImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ - /* NOTE: A simple check for the file extension is not enough - * here. Irrmesh and irr are easy, but xml is too generic - * and could be collada, too. So we need to open the file and - * search for typical tokens. - */ +bool IRRImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { const std::string extension = GetExtension(pFile); - - if (extension == "irr")return true; - else if (extension == "xml" || checkSig) - { + if ( extension == "irr" ) { + return true; + } else if (extension == "xml" || checkSig) { /* If CanRead() is called in order to check whether we * support a specific file extension in general pIOHandler * might be NULL and it's our duty to return true here. */ - if (!pIOHandler)return true; + if ( nullptr == pIOHandler ) { + return true; + } const char* tokens[] = {"irr_scene"}; return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); } + return false; } diff --git a/code/Importer.cpp b/code/Importer.cpp index 4ebc72420..6422ca6d0 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -190,7 +190,7 @@ Importer::~Importer() delete pimpl->mIOHandler; delete pimpl->mProgressHandler; - // Kill imported scene. Destructors should do that recursivly + // Kill imported scene. Destructor's should do that recursively delete pimpl->mScene; // Delete shared post-processing data @@ -200,18 +200,6 @@ Importer::~Importer() delete pimpl; } -// ------------------------------------------------------------------------------------------------ -// Copy constructor - copies the config of another Importer, not the scene -Importer::Importer(const Importer &other) - : pimpl(NULL) { - new(this) Importer(); - - pimpl->mIntProperties = other.pimpl->mIntProperties; - pimpl->mFloatProperties = other.pimpl->mFloatProperties; - pimpl->mStringProperties = other.pimpl->mStringProperties; - pimpl->mMatrixProperties = other.pimpl->mMatrixProperties; -} - // ------------------------------------------------------------------------------------------------ // Register a custom post-processing step aiReturn Importer::RegisterPPStep(BaseProcess* pImp) @@ -634,7 +622,6 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) if (s != std::string::npos) { DefaultLogger::get()->info("File extension not known, trying signature-based detection"); for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { - if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) { imp = pimpl->mImporter[a]; break; @@ -959,6 +946,7 @@ BaseImporter* Importer::GetImporter (const char* szExtension) const size_t Importer::GetImporterIndex (const char* szExtension) const { ai_assert(szExtension); + ASSIMP_BEGIN_EXCEPTION_REGION(); // skip over wildcard and dot characters at string head -- diff --git a/code/Importer/IFC/IFCLoader.cpp b/code/Importer/IFC/IFCLoader.cpp index de7a37037..9faf68cbb 100644 --- a/code/Importer/IFC/IFCLoader.cpp +++ b/code/Importer/IFC/IFCLoader.cpp @@ -141,7 +141,8 @@ bool IFCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool // it is only unambiguous as long as we don't support any further // file formats with STEP as their encoding. const char* tokens[] = {"ISO-10303-21"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); + const bool found( SearchFileHeaderForToken( pIOHandler, pFile, tokens, 1 ) ); + return found; } return false; } @@ -582,9 +583,8 @@ typedef std::map Metadata; // ------------------------------------------------------------------------------------------------ void ProcessMetadata(const Schema_2x3::ListOf< Schema_2x3::Lazy< Schema_2x3::IfcProperty >, 1, 0 >& set, ConversionData& conv, Metadata& properties, - const std::string& prefix = "", - unsigned int nest = 0) -{ + const std::string& prefix = "", + unsigned int nest = 0) { for(const Schema_2x3::IfcProperty& property : set) { const std::string& key = prefix.length() > 0 ? (prefix + "." + property.Name) : property.Name; if (const Schema_2x3::IfcPropertySingleValue* const singleValue = property.ToPtr()) { diff --git a/code/Importer/IFC/IFCReaderGen_4.cpp b/code/Importer/IFC/IFCReaderGen_4.cpp index eb6182674..7a312e691 100644 --- a/code/Importer/IFC/IFCReaderGen_4.cpp +++ b/code/Importer/IFC/IFCReaderGen_4.cpp @@ -40,13 +40,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** MACHINE-GENERATED by scripts/ICFImporter/CppGenerator.py */ -#include "AssimpPCH.h" #ifndef ASSIMP_BUILD_NO_IFC_IMPORTER -#include "IFCReaderGen.h" +#include "AssimpPCH.h" +#include "IFCReaderGen4.h" namespace Assimp { using namespace IFC; +using namespace ::Assimp::IFC::Schema_4; namespace { diff --git a/code/Importer/IFC/IFCReaderGen_4.h b/code/Importer/IFC/IFCReaderGen_4.h index 6c0b5a283..ccfcb6ea0 100644 --- a/code/Importer/IFC/IFCReaderGen_4.h +++ b/code/Importer/IFC/IFCReaderGen_4.h @@ -47,6 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace IFC { +namespace Schema_4 { + using namespace STEP; using namespace STEP::EXPRESS; @@ -4866,583 +4868,584 @@ namespace IFC { } //! IFC namespace STEP { - // ****************************************************************************** - // Converter stubs - // ****************************************************************************** - + // ****************************************************************************** + // Converter stubs + // ****************************************************************************** + #define DECL_CONV_STUB(type) template <> size_t GenericFill(const STEP::DB& db, const EXPRESS::LIST& params, IFC::type* in) - - DECL_CONV_STUB(IfcRoot); - DECL_CONV_STUB(IfcObjectDefinition); - DECL_CONV_STUB(IfcObject); - DECL_CONV_STUB(IfcControl); - DECL_CONV_STUB(IfcActionRequest); - DECL_CONV_STUB(IfcActor); - DECL_CONV_STUB(IfcProduct); - DECL_CONV_STUB(IfcElement); - DECL_CONV_STUB(IfcDistributionElement); - DECL_CONV_STUB(IfcDistributionControlElement); - DECL_CONV_STUB(IfcActuator); - DECL_CONV_STUB(IfcTypeObject); - DECL_CONV_STUB(IfcTypeProduct); - DECL_CONV_STUB(IfcElementType); - DECL_CONV_STUB(IfcDistributionElementType); - DECL_CONV_STUB(IfcDistributionControlElementType); - DECL_CONV_STUB(IfcActuatorType); - DECL_CONV_STUB(IfcRepresentationItem); - DECL_CONV_STUB(IfcGeometricRepresentationItem); - DECL_CONV_STUB(IfcSolidModel); - DECL_CONV_STUB(IfcManifoldSolidBrep); - DECL_CONV_STUB(IfcAdvancedBrep); - DECL_CONV_STUB(IfcAdvancedBrepWithVoids); - DECL_CONV_STUB(IfcTopologicalRepresentationItem); - DECL_CONV_STUB(IfcFace); - DECL_CONV_STUB(IfcFaceSurface); - DECL_CONV_STUB(IfcAdvancedFace); - DECL_CONV_STUB(IfcDistributionFlowElement); - DECL_CONV_STUB(IfcFlowTerminal); - DECL_CONV_STUB(IfcAirTerminal); - DECL_CONV_STUB(IfcFlowController); - DECL_CONV_STUB(IfcAirTerminalBox); - DECL_CONV_STUB(IfcDistributionFlowElementType); - DECL_CONV_STUB(IfcFlowControllerType); - DECL_CONV_STUB(IfcAirTerminalBoxType); - DECL_CONV_STUB(IfcFlowTerminalType); - DECL_CONV_STUB(IfcAirTerminalType); - DECL_CONV_STUB(IfcEnergyConversionDevice); - DECL_CONV_STUB(IfcAirToAirHeatRecovery); - DECL_CONV_STUB(IfcEnergyConversionDeviceType); - DECL_CONV_STUB(IfcAirToAirHeatRecoveryType); - DECL_CONV_STUB(IfcAlarm); - DECL_CONV_STUB(IfcAlarmType); - DECL_CONV_STUB(IfcAnnotation); - DECL_CONV_STUB(IfcAnnotationFillArea); - DECL_CONV_STUB(IfcProfileDef); - DECL_CONV_STUB(IfcArbitraryClosedProfileDef); - DECL_CONV_STUB(IfcArbitraryOpenProfileDef); - DECL_CONV_STUB(IfcArbitraryProfileDefWithVoids); - DECL_CONV_STUB(IfcGroup); - DECL_CONV_STUB(IfcAsset); - DECL_CONV_STUB(IfcParameterizedProfileDef); - DECL_CONV_STUB(IfcAsymmetricIShapeProfileDef); - DECL_CONV_STUB(IfcAudioVisualAppliance); - DECL_CONV_STUB(IfcAudioVisualApplianceType); - DECL_CONV_STUB(IfcPlacement); - DECL_CONV_STUB(IfcAxis1Placement); - DECL_CONV_STUB(IfcAxis2Placement2D); - DECL_CONV_STUB(IfcAxis2Placement3D); - DECL_CONV_STUB(IfcCurve); - DECL_CONV_STUB(IfcBoundedCurve); - DECL_CONV_STUB(IfcBSplineCurve); - DECL_CONV_STUB(IfcBSplineCurveWithKnots); - DECL_CONV_STUB(IfcSurface); - DECL_CONV_STUB(IfcBoundedSurface); - DECL_CONV_STUB(IfcBSplineSurface); - DECL_CONV_STUB(IfcBSplineSurfaceWithKnots); - DECL_CONV_STUB(IfcBuildingElement); - DECL_CONV_STUB(IfcBeam); - DECL_CONV_STUB(IfcBeamStandardCase); - DECL_CONV_STUB(IfcBuildingElementType); - DECL_CONV_STUB(IfcBeamType); - DECL_CONV_STUB(IfcPresentationItem); - DECL_CONV_STUB(IfcCsgPrimitive3D); - DECL_CONV_STUB(IfcBlock); - DECL_CONV_STUB(IfcBoiler); - DECL_CONV_STUB(IfcBoilerType); - DECL_CONV_STUB(IfcBooleanResult); - DECL_CONV_STUB(IfcBooleanClippingResult); - DECL_CONV_STUB(IfcCompositeCurve); - DECL_CONV_STUB(IfcCompositeCurveOnSurface); - DECL_CONV_STUB(IfcBoundaryCurve); - DECL_CONV_STUB(IfcBoundingBox); - DECL_CONV_STUB(IfcHalfSpaceSolid); - DECL_CONV_STUB(IfcBoxedHalfSpace); - DECL_CONV_STUB(IfcSpatialElement); - DECL_CONV_STUB(IfcSpatialStructureElement); - DECL_CONV_STUB(IfcBuilding); - DECL_CONV_STUB(IfcElementComponent); - DECL_CONV_STUB(IfcBuildingElementPart); - DECL_CONV_STUB(IfcElementComponentType); - DECL_CONV_STUB(IfcBuildingElementPartType); - DECL_CONV_STUB(IfcBuildingElementProxy); - DECL_CONV_STUB(IfcBuildingElementProxyType); - DECL_CONV_STUB(IfcBuildingStorey); - DECL_CONV_STUB(IfcSystem); - DECL_CONV_STUB(IfcBuildingSystem); - DECL_CONV_STUB(IfcBurner); - DECL_CONV_STUB(IfcBurnerType); - DECL_CONV_STUB(IfcCShapeProfileDef); - DECL_CONV_STUB(IfcFlowFitting); - DECL_CONV_STUB(IfcCableCarrierFitting); - DECL_CONV_STUB(IfcFlowFittingType); - DECL_CONV_STUB(IfcCableCarrierFittingType); - DECL_CONV_STUB(IfcFlowSegment); - DECL_CONV_STUB(IfcCableCarrierSegment); - DECL_CONV_STUB(IfcFlowSegmentType); - DECL_CONV_STUB(IfcCableCarrierSegmentType); - DECL_CONV_STUB(IfcCableFitting); - DECL_CONV_STUB(IfcCableFittingType); - DECL_CONV_STUB(IfcCableSegment); - DECL_CONV_STUB(IfcCableSegmentType); - DECL_CONV_STUB(IfcPoint); - DECL_CONV_STUB(IfcCartesianPoint); - DECL_CONV_STUB(IfcCartesianPointList); - DECL_CONV_STUB(IfcCartesianPointList2D); - DECL_CONV_STUB(IfcCartesianPointList3D); - DECL_CONV_STUB(IfcCartesianTransformationOperator); - DECL_CONV_STUB(IfcCartesianTransformationOperator2D); - DECL_CONV_STUB(IfcCartesianTransformationOperator2DnonUniform); - DECL_CONV_STUB(IfcCartesianTransformationOperator3D); - DECL_CONV_STUB(IfcCartesianTransformationOperator3DnonUniform); - DECL_CONV_STUB(IfcCenterLineProfileDef); - DECL_CONV_STUB(IfcChiller); - DECL_CONV_STUB(IfcChillerType); - DECL_CONV_STUB(IfcChimney); - DECL_CONV_STUB(IfcChimneyType); - DECL_CONV_STUB(IfcConic); - DECL_CONV_STUB(IfcCircle); - DECL_CONV_STUB(IfcCircleProfileDef); - DECL_CONV_STUB(IfcCircleHollowProfileDef); - DECL_CONV_STUB(IfcCivilElement); - DECL_CONV_STUB(IfcCivilElementType); - DECL_CONV_STUB(IfcConnectedFaceSet); - DECL_CONV_STUB(IfcClosedShell); - DECL_CONV_STUB(IfcCoil); - DECL_CONV_STUB(IfcCoilType); - DECL_CONV_STUB(IfcColourSpecification); - DECL_CONV_STUB(IfcColourRgb); - DECL_CONV_STUB(IfcColumn); - DECL_CONV_STUB(IfcColumnStandardCase); - DECL_CONV_STUB(IfcColumnType); - DECL_CONV_STUB(IfcCommunicationsAppliance); - DECL_CONV_STUB(IfcCommunicationsApplianceType); - DECL_CONV_STUB(IfcPropertyAbstraction); - DECL_CONV_STUB(IfcProperty); - DECL_CONV_STUB(IfcComplexProperty); - DECL_CONV_STUB(IfcPropertyDefinition); - DECL_CONV_STUB(IfcCompositeCurveSegment); - DECL_CONV_STUB(IfcCompositeProfileDef); - DECL_CONV_STUB(IfcFlowMovingDevice); - DECL_CONV_STUB(IfcCompressor); - DECL_CONV_STUB(IfcFlowMovingDeviceType); - DECL_CONV_STUB(IfcCompressorType); - DECL_CONV_STUB(IfcCondenser); - DECL_CONV_STUB(IfcCondenserType); - DECL_CONV_STUB(IfcResource); - DECL_CONV_STUB(IfcConstructionResource); - DECL_CONV_STUB(IfcConstructionEquipmentResource); - DECL_CONV_STUB(IfcTypeResource); - DECL_CONV_STUB(IfcConstructionResourceType); - DECL_CONV_STUB(IfcConstructionEquipmentResourceType); - DECL_CONV_STUB(IfcConstructionMaterialResource); - DECL_CONV_STUB(IfcConstructionMaterialResourceType); - DECL_CONV_STUB(IfcConstructionProductResource); - DECL_CONV_STUB(IfcConstructionProductResourceType); - DECL_CONV_STUB(IfcContext); - DECL_CONV_STUB(IfcNamedUnit); - DECL_CONV_STUB(IfcContextDependentUnit); - DECL_CONV_STUB(IfcController); - DECL_CONV_STUB(IfcControllerType); - DECL_CONV_STUB(IfcConversionBasedUnit); - DECL_CONV_STUB(IfcConversionBasedUnitWithOffset); - DECL_CONV_STUB(IfcCooledBeam); - DECL_CONV_STUB(IfcCooledBeamType); - DECL_CONV_STUB(IfcCoolingTower); - DECL_CONV_STUB(IfcCoolingTowerType); - DECL_CONV_STUB(IfcCostItem); - DECL_CONV_STUB(IfcCostSchedule); - DECL_CONV_STUB(IfcCovering); - DECL_CONV_STUB(IfcCoveringType); - DECL_CONV_STUB(IfcCrewResource); - DECL_CONV_STUB(IfcCrewResourceType); - DECL_CONV_STUB(IfcCsgSolid); - DECL_CONV_STUB(IfcCurtainWall); - DECL_CONV_STUB(IfcCurtainWallType); - DECL_CONV_STUB(IfcCurveBoundedPlane); - DECL_CONV_STUB(IfcCurveBoundedSurface); - DECL_CONV_STUB(IfcPresentationStyle); - DECL_CONV_STUB(IfcElementarySurface); - DECL_CONV_STUB(IfcCylindricalSurface); - DECL_CONV_STUB(IfcDamper); - DECL_CONV_STUB(IfcDamperType); - DECL_CONV_STUB(IfcDerivedProfileDef); - DECL_CONV_STUB(IfcDirection); - DECL_CONV_STUB(IfcDiscreteAccessory); - DECL_CONV_STUB(IfcDiscreteAccessoryType); - DECL_CONV_STUB(IfcDistributionChamberElement); - DECL_CONV_STUB(IfcDistributionChamberElementType); - DECL_CONV_STUB(IfcDistributionSystem); - DECL_CONV_STUB(IfcDistributionCircuit); - DECL_CONV_STUB(IfcPort); - DECL_CONV_STUB(IfcDistributionPort); - DECL_CONV_STUB(IfcDoor); - DECL_CONV_STUB(IfcPropertySetDefinition); - DECL_CONV_STUB(IfcDoorStandardCase); - DECL_CONV_STUB(IfcDoorStyle); - DECL_CONV_STUB(IfcDoorType); - DECL_CONV_STUB(IfcDuctFitting); - DECL_CONV_STUB(IfcDuctFittingType); - DECL_CONV_STUB(IfcDuctSegment); - DECL_CONV_STUB(IfcDuctSegmentType); - DECL_CONV_STUB(IfcFlowTreatmentDevice); - DECL_CONV_STUB(IfcDuctSilencer); - DECL_CONV_STUB(IfcFlowTreatmentDeviceType); - DECL_CONV_STUB(IfcDuctSilencerType); - DECL_CONV_STUB(IfcEdge); - DECL_CONV_STUB(IfcEdgeCurve); - DECL_CONV_STUB(IfcLoop); - DECL_CONV_STUB(IfcEdgeLoop); - DECL_CONV_STUB(IfcElectricAppliance); - DECL_CONV_STUB(IfcElectricApplianceType); - DECL_CONV_STUB(IfcElectricDistributionBoard); - DECL_CONV_STUB(IfcElectricDistributionBoardType); - DECL_CONV_STUB(IfcFlowStorageDevice); - DECL_CONV_STUB(IfcElectricFlowStorageDevice); - DECL_CONV_STUB(IfcFlowStorageDeviceType); - DECL_CONV_STUB(IfcElectricFlowStorageDeviceType); - DECL_CONV_STUB(IfcElectricGenerator); - DECL_CONV_STUB(IfcElectricGeneratorType); - DECL_CONV_STUB(IfcElectricMotor); - DECL_CONV_STUB(IfcElectricMotorType); - DECL_CONV_STUB(IfcElectricTimeControl); - DECL_CONV_STUB(IfcElectricTimeControlType); - DECL_CONV_STUB(IfcElementAssembly); - DECL_CONV_STUB(IfcElementAssemblyType); - DECL_CONV_STUB(IfcQuantitySet); - DECL_CONV_STUB(IfcElementQuantity); - DECL_CONV_STUB(IfcEllipse); - DECL_CONV_STUB(IfcEllipseProfileDef); - DECL_CONV_STUB(IfcEngine); - DECL_CONV_STUB(IfcEngineType); - DECL_CONV_STUB(IfcEvaporativeCooler); - DECL_CONV_STUB(IfcEvaporativeCoolerType); - DECL_CONV_STUB(IfcEvaporator); - DECL_CONV_STUB(IfcEvaporatorType); - DECL_CONV_STUB(IfcProcess); - DECL_CONV_STUB(IfcEvent); - DECL_CONV_STUB(IfcTypeProcess); - DECL_CONV_STUB(IfcEventType); - DECL_CONV_STUB(IfcExternalSpatialStructureElement); - DECL_CONV_STUB(IfcExternalSpatialElement); - DECL_CONV_STUB(IfcSweptAreaSolid); - DECL_CONV_STUB(IfcExtrudedAreaSolid); - DECL_CONV_STUB(IfcExtrudedAreaSolidTapered); - DECL_CONV_STUB(IfcFaceBasedSurfaceModel); - DECL_CONV_STUB(IfcFaceBound); - DECL_CONV_STUB(IfcFaceOuterBound); - DECL_CONV_STUB(IfcFacetedBrep); - DECL_CONV_STUB(IfcFacetedBrepWithVoids); - DECL_CONV_STUB(IfcFan); - DECL_CONV_STUB(IfcFanType); - DECL_CONV_STUB(IfcFastener); - DECL_CONV_STUB(IfcFastenerType); - DECL_CONV_STUB(IfcFeatureElement); - DECL_CONV_STUB(IfcFeatureElementAddition); - DECL_CONV_STUB(IfcFeatureElementSubtraction); - DECL_CONV_STUB(IfcFillAreaStyleHatching); - DECL_CONV_STUB(IfcFillAreaStyleTiles); - DECL_CONV_STUB(IfcFilter); - DECL_CONV_STUB(IfcFilterType); - DECL_CONV_STUB(IfcFireSuppressionTerminal); - DECL_CONV_STUB(IfcFireSuppressionTerminalType); - DECL_CONV_STUB(IfcFixedReferenceSweptAreaSolid); - DECL_CONV_STUB(IfcFlowInstrument); - DECL_CONV_STUB(IfcFlowInstrumentType); - DECL_CONV_STUB(IfcFlowMeter); - DECL_CONV_STUB(IfcFlowMeterType); - DECL_CONV_STUB(IfcFooting); - DECL_CONV_STUB(IfcFootingType); - DECL_CONV_STUB(IfcFurnishingElement); - DECL_CONV_STUB(IfcFurnishingElementType); - DECL_CONV_STUB(IfcFurniture); - DECL_CONV_STUB(IfcFurnitureType); - DECL_CONV_STUB(IfcGeographicElement); - DECL_CONV_STUB(IfcGeographicElementType); - DECL_CONV_STUB(IfcGeometricSet); - DECL_CONV_STUB(IfcGeometricCurveSet); - DECL_CONV_STUB(IfcRepresentationContext); - DECL_CONV_STUB(IfcGeometricRepresentationContext); - DECL_CONV_STUB(IfcGeometricRepresentationSubContext); - DECL_CONV_STUB(IfcGrid); - DECL_CONV_STUB(IfcObjectPlacement); - DECL_CONV_STUB(IfcGridPlacement); - DECL_CONV_STUB(IfcHeatExchanger); - DECL_CONV_STUB(IfcHeatExchangerType); - DECL_CONV_STUB(IfcHumidifier); - DECL_CONV_STUB(IfcHumidifierType); - DECL_CONV_STUB(IfcIShapeProfileDef); - DECL_CONV_STUB(IfcIndexedPolyCurve); - DECL_CONV_STUB(IfcTessellatedItem); - DECL_CONV_STUB(IfcIndexedPolygonalFace); - DECL_CONV_STUB(IfcIndexedPolygonalFaceWithVoids); - DECL_CONV_STUB(IfcInterceptor); - DECL_CONV_STUB(IfcInterceptorType); - DECL_CONV_STUB(IfcSurfaceCurve); - DECL_CONV_STUB(IfcIntersectionCurve); - DECL_CONV_STUB(IfcInventory); - DECL_CONV_STUB(IfcJunctionBox); - DECL_CONV_STUB(IfcJunctionBoxType); - DECL_CONV_STUB(IfcLShapeProfileDef); - DECL_CONV_STUB(IfcLaborResource); - DECL_CONV_STUB(IfcLaborResourceType); - DECL_CONV_STUB(IfcLamp); - DECL_CONV_STUB(IfcLampType); - DECL_CONV_STUB(IfcLightFixture); - DECL_CONV_STUB(IfcLightFixtureType); - DECL_CONV_STUB(IfcLightSource); - DECL_CONV_STUB(IfcLightSourceAmbient); - DECL_CONV_STUB(IfcLightSourceDirectional); - DECL_CONV_STUB(IfcLightSourceGoniometric); - DECL_CONV_STUB(IfcLightSourcePositional); - DECL_CONV_STUB(IfcLightSourceSpot); - DECL_CONV_STUB(IfcLine); - DECL_CONV_STUB(IfcLocalPlacement); - DECL_CONV_STUB(IfcMappedItem); - DECL_CONV_STUB(IfcProductRepresentation); - DECL_CONV_STUB(IfcMaterialDefinitionRepresentation); - DECL_CONV_STUB(IfcMeasureWithUnit); - DECL_CONV_STUB(IfcMechanicalFastener); - DECL_CONV_STUB(IfcMechanicalFastenerType); - DECL_CONV_STUB(IfcMedicalDevice); - DECL_CONV_STUB(IfcMedicalDeviceType); - DECL_CONV_STUB(IfcMember); - DECL_CONV_STUB(IfcMemberStandardCase); - DECL_CONV_STUB(IfcMemberType); - DECL_CONV_STUB(IfcMirroredProfileDef); - DECL_CONV_STUB(IfcMotorConnection); - DECL_CONV_STUB(IfcMotorConnectionType); - DECL_CONV_STUB(IfcOccupant); - DECL_CONV_STUB(IfcOffsetCurve2D); - DECL_CONV_STUB(IfcOffsetCurve3D); - DECL_CONV_STUB(IfcOpenShell); - DECL_CONV_STUB(IfcOpeningElement); - DECL_CONV_STUB(IfcOpeningStandardCase); - DECL_CONV_STUB(IfcOrientedEdge); - DECL_CONV_STUB(IfcOuterBoundaryCurve); - DECL_CONV_STUB(IfcOutlet); - DECL_CONV_STUB(IfcOutletType); - DECL_CONV_STUB(IfcPath); - DECL_CONV_STUB(IfcPcurve); - DECL_CONV_STUB(IfcPerformanceHistory); - DECL_CONV_STUB(IfcPermit); - DECL_CONV_STUB(IfcPile); - DECL_CONV_STUB(IfcPileType); - DECL_CONV_STUB(IfcPipeFitting); - DECL_CONV_STUB(IfcPipeFittingType); - DECL_CONV_STUB(IfcPipeSegment); - DECL_CONV_STUB(IfcPipeSegmentType); - DECL_CONV_STUB(IfcPlanarExtent); - DECL_CONV_STUB(IfcPlanarBox); - DECL_CONV_STUB(IfcPlane); - DECL_CONV_STUB(IfcPlate); - DECL_CONV_STUB(IfcPlateStandardCase); - DECL_CONV_STUB(IfcPlateType); - DECL_CONV_STUB(IfcPointOnCurve); - DECL_CONV_STUB(IfcPointOnSurface); - DECL_CONV_STUB(IfcPolyLoop); - DECL_CONV_STUB(IfcPolygonalBoundedHalfSpace); - DECL_CONV_STUB(IfcTessellatedFaceSet); - DECL_CONV_STUB(IfcPolygonalFaceSet); - DECL_CONV_STUB(IfcPolyline); - DECL_CONV_STUB(IfcPresentationStyleAssignment); - DECL_CONV_STUB(IfcProcedure); - DECL_CONV_STUB(IfcProcedureType); - DECL_CONV_STUB(IfcProductDefinitionShape); - DECL_CONV_STUB(IfcProject); - DECL_CONV_STUB(IfcProjectLibrary); - DECL_CONV_STUB(IfcProjectOrder); - DECL_CONV_STUB(IfcProjectionElement); - DECL_CONV_STUB(IfcSimpleProperty); - DECL_CONV_STUB(IfcPropertyBoundedValue); - DECL_CONV_STUB(IfcPropertyEnumeratedValue); - DECL_CONV_STUB(IfcPropertyListValue); - DECL_CONV_STUB(IfcPropertyReferenceValue); - DECL_CONV_STUB(IfcPropertySet); - DECL_CONV_STUB(IfcPropertySingleValue); - DECL_CONV_STUB(IfcPropertyTableValue); - DECL_CONV_STUB(IfcProtectiveDevice); - DECL_CONV_STUB(IfcProtectiveDeviceTrippingUnit); - DECL_CONV_STUB(IfcProtectiveDeviceTrippingUnitType); - DECL_CONV_STUB(IfcProtectiveDeviceType); - DECL_CONV_STUB(IfcProxy); - DECL_CONV_STUB(IfcPump); - DECL_CONV_STUB(IfcPumpType); - DECL_CONV_STUB(IfcRailing); - DECL_CONV_STUB(IfcRailingType); - DECL_CONV_STUB(IfcRamp); - DECL_CONV_STUB(IfcRampFlight); - DECL_CONV_STUB(IfcRampFlightType); - DECL_CONV_STUB(IfcRampType); - DECL_CONV_STUB(IfcRationalBSplineCurveWithKnots); - DECL_CONV_STUB(IfcRationalBSplineSurfaceWithKnots); - DECL_CONV_STUB(IfcRectangleProfileDef); - DECL_CONV_STUB(IfcRectangleHollowProfileDef); - DECL_CONV_STUB(IfcRectangularPyramid); - DECL_CONV_STUB(IfcRectangularTrimmedSurface); - DECL_CONV_STUB(IfcReinforcingElement); - DECL_CONV_STUB(IfcReinforcingBar); - DECL_CONV_STUB(IfcReinforcingElementType); - DECL_CONV_STUB(IfcReinforcingBarType); - DECL_CONV_STUB(IfcReinforcingMesh); - DECL_CONV_STUB(IfcReinforcingMeshType); - DECL_CONV_STUB(IfcRelationship); - DECL_CONV_STUB(IfcRelDecomposes); - DECL_CONV_STUB(IfcRelAggregates); - DECL_CONV_STUB(IfcRelConnects); - DECL_CONV_STUB(IfcRelContainedInSpatialStructure); - DECL_CONV_STUB(IfcRelDefines); - DECL_CONV_STUB(IfcRelDefinesByProperties); - DECL_CONV_STUB(IfcRelFillsElement); - DECL_CONV_STUB(IfcRelVoidsElement); - DECL_CONV_STUB(IfcReparametrisedCompositeCurveSegment); - DECL_CONV_STUB(IfcRepresentation); - DECL_CONV_STUB(IfcRepresentationMap); - DECL_CONV_STUB(IfcRevolvedAreaSolid); - DECL_CONV_STUB(IfcRevolvedAreaSolidTapered); - DECL_CONV_STUB(IfcRightCircularCone); - DECL_CONV_STUB(IfcRightCircularCylinder); - DECL_CONV_STUB(IfcRoof); - DECL_CONV_STUB(IfcRoofType); - DECL_CONV_STUB(IfcRoundedRectangleProfileDef); - DECL_CONV_STUB(IfcSIUnit); - DECL_CONV_STUB(IfcSanitaryTerminal); - DECL_CONV_STUB(IfcSanitaryTerminalType); - DECL_CONV_STUB(IfcSeamCurve); - DECL_CONV_STUB(IfcSectionedSpine); - DECL_CONV_STUB(IfcSensor); - DECL_CONV_STUB(IfcSensorType); - DECL_CONV_STUB(IfcShadingDevice); - DECL_CONV_STUB(IfcShadingDeviceType); - DECL_CONV_STUB(IfcShapeModel); - DECL_CONV_STUB(IfcShapeRepresentation); - DECL_CONV_STUB(IfcShellBasedSurfaceModel); - DECL_CONV_STUB(IfcSite); - DECL_CONV_STUB(IfcSlab); - DECL_CONV_STUB(IfcSlabElementedCase); - DECL_CONV_STUB(IfcSlabStandardCase); - DECL_CONV_STUB(IfcSlabType); - DECL_CONV_STUB(IfcSolarDevice); - DECL_CONV_STUB(IfcSolarDeviceType); - DECL_CONV_STUB(IfcSpace); - DECL_CONV_STUB(IfcSpaceHeater); - DECL_CONV_STUB(IfcSpaceHeaterType); - DECL_CONV_STUB(IfcSpatialElementType); - DECL_CONV_STUB(IfcSpatialStructureElementType); - DECL_CONV_STUB(IfcSpaceType); - DECL_CONV_STUB(IfcSpatialZone); - DECL_CONV_STUB(IfcSpatialZoneType); - DECL_CONV_STUB(IfcSphere); - DECL_CONV_STUB(IfcSphericalSurface); - DECL_CONV_STUB(IfcStackTerminal); - DECL_CONV_STUB(IfcStackTerminalType); - DECL_CONV_STUB(IfcStair); - DECL_CONV_STUB(IfcStairFlight); - DECL_CONV_STUB(IfcStairFlightType); - DECL_CONV_STUB(IfcStairType); - DECL_CONV_STUB(IfcStructuralActivity); - DECL_CONV_STUB(IfcStructuralAction); - DECL_CONV_STUB(IfcStructuralAnalysisModel); - DECL_CONV_STUB(IfcStructuralItem); - DECL_CONV_STUB(IfcStructuralConnection); - DECL_CONV_STUB(IfcStructuralCurveAction); - DECL_CONV_STUB(IfcStructuralCurveConnection); - DECL_CONV_STUB(IfcStructuralMember); - DECL_CONV_STUB(IfcStructuralCurveMember); - DECL_CONV_STUB(IfcStructuralCurveMemberVarying); - DECL_CONV_STUB(IfcStructuralReaction); - DECL_CONV_STUB(IfcStructuralCurveReaction); - DECL_CONV_STUB(IfcStructuralLinearAction); - DECL_CONV_STUB(IfcStructuralLoadGroup); - DECL_CONV_STUB(IfcStructuralLoadCase); - DECL_CONV_STUB(IfcStructuralSurfaceAction); - DECL_CONV_STUB(IfcStructuralPlanarAction); - DECL_CONV_STUB(IfcStructuralPointAction); - DECL_CONV_STUB(IfcStructuralPointConnection); - DECL_CONV_STUB(IfcStructuralPointReaction); - DECL_CONV_STUB(IfcStructuralResultGroup); - DECL_CONV_STUB(IfcStructuralSurfaceConnection); - DECL_CONV_STUB(IfcStructuralSurfaceMember); - DECL_CONV_STUB(IfcStructuralSurfaceMemberVarying); - DECL_CONV_STUB(IfcStructuralSurfaceReaction); - DECL_CONV_STUB(IfcStyleModel); - DECL_CONV_STUB(IfcStyledItem); - DECL_CONV_STUB(IfcStyledRepresentation); - DECL_CONV_STUB(IfcSubContractResource); - DECL_CONV_STUB(IfcSubContractResourceType); - DECL_CONV_STUB(IfcSubedge); - DECL_CONV_STUB(IfcSurfaceCurveSweptAreaSolid); - DECL_CONV_STUB(IfcSurfaceFeature); - DECL_CONV_STUB(IfcSweptSurface); - DECL_CONV_STUB(IfcSurfaceOfLinearExtrusion); - DECL_CONV_STUB(IfcSurfaceOfRevolution); - DECL_CONV_STUB(IfcSurfaceStyle); - DECL_CONV_STUB(IfcSurfaceStyleShading); - DECL_CONV_STUB(IfcSurfaceStyleRendering); - DECL_CONV_STUB(IfcSurfaceStyleWithTextures); - DECL_CONV_STUB(IfcSweptDiskSolid); - DECL_CONV_STUB(IfcSweptDiskSolidPolygonal); - DECL_CONV_STUB(IfcSwitchingDevice); - DECL_CONV_STUB(IfcSwitchingDeviceType); - DECL_CONV_STUB(IfcSystemFurnitureElement); - DECL_CONV_STUB(IfcSystemFurnitureElementType); - DECL_CONV_STUB(IfcTShapeProfileDef); - DECL_CONV_STUB(IfcTank); - DECL_CONV_STUB(IfcTankType); - DECL_CONV_STUB(IfcTask); - DECL_CONV_STUB(IfcTaskType); - DECL_CONV_STUB(IfcTendon); - DECL_CONV_STUB(IfcTendonAnchor); - DECL_CONV_STUB(IfcTendonAnchorType); - DECL_CONV_STUB(IfcTendonType); - DECL_CONV_STUB(IfcTextLiteral); - DECL_CONV_STUB(IfcTextLiteralWithExtent); - DECL_CONV_STUB(IfcTopologyRepresentation); - DECL_CONV_STUB(IfcToroidalSurface); - DECL_CONV_STUB(IfcTransformer); - DECL_CONV_STUB(IfcTransformerType); - DECL_CONV_STUB(IfcTransportElement); - DECL_CONV_STUB(IfcTransportElementType); - DECL_CONV_STUB(IfcTrapeziumProfileDef); - DECL_CONV_STUB(IfcTriangulatedFaceSet); - DECL_CONV_STUB(IfcTrimmedCurve); - DECL_CONV_STUB(IfcTubeBundle); - DECL_CONV_STUB(IfcTubeBundleType); - DECL_CONV_STUB(IfcUShapeProfileDef); - DECL_CONV_STUB(IfcUnitAssignment); - DECL_CONV_STUB(IfcUnitaryControlElement); - DECL_CONV_STUB(IfcUnitaryControlElementType); - DECL_CONV_STUB(IfcUnitaryEquipment); - DECL_CONV_STUB(IfcUnitaryEquipmentType); - DECL_CONV_STUB(IfcValve); - DECL_CONV_STUB(IfcValveType); - DECL_CONV_STUB(IfcVector); - DECL_CONV_STUB(IfcVertex); - DECL_CONV_STUB(IfcVertexLoop); - DECL_CONV_STUB(IfcVertexPoint); - DECL_CONV_STUB(IfcVibrationIsolator); - DECL_CONV_STUB(IfcVibrationIsolatorType); - DECL_CONV_STUB(IfcVirtualElement); - DECL_CONV_STUB(IfcVoidingFeature); - DECL_CONV_STUB(IfcWall); - DECL_CONV_STUB(IfcWallElementedCase); - DECL_CONV_STUB(IfcWallStandardCase); - DECL_CONV_STUB(IfcWallType); - DECL_CONV_STUB(IfcWasteTerminal); - DECL_CONV_STUB(IfcWasteTerminalType); - DECL_CONV_STUB(IfcWindow); - DECL_CONV_STUB(IfcWindowStandardCase); - DECL_CONV_STUB(IfcWindowStyle); - DECL_CONV_STUB(IfcWindowType); - DECL_CONV_STUB(IfcWorkCalendar); - DECL_CONV_STUB(IfcWorkControl); - DECL_CONV_STUB(IfcWorkPlan); - DECL_CONV_STUB(IfcWorkSchedule); - DECL_CONV_STUB(IfcZShapeProfileDef); - DECL_CONV_STUB(IfcZone); + + DECL_CONV_STUB( IfcRoot ); + DECL_CONV_STUB( IfcObjectDefinition ); + DECL_CONV_STUB( IfcObject ); + DECL_CONV_STUB( IfcControl ); + DECL_CONV_STUB( IfcActionRequest ); + DECL_CONV_STUB( IfcActor ); + DECL_CONV_STUB( IfcProduct ); + DECL_CONV_STUB( IfcElement ); + DECL_CONV_STUB( IfcDistributionElement ); + DECL_CONV_STUB( IfcDistributionControlElement ); + DECL_CONV_STUB( IfcActuator ); + DECL_CONV_STUB( IfcTypeObject ); + DECL_CONV_STUB( IfcTypeProduct ); + DECL_CONV_STUB( IfcElementType ); + DECL_CONV_STUB( IfcDistributionElementType ); + DECL_CONV_STUB( IfcDistributionControlElementType ); + DECL_CONV_STUB( IfcActuatorType ); + DECL_CONV_STUB( IfcRepresentationItem ); + DECL_CONV_STUB( IfcGeometricRepresentationItem ); + DECL_CONV_STUB( IfcSolidModel ); + DECL_CONV_STUB( IfcManifoldSolidBrep ); + DECL_CONV_STUB( IfcAdvancedBrep ); + DECL_CONV_STUB( IfcAdvancedBrepWithVoids ); + DECL_CONV_STUB( IfcTopologicalRepresentationItem ); + DECL_CONV_STUB( IfcFace ); + DECL_CONV_STUB( IfcFaceSurface ); + DECL_CONV_STUB( IfcAdvancedFace ); + DECL_CONV_STUB( IfcDistributionFlowElement ); + DECL_CONV_STUB( IfcFlowTerminal ); + DECL_CONV_STUB( IfcAirTerminal ); + DECL_CONV_STUB( IfcFlowController ); + DECL_CONV_STUB( IfcAirTerminalBox ); + DECL_CONV_STUB( IfcDistributionFlowElementType ); + DECL_CONV_STUB( IfcFlowControllerType ); + DECL_CONV_STUB( IfcAirTerminalBoxType ); + DECL_CONV_STUB( IfcFlowTerminalType ); + DECL_CONV_STUB( IfcAirTerminalType ); + DECL_CONV_STUB( IfcEnergyConversionDevice ); + DECL_CONV_STUB( IfcAirToAirHeatRecovery ); + DECL_CONV_STUB( IfcEnergyConversionDeviceType ); + DECL_CONV_STUB( IfcAirToAirHeatRecoveryType ); + DECL_CONV_STUB( IfcAlarm ); + DECL_CONV_STUB( IfcAlarmType ); + DECL_CONV_STUB( IfcAnnotation ); + DECL_CONV_STUB( IfcAnnotationFillArea ); + DECL_CONV_STUB( IfcProfileDef ); + DECL_CONV_STUB( IfcArbitraryClosedProfileDef ); + DECL_CONV_STUB( IfcArbitraryOpenProfileDef ); + DECL_CONV_STUB( IfcArbitraryProfileDefWithVoids ); + DECL_CONV_STUB( IfcGroup ); + DECL_CONV_STUB( IfcAsset ); + DECL_CONV_STUB( IfcParameterizedProfileDef ); + DECL_CONV_STUB( IfcAsymmetricIShapeProfileDef ); + DECL_CONV_STUB( IfcAudioVisualAppliance ); + DECL_CONV_STUB( IfcAudioVisualApplianceType ); + DECL_CONV_STUB( IfcPlacement ); + DECL_CONV_STUB( IfcAxis1Placement ); + DECL_CONV_STUB( IfcAxis2Placement2D ); + DECL_CONV_STUB( IfcAxis2Placement3D ); + DECL_CONV_STUB( IfcCurve ); + DECL_CONV_STUB( IfcBoundedCurve ); + DECL_CONV_STUB( IfcBSplineCurve ); + DECL_CONV_STUB( IfcBSplineCurveWithKnots ); + DECL_CONV_STUB( IfcSurface ); + DECL_CONV_STUB( IfcBoundedSurface ); + DECL_CONV_STUB( IfcBSplineSurface ); + DECL_CONV_STUB( IfcBSplineSurfaceWithKnots ); + DECL_CONV_STUB( IfcBuildingElement ); + DECL_CONV_STUB( IfcBeam ); + DECL_CONV_STUB( IfcBeamStandardCase ); + DECL_CONV_STUB( IfcBuildingElementType ); + DECL_CONV_STUB( IfcBeamType ); + DECL_CONV_STUB( IfcPresentationItem ); + DECL_CONV_STUB( IfcCsgPrimitive3D ); + DECL_CONV_STUB( IfcBlock ); + DECL_CONV_STUB( IfcBoiler ); + DECL_CONV_STUB( IfcBoilerType ); + DECL_CONV_STUB( IfcBooleanResult ); + DECL_CONV_STUB( IfcBooleanClippingResult ); + DECL_CONV_STUB( IfcCompositeCurve ); + DECL_CONV_STUB( IfcCompositeCurveOnSurface ); + DECL_CONV_STUB( IfcBoundaryCurve ); + DECL_CONV_STUB( IfcBoundingBox ); + DECL_CONV_STUB( IfcHalfSpaceSolid ); + DECL_CONV_STUB( IfcBoxedHalfSpace ); + DECL_CONV_STUB( IfcSpatialElement ); + DECL_CONV_STUB( IfcSpatialStructureElement ); + DECL_CONV_STUB( IfcBuilding ); + DECL_CONV_STUB( IfcElementComponent ); + DECL_CONV_STUB( IfcBuildingElementPart ); + DECL_CONV_STUB( IfcElementComponentType ); + DECL_CONV_STUB( IfcBuildingElementPartType ); + DECL_CONV_STUB( IfcBuildingElementProxy ); + DECL_CONV_STUB( IfcBuildingElementProxyType ); + DECL_CONV_STUB( IfcBuildingStorey ); + DECL_CONV_STUB( IfcSystem ); + DECL_CONV_STUB( IfcBuildingSystem ); + DECL_CONV_STUB( IfcBurner ); + DECL_CONV_STUB( IfcBurnerType ); + DECL_CONV_STUB( IfcCShapeProfileDef ); + DECL_CONV_STUB( IfcFlowFitting ); + DECL_CONV_STUB( IfcCableCarrierFitting ); + DECL_CONV_STUB( IfcFlowFittingType ); + DECL_CONV_STUB( IfcCableCarrierFittingType ); + DECL_CONV_STUB( IfcFlowSegment ); + DECL_CONV_STUB( IfcCableCarrierSegment ); + DECL_CONV_STUB( IfcFlowSegmentType ); + DECL_CONV_STUB( IfcCableCarrierSegmentType ); + DECL_CONV_STUB( IfcCableFitting ); + DECL_CONV_STUB( IfcCableFittingType ); + DECL_CONV_STUB( IfcCableSegment ); + DECL_CONV_STUB( IfcCableSegmentType ); + DECL_CONV_STUB( IfcPoint ); + DECL_CONV_STUB( IfcCartesianPoint ); + DECL_CONV_STUB( IfcCartesianPointList ); + DECL_CONV_STUB( IfcCartesianPointList2D ); + DECL_CONV_STUB( IfcCartesianPointList3D ); + DECL_CONV_STUB( IfcCartesianTransformationOperator ); + DECL_CONV_STUB( IfcCartesianTransformationOperator2D ); + DECL_CONV_STUB( IfcCartesianTransformationOperator2DnonUniform ); + DECL_CONV_STUB( IfcCartesianTransformationOperator3D ); + DECL_CONV_STUB( IfcCartesianTransformationOperator3DnonUniform ); + DECL_CONV_STUB( IfcCenterLineProfileDef ); + DECL_CONV_STUB( IfcChiller ); + DECL_CONV_STUB( IfcChillerType ); + DECL_CONV_STUB( IfcChimney ); + DECL_CONV_STUB( IfcChimneyType ); + DECL_CONV_STUB( IfcConic ); + DECL_CONV_STUB( IfcCircle ); + DECL_CONV_STUB( IfcCircleProfileDef ); + DECL_CONV_STUB( IfcCircleHollowProfileDef ); + DECL_CONV_STUB( IfcCivilElement ); + DECL_CONV_STUB( IfcCivilElementType ); + DECL_CONV_STUB( IfcConnectedFaceSet ); + DECL_CONV_STUB( IfcClosedShell ); + DECL_CONV_STUB( IfcCoil ); + DECL_CONV_STUB( IfcCoilType ); + DECL_CONV_STUB( IfcColourSpecification ); + DECL_CONV_STUB( IfcColourRgb ); + DECL_CONV_STUB( IfcColumn ); + DECL_CONV_STUB( IfcColumnStandardCase ); + DECL_CONV_STUB( IfcColumnType ); + DECL_CONV_STUB( IfcCommunicationsAppliance ); + DECL_CONV_STUB( IfcCommunicationsApplianceType ); + DECL_CONV_STUB( IfcPropertyAbstraction ); + DECL_CONV_STUB( IfcProperty ); + DECL_CONV_STUB( IfcComplexProperty ); + DECL_CONV_STUB( IfcPropertyDefinition ); + DECL_CONV_STUB( IfcCompositeCurveSegment ); + DECL_CONV_STUB( IfcCompositeProfileDef ); + DECL_CONV_STUB( IfcFlowMovingDevice ); + DECL_CONV_STUB( IfcCompressor ); + DECL_CONV_STUB( IfcFlowMovingDeviceType ); + DECL_CONV_STUB( IfcCompressorType ); + DECL_CONV_STUB( IfcCondenser ); + DECL_CONV_STUB( IfcCondenserType ); + DECL_CONV_STUB( IfcResource ); + DECL_CONV_STUB( IfcConstructionResource ); + DECL_CONV_STUB( IfcConstructionEquipmentResource ); + DECL_CONV_STUB( IfcTypeResource ); + DECL_CONV_STUB( IfcConstructionResourceType ); + DECL_CONV_STUB( IfcConstructionEquipmentResourceType ); + DECL_CONV_STUB( IfcConstructionMaterialResource ); + DECL_CONV_STUB( IfcConstructionMaterialResourceType ); + DECL_CONV_STUB( IfcConstructionProductResource ); + DECL_CONV_STUB( IfcConstructionProductResourceType ); + DECL_CONV_STUB( IfcContext ); + DECL_CONV_STUB( IfcNamedUnit ); + DECL_CONV_STUB( IfcContextDependentUnit ); + DECL_CONV_STUB( IfcController ); + DECL_CONV_STUB( IfcControllerType ); + DECL_CONV_STUB( IfcConversionBasedUnit ); + DECL_CONV_STUB( IfcConversionBasedUnitWithOffset ); + DECL_CONV_STUB( IfcCooledBeam ); + DECL_CONV_STUB( IfcCooledBeamType ); + DECL_CONV_STUB( IfcCoolingTower ); + DECL_CONV_STUB( IfcCoolingTowerType ); + DECL_CONV_STUB( IfcCostItem ); + DECL_CONV_STUB( IfcCostSchedule ); + DECL_CONV_STUB( IfcCovering ); + DECL_CONV_STUB( IfcCoveringType ); + DECL_CONV_STUB( IfcCrewResource ); + DECL_CONV_STUB( IfcCrewResourceType ); + DECL_CONV_STUB( IfcCsgSolid ); + DECL_CONV_STUB( IfcCurtainWall ); + DECL_CONV_STUB( IfcCurtainWallType ); + DECL_CONV_STUB( IfcCurveBoundedPlane ); + DECL_CONV_STUB( IfcCurveBoundedSurface ); + DECL_CONV_STUB( IfcPresentationStyle ); + DECL_CONV_STUB( IfcElementarySurface ); + DECL_CONV_STUB( IfcCylindricalSurface ); + DECL_CONV_STUB( IfcDamper ); + DECL_CONV_STUB( IfcDamperType ); + DECL_CONV_STUB( IfcDerivedProfileDef ); + DECL_CONV_STUB( IfcDirection ); + DECL_CONV_STUB( IfcDiscreteAccessory ); + DECL_CONV_STUB( IfcDiscreteAccessoryType ); + DECL_CONV_STUB( IfcDistributionChamberElement ); + DECL_CONV_STUB( IfcDistributionChamberElementType ); + DECL_CONV_STUB( IfcDistributionSystem ); + DECL_CONV_STUB( IfcDistributionCircuit ); + DECL_CONV_STUB( IfcPort ); + DECL_CONV_STUB( IfcDistributionPort ); + DECL_CONV_STUB( IfcDoor ); + DECL_CONV_STUB( IfcPropertySetDefinition ); + DECL_CONV_STUB( IfcDoorStandardCase ); + DECL_CONV_STUB( IfcDoorStyle ); + DECL_CONV_STUB( IfcDoorType ); + DECL_CONV_STUB( IfcDuctFitting ); + DECL_CONV_STUB( IfcDuctFittingType ); + DECL_CONV_STUB( IfcDuctSegment ); + DECL_CONV_STUB( IfcDuctSegmentType ); + DECL_CONV_STUB( IfcFlowTreatmentDevice ); + DECL_CONV_STUB( IfcDuctSilencer ); + DECL_CONV_STUB( IfcFlowTreatmentDeviceType ); + DECL_CONV_STUB( IfcDuctSilencerType ); + DECL_CONV_STUB( IfcEdge ); + DECL_CONV_STUB( IfcEdgeCurve ); + DECL_CONV_STUB( IfcLoop ); + DECL_CONV_STUB( IfcEdgeLoop ); + DECL_CONV_STUB( IfcElectricAppliance ); + DECL_CONV_STUB( IfcElectricApplianceType ); + DECL_CONV_STUB( IfcElectricDistributionBoard ); + DECL_CONV_STUB( IfcElectricDistributionBoardType ); + DECL_CONV_STUB( IfcFlowStorageDevice ); + DECL_CONV_STUB( IfcElectricFlowStorageDevice ); + DECL_CONV_STUB( IfcFlowStorageDeviceType ); + DECL_CONV_STUB( IfcElectricFlowStorageDeviceType ); + DECL_CONV_STUB( IfcElectricGenerator ); + DECL_CONV_STUB( IfcElectricGeneratorType ); + DECL_CONV_STUB( IfcElectricMotor ); + DECL_CONV_STUB( IfcElectricMotorType ); + DECL_CONV_STUB( IfcElectricTimeControl ); + DECL_CONV_STUB( IfcElectricTimeControlType ); + DECL_CONV_STUB( IfcElementAssembly ); + DECL_CONV_STUB( IfcElementAssemblyType ); + DECL_CONV_STUB( IfcQuantitySet ); + DECL_CONV_STUB( IfcElementQuantity ); + DECL_CONV_STUB( IfcEllipse ); + DECL_CONV_STUB( IfcEllipseProfileDef ); + DECL_CONV_STUB( IfcEngine ); + DECL_CONV_STUB( IfcEngineType ); + DECL_CONV_STUB( IfcEvaporativeCooler ); + DECL_CONV_STUB( IfcEvaporativeCoolerType ); + DECL_CONV_STUB( IfcEvaporator ); + DECL_CONV_STUB( IfcEvaporatorType ); + DECL_CONV_STUB( IfcProcess ); + DECL_CONV_STUB( IfcEvent ); + DECL_CONV_STUB( IfcTypeProcess ); + DECL_CONV_STUB( IfcEventType ); + DECL_CONV_STUB( IfcExternalSpatialStructureElement ); + DECL_CONV_STUB( IfcExternalSpatialElement ); + DECL_CONV_STUB( IfcSweptAreaSolid ); + DECL_CONV_STUB( IfcExtrudedAreaSolid ); + DECL_CONV_STUB( IfcExtrudedAreaSolidTapered ); + DECL_CONV_STUB( IfcFaceBasedSurfaceModel ); + DECL_CONV_STUB( IfcFaceBound ); + DECL_CONV_STUB( IfcFaceOuterBound ); + DECL_CONV_STUB( IfcFacetedBrep ); + DECL_CONV_STUB( IfcFacetedBrepWithVoids ); + DECL_CONV_STUB( IfcFan ); + DECL_CONV_STUB( IfcFanType ); + DECL_CONV_STUB( IfcFastener ); + DECL_CONV_STUB( IfcFastenerType ); + DECL_CONV_STUB( IfcFeatureElement ); + DECL_CONV_STUB( IfcFeatureElementAddition ); + DECL_CONV_STUB( IfcFeatureElementSubtraction ); + DECL_CONV_STUB( IfcFillAreaStyleHatching ); + DECL_CONV_STUB( IfcFillAreaStyleTiles ); + DECL_CONV_STUB( IfcFilter ); + DECL_CONV_STUB( IfcFilterType ); + DECL_CONV_STUB( IfcFireSuppressionTerminal ); + DECL_CONV_STUB( IfcFireSuppressionTerminalType ); + DECL_CONV_STUB( IfcFixedReferenceSweptAreaSolid ); + DECL_CONV_STUB( IfcFlowInstrument ); + DECL_CONV_STUB( IfcFlowInstrumentType ); + DECL_CONV_STUB( IfcFlowMeter ); + DECL_CONV_STUB( IfcFlowMeterType ); + DECL_CONV_STUB( IfcFooting ); + DECL_CONV_STUB( IfcFootingType ); + DECL_CONV_STUB( IfcFurnishingElement ); + DECL_CONV_STUB( IfcFurnishingElementType ); + DECL_CONV_STUB( IfcFurniture ); + DECL_CONV_STUB( IfcFurnitureType ); + DECL_CONV_STUB( IfcGeographicElement ); + DECL_CONV_STUB( IfcGeographicElementType ); + DECL_CONV_STUB( IfcGeometricSet ); + DECL_CONV_STUB( IfcGeometricCurveSet ); + DECL_CONV_STUB( IfcRepresentationContext ); + DECL_CONV_STUB( IfcGeometricRepresentationContext ); + DECL_CONV_STUB( IfcGeometricRepresentationSubContext ); + DECL_CONV_STUB( IfcGrid ); + DECL_CONV_STUB( IfcObjectPlacement ); + DECL_CONV_STUB( IfcGridPlacement ); + DECL_CONV_STUB( IfcHeatExchanger ); + DECL_CONV_STUB( IfcHeatExchangerType ); + DECL_CONV_STUB( IfcHumidifier ); + DECL_CONV_STUB( IfcHumidifierType ); + DECL_CONV_STUB( IfcIShapeProfileDef ); + DECL_CONV_STUB( IfcIndexedPolyCurve ); + DECL_CONV_STUB( IfcTessellatedItem ); + DECL_CONV_STUB( IfcIndexedPolygonalFace ); + DECL_CONV_STUB( IfcIndexedPolygonalFaceWithVoids ); + DECL_CONV_STUB( IfcInterceptor ); + DECL_CONV_STUB( IfcInterceptorType ); + DECL_CONV_STUB( IfcSurfaceCurve ); + DECL_CONV_STUB( IfcIntersectionCurve ); + DECL_CONV_STUB( IfcInventory ); + DECL_CONV_STUB( IfcJunctionBox ); + DECL_CONV_STUB( IfcJunctionBoxType ); + DECL_CONV_STUB( IfcLShapeProfileDef ); + DECL_CONV_STUB( IfcLaborResource ); + DECL_CONV_STUB( IfcLaborResourceType ); + DECL_CONV_STUB( IfcLamp ); + DECL_CONV_STUB( IfcLampType ); + DECL_CONV_STUB( IfcLightFixture ); + DECL_CONV_STUB( IfcLightFixtureType ); + DECL_CONV_STUB( IfcLightSource ); + DECL_CONV_STUB( IfcLightSourceAmbient ); + DECL_CONV_STUB( IfcLightSourceDirectional ); + DECL_CONV_STUB( IfcLightSourceGoniometric ); + DECL_CONV_STUB( IfcLightSourcePositional ); + DECL_CONV_STUB( IfcLightSourceSpot ); + DECL_CONV_STUB( IfcLine ); + DECL_CONV_STUB( IfcLocalPlacement ); + DECL_CONV_STUB( IfcMappedItem ); + DECL_CONV_STUB( IfcProductRepresentation ); + DECL_CONV_STUB( IfcMaterialDefinitionRepresentation ); + DECL_CONV_STUB( IfcMeasureWithUnit ); + DECL_CONV_STUB( IfcMechanicalFastener ); + DECL_CONV_STUB( IfcMechanicalFastenerType ); + DECL_CONV_STUB( IfcMedicalDevice ); + DECL_CONV_STUB( IfcMedicalDeviceType ); + DECL_CONV_STUB( IfcMember ); + DECL_CONV_STUB( IfcMemberStandardCase ); + DECL_CONV_STUB( IfcMemberType ); + DECL_CONV_STUB( IfcMirroredProfileDef ); + DECL_CONV_STUB( IfcMotorConnection ); + DECL_CONV_STUB( IfcMotorConnectionType ); + DECL_CONV_STUB( IfcOccupant ); + DECL_CONV_STUB( IfcOffsetCurve2D ); + DECL_CONV_STUB( IfcOffsetCurve3D ); + DECL_CONV_STUB( IfcOpenShell ); + DECL_CONV_STUB( IfcOpeningElement ); + DECL_CONV_STUB( IfcOpeningStandardCase ); + DECL_CONV_STUB( IfcOrientedEdge ); + DECL_CONV_STUB( IfcOuterBoundaryCurve ); + DECL_CONV_STUB( IfcOutlet ); + DECL_CONV_STUB( IfcOutletType ); + DECL_CONV_STUB( IfcPath ); + DECL_CONV_STUB( IfcPcurve ); + DECL_CONV_STUB( IfcPerformanceHistory ); + DECL_CONV_STUB( IfcPermit ); + DECL_CONV_STUB( IfcPile ); + DECL_CONV_STUB( IfcPileType ); + DECL_CONV_STUB( IfcPipeFitting ); + DECL_CONV_STUB( IfcPipeFittingType ); + DECL_CONV_STUB( IfcPipeSegment ); + DECL_CONV_STUB( IfcPipeSegmentType ); + DECL_CONV_STUB( IfcPlanarExtent ); + DECL_CONV_STUB( IfcPlanarBox ); + DECL_CONV_STUB( IfcPlane ); + DECL_CONV_STUB( IfcPlate ); + DECL_CONV_STUB( IfcPlateStandardCase ); + DECL_CONV_STUB( IfcPlateType ); + DECL_CONV_STUB( IfcPointOnCurve ); + DECL_CONV_STUB( IfcPointOnSurface ); + DECL_CONV_STUB( IfcPolyLoop ); + DECL_CONV_STUB( IfcPolygonalBoundedHalfSpace ); + DECL_CONV_STUB( IfcTessellatedFaceSet ); + DECL_CONV_STUB( IfcPolygonalFaceSet ); + DECL_CONV_STUB( IfcPolyline ); + DECL_CONV_STUB( IfcPresentationStyleAssignment ); + DECL_CONV_STUB( IfcProcedure ); + DECL_CONV_STUB( IfcProcedureType ); + DECL_CONV_STUB( IfcProductDefinitionShape ); + DECL_CONV_STUB( IfcProject ); + DECL_CONV_STUB( IfcProjectLibrary ); + DECL_CONV_STUB( IfcProjectOrder ); + DECL_CONV_STUB( IfcProjectionElement ); + DECL_CONV_STUB( IfcSimpleProperty ); + DECL_CONV_STUB( IfcPropertyBoundedValue ); + DECL_CONV_STUB( IfcPropertyEnumeratedValue ); + DECL_CONV_STUB( IfcPropertyListValue ); + DECL_CONV_STUB( IfcPropertyReferenceValue ); + DECL_CONV_STUB( IfcPropertySet ); + DECL_CONV_STUB( IfcPropertySingleValue ); + DECL_CONV_STUB( IfcPropertyTableValue ); + DECL_CONV_STUB( IfcProtectiveDevice ); + DECL_CONV_STUB( IfcProtectiveDeviceTrippingUnit ); + DECL_CONV_STUB( IfcProtectiveDeviceTrippingUnitType ); + DECL_CONV_STUB( IfcProtectiveDeviceType ); + DECL_CONV_STUB( IfcProxy ); + DECL_CONV_STUB( IfcPump ); + DECL_CONV_STUB( IfcPumpType ); + DECL_CONV_STUB( IfcRailing ); + DECL_CONV_STUB( IfcRailingType ); + DECL_CONV_STUB( IfcRamp ); + DECL_CONV_STUB( IfcRampFlight ); + DECL_CONV_STUB( IfcRampFlightType ); + DECL_CONV_STUB( IfcRampType ); + DECL_CONV_STUB( IfcRationalBSplineCurveWithKnots ); + DECL_CONV_STUB( IfcRationalBSplineSurfaceWithKnots ); + DECL_CONV_STUB( IfcRectangleProfileDef ); + DECL_CONV_STUB( IfcRectangleHollowProfileDef ); + DECL_CONV_STUB( IfcRectangularPyramid ); + DECL_CONV_STUB( IfcRectangularTrimmedSurface ); + DECL_CONV_STUB( IfcReinforcingElement ); + DECL_CONV_STUB( IfcReinforcingBar ); + DECL_CONV_STUB( IfcReinforcingElementType ); + DECL_CONV_STUB( IfcReinforcingBarType ); + DECL_CONV_STUB( IfcReinforcingMesh ); + DECL_CONV_STUB( IfcReinforcingMeshType ); + DECL_CONV_STUB( IfcRelationship ); + DECL_CONV_STUB( IfcRelDecomposes ); + DECL_CONV_STUB( IfcRelAggregates ); + DECL_CONV_STUB( IfcRelConnects ); + DECL_CONV_STUB( IfcRelContainedInSpatialStructure ); + DECL_CONV_STUB( IfcRelDefines ); + DECL_CONV_STUB( IfcRelDefinesByProperties ); + DECL_CONV_STUB( IfcRelFillsElement ); + DECL_CONV_STUB( IfcRelVoidsElement ); + DECL_CONV_STUB( IfcReparametrisedCompositeCurveSegment ); + DECL_CONV_STUB( IfcRepresentation ); + DECL_CONV_STUB( IfcRepresentationMap ); + DECL_CONV_STUB( IfcRevolvedAreaSolid ); + DECL_CONV_STUB( IfcRevolvedAreaSolidTapered ); + DECL_CONV_STUB( IfcRightCircularCone ); + DECL_CONV_STUB( IfcRightCircularCylinder ); + DECL_CONV_STUB( IfcRoof ); + DECL_CONV_STUB( IfcRoofType ); + DECL_CONV_STUB( IfcRoundedRectangleProfileDef ); + DECL_CONV_STUB( IfcSIUnit ); + DECL_CONV_STUB( IfcSanitaryTerminal ); + DECL_CONV_STUB( IfcSanitaryTerminalType ); + DECL_CONV_STUB( IfcSeamCurve ); + DECL_CONV_STUB( IfcSectionedSpine ); + DECL_CONV_STUB( IfcSensor ); + DECL_CONV_STUB( IfcSensorType ); + DECL_CONV_STUB( IfcShadingDevice ); + DECL_CONV_STUB( IfcShadingDeviceType ); + DECL_CONV_STUB( IfcShapeModel ); + DECL_CONV_STUB( IfcShapeRepresentation ); + DECL_CONV_STUB( IfcShellBasedSurfaceModel ); + DECL_CONV_STUB( IfcSite ); + DECL_CONV_STUB( IfcSlab ); + DECL_CONV_STUB( IfcSlabElementedCase ); + DECL_CONV_STUB( IfcSlabStandardCase ); + DECL_CONV_STUB( IfcSlabType ); + DECL_CONV_STUB( IfcSolarDevice ); + DECL_CONV_STUB( IfcSolarDeviceType ); + DECL_CONV_STUB( IfcSpace ); + DECL_CONV_STUB( IfcSpaceHeater ); + DECL_CONV_STUB( IfcSpaceHeaterType ); + DECL_CONV_STUB( IfcSpatialElementType ); + DECL_CONV_STUB( IfcSpatialStructureElementType ); + DECL_CONV_STUB( IfcSpaceType ); + DECL_CONV_STUB( IfcSpatialZone ); + DECL_CONV_STUB( IfcSpatialZoneType ); + DECL_CONV_STUB( IfcSphere ); + DECL_CONV_STUB( IfcSphericalSurface ); + DECL_CONV_STUB( IfcStackTerminal ); + DECL_CONV_STUB( IfcStackTerminalType ); + DECL_CONV_STUB( IfcStair ); + DECL_CONV_STUB( IfcStairFlight ); + DECL_CONV_STUB( IfcStairFlightType ); + DECL_CONV_STUB( IfcStairType ); + DECL_CONV_STUB( IfcStructuralActivity ); + DECL_CONV_STUB( IfcStructuralAction ); + DECL_CONV_STUB( IfcStructuralAnalysisModel ); + DECL_CONV_STUB( IfcStructuralItem ); + DECL_CONV_STUB( IfcStructuralConnection ); + DECL_CONV_STUB( IfcStructuralCurveAction ); + DECL_CONV_STUB( IfcStructuralCurveConnection ); + DECL_CONV_STUB( IfcStructuralMember ); + DECL_CONV_STUB( IfcStructuralCurveMember ); + DECL_CONV_STUB( IfcStructuralCurveMemberVarying ); + DECL_CONV_STUB( IfcStructuralReaction ); + DECL_CONV_STUB( IfcStructuralCurveReaction ); + DECL_CONV_STUB( IfcStructuralLinearAction ); + DECL_CONV_STUB( IfcStructuralLoadGroup ); + DECL_CONV_STUB( IfcStructuralLoadCase ); + DECL_CONV_STUB( IfcStructuralSurfaceAction ); + DECL_CONV_STUB( IfcStructuralPlanarAction ); + DECL_CONV_STUB( IfcStructuralPointAction ); + DECL_CONV_STUB( IfcStructuralPointConnection ); + DECL_CONV_STUB( IfcStructuralPointReaction ); + DECL_CONV_STUB( IfcStructuralResultGroup ); + DECL_CONV_STUB( IfcStructuralSurfaceConnection ); + DECL_CONV_STUB( IfcStructuralSurfaceMember ); + DECL_CONV_STUB( IfcStructuralSurfaceMemberVarying ); + DECL_CONV_STUB( IfcStructuralSurfaceReaction ); + DECL_CONV_STUB( IfcStyleModel ); + DECL_CONV_STUB( IfcStyledItem ); + DECL_CONV_STUB( IfcStyledRepresentation ); + DECL_CONV_STUB( IfcSubContractResource ); + DECL_CONV_STUB( IfcSubContractResourceType ); + DECL_CONV_STUB( IfcSubedge ); + DECL_CONV_STUB( IfcSurfaceCurveSweptAreaSolid ); + DECL_CONV_STUB( IfcSurfaceFeature ); + DECL_CONV_STUB( IfcSweptSurface ); + DECL_CONV_STUB( IfcSurfaceOfLinearExtrusion ); + DECL_CONV_STUB( IfcSurfaceOfRevolution ); + DECL_CONV_STUB( IfcSurfaceStyle ); + DECL_CONV_STUB( IfcSurfaceStyleShading ); + DECL_CONV_STUB( IfcSurfaceStyleRendering ); + DECL_CONV_STUB( IfcSurfaceStyleWithTextures ); + DECL_CONV_STUB( IfcSweptDiskSolid ); + DECL_CONV_STUB( IfcSweptDiskSolidPolygonal ); + DECL_CONV_STUB( IfcSwitchingDevice ); + DECL_CONV_STUB( IfcSwitchingDeviceType ); + DECL_CONV_STUB( IfcSystemFurnitureElement ); + DECL_CONV_STUB( IfcSystemFurnitureElementType ); + DECL_CONV_STUB( IfcTShapeProfileDef ); + DECL_CONV_STUB( IfcTank ); + DECL_CONV_STUB( IfcTankType ); + DECL_CONV_STUB( IfcTask ); + DECL_CONV_STUB( IfcTaskType ); + DECL_CONV_STUB( IfcTendon ); + DECL_CONV_STUB( IfcTendonAnchor ); + DECL_CONV_STUB( IfcTendonAnchorType ); + DECL_CONV_STUB( IfcTendonType ); + DECL_CONV_STUB( IfcTextLiteral ); + DECL_CONV_STUB( IfcTextLiteralWithExtent ); + DECL_CONV_STUB( IfcTopologyRepresentation ); + DECL_CONV_STUB( IfcToroidalSurface ); + DECL_CONV_STUB( IfcTransformer ); + DECL_CONV_STUB( IfcTransformerType ); + DECL_CONV_STUB( IfcTransportElement ); + DECL_CONV_STUB( IfcTransportElementType ); + DECL_CONV_STUB( IfcTrapeziumProfileDef ); + DECL_CONV_STUB( IfcTriangulatedFaceSet ); + DECL_CONV_STUB( IfcTrimmedCurve ); + DECL_CONV_STUB( IfcTubeBundle ); + DECL_CONV_STUB( IfcTubeBundleType ); + DECL_CONV_STUB( IfcUShapeProfileDef ); + DECL_CONV_STUB( IfcUnitAssignment ); + DECL_CONV_STUB( IfcUnitaryControlElement ); + DECL_CONV_STUB( IfcUnitaryControlElementType ); + DECL_CONV_STUB( IfcUnitaryEquipment ); + DECL_CONV_STUB( IfcUnitaryEquipmentType ); + DECL_CONV_STUB( IfcValve ); + DECL_CONV_STUB( IfcValveType ); + DECL_CONV_STUB( IfcVector ); + DECL_CONV_STUB( IfcVertex ); + DECL_CONV_STUB( IfcVertexLoop ); + DECL_CONV_STUB( IfcVertexPoint ); + DECL_CONV_STUB( IfcVibrationIsolator ); + DECL_CONV_STUB( IfcVibrationIsolatorType ); + DECL_CONV_STUB( IfcVirtualElement ); + DECL_CONV_STUB( IfcVoidingFeature ); + DECL_CONV_STUB( IfcWall ); + DECL_CONV_STUB( IfcWallElementedCase ); + DECL_CONV_STUB( IfcWallStandardCase ); + DECL_CONV_STUB( IfcWallType ); + DECL_CONV_STUB( IfcWasteTerminal ); + DECL_CONV_STUB( IfcWasteTerminalType ); + DECL_CONV_STUB( IfcWindow ); + DECL_CONV_STUB( IfcWindowStandardCase ); + DECL_CONV_STUB( IfcWindowStyle ); + DECL_CONV_STUB( IfcWindowType ); + DECL_CONV_STUB( IfcWorkCalendar ); + DECL_CONV_STUB( IfcWorkControl ); + DECL_CONV_STUB( IfcWorkPlan ); + DECL_CONV_STUB( IfcWorkSchedule ); + DECL_CONV_STUB( IfcZShapeProfileDef ); + DECL_CONV_STUB( IfcZone ); #undef DECL_CONV_STUB +} //! Schema_4 } //! STEP } //! Assimp diff --git a/code/LWOLoader.cpp b/code/LWOLoader.cpp index 38e330b8f..e908ea178 100644 --- a/code/LWOLoader.cpp +++ b/code/LWOLoader.cpp @@ -432,7 +432,6 @@ void LWOImporter::InternReadFile( const std::string& pFile, unsigned int num = static_cast(apcMeshes.size() - meshStart); if (layer.mName != "" || num > 0) { aiNode* pcNode = new aiNode(); - apcNodes[layer.mIndex] = pcNode; pcNode->mName.Set(layer.mName); pcNode->mParent = (aiNode*)&layer; pcNode->mNumMeshes = num; @@ -442,6 +441,7 @@ void LWOImporter::InternReadFile( const std::string& pFile, for (unsigned int p = 0; p < pcNode->mNumMeshes;++p) pcNode->mMeshes[p] = p + meshStart; } + apcNodes[layer.mIndex] = pcNode; } } @@ -584,7 +584,7 @@ void LWOImporter::GenerateNodeGraph(std::map& apcNodes) //Set parent of all children, inserting pivots //std::cout << "Set parent of all children" << std::endl; std::map mapPivot; - for (std::map::iterator itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) { + for (auto itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) { //Get the parent index LWO::Layer* nodeLayer = (LWO::Layer*)(itapcNodes->second->mParent); @@ -593,7 +593,6 @@ void LWOImporter::GenerateNodeGraph(std::map& apcNodes) //Create pivot node, store it into the pivot map, and set the parent as the pivot aiNode* pivotNode = new aiNode(); pivotNode->mName.Set("Pivot-"+std::string(itapcNodes->second->mName.data)); - mapPivot[-(itapcNodes->first+2)] = pivotNode; itapcNodes->second->mParent = pivotNode; //Look for the parent node to attach the pivot to @@ -611,18 +610,19 @@ void LWOImporter::GenerateNodeGraph(std::map& apcNodes) pivotNode->mTransformation.a4 = nodeLayer->mPivot.x; pivotNode->mTransformation.b4 = nodeLayer->mPivot.y; pivotNode->mTransformation.c4 = nodeLayer->mPivot.z; + mapPivot[-(itapcNodes->first+2)] = pivotNode; } //Merge pivot map into node map //std::cout << "Merge pivot map into node map" << std::endl; - for (std::map::iterator itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end(); ++itMapPivot) { + for (auto itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end(); ++itMapPivot) { apcNodes[itMapPivot->first] = itMapPivot->second; } //Set children of all parents apcNodes[-1] = root; - for (std::map::iterator itMapParentNodes = apcNodes.begin(); itMapParentNodes != apcNodes.end(); ++itMapParentNodes) { - for (std::map::iterator itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) { + for (auto itMapParentNodes = apcNodes.begin(); itMapParentNodes != apcNodes.end(); ++itMapParentNodes) { + for (auto itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) { if ((itMapParentNodes->first != itMapChildNodes->first) && (itMapParentNodes->second == itMapChildNodes->second->mParent)) { ++(itMapParentNodes->second->mNumChildren); } @@ -630,7 +630,7 @@ void LWOImporter::GenerateNodeGraph(std::map& apcNodes) if (itMapParentNodes->second->mNumChildren) { itMapParentNodes->second->mChildren = new aiNode* [ itMapParentNodes->second->mNumChildren ]; uint16_t p = 0; - for (std::map::iterator itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) { + for (auto itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) { if ((itMapParentNodes->first != itMapChildNodes->first) && (itMapParentNodes->second == itMapChildNodes->second->mParent)) { itMapParentNodes->second->mChildren[p++] = itMapChildNodes->second; } diff --git a/code/LimitBoneWeightsProcess.h b/code/LimitBoneWeightsProcess.h index 2fc6e02b1..090181982 100644 --- a/code/LimitBoneWeightsProcess.h +++ b/code/LimitBoneWeightsProcess.h @@ -120,7 +120,11 @@ public: { unsigned int mBone; ///< Index of the bone float mWeight; ///< Weight of that bone on this vertex - Weight() { } + Weight() + : mBone(0) + , mWeight(0.0f) + { } + Weight( unsigned int pBone, float pWeight) { mBone = pBone; diff --git a/code/MDLFileData.h b/code/MDLFileData.h index 56f686280..ba732add0 100644 --- a/code/MDLFileData.h +++ b/code/MDLFileData.h @@ -844,11 +844,11 @@ struct IntGroupInfo_MDL7 struct IntGroupData_MDL7 { IntGroupData_MDL7() - : pcFaces(NULL), bNeed2UV(false) + : bNeed2UV(false) {} //! Array of faces that belong to the group - MDL::IntFace_MDL7* pcFaces; + std::vector pcFaces; //! Array of vertex positions std::vector vPositions; diff --git a/code/MDLLoader.cpp b/code/MDLLoader.cpp index 5dd471cf5..3f2bb084b 100644 --- a/code/MDLLoader.cpp +++ b/code/MDLLoader.cpp @@ -1502,7 +1502,7 @@ void MDLImporter::InternReadFile_3DGS_MDL7( ) groupData.bNeed2UV = true; } } - groupData.pcFaces = new MDL::IntFace_MDL7[groupInfo.pcGroup->numtris]; + groupData.pcFaces.resize(groupInfo.pcGroup->numtris); // read all faces into the preallocated arrays ReadFaces_3DGS_MDL7(groupInfo, groupData); diff --git a/code/ObjExporter.cpp b/code/ObjExporter.cpp index 9beb418f3..d08b5f859 100644 --- a/code/ObjExporter.cpp +++ b/code/ObjExporter.cpp @@ -132,18 +132,18 @@ ObjExporter::ObjExporter(const char* _filename, const aiScene* pScene, bool noMt mOutputMat.precision(16); WriteGeometryFile(noMtl); - if (!noMtl) + if ( !noMtl ) { WriteMaterialFile(); + } } // ------------------------------------------------------------------------------------------------ ObjExporter::~ObjExporter() { - + // empty } // ------------------------------------------------------------------------------------------------ -std::string ObjExporter :: GetMaterialLibName() -{ +std::string ObjExporter::GetMaterialLibName() { // within the Obj file, we use just the relative file name with the path stripped const std::string& s = GetMaterialLibFileName(); std::string::size_type il = s.find_last_of("/\\"); @@ -158,8 +158,9 @@ std::string ObjExporter :: GetMaterialLibName() std::string ObjExporter::GetMaterialLibFileName() { // Remove existing .obj file extension so that the final material file name will be fileName.mtl and not fileName.obj.mtl size_t lastdot = filename.find_last_of('.'); - if (lastdot != std::string::npos) - return filename.substr(0, lastdot) + MaterialExt; + if ( lastdot != std::string::npos ) { + return filename.substr( 0, lastdot ) + MaterialExt; + } return filename + MaterialExt; } @@ -172,8 +173,7 @@ void ObjExporter::WriteHeader(std::ostringstream& out) { } // ------------------------------------------------------------------------------------------------ -std::string ObjExporter::GetMaterialName(unsigned int index) -{ +std::string ObjExporter::GetMaterialName(unsigned int index) { const aiMaterial* const mat = pScene->mMaterials[index]; if ( nullptr == mat ) { static const std::string EmptyStr; @@ -191,8 +191,7 @@ std::string ObjExporter::GetMaterialName(unsigned int index) } // ------------------------------------------------------------------------------------------------ -void ObjExporter::WriteMaterialFile() -{ +void ObjExporter::WriteMaterialFile() { WriteHeader(mOutputMat); for(unsigned int i = 0; i < pScene->mNumMaterials; ++i) { @@ -310,8 +309,9 @@ void ObjExporter::WriteGeometryFile(bool noMtl) { if (!m.name.empty()) { mOutput << "g " << m.name << endl; } - if (!noMtl) + if ( !noMtl ) { mOutput << "usemtl " << m.matname << endl; + } for(const Face& f : m.faces) { mOutput << f.kind << ' '; @@ -382,7 +382,7 @@ void ObjExporter::colIndexMap::getColors( std::vector &colors ) { // ------------------------------------------------------------------------------------------------ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4x4& mat) { - mMeshes.push_back(MeshInstance()); + mMeshes.push_back(MeshInstance() ); MeshInstance& mesh = mMeshes.back(); mesh.name = std::string( name.data, name.length ); @@ -436,8 +436,7 @@ void ObjExporter::AddMesh(const aiString& name, const aiMesh* m, const aiMatrix4 } // ------------------------------------------------------------------------------------------------ -void ObjExporter::AddNode(const aiNode* nd, const aiMatrix4x4& mParent) -{ +void ObjExporter::AddNode(const aiNode* nd, const aiMatrix4x4& mParent) { const aiMatrix4x4& mAbs = mParent * nd->mTransformation; for(unsigned int i = 0; i < nd->mNumMeshes; ++i) { diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index 02d6ac581..bce94ebbb 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -207,30 +207,24 @@ void ObjFileImporter::CreateDataFromImport(const ObjFile::Model* pModel, aiScene // Create the root node of the scene pScene->mRootNode = new aiNode; - if ( !pModel->m_ModelName.empty() ) - { + if ( !pModel->m_ModelName.empty() ) { // Set the name of the scene pScene->mRootNode->mName.Set(pModel->m_ModelName); - } - else - { + } else { // This is a fatal error, so break down the application ai_assert(false); } // Create nodes for the whole scene std::vector MeshArray; - for (size_t index = 0; index < pModel->m_Objects.size(); index++) - { + for (size_t index = 0; index < pModel->m_Objects.size(); ++index ) { createNodes(pModel, pModel->m_Objects[ index ], pScene->mRootNode, pScene, MeshArray); } // Create mesh pointer buffer for this scene - if (pScene->mNumMeshes > 0) - { + if (pScene->mNumMeshes > 0) { pScene->mMeshes = new aiMesh*[ MeshArray.size() ]; - for (size_t index =0; index < MeshArray.size(); index++) - { + for (size_t index =0; index < MeshArray.size(); ++index ) { pScene->mMeshes[ index ] = MeshArray[ index ]; } } @@ -261,8 +255,7 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile appendChildToParentNode( pParent, pNode ); } - for ( size_t i=0; i< pObject->m_Meshes.size(); i++ ) - { + for ( size_t i=0; i< pObject->m_Meshes.size(); ++i ) { unsigned int meshId = pObject->m_Meshes[ i ]; aiMesh *pMesh = createTopology( pModel, pObject, meshId ); if( pMesh ) { @@ -275,8 +268,7 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile } // Create all nodes from the sub-objects stored in the current object - if ( !pObject->m_SubObjects.empty() ) - { + if ( !pObject->m_SubObjects.empty() ) { size_t numChilds = pObject->m_SubObjects.size(); pNode->mNumChildren = static_cast( numChilds ); pNode->mChildren = new aiNode*[ numChilds ]; @@ -286,16 +278,14 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile // Set mesh instances into scene- and node-instances const size_t meshSizeDiff = MeshArray.size()- oldMeshSize; - if ( meshSizeDiff > 0 ) - { + if ( meshSizeDiff > 0 ) { pNode->mMeshes = new unsigned int[ meshSizeDiff ]; pNode->mNumMeshes = static_cast( meshSizeDiff ); size_t index = 0; - for (size_t i = oldMeshSize; i < MeshArray.size(); i++) - { + for (size_t i = oldMeshSize; i < MeshArray.size(); ++i ) { pNode->mMeshes[ index ] = pScene->mNumMeshes; pScene->mNumMeshes++; - index++; + ++index; } } diff --git a/code/ObjFileMtlImporter.cpp b/code/ObjFileMtlImporter.cpp index 835f529cc..584b3115c 100644 --- a/code/ObjFileMtlImporter.cpp +++ b/code/ObjFileMtlImporter.cpp @@ -303,11 +303,12 @@ void ObjFileMtlImporter::createMaterial() // New Material created m_pModel->m_pCurrentMaterial = new ObjFile::Material(); m_pModel->m_pCurrentMaterial->MaterialName.Set( name ); + m_pModel->m_MaterialLib.push_back( name ); + m_pModel->m_MaterialMap[ name ] = m_pModel->m_pCurrentMaterial; + if (m_pModel->m_pCurrentMesh) { m_pModel->m_pCurrentMesh->m_uiMaterialIndex = static_cast(m_pModel->m_MaterialLib.size() - 1); } - m_pModel->m_MaterialLib.push_back( name ); - m_pModel->m_MaterialMap[ name ] = m_pModel->m_pCurrentMaterial; } else { // Use older material m_pModel->m_pCurrentMaterial = (*it).second; diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index ae95edbcb..7cd2d36c2 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -612,13 +612,14 @@ void ObjFileParser::getMaterialLib() { if ( '/' != *path.rbegin() ) { path += '/'; } - absName = path + strMatName; + absName += path; + absName += strMatName; } else { absName = strMatName; } - IOStream *pFile = m_pIO->Open( absName ); - if (!pFile ) { + IOStream *pFile = m_pIO->Open( absName ); + if ( nullptr == pFile ) { DefaultLogger::get()->error("OBJ: Unable to locate material file " + strMatName); std::string strMatFallbackName = m_originalObjFileName.substr(0, m_originalObjFileName.length() - 3) + "mtl"; DefaultLogger::get()->info("OBJ: Opening fallback material file " + strMatFallbackName); diff --git a/code/OgreParsingUtils.h b/code/OgreParsingUtils.h index a77b94121..c6b6e0caf 100644 --- a/code/OgreParsingUtils.h +++ b/code/OgreParsingUtils.h @@ -52,27 +52,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -namespace Assimp -{ -namespace Ogre -{ +namespace Assimp { +namespace Ogre { /// Returns a lower cased copy of @s. -static inline std::string ToLower(std::string s) +static AI_FORCE_INLINE +std::string ToLower(std::string s) { std::transform(s.begin(), s.end(), s.begin(), ::tolower); return s; } /// Returns if @c s ends with @c suffix. If @c caseSensitive is false, both strings will be lower cased before matching. -static inline bool EndsWith(const std::string &s, const std::string &suffix, bool caseSensitive = true) -{ - if (s.empty() || suffix.empty()) - { +static AI_FORCE_INLINE +bool EndsWith(const std::string &s, const std::string &suffix, bool caseSensitive = true) { + if (s.empty() || suffix.empty()) { return false; - } - else if (s.length() < suffix.length()) - { + } else if (s.length() < suffix.length()) { return false; } @@ -82,48 +78,43 @@ static inline bool EndsWith(const std::string &s, const std::string &suffix, boo size_t len = suffix.length(); std::string sSuffix = s.substr(s.length()-len, len); + return (ASSIMP_stricmp(sSuffix, suffix) == 0); } // Below trim functions adapted from http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring /// Trim from start -static inline std::string &TrimLeft(std::string &s, bool newlines = true) -{ - if (!newlines) - { +static AI_FORCE_INLINE +std::string &TrimLeft(std::string &s, bool newlines = true) { + if (!newlines) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](char c) { return !Assimp::IsSpace(c); })); - } - else - { + } else { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](char c) { return !Assimp::IsSpaceOrNewLine(c); })); } return s; } /// Trim from end -static inline std::string &TrimRight(std::string &s, bool newlines = true) -{ - if (!newlines) - { +static AI_FORCE_INLINE +std::string &TrimRight(std::string &s, bool newlines = true) { + if (!newlines) { s.erase(std::find_if(s.rbegin(), s.rend(), [](char c) { return !Assimp::IsSpace(c); }).base(),s.end()); - } - else - { + } else { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](char c) { return !Assimp::IsSpaceOrNewLine(c); })); } return s; } /// Trim from both ends -static inline std::string &Trim(std::string &s, bool newlines = true) -{ +static AI_FORCE_INLINE +std::string &Trim(std::string &s, bool newlines = true) { return TrimLeft(TrimRight(s, newlines), newlines); } /// Skips a line from current @ss position until a newline. Returns the skipped part. -static inline std::string SkipLine(std::stringstream &ss) -{ +static AI_FORCE_INLINE +std::string SkipLine(std::stringstream &ss) { std::string skipped; getline(ss, skipped); return skipped; @@ -131,8 +122,8 @@ static inline std::string SkipLine(std::stringstream &ss) /// Skips a line and reads next element from @c ss to @c nextElement. /** @return Skipped line content until newline. */ -static inline std::string NextAfterNewLine(std::stringstream &ss, std::string &nextElement) -{ +static AI_FORCE_INLINE +std::string NextAfterNewLine(std::stringstream &ss, std::string &nextElement) { std::string skipped = SkipLine(ss); ss >> nextElement; return skipped; diff --git a/code/OgreXmlSerializer.cpp b/code/OgreXmlSerializer.cpp index c777cf363..12b2bcbd9 100644 --- a/code/OgreXmlSerializer.cpp +++ b/code/OgreXmlSerializer.cpp @@ -213,18 +213,18 @@ std::string &OgreXmlSerializer::SkipCurrentNode() DefaultLogger::get()->debug("Skipping node <" + m_currentNodeName + ">"); #endif - for(;;) - { - if (!m_reader->read()) - { + for(;;) { + if (!m_reader->read()) { m_currentNodeName = ""; return m_currentNodeName; } - if (m_reader->getNodeType() != irr::io::EXN_ELEMENT_END) + if ( m_reader->getNodeType() != irr::io::EXN_ELEMENT_END ) { continue; - else if (std::string(m_reader->getNodeName()) == m_currentNodeName) + } else if ( std::string( m_reader->getNodeName() ) == m_currentNodeName ) { break; + } } + return NextNode(); } @@ -303,17 +303,16 @@ static const char *anZ = "z"; // Mesh -MeshXml *OgreXmlSerializer::ImportMesh(XmlReader *reader) -{ +MeshXml *OgreXmlSerializer::ImportMesh(XmlReader *reader) { OgreXmlSerializer serializer(reader); MeshXml *mesh = new MeshXml(); serializer.ReadMesh(mesh); + return mesh; } -void OgreXmlSerializer::ReadMesh(MeshXml *mesh) -{ +void OgreXmlSerializer::ReadMesh(MeshXml *mesh) { if (NextNode() != nnMesh) { throw DeadlyImportError("Root node is <" + m_currentNodeName + "> expecting "); } @@ -835,7 +834,7 @@ void OgreXmlSerializer::ReadAnimationTracks(Animation *dest) void OgreXmlSerializer::ReadAnimationKeyFrames(Animation *anim, VertexAnimationTrack *dest) { - static const aiVector3D zeroVec(0.f, 0.f, 0.f); + const aiVector3D zeroVec(0.f, 0.f, 0.f); NextNode(); while(m_currentNodeName == nnKeyFrame) diff --git a/code/OpenGEXImporter.cpp b/code/OpenGEXImporter.cpp index 37c765f4c..652ba0f98 100644 --- a/code/OpenGEXImporter.cpp +++ b/code/OpenGEXImporter.cpp @@ -221,12 +221,8 @@ static void propId2StdString( Property *prop, std::string &name, std::string &ke //------------------------------------------------------------------------------------------------ OpenGEXImporter::VertexContainer::VertexContainer() -: m_numVerts( 0 ) -, m_vertices( nullptr ) -, m_numColors( 0 ) +: m_numColors( 0 ) , m_colors( nullptr ) -, m_numNormals( 0 ) -, m_normals( nullptr ) , m_numUVComps() , m_textureCoords() { // empty @@ -234,9 +230,7 @@ OpenGEXImporter::VertexContainer::VertexContainer() //------------------------------------------------------------------------------------------------ OpenGEXImporter::VertexContainer::~VertexContainer() { - delete[] m_vertices; delete[] m_colors; - delete[] m_normals; for(auto &texcoords : m_textureCoords) { delete [] texcoords; @@ -697,7 +691,8 @@ void OpenGEXImporter::handleTransformNode( ODDLParser::DDLNode *node, aiScene * void OpenGEXImporter::handleMeshNode( ODDLParser::DDLNode *node, aiScene *pScene ) { m_currentMesh = new aiMesh; const size_t meshidx( m_meshCache.size() ); - m_meshCache.push_back( m_currentMesh ); + // ownership is transfered but a reference remains in m_currentMesh + m_meshCache.emplace_back( m_currentMesh ); Property *prop = node->getProperties(); if( nullptr != prop ) { @@ -736,17 +731,22 @@ enum MeshAttribute { TexCoord }; +static const std::string PosToken = "position"; +static const std::string ColToken = "color"; +static const std::string NormalToken = "normal"; +static const std::string TexCoordToken = "texcoord"; + //------------------------------------------------------------------------------------------------ static MeshAttribute getAttributeByName( const char *attribName ) { ai_assert( nullptr != attribName ); - if ( 0 == strncmp( "position", attribName, strlen( "position" ) ) ) { + if ( 0 == strncmp( PosToken.c_str(), attribName, PosToken.size() ) ) { return Position; - } else if ( 0 == strncmp( "color", attribName, strlen( "color" ) ) ) { + } else if ( 0 == strncmp( ColToken.c_str(), attribName, ColToken.size() ) ) { return Color; - } else if( 0 == strncmp( "normal", attribName, strlen( "normal" ) ) ) { + } else if( 0 == strncmp( NormalToken.c_str(), attribName, NormalToken.size() ) ) { return Normal; - } else if( 0 == strncmp( "texcoord", attribName, strlen( "texcoord" ) ) ) { + } else if( 0 == strncmp( TexCoordToken.c_str(), attribName, TexCoordToken.size() ) ) { return TexCoord; } @@ -857,17 +857,15 @@ void OpenGEXImporter::handleVertexArrayNode( ODDLParser::DDLNode *node, aiScene const size_t numItems( countDataArrayListItems( vaList ) ); if( Position == attribType ) { - m_currentVertices.m_numVerts = numItems; - m_currentVertices.m_vertices = new aiVector3D[ numItems ]; - copyVectorArray( numItems, vaList, m_currentVertices.m_vertices ); + m_currentVertices.m_vertices.resize( numItems ); + copyVectorArray( numItems, vaList, m_currentVertices.m_vertices.data() ); } else if ( Color == attribType ) { m_currentVertices.m_numColors = numItems; m_currentVertices.m_colors = new aiColor4D[ numItems ]; copyColor4DArray( numItems, vaList, m_currentVertices.m_colors ); } else if( Normal == attribType ) { - m_currentVertices.m_numNormals = numItems; - m_currentVertices.m_normals = new aiVector3D[ numItems ]; - copyVectorArray( numItems, vaList, m_currentVertices.m_normals ); + m_currentVertices.m_normals.resize( numItems ); + copyVectorArray( numItems, vaList, m_currentVertices.m_normals.data() ); } else if( TexCoord == attribType ) { m_currentVertices.m_numUVComps[ 0 ] = numItems; m_currentVertices.m_textureCoords[ 0 ] = new aiVector3D[ numItems ]; @@ -904,7 +902,7 @@ void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene * hasColors = true; } bool hasNormalCoords( false ); - if ( m_currentVertices.m_numNormals > 0 ) { + if ( !m_currentVertices.m_normals.empty() ) { m_currentMesh->mNormals = new aiVector3D[ m_currentMesh->mNumVertices ]; hasNormalCoords = true; } @@ -922,7 +920,7 @@ void OpenGEXImporter::handleIndexArrayNode( ODDLParser::DDLNode *node, aiScene * Value *next( vaList->m_dataList ); for( size_t indices = 0; indices < current.mNumIndices; indices++ ) { const int idx( next->getUnsignedInt32() ); - ai_assert( static_cast( idx ) <= m_currentVertices.m_numVerts ); + ai_assert( static_cast( idx ) <= m_currentVertices.m_vertices.size() ); ai_assert( index < m_currentMesh->mNumVertices ); aiVector3D &pos = ( m_currentVertices.m_vertices[ idx ] ); m_currentMesh->mVertices[ index ].Set( pos.x, pos.y, pos.z ); @@ -1105,14 +1103,12 @@ void OpenGEXImporter::handleParamNode( ODDLParser::DDLNode *node, aiScene * /*pS return; } const float floatVal( val->getFloat() ); - if ( prop->m_value != nullptr ) { - if ( 0 == ASSIMP_strincmp( "fov", prop->m_value->getString(), 3 ) ) { - m_currentCamera->mHorizontalFOV = floatVal; - } else if ( 0 == ASSIMP_strincmp( "near", prop->m_value->getString(), 3 ) ) { - m_currentCamera->mClipPlaneNear = floatVal; - } else if ( 0 == ASSIMP_strincmp( "far", prop->m_value->getString(), 3 ) ) { - m_currentCamera->mClipPlaneFar = floatVal; - } + if ( 0 == ASSIMP_strincmp( "fov", prop->m_value->getString(), 3 ) ) { + m_currentCamera->mHorizontalFOV = floatVal; + } else if ( 0 == ASSIMP_strincmp( "near", prop->m_value->getString(), 4 ) ) { + m_currentCamera->mClipPlaneNear = floatVal; + } else if ( 0 == ASSIMP_strincmp( "far", prop->m_value->getString(), 3 ) ) { + m_currentCamera->mClipPlaneFar = floatVal; } } } @@ -1145,7 +1141,9 @@ void OpenGEXImporter::copyMeshes( aiScene *pScene ) { pScene->mNumMeshes = static_cast(m_meshCache.size()); pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes ]; - std::copy( m_meshCache.begin(), m_meshCache.end(), pScene->mMeshes ); + for (unsigned int i = 0; i < pScene->mNumMeshes; i++) { + pScene->mMeshes[i] = m_meshCache[i].release(); + } } //------------------------------------------------------------------------------------------------ diff --git a/code/OpenGEXImporter.h b/code/OpenGEXImporter.h index bbbe3678d..8e86a4aa8 100644 --- a/code/OpenGEXImporter.h +++ b/code/OpenGEXImporter.h @@ -144,12 +144,10 @@ protected: private: struct VertexContainer { - size_t m_numVerts; - aiVector3D *m_vertices; + std::vector m_vertices; size_t m_numColors; aiColor4D *m_colors; - size_t m_numNormals; - aiVector3D *m_normals; + std::vector m_normals; size_t m_numUVComps[ AI_MAX_NUMBER_OF_TEXTURECOORDS ]; aiVector3D *m_textureCoords[ AI_MAX_NUMBER_OF_TEXTURECOORDS ]; @@ -185,7 +183,7 @@ private: typedef std::map > NodeChildMap; NodeChildMap m_nodeChildMap; - std::vector m_meshCache; + std::vector > m_meshCache; typedef std::map ReferenceMap; std::map m_mesh2refMap; std::map m_material2refMap; @@ -194,7 +192,7 @@ private: MetricInfo m_metrics[ MetricInfo::Max ]; aiNode *m_currentNode; VertexContainer m_currentVertices; - aiMesh *m_currentMesh; + aiMesh *m_currentMesh; // not owned, target is owned by m_meshCache aiMaterial *m_currentMaterial; aiLight *m_currentLight; aiCamera *m_currentCamera; diff --git a/code/PlyExporter.cpp b/code/PlyExporter.cpp index 0ddba0d2a..2d528c96c 100644 --- a/code/PlyExporter.cpp +++ b/code/PlyExporter.cpp @@ -148,6 +148,17 @@ PlyExporter::PlyExporter(const char* _filename, const aiScene* pScene, bool bina << aiGetVersionMajor() << '.' << aiGetVersionMinor() << '.' << aiGetVersionRevision() << ")" << endl; + // Look through materials for a diffuse texture, and add it if found + for ( unsigned int i = 0; i < pScene->mNumMaterials; ++i ) + { + const aiMaterial* const mat = pScene->mMaterials[i]; + aiString s; + if ( AI_SUCCESS == mat->Get( AI_MATKEY_TEXTURE_DIFFUSE( 0 ), s ) ) + { + mOutput << "comment TextureFile " << s.data << endl; + } + } + // TODO: probably want to check here rather than just assume something // definitely not good to always write float even if we might have double precision diff --git a/code/PlyLoader.cpp b/code/PlyLoader.cpp index b6ae368df..1bdd6e694 100644 --- a/code/PlyLoader.cpp +++ b/code/PlyLoader.cpp @@ -58,16 +58,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; static const aiImporterDesc desc = { - "Stanford Polygon Library (PLY) Importer", - "", - "", - "", - aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "ply" + "Stanford Polygon Library (PLY) Importer", + "", + "", + "", + aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "ply" }; @@ -92,229 +92,188 @@ namespace // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer PLYImporter::PLYImporter() - : mBuffer(nullptr) - , pcDOM(nullptr) - , mGeneratedMesh(nullptr){ - // empty +: mBuffer(nullptr) +, pcDOM(nullptr) +, mGeneratedMesh(nullptr) { + // empty } // ------------------------------------------------------------------------------------------------ // Destructor, private as well PLYImporter::~PLYImporter() { - // empty + // empty } // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool PLYImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ - const std::string extension = GetExtension(pFile); +bool PLYImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { + const std::string extension = GetExtension(pFile); - if (extension == "ply") - return true; - else if (!extension.length() || checkSig) - { - if (!pIOHandler)return true; - const char* tokens[] = { "ply" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); - } - return false; + if ( extension == "ply" ) { + return true; + } else if (!extension.length() || checkSig) { + if ( !pIOHandler ) { + return true; + } + static const char* tokens[] = { "ply" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); + } + + return false; } // ------------------------------------------------------------------------------------------------ -const aiImporterDesc* PLYImporter::GetInfo() const -{ - return &desc; +const aiImporterDesc* PLYImporter::GetInfo() const { + return &desc; } // ------------------------------------------------------------------------------------------------ static bool isBigEndian(const char* szMe) { - ai_assert(NULL != szMe); + ai_assert(NULL != szMe); - // binary_little_endian - // binary_big_endian - bool isBigEndian(false); + // binary_little_endian + // binary_big_endian + bool isBigEndian(false); #if (defined AI_BUILD_BIG_ENDIAN) - if ( 'l' == *szMe || 'L' == *szMe ) { - isBigEndian = true; - } + if ( 'l' == *szMe || 'L' == *szMe ) { + isBigEndian = true; + } #else - if ('b' == *szMe || 'B' == *szMe) { - isBigEndian = true; - } + if ('b' == *szMe || 'B' == *szMe) { + isBigEndian = true; + } #endif // ! AI_BUILD_BIG_ENDIAN - return isBigEndian; + return isBigEndian; } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. -void PLYImporter::InternReadFile(const std::string& pFile, - aiScene* pScene, IOSystem* pIOHandler) -{ - static const std::string mode = "rb"; - std::unique_ptr fileStream(pIOHandler->Open(pFile, mode)); - if (!fileStream.get()) { - throw DeadlyImportError("Failed to open file " + pFile + "."); - } +void PLYImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { + static const std::string mode = "rb"; + std::unique_ptr fileStream(pIOHandler->Open(pFile, mode)); + if (!fileStream.get()) { + throw DeadlyImportError("Failed to open file " + pFile + "."); + } - // Get the file-size - size_t fileSize = fileStream->FileSize(); - if ( 0 == fileSize ) { - throw DeadlyImportError("File " + pFile + " is empty."); - } + // Get the file-size + const size_t fileSize( fileStream->FileSize() ); + if ( 0 == fileSize ) { + throw DeadlyImportError("File " + pFile + " is empty."); + } - IOStreamBuffer streamedBuffer(1024 * 1024); - streamedBuffer.open(fileStream.get()); + IOStreamBuffer streamedBuffer(1024 * 1024); + streamedBuffer.open(fileStream.get()); - // the beginning of the file must be PLY - magic, magic - std::vector headerCheck; - streamedBuffer.getNextLine(headerCheck); + // the beginning of the file must be PLY - magic, magic + std::vector headerCheck; + streamedBuffer.getNextLine(headerCheck); - if ((headerCheck.size() < 3) || - (headerCheck[0] != 'P' && headerCheck[0] != 'p') || - (headerCheck[1] != 'L' && headerCheck[1] != 'l') || - (headerCheck[2] != 'Y' && headerCheck[2] != 'y') ) - { - streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Magic number \'ply\' is no there"); - } + if ((headerCheck.size() < 3) || + (headerCheck[0] != 'P' && headerCheck[0] != 'p') || + (headerCheck[1] != 'L' && headerCheck[1] != 'l') || + (headerCheck[2] != 'Y' && headerCheck[2] != 'y') ) { + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Magic number \'ply\' is no there"); + } - std::vector mBuffer2; - streamedBuffer.getNextLine(mBuffer2); - mBuffer = (unsigned char*)&mBuffer2[0]; + std::vector mBuffer2; + streamedBuffer.getNextLine(mBuffer2); + mBuffer = (unsigned char*)&mBuffer2[0]; - char* szMe = (char*)&this->mBuffer[0]; - SkipSpacesAndLineEnd(szMe, (const char**)&szMe); + char* szMe = (char*)&this->mBuffer[0]; + SkipSpacesAndLineEnd(szMe, (const char**)&szMe); - // determine the format of the file data and construct the aimesh - PLY::DOM sPlyDom; - this->pcDOM = &sPlyDom; + // determine the format of the file data and construct the aimesh + PLY::DOM sPlyDom; + this->pcDOM = &sPlyDom; - if (TokenMatch(szMe, "format", 6)) { - if (TokenMatch(szMe, "ascii", 5)) { - SkipLine(szMe, (const char**)&szMe); - if (!PLY::DOM::ParseInstance(streamedBuffer, &sPlyDom, this)) - { - if (mGeneratedMesh != NULL) - { - delete(mGeneratedMesh); - mGeneratedMesh = nullptr; + if (TokenMatch(szMe, "format", 6)) { + if (TokenMatch(szMe, "ascii", 5)) { + SkipLine(szMe, (const char**)&szMe); + if (!PLY::DOM::ParseInstance(streamedBuffer, &sPlyDom, this)) { + if (mGeneratedMesh != NULL) { + delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } + + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#1)"); + } + } else if (!::strncmp(szMe, "binary_", 7)) { + szMe += 7; + const bool bIsBE(isBigEndian(szMe)); + + // skip the line, parse the rest of the header and build the DOM + if (!PLY::DOM::ParseInstanceBinary(streamedBuffer, &sPlyDom, this, bIsBE)) { + if (mGeneratedMesh != NULL) { + delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } + + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#2)"); + } + } else { + if (mGeneratedMesh != NULL) { + delete(mGeneratedMesh); + mGeneratedMesh = nullptr; + } + + streamedBuffer.close(); + throw DeadlyImportError("Invalid .ply file: Unknown file format"); + } + } else { + AI_DEBUG_INVALIDATE_PTR(this->mBuffer); + if (mGeneratedMesh != NULL) { + delete(mGeneratedMesh); + mGeneratedMesh = nullptr; } streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#1)"); - } - } - else if (!::strncmp(szMe, "binary_", 7)) - { - szMe += 7; - const bool bIsBE(isBigEndian(szMe)); - - // skip the line, parse the rest of the header and build the DOM - if (!PLY::DOM::ParseInstanceBinary(streamedBuffer, &sPlyDom, this, bIsBE)) - { - if (mGeneratedMesh != NULL) - { - delete(mGeneratedMesh); - mGeneratedMesh = nullptr; - } - - streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Unable to build DOM (#2)"); - } - } - else - { - if (mGeneratedMesh != NULL) - { - delete(mGeneratedMesh); - mGeneratedMesh = nullptr; - } - - streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Unknown file format"); - } - } - else - { - AI_DEBUG_INVALIDATE_PTR(this->mBuffer); - if (mGeneratedMesh != NULL) - { - delete(mGeneratedMesh); - mGeneratedMesh = nullptr; + throw DeadlyImportError("Invalid .ply file: Missing format specification"); } + //free the file buffer streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Missing format specification"); - } - //free the file buffer - streamedBuffer.close(); - - if (mGeneratedMesh == NULL) - { - throw DeadlyImportError("Invalid .ply file: Unable to extract mesh data "); - } - - // if no face list is existing we assume that the vertex - // list is containing a list of points - bool pointsOnly = mGeneratedMesh->mFaces == NULL ? true : false; - if (pointsOnly) - { - if (mGeneratedMesh->mNumVertices < 3) - { - if (mGeneratedMesh != NULL) - { - delete(mGeneratedMesh); - mGeneratedMesh = nullptr; - } - - streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Not enough " - "vertices to build a proper face list. "); + if (mGeneratedMesh == NULL) { + throw DeadlyImportError("Invalid .ply file: Unable to extract mesh data "); } - const unsigned int iNum = (unsigned int)mGeneratedMesh->mNumVertices / 3; - mGeneratedMesh->mNumFaces = iNum; - mGeneratedMesh->mFaces = new aiFace[mGeneratedMesh->mNumFaces]; - - for (unsigned int i = 0; i < iNum; ++i) - { - mGeneratedMesh->mFaces[i].mNumIndices = 3; - mGeneratedMesh->mFaces[i].mIndices = new unsigned int[3]; - mGeneratedMesh->mFaces[i].mIndices[0] = (i * 3); - mGeneratedMesh->mFaces[i].mIndices[1] = (i * 3) + 1; - mGeneratedMesh->mFaces[i].mIndices[2] = (i * 3) + 2; + // if no face list is existing we assume that the vertex + // list is containing a list of points + bool pointsOnly = mGeneratedMesh->mFaces == NULL ? true : false; + if (pointsOnly) { + mGeneratedMesh->mPrimitiveTypes = aiPrimitiveType::aiPrimitiveType_POINT; } - } - // now load a list of all materials - std::vector avMaterials; - std::string defaultTexture; - LoadMaterial(&avMaterials, defaultTexture, pointsOnly); + // now load a list of all materials + std::vector avMaterials; + std::string defaultTexture; + LoadMaterial(&avMaterials, defaultTexture, pointsOnly); - // now generate the output scene object. Fill the material list - pScene->mNumMaterials = (unsigned int)avMaterials.size(); - pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; - for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) { - pScene->mMaterials[i] = avMaterials[i]; - } + // now generate the output scene object. Fill the material list + pScene->mNumMaterials = (unsigned int)avMaterials.size(); + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; + for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) { + pScene->mMaterials[i] = avMaterials[i]; + } - // fill the mesh list - pScene->mNumMeshes = 1; - pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; - pScene->mMeshes[0] = mGeneratedMesh; - mGeneratedMesh = nullptr; + // fill the mesh list + pScene->mNumMeshes = 1; + pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; + pScene->mMeshes[0] = mGeneratedMesh; + mGeneratedMesh = nullptr; - // generate a simple node structure - pScene->mRootNode = new aiNode(); - pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; - pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; + // generate a simple node structure + pScene->mRootNode = new aiNode(); + pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; + pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes]; - for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes; ++i) { - pScene->mRootNode->mMeshes[i] = i; - } + for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes; ++i) { + pScene->mRootNode->mMeshes[i] = i; + } } void PLYImporter::LoadVertex(const PLY::Element* pcElement, const PLY::ElementInstance* instElement, unsigned int pos) { @@ -521,9 +480,7 @@ void PLYImporter::LoadVertex(const PLY::Element* pcElement, const PLY::ElementIn // ------------------------------------------------------------------------------------------------ // Convert a color component to [0...1] -ai_real PLYImporter::NormalizeColorValue(PLY::PropertyInstance::ValueUnion val, - PLY::EDataType eType) -{ +ai_real PLYImporter::NormalizeColorValue(PLY::PropertyInstance::ValueUnion val, PLY::EDataType eType) { switch (eType) { case EDT_Float: diff --git a/code/PlyLoader.h b/code/PlyLoader.h index beada5f23..9199e17d7 100644 --- a/code/PlyLoader.h +++ b/code/PlyLoader.h @@ -57,7 +57,6 @@ struct aiMesh; namespace Assimp { - using namespace PLY; // --------------------------------------------------------------------------- diff --git a/code/PlyParser.cpp b/code/PlyParser.cpp index 85c0f823e..672ac9bde 100644 --- a/code/PlyParser.cpp +++ b/code/PlyParser.cpp @@ -1043,71 +1043,91 @@ bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer &streamBuffer, switch (eType) { case EDT_UInt: - out->iUInt = (uint32_t)*((uint32_t*)pCur); - pCur += 4; + { + uint32_t t; + memcpy(&t, pCur, sizeof(uint32_t)); + pCur += sizeof(uint32_t); // Swap endianness - if (p_bBE)ByteSwap::Swap((int32_t*)&out->iUInt); + if (p_bBE)ByteSwap::Swap(&t); + out->iUInt = t; break; + } case EDT_UShort: { - uint16_t i = *((uint16_t*)pCur); + uint16_t t; + memcpy(&t, pCur, sizeof(uint16_t)); + pCur += sizeof(uint16_t); // Swap endianness - if (p_bBE)ByteSwap::Swap(&i); - out->iUInt = (uint32_t)i; - pCur += 2; + if (p_bBE)ByteSwap::Swap(&t); + out->iUInt = t; break; } case EDT_UChar: { - out->iUInt = (uint32_t)(*((uint8_t*)pCur)); - pCur++; + uint8_t t; + memcpy(&t, pCur, sizeof(uint8_t)); + pCur += sizeof(uint8_t); + out->iUInt = t; break; } case EDT_Int: - out->iInt = *((int32_t*)pCur); - pCur += 4; + { + int32_t t; + memcpy(&t, pCur, sizeof(int32_t)); + pCur += sizeof(int32_t); // Swap endianness - if (p_bBE)ByteSwap::Swap(&out->iInt); + if (p_bBE)ByteSwap::Swap(&t); + out->iInt = t; break; + } case EDT_Short: { - int16_t i = *((int16_t*)pCur); + int16_t t; + memcpy(&t, pCur, sizeof(int16_t)); + pCur += sizeof(int16_t); // Swap endianness - if (p_bBE)ByteSwap::Swap(&i); - out->iInt = (int32_t)i; - pCur += 2; + if (p_bBE)ByteSwap::Swap(&t); + out->iInt = t; break; } case EDT_Char: - out->iInt = (int32_t)*((int8_t*)pCur); - pCur++; + { + int8_t t; + memcpy(&t, pCur, sizeof(int8_t)); + pCur += sizeof(int8_t); + out->iInt = t; break; + } case EDT_Float: { - out->fFloat = *((float*)pCur); + float t; + memcpy(&t, pCur, sizeof(float)); + pCur += sizeof(float); // Swap endianness - if (p_bBE)ByteSwap::Swap((int32_t*)&out->fFloat); - pCur += 4; + if (p_bBE)ByteSwap::Swap(&t); + out->fFloat = t; break; } case EDT_Double: { - out->fDouble = *((double*)pCur); + double t; + memcpy(&t, pCur, sizeof(double)); + pCur += sizeof(double); // Swap endianness - if (p_bBE)ByteSwap::Swap((int64_t*)&out->fDouble); - pCur += 8; + if (p_bBE)ByteSwap::Swap(&t); + out->fDouble = t; break; } default: diff --git a/code/PlyParser.h b/code/PlyParser.h index cba48a4b6..261ac7b82 100644 --- a/code/PlyParser.h +++ b/code/PlyParser.h @@ -39,12 +39,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ - /** @file Defines the helper data structures for importing PLY files */ +#pragma once #ifndef AI_PLYFILEHELPER_H_INC #define AI_PLYFILEHELPER_H_INC - #include #include #include @@ -58,8 +57,7 @@ class PLYImporter; // http://local.wasp.uwa.edu.au/~pbourke/dataformats/ply/ // http://w3.impa.br/~lvelho/outgoing/sossai/old/ViHAP_D4.4.2_PLY_format_v1.1.pdf // http://www.okino.com/conv/exp_ply.htm -namespace PLY -{ +namespace PLY { // --------------------------------------------------------------------------------- /* @@ -78,8 +76,7 @@ int8 int16 uint8 ... forms are also used */ -enum EDataType -{ +enum EDataType { EDT_Char = 0x0u, EDT_UChar, EDT_Short, @@ -98,8 +95,7 @@ enum EDataType * * Semantics define the usage of a property, e.g. x coordinate */ -enum ESemantic -{ +enum ESemantic { //! vertex position x coordinate EST_XCoord = 0x0u, //! vertex position x coordinate @@ -182,15 +178,14 @@ enum ESemantic * * Semantics define the usage of an element, e.g. vertex or material */ -enum EElementSemantic -{ +enum EElementSemantic { //! The element is a vertex EEST_Vertex = 0x0u, //! The element is a face description (index table) EEST_Face, - //! The element is a tristrip description (index table) + //! The element is a triangle-strip description (index table) EEST_TriStrip, //! The element is an edge description (ignored) @@ -211,17 +206,16 @@ enum EElementSemantic * * This can e.g. be a part of the vertex declaration */ -class Property -{ +class Property { public: - //! Default constructor Property() - : eType (EDT_Int), - Semantic(), - bIsList(false), - eFirstType(EDT_UChar) - {} + : eType (EDT_Int) + , Semantic() + , bIsList(false) + , eFirstType(EDT_UChar) { + // empty + } //! Data type of the property EDataType eType; @@ -260,15 +254,14 @@ public: * This can e.g. be the vertex declaration. Elements contain a * well-defined number of properties. */ -class Element -{ +class Element { public: - //! Default constructor Element() - : eSemantic (EEST_INVALID) - , NumOccur(0) - {} + : eSemantic (EEST_INVALID) + , NumOccur(0) { + // empty + } //! List of properties assigned to the element //! std::vector to support operator[] diff --git a/code/PostStepRegistry.cpp b/code/PostStepRegistry.cpp index 49f5d7375..2a5e211c1 100644 --- a/code/PostStepRegistry.cpp +++ b/code/PostStepRegistry.cpp @@ -125,6 +125,9 @@ corresponding preprocessor flag to selectively disable steps. #ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS # include "DeboneProcess.h" #endif +#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) +# include "ScaleProcess.h" +#endif namespace Assimp { @@ -136,7 +139,7 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out) // of sequence it is executed. Steps that are added here are not // validated - as RegisterPPStep() does - all dependencies must be given. // ---------------------------------------------------------------------------- - out.reserve(30); + out.reserve(31); #if (!defined ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS) out.push_back( new MakeLeftHandedProcess()); #endif @@ -197,7 +200,9 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out) #if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS) out.push_back( new GenFaceNormalsProcess()); #endif - +#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) + out.push_back( new ScaleProcess()); +#endif // ......................................................................... // DON'T change the order of these five .. // XXX this is actually a design weakness that dates back to the time diff --git a/code/PretransformVertices.cpp b/code/PretransformVertices.cpp index 6c569c185..cc5c669e4 100644 --- a/code/PretransformVertices.cpp +++ b/code/PretransformVertices.cpp @@ -651,7 +651,8 @@ void PretransformVertices::Execute( aiScene* pScene) // generate mesh nodes for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes) { - aiNode* pcNode = *nodes = new aiNode(); + aiNode* pcNode = new aiNode(); + *nodes = pcNode; pcNode->mParent = pScene->mRootNode; pcNode->mName = pScene->mMeshes[i]->mName; @@ -663,7 +664,8 @@ void PretransformVertices::Execute( aiScene* pScene) // generate light nodes for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes) { - aiNode* pcNode = *nodes = new aiNode(); + aiNode* pcNode = new aiNode(); + *nodes = pcNode; pcNode->mParent = pScene->mRootNode; pcNode->mName.length = ai_snprintf(pcNode->mName.data, MAXLEN, "light_%u",i); pScene->mLights[i]->mName = pcNode->mName; @@ -671,7 +673,8 @@ void PretransformVertices::Execute( aiScene* pScene) // generate camera nodes for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes) { - aiNode* pcNode = *nodes = new aiNode(); + aiNode* pcNode = new aiNode(); + *nodes = pcNode; pcNode->mParent = pScene->mRootNode; pcNode->mName.length = ::ai_snprintf(pcNode->mName.data,MAXLEN,"cam_%u",i); pScene->mCameras[i]->mName = pcNode->mName; diff --git a/code/STLExporter.cpp b/code/STLExporter.cpp index 4c7a8a639..3d681f8c7 100644 --- a/code/STLExporter.cpp +++ b/code/STLExporter.cpp @@ -172,12 +172,16 @@ void STLExporter :: WriteMeshBinary(const aiMesh* m) } nor.Normalize(); } - ai_real nx = nor.x, ny = nor.y, nz = nor.z; + // STL binary files use 4-byte floats. This may possibly cause loss of precision + // for clients using 8-byte doubles + float nx = (float) nor.x; + float ny = (float) nor.y; + float nz = (float) nor.z; AI_SWAP4(nx); AI_SWAP4(ny); AI_SWAP4(nz); mOutput.write((char *)&nx, 4); mOutput.write((char *)&ny, 4); mOutput.write((char *)&nz, 4); for(unsigned int a = 0; a < f.mNumIndices; ++a) { const aiVector3D& v = m->mVertices[f.mIndices[a]]; - ai_real vx = v.x, vy = v.y, vz = v.z; + float vx = (float) v.x, vy = (float) v.y, vz = (float) v.z; AI_SWAP4(vx); AI_SWAP4(vy); AI_SWAP4(vz); mOutput.write((char *)&vx, 4); mOutput.write((char *)&vy, 4); mOutput.write((char *)&vz, 4); } diff --git a/code/STLLoader.cpp b/code/STLLoader.cpp index fc326b2c7..3a85a5495 100644 --- a/code/STLLoader.cpp +++ b/code/STLLoader.cpp @@ -449,26 +449,43 @@ bool STLImporter::LoadBinaryFile() aiVector3D *vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; aiVector3D *vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + typedef aiVector3t aiVector3F; + aiVector3F* theVec; + aiVector3F theVec3F; + for ( unsigned int i = 0; i < pMesh->mNumFaces; ++i ) { // NOTE: Blender sometimes writes empty normals ... this is not // our fault ... the RemoveInvalidData helper step should fix that - ::memcpy( vn, sz, sizeof( aiVector3D ) ); - sz += sizeof(aiVector3D); + + // There's one normal for the face in the STL; use it three times + // for vertex normals + theVec = (aiVector3F*) sz; + ::memcpy( &theVec3F, theVec, sizeof(aiVector3F) ); + vn->x = theVec3F.x; vn->y = theVec3F.y; vn->z = theVec3F.z; *(vn+1) = *vn; *(vn+2) = *vn; + ++theVec; vn += 3; - ::memcpy( vp, sz, sizeof( aiVector3D ) ); + // vertex 1 + ::memcpy( &theVec3F, theVec, sizeof(aiVector3F) ); + vp->x = theVec3F.x; vp->y = theVec3F.y; vp->z = theVec3F.z; + ++theVec; ++vp; - sz += sizeof(aiVector3D); - ::memcpy( vp, sz, sizeof( aiVector3D ) ); + // vertex 2 + ::memcpy( &theVec3F, theVec, sizeof(aiVector3F) ); + vp->x = theVec3F.x; vp->y = theVec3F.y; vp->z = theVec3F.z; + ++theVec; ++vp; - sz += sizeof(aiVector3D); - ::memcpy( vp, sz, sizeof( aiVector3D ) ); + // vertex 3 + ::memcpy( &theVec3F, theVec, sizeof(aiVector3F) ); + vp->x = theVec3F.x; vp->y = theVec3F.y; vp->z = theVec3F.z; + ++theVec; ++vp; - sz += sizeof(aiVector3D); + + sz = (const unsigned char*) theVec; uint16_t color = *((uint16_t*)sz); sz += 2; diff --git a/code/ScaleProcess.cpp b/code/ScaleProcess.cpp index ef7357b68..facc39d7d 100644 --- a/code/ScaleProcess.cpp +++ b/code/ScaleProcess.cpp @@ -39,6 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ +#ifndef ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS + #include "ScaleProcess.h" #include @@ -86,13 +88,6 @@ void ScaleProcess::Execute( aiScene* pScene ) { void ScaleProcess::traverseNodes( aiNode *node ) { applyScaling( node ); - - /*for ( unsigned int i = 0; i < node->mNumChildren; ++i ) { - aiNode *currentNode = currentNode->mChildren[ i ]; - if ( nullptr != currentNode ) { - traverseNodes( currentNode ); - } - }*/ } void ScaleProcess::applyScaling( aiNode *currentNode ) { @@ -104,3 +99,5 @@ void ScaleProcess::applyScaling( aiNode *currentNode ) { } } // Namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS diff --git a/code/SceneCombiner.cpp b/code/SceneCombiner.cpp index 30722618f..589291131 100644 --- a/code/SceneCombiner.cpp +++ b/code/SceneCombiner.cpp @@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include #include "time.h" #include @@ -806,8 +807,9 @@ void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, for (std::vector::const_iterator it = begin; it != end;++it) { if ((*it)->mNormals) { ::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + DefaultLogger::get()->warn( "JoinMeshes: Normals expected but input mesh contains no normals" ); } - else DefaultLogger::get()->warn("JoinMeshes: Normals expected but input mesh contains no normals"); pv2 += (*it)->mNumVertices; } } @@ -817,28 +819,29 @@ void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, pv2 = out->mTangents = new aiVector3D[out->mNumVertices]; aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices]; - for (std::vector::const_iterator it = begin; it != end;++it) { + for (std::vector::const_iterator it = begin; it != end;++it) { if ((*it)->mTangents) { ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D)); ::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + DefaultLogger::get()->warn( "JoinMeshes: Tangents expected but input mesh contains no tangents" ); } - else DefaultLogger::get()->warn("JoinMeshes: Tangents expected but input mesh contains no tangents"); pv2 += (*it)->mNumVertices; pv2b += (*it)->mNumVertices; } } // copy texture coordinates unsigned int n = 0; - while ((**begin).HasTextureCoords(n)) { + while ((**begin).HasTextureCoords(n)) { out->mNumUVComponents[n] = (*begin)->mNumUVComponents[n]; pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices]; for (std::vector::const_iterator it = begin; it != end;++it) { - if ((*it)->mTextureCoords[n]) { ::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D)); + } else { + DefaultLogger::get()->warn( "JoinMeshes: UVs expected but input mesh contains no UVs" ); } - else DefaultLogger::get()->warn("JoinMeshes: UVs expected but input mesh contains no UVs"); pv2 += (*it)->mNumVertices; } ++n; @@ -848,11 +851,11 @@ void SceneCombiner::MergeMeshes(aiMesh** _out, unsigned int /*flags*/, while ((**begin).HasVertexColors(n)) { aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices]; for (std::vector::const_iterator it = begin; it != end;++it) { - if ((*it)->mColors[n]) { ::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D)); + } else { + DefaultLogger::get()->warn( "JoinMeshes: VCs expected but input mesh contains no VCs" ); } - else DefaultLogger::get()->warn("JoinMeshes: VCs expected but input mesh contains no VCs"); pv2 += (*it)->mNumVertices; } ++n; @@ -1001,7 +1004,12 @@ void SceneCombiner::CopyScene(aiScene** _dest,const aiScene* src,bool allocate) *_dest = new aiScene(); } aiScene* dest = *_dest; - ai_assert(dest); + ai_assert(nullptr != dest); + + // copy metadata + if ( nullptr != src->mMetaData ) { + dest->mMetaData = new aiMetadata( *src->mMetaData ); + } // copy animations dest->mNumAnimations = src->mNumAnimations; @@ -1256,29 +1264,30 @@ void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) { aiMetadataEntry& out = dest->mValues[i]; out.mType = in.mType; switch (dest->mValues[i].mType) { - case AI_BOOL: - out.mData = new bool(*static_cast(in.mData)); - break; - case AI_INT32: - out.mData = new int32_t(*static_cast(in.mData)); - break; - case AI_UINT64: - out.mData = new uint64_t(*static_cast(in.mData)); - break; - case AI_FLOAT: - out.mData = new float(*static_cast(in.mData)); - break; - case AI_DOUBLE: - out.mData = new double(*static_cast(in.mData)); - break; - case AI_AISTRING: - out.mData = new aiString(*static_cast(in.mData)); - break; - case AI_AIVECTOR3D: - out.mData = new aiVector3D(*static_cast(in.mData)); - break; - default: - ai_assert(false); + case AI_BOOL: + out.mData = new bool(*static_cast(in.mData)); + break; + case AI_INT32: + out.mData = new int32_t(*static_cast(in.mData)); + break; + case AI_UINT64: + out.mData = new uint64_t(*static_cast(in.mData)); + break; + case AI_FLOAT: + out.mData = new float(*static_cast(in.mData)); + break; + case AI_DOUBLE: + out.mData = new double(*static_cast(in.mData)); + break; + case AI_AISTRING: + out.mData = new aiString(*static_cast(in.mData)); + break; + case AI_AIVECTOR3D: + out.mData = new aiVector3D(*static_cast(in.mData)); + break; + default: + ai_assert(false); + break; } } } diff --git a/code/ScenePrivate.h b/code/ScenePrivate.h index a09d8784b..50959f455 100644 --- a/code/ScenePrivate.h +++ b/code/ScenePrivate.h @@ -42,22 +42,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file Stuff to deal with aiScene::mPrivate */ +#pragma once #ifndef AI_SCENEPRIVATE_H_INCLUDED #define AI_SCENEPRIVATE_H_INCLUDED +#include #include -namespace Assimp { +namespace Assimp { +// Forward declarations class Importer; struct ScenePrivateData { - ScenePrivateData() - : mOrigImporter() - , mPPStepsApplied() - , mIsCopy() - {} + : mOrigImporter( nullptr ) + , mPPStepsApplied( 0 ) + , mIsCopy( false ) { + // empty + } // Importer that originally loaded the scene though the C-API // If set, this object is owned by this private data instance. @@ -75,14 +78,24 @@ struct ScenePrivateData { }; // Access private data stored in the scene -inline ScenePrivateData* ScenePriv(aiScene* in) { +inline +ScenePrivateData* ScenePriv(aiScene* in) { + ai_assert( nullptr != in ); + if ( nullptr == in ) { + return nullptr; + } return static_cast(in->mPrivate); } -inline const ScenePrivateData* ScenePriv(const aiScene* in) { +inline +const ScenePrivateData* ScenePriv(const aiScene* in) { + ai_assert( nullptr != in ); + if ( nullptr == in ) { + return nullptr; + } return static_cast(in->mPrivate); } -} +} // Namespace Assimp -#endif +#endif // AI_SCENEPRIVATE_H_INCLUDED diff --git a/code/SpatialSort.cpp b/code/SpatialSort.cpp index 010cad7c1..e6ffeb260 100644 --- a/code/SpatialSort.cpp +++ b/code/SpatialSort.cpp @@ -294,7 +294,7 @@ void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition, index++; // Now start iterating from there until the first position lays outside of the distance range. - // Add all positions inside the distance range within the tolerance to the result aray + // Add all positions inside the distance range within the tolerance to the result array std::vector::const_iterator it = mPositions.begin() + index; while( ToBinary(it->mDistance) < maxDistBinary) { diff --git a/code/VertexTriangleAdjacency.cpp b/code/VertexTriangleAdjacency.cpp index 022d5d902..5886ca372 100644 --- a/code/VertexTriangleAdjacency.cpp +++ b/code/VertexTriangleAdjacency.cpp @@ -48,7 +48,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "VertexTriangleAdjacency.h" #include - using namespace Assimp; // ------------------------------------------------------------------------------------------------ @@ -60,8 +59,8 @@ VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces, // compute the number of referenced vertices if it wasn't specified by the caller const aiFace* const pcFaceEnd = pcFaces + iNumFaces; if (!iNumVertices) { - for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) { + ai_assert( nullptr != pcFace ); ai_assert(3 == pcFace->mNumIndices); iNumVertices = std::max(iNumVertices,pcFace->mIndices[0]); iNumVertices = std::max(iNumVertices,pcFace->mIndices[1]); @@ -69,19 +68,18 @@ VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces, } } - this->iNumVertices = iNumVertices; + mNumVertices = iNumVertices; unsigned int* pi; // allocate storage if (bComputeNumTriangles) { pi = mLiveTriangles = new unsigned int[iNumVertices+1]; - memset(mLiveTriangles,0,sizeof(unsigned int)*(iNumVertices+1)); + ::memset(mLiveTriangles,0,sizeof(unsigned int)*(iNumVertices+1)); mOffsetTable = new unsigned int[iNumVertices+2]+1; - } - else { + } else { pi = mOffsetTable = new unsigned int[iNumVertices+2]+1; - memset(mOffsetTable,0,sizeof(unsigned int)*(iNumVertices+1)); + ::memset(mOffsetTable,0,sizeof(unsigned int)*(iNumVertices+1)); mLiveTriangles = NULL; // important, otherwise the d'tor would crash } @@ -90,8 +88,7 @@ VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces, *piEnd++ = 0u; // first pass: compute the number of faces referencing each vertex - for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) - { + for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) { pi[pcFace->mIndices[0]]++; pi[pcFace->mIndices[1]]++; pi[pcFace->mIndices[2]]++; diff --git a/code/VertexTriangleAdjacency.h b/code/VertexTriangleAdjacency.h index 566783835..23624a5be 100644 --- a/code/VertexTriangleAdjacency.h +++ b/code/VertexTriangleAdjacency.h @@ -60,10 +60,8 @@ namespace Assimp { * @note Although it is called #VertexTriangleAdjacency, the current version does also * support arbitrary polygons. */ // -------------------------------------------------------------------------------------------- -class ASSIMP_API VertexTriangleAdjacency -{ +class ASSIMP_API VertexTriangleAdjacency { public: - // ---------------------------------------------------------------------------- /** @brief Construction from an existing index buffer * @param pcFaces Index buffer @@ -77,39 +75,30 @@ public: unsigned int iNumVertices = 0, bool bComputeNumTriangles = true); - // ---------------------------------------------------------------------------- /** @brief Destructor */ ~VertexTriangleAdjacency(); - -public: - // ---------------------------------------------------------------------------- /** @brief Get all triangles adjacent to a vertex * @param iVertIndex Index of the vertex * @return A pointer to the adjacency list. */ - unsigned int* GetAdjacentTriangles(unsigned int iVertIndex) const - { - ai_assert(iVertIndex < iNumVertices); + unsigned int* GetAdjacentTriangles(unsigned int iVertIndex) const { + ai_assert(iVertIndex < mNumVertices); return &mAdjacencyTable[ mOffsetTable[iVertIndex]]; } - // ---------------------------------------------------------------------------- /** @brief Get the number of triangles that are referenced by * a vertex. This function returns a reference that can be modified * @param iVertIndex Index of the vertex * @return Number of referenced triangles */ - unsigned int& GetNumTrianglesPtr(unsigned int iVertIndex) - { - ai_assert(iVertIndex < iNumVertices && NULL != mLiveTriangles); + unsigned int& GetNumTrianglesPtr(unsigned int iVertIndex) { + ai_assert( iVertIndex < mNumVertices ); + ai_assert( nullptr != mLiveTriangles ); return mLiveTriangles[iVertIndex]; } - -public: - //! Offset table unsigned int* mOffsetTable; @@ -120,9 +109,9 @@ public: unsigned int* mLiveTriangles; //! Debug: Number of referenced vertices - unsigned int iNumVertices; - + unsigned int mNumVertices; }; -} + +} //! ns Assimp #endif // !! AI_VTADJACENCY_H_INC diff --git a/code/XFileImporter.cpp b/code/XFileImporter.cpp index 93ae6173b..fd28fbb79 100644 --- a/code/XFileImporter.cpp +++ b/code/XFileImporter.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2018, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -44,7 +42,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Implementation of the XFile importer class */ - #ifndef ASSIMP_BUILD_NO_X_IMPORTER #include "XFileImporter.h" @@ -79,17 +76,19 @@ static const aiImporterDesc desc = { // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer XFileImporter::XFileImporter() -{} +: mBuffer() { + // empty +} // ------------------------------------------------------------------------------------------------ // Destructor, private as well -XFileImporter::~XFileImporter() -{} +XFileImporter::~XFileImporter() { + // empty +} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ +bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { std::string extension = GetExtension(pFile); if(extension == "x") { return true; @@ -104,23 +103,24 @@ bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, boo // ------------------------------------------------------------------------------------------------ // Get file extension list -const aiImporterDesc* XFileImporter::GetInfo () const -{ +const aiImporterDesc* XFileImporter::GetInfo () const { return &desc; } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. -void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) -{ +void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { // read file into memory std::unique_ptr file( pIOHandler->Open( pFile)); - if( file.get() == NULL) - throw DeadlyImportError( "Failed to open file " + pFile + "."); + if ( file.get() == NULL ) { + throw DeadlyImportError( "Failed to open file " + pFile + "." ); + } + static const size_t MinSize = 16; size_t fileSize = file->FileSize(); - if( fileSize < 16) - throw DeadlyImportError( "XFile is too small."); + if ( fileSize < MinSize ) { + throw DeadlyImportError( "XFile is too small." ); + } // in the hope that binary files will never start with a BOM ... mBuffer.resize( fileSize + 1); @@ -134,8 +134,9 @@ void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, I CreateDataRepresentationFromImport( pScene, parser.GetImportedData()); // if nothing came from it, report it as error - if( !pScene->mRootNode) - throw DeadlyImportError( "XFile is ill-formatted - no content imported."); + if ( !pScene->mRootNode ) { + throw DeadlyImportError( "XFile is ill-formatted - no content imported." ); + } } // ------------------------------------------------------------------------------------------------ @@ -146,17 +147,15 @@ void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, XFile:: ConvertMaterials( pScene, pData->mGlobalMaterials); // copy nodes, extracting meshes and materials on the way - pScene->mRootNode = CreateNodes( pScene, NULL, pData->mRootNode); + pScene->mRootNode = CreateNodes( pScene, nullptr, pData->mRootNode); // extract animations CreateAnimations( pScene, pData); // read the global meshes that were stored outside of any node - if( pData->mGlobalMeshes.size() > 0) - { + if( !pData->mGlobalMeshes.empty() ) { // create a root node to hold them if there isn't any, yet - if( pScene->mRootNode == NULL) - { + if( pScene->mRootNode == nullptr ) { pScene->mRootNode = new aiNode; pScene->mRootNode->mName.Set( "$dummy_node"); } @@ -180,8 +179,7 @@ void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, XFile:: flipper.Execute(pScene); // finally: create a dummy material if not material was imported - if( pScene->mNumMaterials == 0) - { + if( pScene->mNumMaterials == 0) { pScene->mNumMaterials = 1; // create the Material aiMaterial* mat = new aiMaterial; @@ -205,10 +203,10 @@ void XFileImporter::CreateDataRepresentationFromImport( aiScene* pScene, XFile:: // ------------------------------------------------------------------------------------------------ // Recursively creates scene nodes from the imported hierarchy. -aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode) -{ - if( !pNode) - return NULL; +aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFile::Node* pNode) { + if ( !pNode ) { + return nullptr; + } // create node aiNode* node = new aiNode; @@ -222,13 +220,13 @@ aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFil CreateMeshes( pScene, node, pNode->mMeshes); // handle childs - if( pNode->mChildren.size() > 0) - { + if( !pNode->mChildren.empty() ) { node->mNumChildren = (unsigned int)pNode->mChildren.size(); node->mChildren = new aiNode* [node->mNumChildren]; - for( unsigned int a = 0; a < pNode->mChildren.size(); a++) - node->mChildren[a] = CreateNodes( pScene, node, pNode->mChildren[a]); + for ( unsigned int a = 0; a < pNode->mChildren.size(); ++a ) { + node->mChildren[ a ] = CreateNodes( pScene, node, pNode->mChildren[ a ] ); + } } return node; @@ -236,16 +234,14 @@ aiNode* XFileImporter::CreateNodes( aiScene* pScene, aiNode* pParent, const XFil // ------------------------------------------------------------------------------------------------ // Creates the meshes for the given node. -void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector& pMeshes) -{ +void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vector& pMeshes) { if (pMeshes.empty()) { return; } // create a mesh for each mesh-material combination in the source node std::vector meshes; - for( unsigned int a = 0; a < pMeshes.size(); a++) - { + for( unsigned int a = 0; a < pMeshes.size(); ++a ) { XFile::Mesh* sourceMesh = pMeshes[a]; if ( nullptr == sourceMesh ) { continue; @@ -255,35 +251,30 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec ConvertMaterials( pScene, sourceMesh->mMaterials); unsigned int numMaterials = std::max( (unsigned int)sourceMesh->mMaterials.size(), 1u); - for( unsigned int b = 0; b < numMaterials; b++) - { + for( unsigned int b = 0; b < numMaterials; ++b ) { // collect the faces belonging to this material std::vector faces; unsigned int numVertices = 0; - if( sourceMesh->mFaceMaterials.size() > 0) - { + if( !sourceMesh->mFaceMaterials.empty() ) { // if there is a per-face material defined, select the faces with the corresponding material - for( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); c++) - { - if( sourceMesh->mFaceMaterials[c] == b) - { + for( unsigned int c = 0; c < sourceMesh->mFaceMaterials.size(); ++c ) { + if( sourceMesh->mFaceMaterials[c] == b) { faces.push_back( c); numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size(); } } - } else - { + } else { // if there is no per-face material, place everything into one mesh - for( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); c++) - { + for( unsigned int c = 0; c < sourceMesh->mPosFaces.size(); ++c ) { faces.push_back( c); numVertices += (unsigned int)sourceMesh->mPosFaces[c].mIndices.size(); } } // no faces/vertices using this material? strange... - if( numVertices == 0) + if ( numVertices == 0 ) { continue; + } // create a submesh using this material aiMesh* mesh = new aiMesh; @@ -291,11 +282,9 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec // find the material in the scene's material list. Either own material // or referenced material, it should already have a valid index - if( sourceMesh->mFaceMaterials.size() > 0) - { + if( !sourceMesh->mFaceMaterials.empty() ) { mesh->mMaterialIndex = static_cast(sourceMesh->mMaterials[b].sceneIndex); - } else - { + } else { mesh->mMaterialIndex = 0; } @@ -310,28 +299,28 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec mesh->mName.Set(sourceMesh->mName); // normals? - if( sourceMesh->mNormals.size() > 0) - mesh->mNormals = new aiVector3D[numVertices]; + if ( sourceMesh->mNormals.size() > 0 ) { + mesh->mNormals = new aiVector3D[ numVertices ]; + } // texture coords - for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; c++) - { - if( sourceMesh->mTexCoords[c].size() > 0) - mesh->mTextureCoords[c] = new aiVector3D[numVertices]; + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c ) { + if ( !sourceMesh->mTexCoords[ c ].empty() ) { + mesh->mTextureCoords[ c ] = new aiVector3D[ numVertices ]; + } } // vertex colors - for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; c++) - { - if( sourceMesh->mColors[c].size() > 0) - mesh->mColors[c] = new aiColor4D[numVertices]; + for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c ) { + if ( !sourceMesh->mColors[ c ].empty() ) { + mesh->mColors[ c ] = new aiColor4D[ numVertices ]; + } } // now collect the vertex data of all data streams present in the imported mesh - unsigned int newIndex = 0; + unsigned int newIndex( 0 ); std::vector orgPoints; // from which original point each new vertex stems orgPoints.resize( numVertices, 0); - for( unsigned int c = 0; c < faces.size(); c++) - { + for( unsigned int c = 0; c < faces.size(); ++c ) { unsigned int f = faces[c]; // index of the source face const XFile::Face& pf = sourceMesh->mPosFaces[f]; // position source face @@ -341,30 +330,30 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec df.mIndices = new unsigned int[ df.mNumIndices]; // collect vertex data for indices of this face - for( unsigned int d = 0; d < df.mNumIndices; d++) - { + for( unsigned int d = 0; d < df.mNumIndices; ++d ) { df.mIndices[d] = newIndex; orgPoints[newIndex] = pf.mIndices[d]; // Position mesh->mVertices[newIndex] = sourceMesh->mPositions[pf.mIndices[d]]; // Normal, if present - if( mesh->HasNormals()) - mesh->mNormals[newIndex] = sourceMesh->mNormals[sourceMesh->mNormFaces[f].mIndices[d]]; + if ( mesh->HasNormals() ) { + mesh->mNormals[ newIndex ] = sourceMesh->mNormals[ sourceMesh->mNormFaces[ f ].mIndices[ d ] ]; + } // texture coord sets - for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; e++) - { - if( mesh->HasTextureCoords( e)) - { + for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++e ) { + if( mesh->HasTextureCoords( e)) { aiVector2D tex = sourceMesh->mTexCoords[e][pf.mIndices[d]]; mesh->mTextureCoords[e][newIndex] = aiVector3D( tex.x, 1.0f - tex.y, 0.0f); } } // vertex color sets - for( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; e++) - if( mesh->HasVertexColors( e)) - mesh->mColors[e][newIndex] = sourceMesh->mColors[e][pf.mIndices[d]]; + for ( unsigned int e = 0; e < AI_MAX_NUMBER_OF_COLOR_SETS; ++e ) { + if ( mesh->HasVertexColors( e ) ) { + mesh->mColors[ e ][ newIndex ] = sourceMesh->mColors[ e ][ pf.mIndices[ d ] ]; + } + } newIndex++; } @@ -376,28 +365,29 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec // convert all bones of the source mesh which influence vertices in this newly created mesh const std::vector& bones = sourceMesh->mBones; std::vector newBones; - for( unsigned int c = 0; c < bones.size(); c++) - { + for( unsigned int c = 0; c < bones.size(); ++c ) { const XFile::Bone& obone = bones[c]; // set up a vertex-linear array of the weights for quick searching if a bone influences a vertex std::vector oldWeights( sourceMesh->mPositions.size(), 0.0); - for( unsigned int d = 0; d < obone.mWeights.size(); d++) - oldWeights[obone.mWeights[d].mVertex] = obone.mWeights[d].mWeight; + for ( unsigned int d = 0; d < obone.mWeights.size(); ++d ) { + oldWeights[ obone.mWeights[ d ].mVertex ] = obone.mWeights[ d ].mWeight; + } // collect all vertex weights that influence a vertex in the new mesh std::vector newWeights; newWeights.reserve( numVertices); - for( unsigned int d = 0; d < orgPoints.size(); d++) - { + for( unsigned int d = 0; d < orgPoints.size(); ++d ) { // does the new vertex stem from an old vertex which was influenced by this bone? ai_real w = oldWeights[orgPoints[d]]; - if( w > 0.0) - newWeights.push_back( aiVertexWeight( d, w)); + if ( w > 0.0 ) { + newWeights.push_back( aiVertexWeight( d, w ) ); + } } // if the bone has no weights in the newly created mesh, ignore it - if( newWeights.size() == 0) + if ( newWeights.empty() ) { continue; + } // create aiBone* nbone = new aiBone; @@ -407,14 +397,14 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec nbone->mOffsetMatrix = obone.mOffsetMatrix; nbone->mNumWeights = (unsigned int)newWeights.size(); nbone->mWeights = new aiVertexWeight[nbone->mNumWeights]; - for( unsigned int d = 0; d < newWeights.size(); d++) - nbone->mWeights[d] = newWeights[d]; + for ( unsigned int d = 0; d < newWeights.size(); ++d ) { + nbone->mWeights[ d ] = newWeights[ d ]; + } } // store the bones in the mesh mesh->mNumBones = (unsigned int)newBones.size(); - if( newBones.size() > 0) - { + if( !newBones.empty()) { mesh->mBones = new aiBone*[mesh->mNumBones]; std::copy( newBones.begin(), newBones.end(), mesh->mBones); } @@ -424,8 +414,7 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec // reallocate scene mesh array to be large enough aiMesh** prevArray = pScene->mMeshes; pScene->mMeshes = new aiMesh*[pScene->mNumMeshes + meshes.size()]; - if( prevArray) - { + if( prevArray) { memcpy( pScene->mMeshes, prevArray, pScene->mNumMeshes * sizeof( aiMesh*)); delete [] prevArray; } @@ -435,8 +424,7 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec pNode->mMeshes = new unsigned int[pNode->mNumMeshes]; // store all meshes in the mesh library of the scene and store their indices in the node - for( unsigned int a = 0; a < meshes.size(); a++) - { + for( unsigned int a = 0; a < meshes.size(); a++) { pScene->mMeshes[pScene->mNumMeshes] = meshes[a]; pNode->mMeshes[a] = pScene->mNumMeshes; pScene->mNumMeshes++; @@ -445,16 +433,15 @@ void XFileImporter::CreateMeshes( aiScene* pScene, aiNode* pNode, const std::vec // ------------------------------------------------------------------------------------------------ // Converts the animations from the given imported data and creates them in the scene. -void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData) -{ +void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData) { std::vector newAnims; - for( unsigned int a = 0; a < pData->mAnims.size(); a++) - { + for( unsigned int a = 0; a < pData->mAnims.size(); ++a ) { const XFile::Animation* anim = pData->mAnims[a]; // some exporters mock me with empty animation tags. - if( anim->mAnims.size() == 0) + if ( anim->mAnims.empty() ) { continue; + } // create a new animation to hold the data aiAnimation* nanim = new aiAnimation; @@ -466,15 +453,14 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData nanim->mNumChannels = (unsigned int)anim->mAnims.size(); nanim->mChannels = new aiNodeAnim*[nanim->mNumChannels]; - for( unsigned int b = 0; b < anim->mAnims.size(); b++) - { + for( unsigned int b = 0; b < anim->mAnims.size(); ++b ) { const XFile::AnimBone* bone = anim->mAnims[b]; aiNodeAnim* nbone = new aiNodeAnim; nbone->mNodeName.Set( bone->mBoneName); nanim->mChannels[b] = nbone; // keyframes are given as combined transformation matrix keys - if( bone->mTrafoKeys.size() > 0) + if( !bone->mTrafoKeys.empty() ) { nbone->mNumPositionKeys = (unsigned int)bone->mTrafoKeys.size(); nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys]; @@ -483,8 +469,7 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData nbone->mNumScalingKeys = (unsigned int)bone->mTrafoKeys.size(); nbone->mScalingKeys = new aiVectorKey[nbone->mNumScalingKeys]; - for( unsigned int c = 0; c < bone->mTrafoKeys.size(); c++) - { + for( unsigned int c = 0; c < bone->mTrafoKeys.size(); ++c) { // deconstruct each matrix into separate position, rotation and scaling double time = bone->mTrafoKeys[c].mTime; aiMatrix4x4 trafo = bone->mTrafoKeys[c].mMatrix; @@ -516,13 +501,11 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData // longest lasting key sequence determines duration nanim->mDuration = std::max( nanim->mDuration, bone->mTrafoKeys.back().mTime); - } else - { + } else { // separate key sequences for position, rotation, scaling nbone->mNumPositionKeys = (unsigned int)bone->mPosKeys.size(); nbone->mPositionKeys = new aiVectorKey[nbone->mNumPositionKeys]; - for( unsigned int c = 0; c < nbone->mNumPositionKeys; c++) - { + for( unsigned int c = 0; c < nbone->mNumPositionKeys; ++c ) { aiVector3D pos = bone->mPosKeys[c].mValue; nbone->mPositionKeys[c].mTime = bone->mPosKeys[c].mTime; @@ -532,8 +515,7 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData // rotation nbone->mNumRotationKeys = (unsigned int)bone->mRotKeys.size(); nbone->mRotationKeys = new aiQuatKey[nbone->mNumRotationKeys]; - for( unsigned int c = 0; c < nbone->mNumRotationKeys; c++) - { + for( unsigned int c = 0; c < nbone->mNumRotationKeys; ++c ) { aiMatrix3x3 rotmat = bone->mRotKeys[c].mValue.GetMatrix(); nbone->mRotationKeys[c].mTime = bone->mRotKeys[c].mTime; @@ -573,56 +555,51 @@ void XFileImporter::CreateAnimations( aiScene* pScene, const XFile::Scene* pData void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector& pMaterials) { // count the non-referrer materials in the array - unsigned int numNewMaterials = 0; - for( unsigned int a = 0; a < pMaterials.size(); a++) - if( !pMaterials[a].mIsReference) - numNewMaterials++; + unsigned int numNewMaterials( 0 ); + for ( unsigned int a = 0; a < pMaterials.size(); ++a ) { + if ( !pMaterials[ a ].mIsReference ) { + ++numNewMaterials; + } + } // resize the scene's material list to offer enough space for the new materials - if( numNewMaterials > 0 ) - { - aiMaterial** prevMats = pScene->mMaterials; - pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numNewMaterials]; - if( prevMats) - { - memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*)); - delete [] prevMats; - } - } + if( numNewMaterials > 0 ) { + aiMaterial** prevMats = pScene->mMaterials; + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials + numNewMaterials]; + if( nullptr != prevMats) { + ::memcpy( pScene->mMaterials, prevMats, pScene->mNumMaterials * sizeof( aiMaterial*)); + delete [] prevMats; + } + } // convert all the materials given in the array - for( unsigned int a = 0; a < pMaterials.size(); a++) - { + for( unsigned int a = 0; a < pMaterials.size(); ++a ) { XFile::Material& oldMat = pMaterials[a]; - if( oldMat.mIsReference) - { - // find the material it refers to by name, and store its index - for( size_t a = 0; a < pScene->mNumMaterials; ++a ) - { - aiString name; - pScene->mMaterials[a]->Get( AI_MATKEY_NAME, name); - if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 ) - { - oldMat.sceneIndex = a; - break; + if( oldMat.mIsReference) { + // find the material it refers to by name, and store its index + for( size_t a = 0; a < pScene->mNumMaterials; ++a ) { + aiString name; + pScene->mMaterials[a]->Get( AI_MATKEY_NAME, name); + if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 ) { + oldMat.sceneIndex = a; + break; + } + } + + if( oldMat.sceneIndex == SIZE_MAX ) { + DefaultLogger::get()->warn( format() << "Could not resolve global material reference \"" << oldMat.mName << "\"" ); + oldMat.sceneIndex = 0; + } + + continue; } - } - - if( oldMat.sceneIndex == SIZE_MAX ) - { - DefaultLogger::get()->warn( format() << "Could not resolve global material reference \"" << oldMat.mName << "\"" ); - oldMat.sceneIndex = 0; - } - - continue; - } aiMaterial* mat = new aiMaterial; aiString name; name.Set( oldMat.mName); mat->AddProperty( &name, AI_MATKEY_NAME); - // Shading model: hardcoded to PHONG, there is no such information in an XFile + // Shading model: hard-coded to PHONG, there is no such information in an XFile // FIX (aramis): If the specular exponent is 0, use gouraud shading. This is a bugfix // for some models in the SDK (e.g. good old tiny.x) int shadeMode = (int)oldMat.mSpecularExponent == 0.0f @@ -630,8 +607,8 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vectorAddProperty( &shadeMode, 1, AI_MATKEY_SHADING_MODEL); // material colours - // Unclear: there's no ambient colour, but emissive. What to put for ambient? - // Probably nothing at all, let the user select a suitable default. + // Unclear: there's no ambient colour, but emissive. What to put for ambient? + // Probably nothing at all, let the user select a suitable default. mat->AddProperty( &oldMat.mEmissive, 1, AI_MATKEY_COLOR_EMISSIVE); mat->AddProperty( &oldMat.mDiffuse, 1, AI_MATKEY_COLOR_DIFFUSE); mat->AddProperty( &oldMat.mSpecular, 1, AI_MATKEY_COLOR_SPECULAR); @@ -639,36 +616,33 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vectorAddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(0)); - else - mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(0)); + if ( otex.mIsNormalMap ) { + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS( 0 ) ); + } else { + mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE( 0 ) ); + } } - } - else - { + } else { // Otherwise ... try to search for typical strings in the // texture's file name like 'bump' or 'diffuse' unsigned int iHM = 0,iNM = 0,iDM = 0,iSM = 0,iAM = 0,iEM = 0; - for( unsigned int b = 0; b < oldMat.mTextures.size(); b++) - { + for( unsigned int b = 0; b < oldMat.mTextures.size(); ++b ) { const XFile::TexEntry& otex = oldMat.mTextures[b]; std::string sz = otex.mName; - if (!sz.length())continue; - + if ( !sz.length() ) { + continue; + } // find the file name - //const size_t iLen = sz.length(); std::string::size_type s = sz.find_last_of("\\/"); - if (std::string::npos == s) + if ( std::string::npos == s ) { s = 0; + } // cut off the file extension std::string::size_type sExt = sz.find_last_of('.'); @@ -677,36 +651,27 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vectorAddProperty( &tex, AI_MATKEY_TEXTURE_HEIGHT(iHM++)); - } else - if (otex.mIsNormalMap || std::string::npos != sz.find( "normal", s) || std::string::npos != sz.find("nm", s)) - { + } else if (otex.mIsNormalMap || std::string::npos != sz.find( "normal", s) || std::string::npos != sz.find("nm", s)) { mat->AddProperty( &tex, AI_MATKEY_TEXTURE_NORMALS(iNM++)); - } else - if (std::string::npos != sz.find( "spec", s) || std::string::npos != sz.find( "glanz", s)) - { + } else if (std::string::npos != sz.find( "spec", s) || std::string::npos != sz.find( "glanz", s)) { mat->AddProperty( &tex, AI_MATKEY_TEXTURE_SPECULAR(iSM++)); - } else - if (std::string::npos != sz.find( "ambi", s) || std::string::npos != sz.find( "env", s)) - { + } else if (std::string::npos != sz.find( "ambi", s) || std::string::npos != sz.find( "env", s)) { mat->AddProperty( &tex, AI_MATKEY_TEXTURE_AMBIENT(iAM++)); - } else - if (std::string::npos != sz.find( "emissive", s) || std::string::npos != sz.find( "self", s)) - { + } else if (std::string::npos != sz.find( "emissive", s) || std::string::npos != sz.find( "self", s)) { mat->AddProperty( &tex, AI_MATKEY_TEXTURE_EMISSIVE(iEM++)); - } else - { + } else { // Assume it is a diffuse texture mat->AddProperty( &tex, AI_MATKEY_TEXTURE_DIFFUSE(iDM++)); } diff --git a/code/XFileParser.cpp b/code/XFileParser.cpp index c7efb83c4..4dd2c990b 100644 --- a/code/XFileParser.cpp +++ b/code/XFileParser.cpp @@ -87,59 +87,60 @@ static void dummy_free (void* /*opaque*/, void* address) { // ------------------------------------------------------------------------------------------------ // Constructor. Creates a data structure out of the XFile given in the memory block. XFileParser::XFileParser( const std::vector& pBuffer) -{ - mMajorVersion = mMinorVersion = 0; - mIsBinaryFormat = false; - mBinaryNumCount = 0; - P = End = NULL; - mLineNumber = 0; - mScene = NULL; - +: mMajorVersion( 0 ) +, mMinorVersion( 0 ) +, mIsBinaryFormat( false ) +, mBinaryNumCount( 0 ) +, mP( nullptr ) +, mEnd( nullptr ) +, mLineNumber( 0 ) +, mScene( nullptr ) { // vector to store uncompressed file for INFLATE'd X files std::vector uncompressed; // set up memory pointers - P = &pBuffer.front(); - End = P + pBuffer.size() - 1; + mP = &pBuffer.front(); + mEnd = mP + pBuffer.size() - 1; // check header - if( strncmp( P, "xof ", 4) != 0) - throw DeadlyImportError( "Header mismatch, file is not an XFile."); + if ( 0 != strncmp( mP, "xof ", 4 ) ) { + throw DeadlyImportError( "Header mismatch, file is not an XFile." ); + } // read version. It comes in a four byte format such as "0302" - mMajorVersion = (unsigned int)(P[4] - 48) * 10 + (unsigned int)(P[5] - 48); - mMinorVersion = (unsigned int)(P[6] - 48) * 10 + (unsigned int)(P[7] - 48); + mMajorVersion = (unsigned int)(mP[4] - 48) * 10 + (unsigned int)(mP[5] - 48); + mMinorVersion = (unsigned int)(mP[6] - 48) * 10 + (unsigned int)(mP[7] - 48); bool compressed = false; // txt - pure ASCII text format - if( strncmp( P + 8, "txt ", 4) == 0) + if( strncmp( mP + 8, "txt ", 4) == 0) mIsBinaryFormat = false; // bin - Binary format - else if( strncmp( P + 8, "bin ", 4) == 0) + else if( strncmp( mP + 8, "bin ", 4) == 0) mIsBinaryFormat = true; // tzip - Inflate compressed text format - else if( strncmp( P + 8, "tzip", 4) == 0) + else if( strncmp( mP + 8, "tzip", 4) == 0) { mIsBinaryFormat = false; compressed = true; } // bzip - Inflate compressed binary format - else if( strncmp( P + 8, "bzip", 4) == 0) + else if( strncmp( mP + 8, "bzip", 4) == 0) { mIsBinaryFormat = true; compressed = true; } else ThrowException( format() << "Unsupported xfile format '" << - P[8] << P[9] << P[10] << P[11] << "'"); + mP[8] << mP[9] << mP[10] << mP[11] << "'"); // float size - mBinaryFloatSize = (unsigned int)(P[12] - 48) * 1000 - + (unsigned int)(P[13] - 48) * 100 - + (unsigned int)(P[14] - 48) * 10 - + (unsigned int)(P[15] - 48); + mBinaryFloatSize = (unsigned int)(mP[12] - 48) * 1000 + + (unsigned int)(mP[13] - 48) * 100 + + (unsigned int)(mP[14] - 48) * 10 + + (unsigned int)(mP[15] - 48); if( mBinaryFloatSize != 32 && mBinaryFloatSize != 64) ThrowException( format() << "Unknown float size " << mBinaryFloatSize << " specified in xfile header." ); @@ -147,7 +148,7 @@ XFileParser::XFileParser( const std::vector& pBuffer) // The x format specifies size in bits, but we work in bytes mBinaryFloatSize /= 8; - P += 16; + mP += 16; // If this is a compressed X file, apply the inflate algorithm to it if (compressed) @@ -186,13 +187,13 @@ XFileParser::XFileParser( const std::vector& pBuffer) ::inflateInit2(&stream, -MAX_WBITS); // skip unknown data (checksum, flags?) - P += 6; + mP += 6; // First find out how much storage we'll need. Count sections. - const char* P1 = P; + const char* P1 = mP; unsigned int est_out = 0; - while (P1 + 3 < End) + while (P1 + 3 < mEnd) { // read next offset uint16_t ofs = *((uint16_t*)P1); @@ -216,18 +217,18 @@ XFileParser::XFileParser( const std::vector& pBuffer) // Allocate storage and terminating zero and do the actual uncompressing uncompressed.resize(est_out + 1); char* out = &uncompressed.front(); - while (P + 3 < End) + while (mP + 3 < mEnd) { - uint16_t ofs = *((uint16_t*)P); + uint16_t ofs = *((uint16_t*)mP); AI_SWAP2(ofs); - P += 4; + mP += 4; - if (P + ofs > End + 2) { + if (mP + ofs > mEnd + 2) { throw DeadlyImportError("X: Unexpected EOF in compressed chunk"); } // push data to the stream - stream.next_in = (Bytef*)P; + stream.next_in = (Bytef*)mP; stream.avail_in = ofs; stream.next_out = (Bytef*)out; stream.avail_out = MSZIP_BLOCK; @@ -242,15 +243,15 @@ XFileParser::XFileParser( const std::vector& pBuffer) // and advance to the next offset out += MSZIP_BLOCK - stream.avail_out; - P += ofs; + mP += ofs; } // terminate zlib ::inflateEnd(&stream); // ok, update pointers to point to the uncompressed file data - P = &uncompressed[0]; - End = out; + mP = &uncompressed[0]; + mEnd = out; // FIXME: we don't need the compressed data anymore, could release // it already for better memory usage. Consider breaking const-co. @@ -465,12 +466,11 @@ void XFileParser::ParseDataObjectMesh( Mesh* pMesh) // read position faces unsigned int numPosFaces = ReadInt(); pMesh->mPosFaces.resize( numPosFaces); - for( unsigned int a = 0; a < numPosFaces; a++) - { + for( unsigned int a = 0; a < numPosFaces; ++a) { // read indices unsigned int numIndices = ReadInt(); Face& face = pMesh->mPosFaces[a]; - for (unsigned int b = 0; b < numIndices; b++) { + for (unsigned int b = 0; b < numIndices; ++b) { face.mIndices.push_back( ReadInt() ); } TestForSeparator(); @@ -478,11 +478,10 @@ void XFileParser::ParseDataObjectMesh( Mesh* pMesh) // here, other data objects may follow bool running = true; - while ( running ) - { + while ( running ) { std::string objectName = GetNextToken(); - if( objectName.size() == 0) + if( objectName.empty() ) ThrowException( "Unexpected end of file while parsing mesh structure"); else if( objectName == "}") @@ -517,8 +516,10 @@ void XFileParser::ParseDataObjectMesh( Mesh* pMesh) } // ------------------------------------------------------------------------------------------------ -void XFileParser::ParseDataObjectSkinWeights( Mesh *pMesh) -{ +void XFileParser::ParseDataObjectSkinWeights( Mesh *pMesh) { + if ( nullptr == pMesh ) { + return; + } readHeadOfDataObject(); std::string transformNodeName; @@ -647,8 +648,8 @@ void XFileParser::ParseDataObjectMeshVertexColors( Mesh* pMesh) if( !mIsBinaryFormat) { FindNextNoneWhiteSpace(); - if( *P == ';' || *P == ',') - P++; + if( *mP == ';' || *mP == ',') + mP++; } } @@ -678,8 +679,8 @@ void XFileParser::ParseDataObjectMeshMaterialList( Mesh* pMesh) // commented out version check, as version 03.03 exported from blender also has 2 semicolons if( !mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2) { - if(P < End && *P == ';') - ++P; + if(mP < mEnd && *mP == ';') + ++mP; } // if there was only a single material index, replicate it on all faces @@ -1029,12 +1030,12 @@ void XFileParser::TestForSeparator() return; FindNextNoneWhiteSpace(); - if( P >= End) + if( mP >= mEnd) return; // test and skip - if( *P == ';' || *P == ',') - P++; + if( *mP == ';' || *mP == ',') + mP++; } // ------------------------------------------------------------------------------------------------ @@ -1046,62 +1047,73 @@ void XFileParser::readHeadOfDataObject( std::string* poName) if( poName) *poName = nameOrBrace; - if( GetNextToken() != "{") - ThrowException( "Opening brace expected."); + if ( GetNextToken() != "{" ) { + delete mScene; + ThrowException( "Opening brace expected." ); + } } } // ------------------------------------------------------------------------------------------------ -std::string XFileParser::GetNextToken() -{ +std::string XFileParser::GetNextToken() { std::string s; // process binary-formatted file - if( mIsBinaryFormat) - { + if( mIsBinaryFormat) { // in binary mode it will only return NAME and STRING token // and (correctly) skip over other tokens. - - if( End - P < 2) return s; + if ( mEnd - mP < 2 ) { + return s; + } unsigned int tok = ReadBinWord(); unsigned int len; // standalone tokens - switch( tok) - { - case 1: + switch( tok ) { + case 1: { // name token - if( End - P < 4) return s; + if ( mEnd - mP < 4 ) { + return s; + } len = ReadBinDWord(); - if( End - P < int(len)) return s; - s = std::string(P, len); - P += len; - return s; + const int bounds = int( mEnd - mP ); + const int iLen = int( len ); + if ( iLen < 0 ) { + return s; + } + if ( bounds < iLen ) { + return s; + } + s = std::string( mP, len ); + mP += len; + } + return s; + case 2: // string token - if( End - P < 4) return s; + if( mEnd - mP < 4) return s; len = ReadBinDWord(); - if( End - P < int(len)) return s; - s = std::string(P, len); - P += (len + 2); + if( mEnd - mP < int(len)) return s; + s = std::string(mP, len); + mP += (len + 2); return s; case 3: // integer token - P += 4; + mP += 4; return ""; case 5: // GUID token - P += 16; + mP += 16; return ""; case 6: - if( End - P < 4) return s; + if( mEnd - mP < 4) return s; len = ReadBinDWord(); - P += (len * 4); + mP += (len * 4); return ""; case 7: - if( End - P < 4) return s; + if( mEnd - mP < 4) return s; len = ReadBinDWord(); - P += (len * mBinaryFloatSize); + mP += (len * mBinaryFloatSize); return ""; case 0x0a: return "{"; @@ -1159,19 +1171,19 @@ std::string XFileParser::GetNextToken() else { FindNextNoneWhiteSpace(); - if( P >= End) + if( mP >= mEnd) return s; - while( (P < End) && !isspace( (unsigned char) *P)) + while( (mP < mEnd) && !isspace( (unsigned char) *mP)) { // either keep token delimiters when already holding a token, or return if first valid char - if( *P == ';' || *P == '}' || *P == '{' || *P == ',') + if( *mP == ';' || *mP == '}' || *mP == '{' || *mP == ',') { if( !s.size()) - s.append( P++, 1); + s.append( mP++, 1); break; // stop for delimiter } - s.append( P++, 1); + s.append( mP++, 1); } } return s; @@ -1186,18 +1198,18 @@ void XFileParser::FindNextNoneWhiteSpace() bool running = true; while( running ) { - while( P < End && isspace( (unsigned char) *P)) + while( mP < mEnd && isspace( (unsigned char) *mP)) { - if( *P == '\n') + if( *mP == '\n') mLineNumber++; - ++P; + ++mP; } - if( P >= End) + if( mP >= mEnd) return; // check if this is a comment - if( (P[0] == '/' && P[1] == '/') || P[0] == '#') + if( (mP[0] == '/' && mP[1] == '/') || mP[0] == '#') ReadUntilEndOfLine(); else break; @@ -1214,22 +1226,30 @@ void XFileParser::GetNextTokenAsString( std::string& poString) } FindNextNoneWhiteSpace(); - if( P >= End) - ThrowException( "Unexpected end of file while parsing string"); + if ( mP >= mEnd ) { + delete mScene; + ThrowException( "Unexpected end of file while parsing string" ); + } - if( *P != '"') - ThrowException( "Expected quotation mark."); - ++P; + if ( *mP != '"' ) { + delete mScene; + ThrowException( "Expected quotation mark." ); + } + ++mP; - while( P < End && *P != '"') - poString.append( P++, 1); + while( mP < mEnd && *mP != '"') + poString.append( mP++, 1); - if( P >= End-1) - ThrowException( "Unexpected end of file while parsing string"); + if ( mP >= mEnd - 1 ) { + delete mScene; + ThrowException( "Unexpected end of file while parsing string" ); + } - if( P[1] != ';' || P[0] != '"') - ThrowException( "Expected quotation mark and semicolon at the end of a string."); - P+=2; + if ( mP[ 1 ] != ';' || mP[ 0 ] != '"' ) { + delete mScene; + ThrowException( "Expected quotation mark and semicolon at the end of a string." ); + } + mP+=2; } // ------------------------------------------------------------------------------------------------ @@ -1238,35 +1258,35 @@ void XFileParser::ReadUntilEndOfLine() if( mIsBinaryFormat) return; - while( P < End) + while( mP < mEnd) { - if( *P == '\n' || *P == '\r') + if( *mP == '\n' || *mP == '\r') { - ++P; mLineNumber++; + ++mP; mLineNumber++; return; } - ++P; + ++mP; } } // ------------------------------------------------------------------------------------------------ unsigned short XFileParser::ReadBinWord() { - ai_assert(End - P >= 2); - const unsigned char* q = (const unsigned char*) P; + ai_assert(mEnd - mP >= 2); + const unsigned char* q = (const unsigned char*) mP; unsigned short tmp = q[0] | (q[1] << 8); - P += 2; + mP += 2; return tmp; } // ------------------------------------------------------------------------------------------------ -unsigned int XFileParser::ReadBinDWord() -{ - ai_assert(End - P >= 4); - const unsigned char* q = (const unsigned char*) P; +unsigned int XFileParser::ReadBinDWord() { + ai_assert(mEnd - mP >= 4); + + const unsigned char* q = (const unsigned char*) mP; unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24); - P += 4; + mP += 4; return tmp; } @@ -1275,20 +1295,20 @@ unsigned int XFileParser::ReadInt() { if( mIsBinaryFormat) { - if( mBinaryNumCount == 0 && End - P >= 2) + if( mBinaryNumCount == 0 && mEnd - mP >= 2) { unsigned short tmp = ReadBinWord(); // 0x06 or 0x03 - if( tmp == 0x06 && End - P >= 4) // array of ints follows + if( tmp == 0x06 && mEnd - mP >= 4) // array of ints follows mBinaryNumCount = ReadBinDWord(); else // single int follows mBinaryNumCount = 1; } --mBinaryNumCount; - if ( End - P >= 4) { + if ( mEnd - mP >= 4) { return ReadBinDWord(); } else { - P = End; + mP = mEnd; return 0; } } else @@ -1299,24 +1319,24 @@ unsigned int XFileParser::ReadInt() // check preceding minus sign bool isNegative = false; - if( *P == '-') + if( *mP == '-') { isNegative = true; - P++; + mP++; } // at least one digit expected - if( !isdigit( *P)) + if( !isdigit( *mP)) ThrowException( "Number expected."); // read digits unsigned int number = 0; - while( P < End) + while( mP < mEnd) { - if( !isdigit( *P)) + if( !isdigit( *mP)) break; - number = number * 10 + (*P - 48); - P++; + number = number * 10 + (*mP - 48); + mP++; } CheckForSeparator(); @@ -1329,34 +1349,35 @@ ai_real XFileParser::ReadFloat() { if( mIsBinaryFormat) { - if( mBinaryNumCount == 0 && End - P >= 2) + if( mBinaryNumCount == 0 && mEnd - mP >= 2) { unsigned short tmp = ReadBinWord(); // 0x07 or 0x42 - if( tmp == 0x07 && End - P >= 4) // array of floats following + if( tmp == 0x07 && mEnd - mP >= 4) // array of floats following mBinaryNumCount = ReadBinDWord(); else // single float following mBinaryNumCount = 1; } --mBinaryNumCount; - if( mBinaryFloatSize == 8) - { - if( End - P >= 8) { - ai_real result = (ai_real) (*(double*) P); - P += 8; + if( mBinaryFloatSize == 8) { + if( mEnd - mP >= 8) { + double res; + ::memcpy( &res, mP, 8 ); + mP += 8; + const ai_real result( static_cast( res ) ); return result; } else { - P = End; + mP = mEnd; return 0; } - } else - { - if( End - P >= 4) { - ai_real result = *(ai_real*) P; - P += 4; + } else { + if( mEnd - mP >= 4) { + ai_real result; + ::memcpy( &result, mP, 4 ); + mP += 4; return result; } else { - P = End; + mP = mEnd; return 0; } } @@ -1367,21 +1388,21 @@ ai_real XFileParser::ReadFloat() // check for various special strings to allow reading files from faulty exporters // I mean you, Blender! // Reading is safe because of the terminating zero - if( strncmp( P, "-1.#IND00", 9) == 0 || strncmp( P, "1.#IND00", 8) == 0) + if( strncmp( mP, "-1.#IND00", 9) == 0 || strncmp( mP, "1.#IND00", 8) == 0) { - P += 9; + mP += 9; CheckForSeparator(); return 0.0; } else - if( strncmp( P, "1.#QNAN0", 8) == 0) + if( strncmp( mP, "1.#QNAN0", 8) == 0) { - P += 8; + mP += 8; CheckForSeparator(); return 0.0; } ai_real result = 0.0; - P = fast_atoreal_move( P, result); + mP = fast_atoreal_move( mP, result); CheckForSeparator(); @@ -1438,15 +1459,14 @@ aiColor3D XFileParser::ReadRGB() // ------------------------------------------------------------------------------------------------ // Throws an exception with a line number and the given text. -AI_WONT_RETURN void XFileParser::ThrowException( const std::string& pText) -{ - if( mIsBinaryFormat) - throw DeadlyImportError( pText); - else +AI_WONT_RETURN void XFileParser::ThrowException( const std::string& pText) { + if ( mIsBinaryFormat ) { + throw DeadlyImportError( pText ); + } else { throw DeadlyImportError( format() << "Line " << mLineNumber << ": " << pText ); + } } - // ------------------------------------------------------------------------------------------------ // Filters the imported hierarchy for some degenerated cases that some exporters produce. void XFileParser::FilterHierarchy( XFile::Node* pNode) diff --git a/code/XFileParser.h b/code/XFileParser.h index 050ecb9ca..24eb6104d 100644 --- a/code/XFileParser.h +++ b/code/XFileParser.h @@ -49,10 +49,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -namespace Assimp -{ - namespace XFile - { +namespace Assimp { + namespace XFile { struct Node; struct Mesh; struct Scene; @@ -61,21 +59,20 @@ namespace Assimp struct AnimBone; } -/** The XFileParser reads a XFile either in text or binary form and builds a temporary - * data structure out of it. - */ -class XFileParser -{ +/** + * @brief The XFileParser reads a XFile either in text or binary form and builds a temporary + * data structure out of it. + */ +class XFileParser { public: - /** Constructor. Creates a data structure out of the XFile given in the memory block. - * @param pBuffer Null-terminated memory buffer containing the XFile - */ + /// Constructor. Creates a data structure out of the XFile given in the memory block. + /// @param pBuffer Null-terminated memory buffer containing the XFile explicit XFileParser( const std::vector& pBuffer); - /** Destructor. Destroys all imported data along with it */ + /// Destructor. Destroys all imported data along with it ~XFileParser(); - /** Returns the temporary representation of the imported data */ + /// Returns the temporary representation of the imported data. XFile::Scene* GetImportedData() const { return mScene; } protected: @@ -101,10 +98,10 @@ protected: //! places pointer to next begin of a token, and ignores comments void FindNextNoneWhiteSpace(); - //! returns next parseable token. Returns empty string if no token there + //! returns next valid token. Returns empty string if no token there std::string GetNextToken(); - //! reads header of dataobject including the opening brace. + //! reads header of data object including the opening brace. //! returns false if error happened, and writes name of object //! if there is one void readHeadOfDataObject( std::string* poName = NULL); @@ -118,8 +115,8 @@ protected: //! checks for a separator char, either a ',' or a ';' void CheckForSeparator(); - /// tests and possibly consumes a separator char, but does nothing if there was no separator - void TestForSeparator(); + /// tests and possibly consumes a separator char, but does nothing if there was no separator + void TestForSeparator(); //! reads a x file style string void GetNextTokenAsString( std::string& poString); @@ -138,27 +135,23 @@ protected: /** Throws an exception with a line number and the given text. */ AI_WONT_RETURN void ThrowException( const std::string& pText) AI_WONT_RETURN_SUFFIX; - /** Filters the imported hierarchy for some degenerated cases that some exporters produce. - * @param pData The sub-hierarchy to filter - */ + /** + * @brief Filters the imported hierarchy for some degenerated cases that some exporters produce. + * @param pData The sub-hierarchy to filter + */ void FilterHierarchy( XFile::Node* pNode); protected: unsigned int mMajorVersion, mMinorVersion; ///< version numbers bool mIsBinaryFormat; ///< true if the file is in binary, false if it's in text form unsigned int mBinaryFloatSize; ///< float size in bytes, either 4 or 8 - // counter for number arrays in binary format - unsigned int mBinaryNumCount; - - const char* P; - const char* End; - - /// Line number when reading in text format - unsigned int mLineNumber; - - /// Imported data - XFile::Scene* mScene; + unsigned int mBinaryNumCount; /// < counter for number arrays in binary format + const char* mP; + const char* mEnd; + unsigned int mLineNumber; ///< Line number when reading in text format + XFile::Scene* mScene; ///< Imported data }; -} +} //! ns Assimp + #endif // AI_XFILEPARSER_H_INC diff --git a/code/XGLLoader.cpp b/code/XGLLoader.cpp index 2d7a40362..a9fd6a5ab 100644 --- a/code/XGLLoader.cpp +++ b/code/XGLLoader.cpp @@ -72,17 +72,6 @@ using namespace irr::io; #endif -// scopeguard for a malloc'ed buffer -struct free_it -{ - free_it(void* free) : free(free) {} - ~free_it() { - ::free(this->free); - } - - void* free; -}; - namespace Assimp { // this has to be in here because LogFunctions is in ::Assimp template<> const char* LogFunctions::Prefix() { @@ -155,8 +144,7 @@ void XGLImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { #ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL - Bytef* dest = NULL; - free_it free_it_really(dest); + std::vector uncompressed; #endif m_scene = pScene; @@ -192,6 +180,7 @@ void XGLImporter::InternReadFile( const std::string& pFile, size_t total = 0l; + // TODO: be smarter about this, decompress directly into heap buffer // and decompress the data .... do 1k chunks in the hope that we won't kill the stack #define MYBLOCK 1024 Bytef block[MYBLOCK]; @@ -206,8 +195,8 @@ void XGLImporter::InternReadFile( const std::string& pFile, } const size_t have = MYBLOCK - zstream.avail_out; total += have; - dest = reinterpret_cast( realloc(dest,total) ); - memcpy(dest + total - have,block,have); + uncompressed.resize(total); + memcpy(uncompressed.data() + total - have,block,have); } while (ret != Z_STREAM_END); @@ -215,7 +204,7 @@ void XGLImporter::InternReadFile( const std::string& pFile, inflateEnd(&zstream); // replace the input stream with a memory stream - stream.reset(new MemoryIOStream(reinterpret_cast(dest),total)); + stream.reset(new MemoryIOStream(reinterpret_cast(uncompressed.data()),total)); #endif } diff --git a/code/glTF2Asset.h b/code/glTF2Asset.h index 1ac1d538b..de3ae5cf8 100644 --- a/code/glTF2Asset.h +++ b/code/glTF2Asset.h @@ -137,7 +137,7 @@ namespace glTF2 // Vec/matrix types, as raw float arrays typedef float (vec3)[3]; typedef float (vec4)[4]; - typedef float (mat4)[16]; + typedef float (mat4)[16]; namespace Util { @@ -166,33 +166,8 @@ namespace glTF2 //! Magic number for GLB files #define AI_GLB_MAGIC_NUMBER "glTF" + #include - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 - #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 - #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 - #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 - #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 - #define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 - - #define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" - #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" - #define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid" - #define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag" - #define _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE "$tex.mappingfiltermin" - #define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale" - #define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength" - - #define AI_MATKEY_GLTF_TEXTURE_TEXCOORD _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N - #define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N - #define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N - #define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N - #define AI_MATKEY_GLTF_MAPPINGFILTER_MIN(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE, type, N - #define AI_MATKEY_GLTF_TEXTURE_SCALE(type, N) _AI_MATKEY_GLTF_SCALE_BASE, type, N - #define AI_MATKEY_GLTF_TEXTURE_STRENGTH(type, N) _AI_MATKEY_GLTF_STRENGTH_BASE, type, N - #ifdef ASSIMP_API #include "./../include/assimp/Compiler/pushpack1.h" #endif diff --git a/code/glTF2Asset.inl b/code/glTF2Asset.inl index 520785081..8f2b14e1a 100644 --- a/code/glTF2Asset.inl +++ b/code/glTF2Asset.inl @@ -669,7 +669,7 @@ inline Image::Image() } -inline void Image::Read(Value& obj, Asset& /*r*/) +inline void Image::Read(Value& obj, Asset& r) { if (!mDataLength) { if (Value* uri = FindString(obj, "uri")) { @@ -686,6 +686,19 @@ inline void Image::Read(Value& obj, Asset& /*r*/) this->uri = uristr; } } + else if (Value* bufferViewVal = FindUInt(obj, "bufferView")) { + this->bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); + Ref buffer = this->bufferView->buffer; + + this->mDataLength = this->bufferView->byteLength; + // maybe this memcpy could be avoided if aiTexture does not delete[] pcData at destruction. + this->mData = new uint8_t [this->mDataLength]; + memcpy(this->mData, buffer->GetPointer() + this->bufferView->byteOffset, this->mDataLength); + + if (Value* mtype = FindString(obj, "mimeType")) { + this->mimeType = mtype->GetString(); + } + } } } diff --git a/code/glTF2Exporter.cpp b/code/glTF2Exporter.cpp index b873d9d22..e146327ea 100644 --- a/code/glTF2Exporter.cpp +++ b/code/glTF2Exporter.cpp @@ -316,11 +316,9 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe std::string path = tex.C_Str(); if (path.size() > 0) { - if (path[0] != '*') { - std::map::iterator it = mTexturesByPath.find(path); - if (it != mTexturesByPath.end()) { - texture = mAsset->textures.Get(it->second); - } + std::map::iterator it = mTexturesByPath.find(path); + if (it != mTexturesByPath.end()) { + texture = mAsset->textures.Get(it->second); } if (!texture) { diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index b31846abd..bd43b19f2 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -948,24 +948,24 @@ Ref buf = pAsset_Root.buffers.Get(pCompression_Open3DGC.Buffer); size_t size_coordindex = ifs.GetNCoordIndex() * 3;// See float attributes note. if(primitives[0].indices->count != size_coordindex) - throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (" + std::to_string(size_coordindex) + - ") not equal to uncompressed (" + std::to_string(primitives[0].indices->count) + ")."); + throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (" + to_string(size_coordindex) + + ") not equal to uncompressed (" + to_string(primitives[0].indices->count) + ")."); size_coordindex *= sizeof(IndicesType); // Coordinates size_t size_coord = ifs.GetNCoord();// See float attributes note. if(primitives[0].attributes.position[0]->count != size_coord) - throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (" + std::to_string(size_coord) + - ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.position[0]->count) + ")."); + throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (" + to_string(size_coord) + + ") not equal to uncompressed (" + to_string(primitives[0].attributes.position[0]->count) + ")."); size_coord *= 3 * sizeof(float); // Normals size_t size_normal = ifs.GetNNormal();// See float attributes note. if(primitives[0].attributes.normal[0]->count != size_normal) - throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (" + std::to_string(size_normal) + - ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.normal[0]->count) + ")."); + throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (" + to_string(size_normal) + + ") not equal to uncompressed (" + to_string(primitives[0].attributes.normal[0]->count) + ")."); size_normal *= 3 * sizeof(float); // Additional attributes. @@ -989,8 +989,8 @@ Ref buf = pAsset_Root.buffers.Get(pCompression_Open3DGC.Buffer); if(idx_texcoord < primitives[0].attributes.texcoord.size()) { if(primitives[0].attributes.texcoord[idx]->count != tval) - throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (" + std::to_string(tval) + - ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.texcoord[idx]->count) + ")."); + throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (" + to_string(tval) + + ") not equal to uncompressed (" + to_string(primitives[0].attributes.texcoord[idx]->count) + ")."); idx_texcoord++; } diff --git a/code/glTFImporter.cpp b/code/glTFImporter.cpp index 2c3fa2046..54c32fb8b 100644 --- a/code/glTFImporter.cpp +++ b/code/glTFImporter.cpp @@ -194,9 +194,16 @@ void glTFImporter::ImportMaterials(glTF::Asset& r) aimat->AddProperty(&str, AI_MATKEY_NAME); } - SetMaterialColorProperty(embeddedTexIdxs, r, mat.diffuse, aimat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE); + SetMaterialColorProperty(embeddedTexIdxs, r, mat.ambient, aimat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT ); + SetMaterialColorProperty(embeddedTexIdxs, r, mat.diffuse, aimat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE ); SetMaterialColorProperty(embeddedTexIdxs, r, mat.specular, aimat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR); - SetMaterialColorProperty(embeddedTexIdxs, r, mat.ambient, aimat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT); + SetMaterialColorProperty(embeddedTexIdxs, r, mat.emission, aimat, aiTextureType_EMISSIVE, AI_MATKEY_COLOR_EMISSIVE); + + aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED); + + if (mat.transparent && (mat.transparency != 1.0f)) { + aimat->AddProperty(&mat.transparency, 1, AI_MATKEY_OPACITY); + } if (mat.shininess > 0.f) { aimat->AddProperty(&mat.shininess, 1, AI_MATKEY_SHININESS); diff --git a/code/simd.cpp b/code/simd.cpp new file mode 100644 index 000000000..bd951bffa --- /dev/null +++ b/code/simd.cpp @@ -0,0 +1,78 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ +#include "simd.h" + +namespace Assimp { + +bool CPUSupportsSSE2() { +#if defined(__x86_64__) || defined(_M_X64) + //* x86_64 always has SSE2 instructions */ + return true; +#elif defined(__GNUC__) && defined(i386) + // for GCC x86 we check cpuid + unsigned int d; + __asm__( + "pushl %%ebx\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=d" ( d ) + :"a" ( 1 ) ); + return ( d & 0x04000000 ) != 0; +#elif (defined(_MSC_VER) && defined(_M_IX86)) + // also check cpuid for MSVC x86 + unsigned int d; + __asm { + xor eax, eax + inc eax + push ebx + cpuid + pop ebx + mov d, edx + } + return ( d & 0x04000000 ) != 0; +#else + return false; +#endif +} + +} // Namespace Assimp diff --git a/code/simd.h b/code/simd.h new file mode 100644 index 000000000..19117569d --- /dev/null +++ b/code/simd.h @@ -0,0 +1,53 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2018, 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. +--------------------------------------------------------------------------- +*/ +#pragma once + +#include + +namespace Assimp { + +/// @brief Checks if the platform supports SSE2 optimization +/// @return true, if SSE2 is supported. false if SSE2 is not supported. +bool ASSIMP_API CPUSupportsSSE2(); + +} // Namespace Assimp diff --git a/contrib/zlib/zlib.h b/contrib/zlib/zlib.h index 2d6bb2976..dcb7b5063 100644 --- a/contrib/zlib/zlib.h +++ b/contrib/zlib/zlib.h @@ -77,8 +77,9 @@ extern "C" { the consistency of the compressed data, so the library should never crash even in the case of corrupted input. */ -#ifdef __ANDROID__ -using zcrc_t = unsigned_long; + +#ifdef __ANDROID__ +typedef unsigned long zcrc_t; #endif typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); diff --git a/doc/dox.h b/doc/dox.h index ece3e31d9..bc47e5584 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -62,7 +62,7 @@ that it has not been implemented yet and some (most ...) formats lack proper spe See the @link importer_notes Importer Notes Page @endlink for information, what a specific importer can do and what not. Note that although this paper claims to be the official documentation, -http://assimp.sourceforge.net/main_features_formats.html +https://github.com/assimp/assimp/blob/master/Readme.md
is usually the most up-to-date list of file formats supported by the library.
1: Experimental loaders
@@ -90,9 +90,16 @@ but not all of them are *open-source*. If there's an accompagning '\source @section main_install Installation assimp can be used in two ways: linking against the pre-built libraries or building the library on your own. The former -option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2005 and 2008. For other -compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but needs a bit -more work. Both ways are described at the @link install Installation page. @endlink +option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2013, 2015 and 2017. +For other compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but +needs a bit more work. Both ways are described at the @link install Installation page. @endlink +If you want to use assimp on Ubuntu you can install it via the following command: + +@code +sudo apt-get install assimp +@endcode + +If you want to use the python-assimp-port just follow these instructions: https://github.com/assimp/assimp/tree/master/port/PyAssimp @section main_usage Usage @@ -115,7 +122,6 @@ assimp is considerably easy, as the whole postprocessing infrastructure is avail See the @link extend Extending the library @endlink page for more information. - @section main_support Support & Feedback If you have any questions/comments/suggestions/bug reports you're welcome to post them in our @@ -133,79 +139,39 @@ assimp-discussions. @section install_prebuilt Using the pre-built libraries with Visual C++ 8/9 -If you develop at Visual Studio 2005 or 2008, you can simply use the pre-built linker libraries provided in the distribution. +If you develop at Visual Studio 2015 or 2017, you can simply use the pre-built linker libraries provided in the distribution. Extract all files to a place of your choice. A directory called "assimp" will be created there. Add the assimp/include path to your include paths (Menu->Extras->Options->Projects and Solutions->VC++ Directories->Include files) and the assimp/lib/<Compiler> path to your linker paths (Menu->Extras->Options->Projects and Solutions->VC++ Directories->Library files). This is necessary only once to setup all paths inside you IDE. -To use the library in your C++ project you have to include either <assimp/Importer.hpp> or <assimp/cimport.h> plus some others starting with <types.h>. -If you set up your IDE correctly the compiler should be able to find the files. Then you have to add the linker library to your -project dependencies. Link to /lib//assimp.lib. config-name is one of the predefined -project configs. For static linking, use release/debug. See the sections below on this page for more information on the -other build configs. -If done correctly you should now be able to compile, link, -run and use the application. If the linker complains about some integral functions being defined twice you probably have -mixed the runtimes. Recheck the project configuration (project properties -> C++ -> Code generation -> Runtime) if you use -static runtimes (Multithreaded / Multithreaded Debug) or dynamic runtimes (Multithreaded DLL / Multithreaded Debug DLL). -Choose the assimp linker lib accordingly. -

-Please don't forget to also read the @ref assimp_stl section on MSVC and the STL. - -@section assimp_stl Microsoft Compilers and the C++ Standard Library - -In VC8 and VC9 Microsoft introduced some Standard Library debugging features. A good example are improved iterator checks and -various useful debug checks. The problem is the performance penalty that incurs with those extra checks. - -Most of these security enhancements are active in release builds by default, rendering assimp several times -slower. However, it is possible to disable them by setting +To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder +as a subdirectory via the cmake-command @code -_HAS_ITERATOR_DEBUGGING=0 -_SECURE_SCL=0 +addsubdiectory(assimp) @endcode -in the preprocessor options (or alternatively in the source code, just before the STL is included for the first time). -assimp's vc8 and vc9 configs enable these flags by default. +Now just add the assimp-dependency to your application: -If you're linking statically against assimp: Make sure your applications uses the same STl settings! -If you do not, there are two binary incompatible STL versions mangled together and you'll crash. -Alternatively you can disable the fast STL settings for assimp by removing the 'FastSTL' property sheet from -the vc project file. +@code +TARGET_LINK_LIBRARIES(my_game assimp) +@endcode -If you're using assimp in a DLL/SO: It's ok. There's no STL used in the binary DLL/SO interface, so it doesn't care whether -your application uses the same STL settings or not. -

-Another option is to build against a different STL implementation, for example STlport. There's a special -@ref assimp_stlport section that has a description how to achieve this. +If done correctly you should now be able to compile, link, run and use the application. @section install_own Building the library from scratch -To build the library on your own you first have to get hold of the dependencies. Fortunately, special attention was paid to -keep the list of dependencies short. Unfortunately, the only dependency is boost which -can be a bit painful to set up for certain development environments. Boost is a widely used collection of classes and -functions for various purposes. Chances are that it was already installed along with your compiler. If not, you have to install -it for yourself. Read the "Getting Started" section of the Boost documentation for how to setup boost. VisualStudio users -can use a comfortable installer from -http://www.boost-consulting.com/products/free. Choose the appropriate version of boost for your runtime of choice. - -If you don't want to use boost, you can build against our "Boost-Workaround". It consists of very small -implementations of the various boost utility classes used. However, you'll lose functionality (e.g. threading) by doing this. -So, if you can use boost, you should use boost. Otherwise, See the @link use_noboost NoBoost-Section @endlink -later on this page for the details of the workaround. - -Once boost is working, you have to set up a project for the assimp library in your favorite IDE. If you use VC2005 or -VC2008, you can simply load the solution or project files in the workspaces/ folder, otherwise you have to create a new -package and add all the headers and source files from the include/ and code/ directories. Set the temporary output folder -to obj/, for example, and redirect the output folder to bin/. Then build the library - it should compile and link fine. - -The last step is to integrate the library into your project. This is basically the same task as described in the -"Using the pre-built libraries" section above: add the include/ and bin/ directories to your IDE's paths so that the compiler can find -the library files. Alternatively you can simply add the assimp project to your project's overall solution and build it inside -your solution. +First you need to install cmake. Now just get the code from github or download the latest version from the webside. +to buil the library just open a command-prompt / bash, navigate into the repo-folder and run cmake via: +@code +cmake CMakeLists.txt +@endcode +A project-file of your default make-system ( like gnu-make on linux or Visual-Studio on Windows ) will be generated. +Run the build and you are done. You can find the libs at assimp/lib and the dll's / so's at bin. @section assimp_dll Windows DLL Build @@ -700,27 +666,31 @@ need them at all. Normally textures used by assets are stored in separate files, however, there are file formats embedding their textures directly into the model file. Such textures are loaded into an aiTexture structure. -For embedded textures, the value of `AI_MATKEY_TEXTURE(textureType, index)` will be `*` where -`` is the index of the texture in aiScene::mTextures. -
+ +In previous versions, the path from the query for `AI_MATKEY_TEXTURE(textureType, index)` would be +`*` where `` is the index of the texture in aiScene::mTextures. Now this call will +return a file path for embedded textures in FBX files. To test if it is an embdedded texture use +aiScene::GetEmbeddedTexture. If the returned pointer is not null, it is embedded und can be loaded +from the data structure. If it is null, search for a separate file. Other file types still use the +old behaviour.
+If your rely on the old behaviour, you can use Assimp::Importer::SetPropertyBool with the key +#AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING to force the old behaviour. + There are two cases: -
-1) The texture is NOT compressed. Its color data is directly stored -in the aiTexture structure as an array of aiTexture::mWidth * aiTexture::mHeight aiTexel structures. Each aiTexel represents a pixel (or "texel") of the texture -image. The color data is stored in an unsigned RGBA8888 format, which can be easily used for -both Direct3D and OpenGL (swizzling the order of the color components might be necessary). -RGBA8888 has been chosen because it is well-known, easy to use and natively -supported by nearly all graphics APIs. -
-2) This applies if aiTexture::mHeight == 0 is fulfilled. Then, texture is stored in a -"compressed" format such as DDS or PNG. The term "compressed" does not mean that the -texture data must actually be compressed, however the texture was found in the -model file as if it was stored in a separate file on the harddisk. Appropriate -decoders (such as libjpeg, libpng, D3DX, DevIL) are required to load theses textures. -aiTexture::mWidth specifies the size of the texture data in bytes, aiTexture::pcData is -a pointer to the raw image data and aiTexture::achFormatHint is either zeroed or -contains the most common file extension of the embedded texture's format. This value is only -set if assimp is able to determine the file format. +1. The texture is NOT compressed. Its color data is directly stored in the aiTexture structure as an + array of aiTexture::mWidth * aiTexture::mHeight aiTexel structures. Each aiTexel represents a + pixel (or "texel") of the texture image. The color data is stored in an unsigned RGBA8888 format, + which can be easily used for both Direct3D and OpenGL (swizzling the order of the color + components might be necessary). RGBA8888 has been chosen because it is well-known, easy to use + and natively supported by nearly all graphics APIs. +2. This applies if aiTexture::mHeight == 0 is fulfilled. Then, texture is stored in a "compressed" + format such as DDS or PNG. The term "compressed" does not mean that the texture data must + actually be compressed, however the texture was found in the model file as if it was stored in a + separate file on the harddisk. Appropriate decoders (such as libjpeg, libpng, D3DX, DevIL) are + required to load theses textures. aiTexture::mWidth specifies the size of the texture data in + bytes, aiTexture::pcData is a pointer to the raw image data and aiTexture::achFormatHint is + either zeroed or contains the most common file extension of the embedded texture's format. This + value is only set if assimp is able to determine the file format. */ @@ -904,8 +874,11 @@ All material key constants start with 'AI_MATKEY' (it's an ugly macro for histor TEXTURE(t,n) aiString n/a - Defines the path of the n'th texture on the stack 't', where 'n' is any value >= 0 and 't' is one of the #aiTextureType enumerated values. Either a filepath or `*`, where `` is the index of an embedded texture in aiScene::mTextures. - See the 'Textures' section above. + Defines the path of the n'th texture on the stack 't', where 'n' is any value >= 0 and 't' + is one of the #aiTextureType enumerated values. A file path to an external file or an embedded + texture. Use aiScene::GetEmbeddedTexture to test if it is embedded for FBX files, in other cases + embedded textures start with '*' followed by an index into aiScene::mTextures. + See the @ref mat_tex section above. Also see @ref textures for a more information about texture retrieval. diff --git a/include/assimp/DefaultLogger.hpp b/include/assimp/DefaultLogger.hpp index d6a88b0f3..1f0c899be 100644 --- a/include/assimp/DefaultLogger.hpp +++ b/include/assimp/DefaultLogger.hpp @@ -131,9 +131,7 @@ public: bool detatchStream(LogStream *pStream, unsigned int severity); - private: - // ---------------------------------------------------------------------- /** @briefPrivate construction for internal use by create(). * @param severity Logging granularity */ @@ -143,8 +141,6 @@ private: /** @briefDestructor */ ~DefaultLogger(); -private: - /** @brief Logs debug infos, only been written when severity level VERBOSE is set */ void OnDebug(const char* message); diff --git a/include/assimp/IOStreamBuffer.h b/include/assimp/IOStreamBuffer.h index a503b3874..c93a191df 100644 --- a/include/assimp/IOStreamBuffer.h +++ b/include/assimp/IOStreamBuffer.h @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include + #include "ParsingUtils.h" #include @@ -248,9 +249,9 @@ bool IOStreamBuffer::getNextDataLine( std::vector &buffer, T continuationT } } - bool continuationFound( false ), endOfDataLine( false ); + bool continuationFound( false ); size_t i = 0; - while ( !endOfDataLine ) { + for( ;; ) { if ( continuationToken == m_cache[ m_cachePos ] ) { continuationFound = true; ++m_cachePos; @@ -270,8 +271,8 @@ bool IOStreamBuffer::getNextDataLine( std::vector &buffer, T continuationT } buffer[ i ] = m_cache[ m_cachePos ]; - m_cachePos++; - i++; + ++m_cachePos; + ++i; if ( m_cachePos >= m_cacheSize ) { if ( !readNextBlock() ) { return false; @@ -280,13 +281,12 @@ bool IOStreamBuffer::getNextDataLine( std::vector &buffer, T continuationT } buffer[ i ] = '\n'; - m_cachePos++; + ++m_cachePos; return true; } -static -inline +static inline bool isEndOfCache( size_t pos, size_t cacheSize ) { return ( pos == cacheSize ); } @@ -314,11 +314,11 @@ bool IOStreamBuffer::getNextLine(std::vector &buffer) { } } - size_t i = 0; + size_t i( 0 ); while (!IsLineEnd(m_cache[ m_cachePos ])) { buffer[i] = m_cache[ m_cachePos ]; - m_cachePos++; - i++; + ++m_cachePos; + ++i; if (m_cachePos >= m_cacheSize) { if (!readNextBlock()) { return false; @@ -326,7 +326,7 @@ bool IOStreamBuffer::getNextLine(std::vector &buffer) { } } buffer[i] = '\n'; - m_cachePos++; + ++m_cachePos; return true; } @@ -334,18 +334,19 @@ bool IOStreamBuffer::getNextLine(std::vector &buffer) { template inline bool IOStreamBuffer::getNextBlock( std::vector &buffer) { - //just return the last blockvalue if getNextLine was used before - if ( m_cachePos != 0) { - buffer = std::vector(m_cache.begin() + m_cachePos, m_cache.end()); - m_cachePos = 0; - } - else { - if ( !readNextBlock() ) - return false; + // Return the last block-value if getNextLine was used before + if ( 0 != m_cachePos ) { + buffer = std::vector( m_cache.begin() + m_cachePos, m_cache.end() ); + m_cachePos = 0; + } else { + if ( !readNextBlock() ) { + return false; + } - buffer = std::vector(m_cache.begin(), m_cache.end()); - } - return true; + buffer = std::vector(m_cache.begin(), m_cache.end()); + } + + return true; } } // !ns Assimp diff --git a/include/assimp/Importer.hpp b/include/assimp/Importer.hpp index 1263868bc..7445c9797 100644 --- a/include/assimp/Importer.hpp +++ b/include/assimp/Importer.hpp @@ -137,7 +137,12 @@ public: * If this Importer owns a scene it won't be copied. * Call ReadFile() to start the import process. */ - Importer(const Importer& other); + Importer(const Importer& other)=delete; + + // ------------------------------------------------------------------- + /** Assignment operator has been deleted + */ + Importer &operator=(const Importer &) = delete; // ------------------------------------------------------------------- /** Destructor. The object kept ownership of the imported data, diff --git a/include/assimp/MemoryIOWrapper.h b/include/assimp/MemoryIOWrapper.h index 0e9b23447..bfcfff9c2 100644 --- a/include/assimp/MemoryIOWrapper.h +++ b/include/assimp/MemoryIOWrapper.h @@ -99,19 +99,19 @@ public: // Seek specific position aiReturn Seek(size_t pOffset, aiOrigin pOrigin) { if (aiOrigin_SET == pOrigin) { - if (pOffset >= length) { + if (pOffset > length) { return AI_FAILURE; } pos = pOffset; } else if (aiOrigin_END == pOrigin) { - if (pOffset >= length) { + if (pOffset > length) { return AI_FAILURE; } pos = length-pOffset; } else { - if (pOffset+pos >= length) { + if (pOffset+pos > length) { return AI_FAILURE; } pos += pOffset; diff --git a/include/assimp/ParsingUtils.h b/include/assimp/ParsingUtils.h index 555b2a309..4553072db 100644 --- a/include/assimp/ParsingUtils.h +++ b/include/assimp/ParsingUtils.h @@ -66,56 +66,57 @@ static const unsigned int BufferSize = 4096; // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE char_t ToLower( char_t in) -{ +AI_FORCE_INLINE +char_t ToLower( char_t in ) { return (in >= (char_t)'A' && in <= (char_t)'Z') ? (char_t)(in+0x20) : in; } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE char_t ToUpper( char_t in) { +AI_FORCE_INLINE +char_t ToUpper( char_t in) { return (in >= (char_t)'a' && in <= (char_t)'z') ? (char_t)(in-0x20) : in; } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool IsUpper( char_t in) -{ +AI_FORCE_INLINE +bool IsUpper( char_t in) { return (in >= (char_t)'A' && in <= (char_t)'Z'); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool IsLower( char_t in) -{ +AI_FORCE_INLINE +bool IsLower( char_t in) { return (in >= (char_t)'a' && in <= (char_t)'z'); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool IsSpace( char_t in) -{ +AI_FORCE_INLINE +bool IsSpace( char_t in) { return (in == (char_t)' ' || in == (char_t)'\t'); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool IsLineEnd( char_t in) -{ +AI_FORCE_INLINE +bool IsLineEnd( char_t in) { return (in==(char_t)'\r'||in==(char_t)'\n'||in==(char_t)'\0'||in==(char_t)'\f'); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool IsSpaceOrNewLine( char_t in) -{ +AI_FORCE_INLINE +bool IsSpaceOrNewLine( char_t in) { return IsSpace(in) || IsLineEnd(in); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipSpaces( const char_t* in, const char_t** out) -{ +AI_FORCE_INLINE +bool SkipSpaces( const char_t* in, const char_t** out) { while( *in == ( char_t )' ' || *in == ( char_t )'\t' ) { ++in; } @@ -125,15 +126,15 @@ AI_FORCE_INLINE bool SkipSpaces( const char_t* in, const char_t** out) // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipSpaces( const char_t** inout) -{ +AI_FORCE_INLINE +bool SkipSpaces( const char_t** inout) { return SkipSpaces(*inout,inout); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipLine( const char_t* in, const char_t** out) -{ +AI_FORCE_INLINE +bool SkipLine( const char_t* in, const char_t** out) { while( *in != ( char_t )'\r' && *in != ( char_t )'\n' && *in != ( char_t )'\0' ) { ++in; } @@ -148,15 +149,15 @@ AI_FORCE_INLINE bool SkipLine( const char_t* in, const char_t** out) // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipLine( const char_t** inout) -{ +AI_FORCE_INLINE +bool SkipLine( const char_t** inout) { return SkipLine(*inout,inout); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out) -{ +AI_FORCE_INLINE +bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out) { while( *in == ( char_t )' ' || *in == ( char_t )'\t' || *in == ( char_t )'\r' || *in == ( char_t )'\n' ) { ++in; } @@ -166,15 +167,15 @@ AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t* in, const char_t** out) // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool SkipSpacesAndLineEnd( const char_t** inout) -{ +AI_FORCE_INLINE +bool SkipSpacesAndLineEnd( const char_t** inout) { return SkipSpacesAndLineEnd(*inout,inout); } // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize ] ) -{ +AI_FORCE_INLINE +bool GetNextLine( const char_t*& buffer, char_t out[ BufferSize ] ) { if( ( char_t )'\0' == *buffer ) { return false; } @@ -202,7 +203,8 @@ AI_FORCE_INLINE bool IsNumeric( char_t in) // --------------------------------------------------------------------------------- template -AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len) +AI_FORCE_INLINE +bool TokenMatch(char_t*& in, const char* token, unsigned int len) { if (!::strncmp(token,in,len) && IsSpaceOrNewLine(in[len])) { if (in[len] != '\0') { @@ -222,26 +224,32 @@ AI_FORCE_INLINE bool TokenMatch(char_t*& in, const char* token, unsigned int len * @param token Token to check for * @param len Number of characters to check */ -AI_FORCE_INLINE bool TokenMatchI(const char*& in, const char* token, unsigned int len) -{ +AI_FORCE_INLINE +bool TokenMatchI(const char*& in, const char* token, unsigned int len) { if (!ASSIMP_strincmp(token,in,len) && IsSpaceOrNewLine(in[len])) { in += len+1; return true; } return false; } + // --------------------------------------------------------------------------------- -AI_FORCE_INLINE void SkipToken(const char*& in) -{ +AI_FORCE_INLINE +void SkipToken(const char*& in) { SkipSpaces(&in); - while (!IsSpaceOrNewLine(*in))++in; + while ( !IsSpaceOrNewLine( *in ) ) { + ++in; + } } + // --------------------------------------------------------------------------------- -AI_FORCE_INLINE std::string GetNextToken(const char*& in) -{ +AI_FORCE_INLINE +std::string GetNextToken(const char*& in) { SkipSpacesAndLineEnd(&in); const char* cur = in; - while (!IsSpaceOrNewLine(*in))++in; + while ( !IsSpaceOrNewLine( *in ) ) { + ++in; + } return std::string(cur,(size_t)(in-cur)); } diff --git a/include/assimp/SGSpatialSort.h b/include/assimp/SGSpatialSort.h index d8b4b418b..203e32512 100644 --- a/include/assimp/SGSpatialSort.h +++ b/include/assimp/SGSpatialSort.h @@ -123,7 +123,7 @@ protected: Entry() { /** intentionally not initialized.*/ } Entry( unsigned int pIndex, const aiVector3D& pPosition, float pDistance,uint32_t pSG) - : + : mIndex( pIndex), mPosition( pPosition), mSmoothGroups (pSG), diff --git a/include/assimp/SpatialSort.h b/include/assimp/SpatialSort.h index 323c2970f..4c45e95ad 100644 --- a/include/assimp/SpatialSort.h +++ b/include/assimp/SpatialSort.h @@ -47,8 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -namespace Assimp -{ +namespace Assimp { // ------------------------------------------------------------------------------------------------ /** A little helper class to quickly find all vertices in the epsilon environment of a given @@ -148,17 +147,20 @@ protected: aiVector3D mPlaneNormal; /** An entry in a spatially sorted position array. Consists of a vertex index, - * its position and its precalculated distance from the reference plane */ - struct Entry - { + * its position and its pre-calculated distance from the reference plane */ + struct Entry { unsigned int mIndex; ///< The vertex referred by this entry aiVector3D mPosition; ///< Position ai_real mDistance; ///< Distance of this vertex to the sorting plane - Entry() { /** intentionally not initialized.*/ } + Entry() + : mIndex( 999999999 ), mPosition(), mDistance( 99999. ) { + // empty + } Entry( unsigned int pIndex, const aiVector3D& pPosition, ai_real pDistance) - : mIndex( pIndex), mPosition( pPosition), mDistance( pDistance) - { } + : mIndex( pIndex), mPosition( pPosition), mDistance( pDistance) { + // empty + } bool operator < (const Entry& e) const { return mDistance < e.mDistance; } }; diff --git a/include/assimp/StreamReader.h b/include/assimp/StreamReader.h index 90cc65fee..3fab00ad2 100644 --- a/include/assimp/StreamReader.h +++ b/include/assimp/StreamReader.h @@ -283,7 +283,6 @@ public: return *this; } -private: // --------------------------------------------------------------------- /** Generic read method. ByteSwap::Swap(T*) *must* be defined */ template @@ -300,6 +299,7 @@ private: return f; } +private: // --------------------------------------------------------------------- void InternBegin() { if (!stream) { diff --git a/include/assimp/StreamWriter.h b/include/assimp/StreamWriter.h index 7ee0944a7..deb35fb4d 100644 --- a/include/assimp/StreamWriter.h +++ b/include/assimp/StreamWriter.h @@ -58,7 +58,7 @@ namespace Assimp { // -------------------------------------------------------------------------------------------- /** Wrapper class around IOStream to allow for consistent writing of binary data in both * little and big endian format. Don't attempt to instance the template directly. Use - * StreamWriterLE to read from a little-endian stream and StreamWriterBE to read from a + * StreamWriterLE to write to a little-endian stream and StreamWriterBE to write to a * BE stream. Alternatively, there is StreamWriterAny if the endianness of the output * stream is to be determined at runtime. */ @@ -104,10 +104,42 @@ public: // --------------------------------------------------------------------- ~StreamWriter() { - stream->Write(&buffer[0], 1, buffer.size()); + stream->Write(buffer.data(), 1, buffer.size()); stream->Flush(); } +public: + + // --------------------------------------------------------------------- + /** Flush the contents of the internal buffer, and the output IOStream */ + void Flush() + { + stream->Write(buffer.data(), 1, buffer.size()); + stream->Flush(); + buffer.clear(); + cursor = 0; + } + + // --------------------------------------------------------------------- + /** Seek to the given offset / origin in the output IOStream. + * + * Flushes the internal buffer and the output IOStream prior to seeking. */ + aiReturn Seek(size_t pOffset, aiOrigin pOrigin=aiOrigin_SET) + { + Flush(); + return stream->Seek(pOffset, pOrigin); + } + + // --------------------------------------------------------------------- + /** Tell the current position in the output IOStream. + * + * First flushes the internal buffer and the output IOStream. */ + size_t Tell() + { + Flush(); + return stream->Tell(); + } + public: // --------------------------------------------------------------------- @@ -171,6 +203,38 @@ public: Put(n); } + // --------------------------------------------------------------------- + /** Write a single character to the stream */ + void PutChar(char c) { + Put(c); + } + + // --------------------------------------------------------------------- + /** Write an aiString to the stream */ + void PutString(const aiString& s) + { + // as Put(T f) below + if (cursor + s.length >= buffer.size()) { + buffer.resize(cursor + s.length); + } + void* dest = &buffer[cursor]; + ::memcpy(dest, s.C_Str(), s.length); + cursor += s.length; + } + + // --------------------------------------------------------------------- + /** Write a std::string to the stream */ + void PutString(const std::string& s) + { + // as Put(T f) below + if (cursor + s.size() >= buffer.size()) { + buffer.resize(cursor + s.size()); + } + void* dest = &buffer[cursor]; + ::memcpy(dest, s.c_str(), s.size()); + cursor += s.size(); + } + public: // --------------------------------------------------------------------- @@ -191,8 +255,6 @@ public: cursor = new_cursor; } -private: - // --------------------------------------------------------------------- /** Generic write method. ByteSwap::Swap(T*) *must* be defined */ template diff --git a/include/assimp/StringComparison.h b/include/assimp/StringComparison.h index 1fada49dd..aea7f001a 100644 --- a/include/assimp/StringComparison.h +++ b/include/assimp/StringComparison.h @@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define INCLUDED_AI_STRING_WORKERS_H #include +#include #include "StringComparison.h" #include @@ -72,8 +73,8 @@ namespace Assimp { * @param number Number to be written * @return Length of the output string, excluding the '\0' */ -inline unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number) -{ +AI_FORCE_INLINE +unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number) { ai_assert(NULL != out); // write the unary minus to indicate we have a negative number @@ -91,7 +92,7 @@ inline unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number) const unsigned int digit = number / cur; if (mustPrint || digit > 0 || 1 == cur) { - // print all future zeroes from now + // print all future zeroe's from now mustPrint = true; *out++ = '0'+static_cast(digit); @@ -116,8 +117,8 @@ inline unsigned int ASSIMP_itoa10( char* out, unsigned int max, int32_t number) * size of the array automatically. */ template -inline unsigned int ASSIMP_itoa10( char(& out)[length], int32_t number) -{ +AI_FORCE_INLINE +unsigned int ASSIMP_itoa10( char(& out)[length], int32_t number) { return ASSIMP_itoa10(out,length,number); } @@ -132,9 +133,10 @@ inline unsigned int ASSIMP_itoa10( char(& out)[length], int32_t number) * @param s2 Second input string * @return 0 if the given strings are identical */ -inline int ASSIMP_stricmp(const char *s1, const char *s2) -{ - ai_assert(NULL != s1 && NULL != s2); +AI_FORCE_INLINE +int ASSIMP_stricmp(const char *s1, const char *s2) { + ai_assert( NULL != s1 ); + ai_assert( NULL != s2 ); #if (defined _MSC_VER) @@ -161,8 +163,8 @@ inline int ASSIMP_stricmp(const char *s1, const char *s2) * @param b Second string * @return 0 if a == b */ -inline int ASSIMP_stricmp(const std::string& a, const std::string& b) -{ +AI_FORCE_INLINE +int ASSIMP_stricmp(const std::string& a, const std::string& b) { int i = (int)b.length()-(int)a.length(); return (i ? i : ASSIMP_stricmp(a.c_str(),b.c_str())); } @@ -179,10 +181,13 @@ inline int ASSIMP_stricmp(const std::string& a, const std::string& b) * @param n Macimum number of characters to compare * @return 0 if the given strings are identical */ -inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) -{ - ai_assert(NULL != s1 && NULL != s2); - if (!n)return 0; +AI_FORCE_INLINE +int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) { + ai_assert( NULL != s1 ); + ai_assert( NULL != s2 ); + if ( !n ) { + return 0; + } #if (defined _MSC_VER) @@ -213,14 +218,16 @@ inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) * * todo: move somewhere where it fits better in than here */ -inline unsigned int integer_pow (unsigned int base, unsigned int power) -{ +AI_FORCE_INLINE +unsigned int integer_pow( unsigned int base, unsigned int power ) { unsigned int res = 1; - for (unsigned int i = 0; i < power;++i) + for ( unsigned int i = 0; i < power; ++i ) { res *= base; + } return res; } + } // end of namespace #endif // ! AI_STRINGCOMPARISON_H_INC diff --git a/include/assimp/StringUtils.h b/include/assimp/StringUtils.h index 3c3afd264..906898b53 100644 --- a/include/assimp/StringUtils.h +++ b/include/assimp/StringUtils.h @@ -42,6 +42,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_AI_STRINGUTILS_H #define INCLUDED_AI_STRINGUTILS_H +#include + #include #include #include @@ -55,7 +57,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /// @return The number of written characters if the buffer size was big enough. If an encoding error occurs, a negative number is returned. #if defined(_MSC_VER) && _MSC_VER < 1900 - inline int c99_ai_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { + AI_FORCE_INLINE + int c99_ai_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) { int count(-1); if (0 != size) { count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); @@ -67,7 +70,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. return count; } - inline int ai_snprintf(char *outBuf, size_t size, const char *format, ...) { + AI_FORCE_INLINE + int ai_snprintf(char *outBuf, size_t size, const char *format, ...) { int count; va_list ap; @@ -82,15 +86,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # define ai_snprintf snprintf #endif +/// @fn to_string +/// @brief The portable version of to_string ( some gcc-versions on embedded devices are not supporting this). +/// @param value The value to write into the std::string. +/// @return The value as a std::string template -inline +AI_FORCE_INLINE std::string to_string( T value ) { std::ostringstream os; os << value; + return os.str(); } -inline +/// @fn ai_strtof +/// @brief The portable version of strtof. +/// @param begin The first character of the string. +/// @param end The last character +/// @return The float value, 0.0f in cas of an error. +AI_FORCE_INLINE float ai_strtof( const char *begin, const char *end ) { if ( nullptr == begin ) { return 0.0f; @@ -107,5 +121,23 @@ float ai_strtof( const char *begin, const char *end ) { return val; } -#endif // INCLUDED_AI_STRINGUTILS_H +/// @fn DecimalToHexa +/// @brief The portable to convert a decimal value into a hexadecimal string. +/// @param toConvert Value to convert +/// @return The hexadecimal string, is empty in case of an error. +template +AI_FORCE_INLINE +std::string DecimalToHexa( T toConvert ) { + std::string result; + std::stringstream ss; + ss << std::hex << toConvert; + ss >> result; + for ( size_t i = 0; i < result.size(); ++i ) { + result[ i ] = toupper( result[ i ] ); + } + + return result; +} + +#endif // INCLUDED_AI_STRINGUTILS_H diff --git a/include/assimp/anim.h b/include/assimp/anim.h index 9156aac06..1a2c11044 100644 --- a/include/assimp/anim.h +++ b/include/assimp/anim.h @@ -162,7 +162,10 @@ struct aiMeshKey #ifdef __cplusplus - aiMeshKey() { + aiMeshKey() + : mTime(0.0) + , mValue(0) + { } /** Construction from a given time and key value */ diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index 071d58cd2..c9555fb38 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -934,6 +934,16 @@ enum aiComponent */ #define AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION "IMPORT_COLLADA_IGNORE_UP_DIRECTION" +// --------------------------------------------------------------------------- +/** @brief Specifies whether the Collada loader should use Collada names as node names. + * + * If this property is set to true, the Collada names will be used as the + * node name. The default is to use the id tag (resp. sid tag, if no id tag is present) + * instead. + * Property type: Bool. Default value: false. + */ +#define AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES "IMPORT_COLLADA_USE_COLLADA_NAMES" + // ---------- All the Export defines ------------ /** @brief Specifies the xfile use double for real values of float diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index 058a7ff87..e66f1b37d 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -14,8 +14,8 @@ // ------------------------------------------------------------------------------------ -#ifndef __FAST_A_TO_F_H_INCLUDED__ -#define __FAST_A_TO_F_H_INCLUDED__ +#ifndef FAST_A_TO_F_H_INCLUDED +#define FAST_A_TO_F_H_INCLUDED #include #include @@ -26,15 +26,13 @@ #include "StringComparison.h" #include - #ifdef _MSC_VER # include #else # include #endif -namespace Assimp -{ +namespace Assimp { const double fast_atof_table[16] = { // we write [16] here instead of [] to work around a swig bug 0.0, @@ -59,69 +57,65 @@ const double fast_atof_table[16] = { // we write [16] here instead of [] to wo // ------------------------------------------------------------------------------------ // Convert a string in decimal format to a number // ------------------------------------------------------------------------------------ -inline unsigned int strtoul10( const char* in, const char** out=0) -{ +inline +unsigned int strtoul10( const char* in, const char** out=0) { unsigned int value = 0; - bool running = true; - while ( running ) - { - if ( *in < '0' || *in > '9' ) + for ( ;; ) { + if ( *in < '0' || *in > '9' ) { break; + } value = ( value * 10 ) + ( *in - '0' ); ++in; } - if (out)*out = in; + if ( out ) { + *out = in; + } return value; } // ------------------------------------------------------------------------------------ // Convert a string in octal format to a number // ------------------------------------------------------------------------------------ -inline unsigned int strtoul8( const char* in, const char** out=0) -{ - unsigned int value = 0; - - bool running = true; - while ( running ) - { - if ( *in < '0' || *in > '7' ) +inline +unsigned int strtoul8( const char* in, const char** out=0) { + unsigned int value( 0 ); + for ( ;; ) { + if ( *in < '0' || *in > '7' ) { break; + } value = ( value << 3 ) + ( *in - '0' ); ++in; } - if (out)*out = in; + if ( out ) { + *out = in; + } return value; } // ------------------------------------------------------------------------------------ // Convert a string in hex format to a number // ------------------------------------------------------------------------------------ -inline unsigned int strtoul16( const char* in, const char** out=0) -{ - unsigned int value = 0; - - bool running = true; - while ( running ) - { - if ( *in >= '0' && *in <= '9' ) - { +inline +unsigned int strtoul16( const char* in, const char** out=0) { + unsigned int value( 0 ); + for ( ;; ) { + if ( *in >= '0' && *in <= '9' ) { value = ( value << 4u ) + ( *in - '0' ); - } - else if (*in >= 'A' && *in <= 'F') - { + } else if (*in >= 'A' && *in <= 'F') { value = ( value << 4u ) + ( *in - 'A' ) + 10; - } - else if (*in >= 'a' && *in <= 'f') - { + } else if (*in >= 'a' && *in <= 'f') { value = ( value << 4u ) + ( *in - 'a' ) + 10; + } else { + break; } - else break; ++in; } - if (out)*out = in; + if ( out ) { + *out = in; + } return value; } @@ -129,17 +123,16 @@ inline unsigned int strtoul16( const char* in, const char** out=0) // Convert just one hex digit // Return value is UINT_MAX if the input character is not a hex digit. // ------------------------------------------------------------------------------------ -inline unsigned int HexDigitToDecimal(char in) -{ - unsigned int out = UINT_MAX; - if (in >= '0' && in <= '9') +inline +unsigned int HexDigitToDecimal(char in) { + unsigned int out( UINT_MAX ); + if ( in >= '0' && in <= '9' ) { out = in - '0'; - - else if (in >= 'a' && in <= 'f') + } else if ( in >= 'a' && in <= 'f' ) { out = 10u + in - 'a'; - - else if (in >= 'A' && in <= 'F') + } else if ( in >= 'A' && in <= 'F' ) { out = 10u + in - 'A'; + } // return value is UINT_MAX if the input is not a hex digit return out; @@ -148,20 +141,20 @@ inline unsigned int HexDigitToDecimal(char in) // ------------------------------------------------------------------------------------ // Convert a hex-encoded octet (2 characters, i.e. df or 1a). // ------------------------------------------------------------------------------------ -inline uint8_t HexOctetToDecimal(const char* in) -{ +inline +uint8_t HexOctetToDecimal(const char* in) { return ((uint8_t)HexDigitToDecimal(in[0])<<4)+(uint8_t)HexDigitToDecimal(in[1]); } - // ------------------------------------------------------------------------------------ // signed variant of strtoul10 // ------------------------------------------------------------------------------------ -inline int strtol10( const char* in, const char** out=0) -{ +inline +int strtol10( const char* in, const char** out=0) { bool inv = (*in=='-'); - if (inv || *in=='+') + if ( inv || *in == '+' ) { ++in; + } int value = strtoul10(in,out); if (inv) { @@ -176,10 +169,9 @@ inline int strtol10( const char* in, const char** out=0) // 0NNN - oct // NNN - dec // ------------------------------------------------------------------------------------ -inline unsigned int strtoul_cppstyle( const char* in, const char** out=0) -{ - if ('0' == in[0]) - { +inline +unsigned int strtoul_cppstyle( const char* in, const char** out=0) { + if ('0' == in[0]) { return 'x' == in[1] ? strtoul16(in+2,out) : strtoul8(in+1,out); } return strtoul10(in, out); @@ -189,19 +181,19 @@ inline unsigned int strtoul_cppstyle( const char* in, const char** out=0) // Special version of the function, providing higher accuracy and safety // It is mainly used by fast_atof to prevent ugly and unwanted integer overflows. // ------------------------------------------------------------------------------------ -inline uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_inout=0) -{ +inline +uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_inout=0) { unsigned int cur = 0; uint64_t value = 0; - if ( *in < '0' || *in > '9' ) - throw std::invalid_argument(std::string("The string \"") + in + "\" cannot be converted into a value."); + if ( *in < '0' || *in > '9' ) { + throw std::invalid_argument( std::string( "The string \"" ) + in + "\" cannot be converted into a value." ); + } - bool running = true; - while ( running ) - { - if ( *in < '0' || *in > '9' ) + for ( ;; ) { + if ( *in < '0' || *in > '9' ) { break; + } const uint64_t new_value = ( value * 10 ) + ( *in - '0' ); @@ -210,7 +202,6 @@ inline uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* DefaultLogger::get()->warn( std::string( "Converting the string \"" ) + in + "\" into a value resulted in overflow." ); return 0; } - //throw std::overflow_error(); value = new_value; @@ -218,21 +209,23 @@ inline uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* ++cur; if (max_inout && *max_inout == cur) { - if (out) { /* skip to end */ - while (*in >= '0' && *in <= '9') + while ( *in >= '0' && *in <= '9' ) { ++in; + } *out = in; } return value; } } - if (out) + if ( out ) { *out = in; + } - if (max_inout) + if ( max_inout ) { *max_inout = cur; + } return value; } @@ -240,11 +233,12 @@ inline uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* // ------------------------------------------------------------------------------------ // signed variant of strtoul10_64 // ------------------------------------------------------------------------------------ -inline int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) -{ +inline +int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) { bool inv = (*in == '-'); - if (inv || *in == '+') + if ( inv || *in == '+' ) { ++in; + } int64_t value = strtoul10_64(in, out, max_inout); if (inv) { @@ -253,7 +247,6 @@ inline int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* m return value; } - // Number of relevant decimals for floating-point parsing. #define AI_FAST_ATOF_RELAVANT_DECIMALS 15 @@ -262,9 +255,9 @@ inline int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* m //! about 6 times faster than atof in win32. // If you find any bugs, please send them to me, niko (at) irrlicht3d.org. // ------------------------------------------------------------------------------------ -template -inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) -{ +template +inline +const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) { Real f = 0; bool inv = (*c == '-'); @@ -272,42 +265,36 @@ inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma ++c; } - if ((c[0] == 'N' || c[0] == 'n') && ASSIMP_strincmp(c, "nan", 3) == 0) - { + if ((c[0] == 'N' || c[0] == 'n') && ASSIMP_strincmp(c, "nan", 3) == 0) { out = std::numeric_limits::quiet_NaN(); c += 3; return c; } - if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inf", 3) == 0) - { + if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inf", 3) == 0) { out = std::numeric_limits::infinity(); if (inv) { out = -out; } c += 3; - if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inity", 5) == 0) - { + if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inity", 5) == 0) { c += 5; } return c; - } + } if (!(c[0] >= '0' && c[0] <= '9') && - !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) - { + !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) { throw std::invalid_argument("Cannot parse string " "as real number: does not start with digit " "or decimal point followed by digit."); } - if (*c != '.' && (! check_comma || c[0] != ',')) - { + if (*c != '.' && (! check_comma || c[0] != ',')) { f = static_cast( strtoul10_64 ( c, &c) ); } - if ((*c == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9') - { + if ((*c == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9') { ++c; // NOTE: The original implementation is highly inaccurate here. The precision of a single @@ -332,7 +319,6 @@ inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma // A major 'E' must be allowed. Necessary for proper reading of some DXF files. // Thanks to Zhao Lei to point out that this if() must be outside the if (*c == '.' ..) if (*c == 'e' || *c == 'E') { - ++c; const bool einv = (*c=='-'); if (einv || *c=='+') { @@ -358,30 +344,30 @@ inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma // ------------------------------------------------------------------------------------ // The same but more human. -inline ai_real fast_atof(const char* c) -{ +inline +ai_real fast_atof(const char* c) { ai_real ret(0.0); fast_atoreal_move(c, ret); + return ret; } - -inline ai_real fast_atof( const char* c, const char** cout) -{ +inline +ai_real fast_atof( const char* c, const char** cout) { ai_real ret(0.0); *cout = fast_atoreal_move(c, ret); return ret; } -inline ai_real fast_atof( const char** inout) -{ +inline +ai_real fast_atof( const char** inout) { ai_real ret(0.0); *inout = fast_atoreal_move(*inout, ret); return ret; } -} // end of namespace Assimp +} //! namespace Assimp -#endif +#endif // FAST_A_TO_F_H_INCLUDED diff --git a/include/assimp/matrix3x3.inl b/include/assimp/matrix3x3.inl index aaf62eb0f..ab2cc410b 100644 --- a/include/assimp/matrix3x3.inl +++ b/include/assimp/matrix3x3.inl @@ -313,16 +313,16 @@ inline aiMatrix3x3t& aiMatrix3x3t::FromToMatrix(const aiVector3t(2.0) / (u * u); - const TReal c2 = static_cast(2.0) / (v * v); - const TReal c3 = c1 * c2 * (u * v); + const TReal c1_ = static_cast(2.0) / (u * u); + const TReal c2_ = static_cast(2.0) / (v * v); + const TReal c3_ = c1_ * c2_ * (u * v); for (unsigned int i = 0; i < 3; i++) { for (unsigned int j = 0; j < 3; j++) { - mtx[i][j] = - c1 * u[i] * u[j] - c2 * v[i] * v[j] - + c3 * v[i] * u[j]; + mtx[i][j] = - c1_ * u[i] * u[j] - c2_ * v[i] * v[j] + + c3_ * v[i] * u[j]; } mtx[i][i] += static_cast(1.0); } diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index a532e2b46..731053614 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -200,8 +200,7 @@ struct aiFace // --------------------------------------------------------------------------- /** @brief A single influence of a bone on a vertex. */ -struct aiVertexWeight -{ +struct aiVertexWeight { //! Index of the vertex which is influenced by the bone. unsigned int mVertexId; @@ -212,14 +211,28 @@ struct aiVertexWeight #ifdef __cplusplus //! Default constructor - aiVertexWeight() { } + aiVertexWeight() + : mVertexId(0) + , mWeight(0.0f) { + // empty + } //! Initialisation from a given index and vertex weight factor //! \param pID ID //! \param pWeight Vertex weight factor - aiVertexWeight( unsigned int pID, float pWeight) - : mVertexId( pID), mWeight( pWeight) - { /* nothing to do here */ } + aiVertexWeight( unsigned int pID, float pWeight ) + : mVertexId( pID ) + , mWeight( pWeight ) { + // empty + } + + bool operator == ( const aiVertexWeight &rhs ) const { + return ( mVertexId == rhs.mVertexId && mWeight == rhs.mWeight ); + } + + bool operator != ( const aiVertexWeight &rhs ) const { + return ( *this == rhs ); + } #endif // __cplusplus }; @@ -230,31 +243,41 @@ struct aiVertexWeight * * A bone has a name by which it can be found in the frame hierarchy and by * which it can be addressed by animations. In addition it has a number of - * influences on vertices. + * influences on vertices, and a matrix relating the mesh position to the + * position of the bone at the time of binding. */ -struct aiBone -{ +struct aiBone { //! The name of the bone. C_STRUCT aiString mName; - //! The number of vertices affected by this bone + //! The number of vertices affected by this bone. //! The maximum value for this member is #AI_MAX_BONE_WEIGHTS. unsigned int mNumWeights; - //! The vertices affected by this bone + //! The influence weights of this bone, by vertex index. C_STRUCT aiVertexWeight* mWeights; - //! Matrix that transforms from mesh space to bone space in bind pose + /** Matrix that transforms from bone space to mesh space in bind pose. + * + * This matrix describes the position of the mesh + * in the local space of this bone when the skeleton was bound. + * Thus it can be used directly to determine a desired vertex position, + * given the world-space transform of the bone when animated, + * and the position of the vertex in mesh space. + * + * It is sometimes called an inverse-bind matrix, + * or inverse bind pose matrix. + */ C_STRUCT aiMatrix4x4 mOffsetMatrix; #ifdef __cplusplus //! Default constructor aiBone() - : mName() - , mNumWeights( 0 ) - , mWeights( NULL ) - { + : mName() + , mNumWeights( 0 ) + , mWeights( nullptr ) { + // empty } //! Copy constructor @@ -270,6 +293,44 @@ struct aiBone } } + + //! Assignment operator + aiBone &operator=(const aiBone& other) + { + if (this == &other) { + return *this; + } + + mName = other.mName; + mNumWeights = other.mNumWeights; + mOffsetMatrix = other.mOffsetMatrix; + + if (other.mWeights && other.mNumWeights) + { + if (mWeights) { + delete[] mWeights; + } + + mWeights = new aiVertexWeight[mNumWeights]; + ::memcpy(mWeights,other.mWeights,mNumWeights * sizeof(aiVertexWeight)); + } + + return *this; + } + + bool operator == ( const aiBone &rhs ) const { + if ( mName != rhs.mName || mNumWeights != rhs.mNumWeights ) { + return false; + } + + for ( size_t i = 0; i < mNumWeights; ++i ) { + if ( mWeights[ i ] != rhs.mWeights[ i ] ) { + return false; + } + } + + return true; + } //! Destructor - deletes the array of vertex weights ~aiBone() { diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index 2dba61abc..70a604de9 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -67,6 +67,7 @@ typedef enum aiMetadataType { AI_DOUBLE = 4, AI_AISTRING = 5, AI_AIVECTOR3D = 6, + AI_META_MAX = 7, #ifndef SWIG FORCE_32BIT = INT_MAX @@ -130,42 +131,103 @@ struct aiMetadata { */ aiMetadata() : mNumProperties(0) - , mKeys(NULL) - , mValues(NULL) { + , mKeys(nullptr) + , mValues(nullptr) { // empty } + aiMetadata( const aiMetadata &rhs ) + : mNumProperties( rhs.mNumProperties ) + , mKeys( nullptr ) + , mValues( nullptr ) { + mKeys = new aiString[ mNumProperties ]; + for ( unsigned int i = 0; i < mNumProperties; ++i ) { + mKeys[ i ] = rhs.mKeys[ i ]; + } + mValues = new aiMetadataEntry[ mNumProperties ]; + for ( unsigned int i = 0; i < mNumProperties; ++i ) { + mValues[ i ].mType = rhs.mValues[ i ].mType; + switch ( rhs.mValues[ i ].mType ) { + case AI_BOOL: + mValues[ i ].mData = new bool( rhs.mValues[i].mData ); + break; + case AI_INT32: { + int32_t v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( int32_t ) ); + mValues[ i ].mData = new int32_t( v ); + } + break; + case AI_UINT64: { + uint64_t v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( uint64_t ) ); + mValues[ i ].mData = new uint64_t( v ); + } + break; + case AI_FLOAT: { + float v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( float ) ); + mValues[ i ].mData = new float( v ); + } + break; + case AI_DOUBLE: { + double v; + ::memcpy( &v, rhs.mValues[ i ].mData, sizeof( double ) ); + mValues[ i ].mData = new double( v ); + } + break; + case AI_AISTRING: { + aiString v; + rhs.Get( mKeys[ i ], v ); + mValues[ i ].mData = new aiString( v ); + } + break; + case AI_AIVECTOR3D: { + aiVector3D v; + rhs.Get( mKeys[ i ], v ); + mValues[ i ].mData = new aiVector3D( v ); + } + break; +#ifndef SWIG + case FORCE_32BIT: +#endif + default: + break; + } + + } + } + /** * @brief The destructor. */ ~aiMetadata() { delete [] mKeys; - mKeys = NULL; + mKeys = nullptr; if (mValues) { // Delete each metadata entry for (unsigned i=0; i(data); + delete static_cast< bool* >( data ); break; case AI_INT32: - delete static_cast(data); + delete static_cast< int32_t* >( data ); break; case AI_UINT64: - delete static_cast(data); + delete static_cast< uint64_t* >( data ); break; case AI_FLOAT: - delete static_cast(data); + delete static_cast< float* >( data ); break; case AI_DOUBLE: - delete static_cast(data); + delete static_cast< double* >( data ); break; case AI_AISTRING: - delete static_cast(data); + delete static_cast< aiString* >( data ); break; case AI_AIVECTOR3D: - delete static_cast(data); + delete static_cast< aiVector3D* >( data ); break; #ifndef SWIG case FORCE_32BIT: @@ -177,7 +239,7 @@ struct aiMetadata { // Delete the metadata array delete [] mValues; - mValues = NULL; + mValues = nullptr; } } @@ -208,8 +270,8 @@ struct aiMetadata { } template - inline void Add(const std::string& key, const T& value) - { + inline + void Add(const std::string& key, const T& value) { aiString* new_keys = new aiString[mNumProperties + 1]; aiMetadataEntry* new_values = new aiMetadataEntry[mNumProperties + 1]; @@ -256,7 +318,7 @@ struct aiMetadata { template inline - bool Get( unsigned index, T& value ) { + bool Get( unsigned index, T& value ) const { // In range assertion if ( index >= mNumProperties ) { return false; @@ -277,7 +339,7 @@ struct aiMetadata { template inline - bool Get( const aiString& key, T& value ) { + bool Get( const aiString& key, T& value ) const { // Search for the given key for ( unsigned int i = 0; i < mNumProperties; ++i ) { if ( mKeys[ i ] == key ) { @@ -288,7 +350,8 @@ struct aiMetadata { } template - inline bool Get( const std::string& key, T& value ) { + inline + bool Get( const std::string& key, T& value ) const { return Get(aiString(key), value); } @@ -297,7 +360,8 @@ struct aiMetadata { /// \param [out] pKey - pointer to the key value. /// \param [out] pEntry - pointer to the entry: type and value. /// \return false - if pIndex is out of range, else - true. - inline bool Get(size_t index, const aiString*& key, const aiMetadataEntry*& entry) { + inline + bool Get(size_t index, const aiString*& key, const aiMetadataEntry*& entry) const { if ( index >= mNumProperties ) { return false; } diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h new file mode 100644 index 000000000..cd9b5e2bf --- /dev/null +++ b/include/assimp/pbrmaterial.h @@ -0,0 +1,76 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2018, 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 pbrmaterial.h + * @brief Defines the material system of the library + */ +#ifndef AI_PBRMATERIAL_H_INC +#define AI_PBRMATERIAL_H_INC + +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 +#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 +#define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 +#define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 +#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 +#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 + +#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" +#define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" +#define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid" +#define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag" +#define _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE "$tex.mappingfiltermin" +#define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale" +#define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength" + +#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N +#define AI_MATKEY_GLTF_MAPPINGFILTER_MIN(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MIN_BASE, type, N +#define AI_MATKEY_GLTF_TEXTURE_SCALE(type, N) _AI_MATKEY_GLTF_SCALE_BASE, type, N +#define AI_MATKEY_GLTF_TEXTURE_STRENGTH(type, N) _AI_MATKEY_GLTF_STRENGTH_BASE, type, N + +#endif //!!AI_PBRMATERIAL_H_INC diff --git a/include/assimp/postprocess.h b/include/assimp/postprocess.h index ebb6728f3..f6c0833ee 100644 --- a/include/assimp/postprocess.h +++ b/include/assimp/postprocess.h @@ -537,9 +537,11 @@ enum aiPostProcessSteps /**
This step will perform a global scale of the model. * * Some importers are providing a mechanism to define a scaling unit for the - * model. This post processing step can be used to do so. + * model. This post processing step can be used to do so. You need to get the + * global scaling from your importer settings like in FBX. Use the flag + * AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY from the global property table to configure this. * - * Use #AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY to control this. + * Use #AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY to setup the global scaing factor. */ aiProcess_GlobalScale = 0x8000000, diff --git a/include/assimp/types.h b/include/assimp/types.h index f0d9b2428..9868f657c 100644 --- a/include/assimp/types.h +++ b/include/assimp/types.h @@ -304,6 +304,20 @@ struct aiString data[len] = 0; } + + /** Assigment operator */ + aiString& operator = (const aiString &rOther) { + if (this == &rOther) { + return *this; + } + + length = rOther.length;; + memcpy( data, rOther.data, length); + data[length] = '\0'; + return *this; + } + + /** Assign a const char* to the string */ aiString& operator = (const char* sz) { Set(sz); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a9dfaf83f..7c5ff3706 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -45,12 +45,15 @@ INCLUDE_DIRECTORIES( ${Assimp_SOURCE_DIR}/include ${Assimp_SOURCE_DIR}/code ) - +if (MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING") +endif() # Add the temporary output directories to the library path to make sure the # Assimp library can be found, even if it is not installed system-wide yet. LINK_DIRECTORIES( ${Assimp_BINARY_DIR} ${AssetImporter_BINARY_DIR}/lib ) SET( COMMON + unit/utSimd.cpp unit/utIOSystem.cpp unit/utIOStreamBuffer.cpp unit/utIssues.cpp diff --git a/test/models/FBX/global_settings.fbx b/test/models/FBX/global_settings.fbx new file mode 100644 index 000000000..3fc616bb0 Binary files /dev/null and b/test/models/FBX/global_settings.fbx differ diff --git a/test/models/OBJ/cube_mtllib_after_g.mtl b/test/models/OBJ/cube_mtllib_after_g.mtl new file mode 100644 index 000000000..63dde71af --- /dev/null +++ b/test/models/OBJ/cube_mtllib_after_g.mtl @@ -0,0 +1,5 @@ +newmtl MyMaterial +Ka 1.000 1.000 1.000 +Kd 1.000 1.000 1.000 +Ns 200.000 +Ks 0.050 0.050 0.050 diff --git a/test/models/OBJ/cube_mtllib_after_g.obj b/test/models/OBJ/cube_mtllib_after_g.obj new file mode 100644 index 000000000..dfd5997e8 --- /dev/null +++ b/test/models/OBJ/cube_mtllib_after_g.obj @@ -0,0 +1,32 @@ +g Object +mtllib cube_mtllib_after_g.mat +usemtl MyMaterial + +v 0.0 0.0 0.0 +v 0.0 0.0 1.0 +v 0.0 1.0 0.0 +v 0.0 1.0 1.0 +v 1.0 0.0 0.0 +v 1.0 0.0 1.0 +v 1.0 1.0 0.0 +v 1.0 1.0 1.0 + +vn 0.0 0.0 1.0 +vn 0.0 0.0 -1.0 +vn 0.0 1.0 0.0 +vn 0.0 -1.0 0.0 +vn 1.0 0.0 0.0 +vn -1.0 0.0 0.0 + +f 1//2 7//2 5//2 +f 1//2 3//2 7//2 +f 1//6 4//6 3//6 +f 1//6 2//6 4//6 +f 3//3 8//3 7//3 +f 3//3 4//3 8//3 +f 5//5 7//5 8//5 +f 5//5 8//5 6//5 +f 1//4 5//4 6//4 +f 1//4 6//4 2//4 +f 2//1 6//1 8//1 +f 2//1 8//1 4//1 diff --git a/test/models/PLY/cube_binary.ply b/test/models/PLY/cube_binary.ply new file mode 100644 index 000000000..14d29ebd8 Binary files /dev/null and b/test/models/PLY/cube_binary.ply differ diff --git a/test/models/PLY/cube_uv.ply b/test/models/PLY/cube_uv.ply new file mode 100644 index 000000000..45ab0607a --- /dev/null +++ b/test/models/PLY/cube_uv.ply @@ -0,0 +1,45 @@ +ply +format ascii 1.0 +comment Created by Blender 2.77 (sub 0) - www.blender.org, source file: '' +element vertex 24 +property float x +property float y +property float z +property float nx +property float ny +property float nz +property float s +property float t +element face 6 +property list uchar uint vertex_indices +end_header +1.000000 1.000000 -1.000000 0.000000 0.000000 -1.000000 0.000000 0.000000 +1.000000 -1.000000 -1.000000 0.000000 0.000000 -1.000000 1.000000 0.000000 +-1.000000 -1.000000 -1.000000 0.000000 0.000000 -1.000000 1.000000 1.000000 +-1.000000 1.000000 -1.000000 0.000000 0.000000 -1.000000 0.000000 1.000000 +1.000000 0.999999 1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 +-1.000000 1.000000 1.000000 0.000000 -0.000000 1.000000 1.000000 0.000000 +-1.000000 -1.000000 1.000000 0.000000 -0.000000 1.000000 1.000000 1.000000 +0.999999 -1.000001 1.000000 0.000000 -0.000000 1.000000 0.000000 1.000000 +1.000000 1.000000 -1.000000 1.000000 -0.000000 0.000000 0.000000 0.000000 +1.000000 0.999999 1.000000 1.000000 -0.000000 0.000000 1.000000 0.000000 +0.999999 -1.000001 1.000000 1.000000 -0.000000 0.000000 1.000000 1.000000 +1.000000 -1.000000 -1.000000 1.000000 -0.000000 0.000000 0.000000 1.000000 +1.000000 -1.000000 -1.000000 -0.000000 -1.000000 -0.000000 0.000000 0.000000 +0.999999 -1.000001 1.000000 -0.000000 -1.000000 -0.000000 1.000000 0.000000 +-1.000000 -1.000000 1.000000 -0.000000 -1.000000 -0.000000 1.000000 1.000000 +-1.000000 -1.000000 -1.000000 -0.000000 -1.000000 -0.000000 0.000000 1.000000 +-1.000000 -1.000000 -1.000000 -1.000000 0.000000 -0.000000 0.000000 0.000000 +-1.000000 -1.000000 1.000000 -1.000000 0.000000 -0.000000 1.000000 0.000000 +-1.000000 1.000000 1.000000 -1.000000 0.000000 -0.000000 1.000000 1.000000 +-1.000000 1.000000 -1.000000 -1.000000 0.000000 -0.000000 0.000000 1.000000 +1.000000 0.999999 1.000000 0.000000 1.000000 0.000000 0.000000 0.000000 +1.000000 1.000000 -1.000000 0.000000 1.000000 0.000000 1.000000 0.000000 +-1.000000 1.000000 -1.000000 0.000000 1.000000 0.000000 1.000000 1.000000 +-1.000000 1.000000 1.000000 0.000000 1.000000 0.000000 0.000000 1.000000 +4 0 1 2 3 +4 4 5 6 7 +4 8 9 10 11 +4 12 13 14 15 +4 16 17 18 19 +4 20 21 22 23 diff --git a/test/models/PLY/float-color.ply b/test/models/PLY/float-color.ply index 34353ad53..f419be34e 100644 --- a/test/models/PLY/float-color.ply +++ b/test/models/PLY/float-color.ply @@ -15,4 +15,4 @@ end_header 0.0 0.0 0.0 0 0 1 1 100.0 0.0 0.0 0 0 1 1 200.0 200.0 0.0 0 0 1 1 -3 0 1 2 \ No newline at end of file +3 0 1 2 diff --git a/test/models/PLY/issue623.ply b/test/models/PLY/issue623.ply new file mode 100644 index 000000000..af8811752 --- /dev/null +++ b/test/models/PLY/issue623.ply @@ -0,0 +1,36 @@ +ply +format ascii 1.0 +comment Created by Blender 2.77 (sub 0) - www.blender.org, source file: '' +element vertex 24 +property float x +property float y +property float z +property float nx +property float ny +property float nz +property list uchar uint vertex_indices +end_header +7.941797 1.432409 -0.927566 0.000000 0.000000 -1.000000 +7.941797 -2.321273 -0.927566 0.000000 0.000000 -1.000000 +4.188114 -2.321273 -0.927566 0.000000 0.000000 -1.000000 +4.188115 1.432410 -0.927566 0.000000 0.000000 -1.000000 +7.941798 1.432408 2.826117 0.000000 -0.000000 1.000000 +4.188114 1.432409 2.826117 0.000000 -0.000000 1.000000 +4.188113 -2.321273 2.826117 0.000000 -0.000000 1.000000 +7.941795 -2.321275 2.826117 0.000000 -0.000000 1.000000 +7.941797 1.432409 -0.927566 1.000000 -0.000000 0.000000 +7.941798 1.432408 2.826117 1.000000 -0.000000 0.000000 +7.941795 -2.321275 2.826117 1.000000 -0.000000 0.000000 +7.941797 -2.321273 -0.927566 1.000000 -0.000000 0.000000 +7.941797 -2.321273 -0.927566 -0.000000 -1.000000 -0.000000 +7.941795 -2.321275 2.826117 -0.000000 -1.000000 -0.000000 +4.188113 -2.321273 2.826117 -0.000000 -1.000000 -0.000000 +4.188114 -2.321273 -0.927566 -0.000000 -1.000000 -0.000000 +4.188114 -2.321273 -0.927566 -1.000000 0.000000 -0.000000 +4.188113 -2.321273 2.826117 -1.000000 0.000000 -0.000000 +4.188114 1.432409 2.826117 -1.000000 0.000000 -0.000000 +4.188115 1.432410 -0.927566 -1.000000 0.000000 -0.000000 +7.941798 1.432408 2.826117 0.000000 1.000000 0.000000 +7.941797 1.432409 -0.927566 0.000000 1.000000 0.000000 +4.188115 1.432410 -0.927566 0.000000 1.000000 0.000000 +4.188114 1.432409 2.826117 0.000000 1.000000 0.000000 diff --git a/test/models/X/OV_GetNextToken b/test/models/X/OV_GetNextToken new file mode 100644 index 000000000..8b2a0d0ca Binary files /dev/null and b/test/models/X/OV_GetNextToken differ diff --git a/test/unit/AbstractImportExportBase.cpp b/test/unit/AbstractImportExportBase.cpp index f75ba4ea2..c09ec0fd7 100644 --- a/test/unit/AbstractImportExportBase.cpp +++ b/test/unit/AbstractImportExportBase.cpp @@ -45,5 +45,5 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace ::Assimp; AbstractImportExportBase::~AbstractImportExportBase() { - // empty + // empty } diff --git a/test/unit/utAnim.cpp b/test/unit/utAnim.cpp index 04ee8c96d..40c840d7e 100644 --- a/test/unit/utAnim.cpp +++ b/test/unit/utAnim.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2018, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -106,4 +104,4 @@ TEST_F( utAnim, aiAnimationTest ) { ok = false; } EXPECT_TRUE( ok ); -} \ No newline at end of file +} diff --git a/test/unit/utBlenderIntermediate.cpp b/test/unit/utBlenderIntermediate.cpp index 9146d6d1e..55490e613 100644 --- a/test/unit/utBlenderIntermediate.cpp +++ b/test/unit/utBlenderIntermediate.cpp @@ -57,17 +57,26 @@ class BlenderIntermediateTest : public ::testing::Test { #define NAME_1 "name1" #define NAME_2 "name2" +// Updated this test after fixing #1776: +// A comparator in C++ is used for ordering and must implement strict weak ordering, +// which means it must return false for equal values. +// The C++ standard defines and expects this behavior: true if lhs < rhs, false otherwise. TEST_F( BlenderIntermediateTest,ConversionData_ObjectCompareTest ) { Object obj1, obj2; strncpy( obj1.id.name, NAME_1, sizeof(NAME_1) ); strncpy( obj2.id.name, NAME_2, sizeof(NAME_2) ); - Blender::ObjectCompare cmp_false; - bool res( cmp_false( &obj1, &obj2 ) ); + + Blender::ObjectCompare cmp_true_because_first_is_smaller_than_second; + bool res( cmp_true_because_first_is_smaller_than_second( &obj1, &obj2 ) ); + EXPECT_TRUE( res ); + + Blender::ObjectCompare cmp_false_because_equal; + res = cmp_false_because_equal( &obj1, &obj1 ); EXPECT_FALSE( res ); - Blender::ObjectCompare cmp_true; - res = cmp_true( &obj1, &obj1 ); - EXPECT_TRUE( res ); + Blender::ObjectCompare cmp_false_because_first_is_greater_than_second; + res = cmp_false_because_first_is_greater_than_second( &obj2, &obj1 ); + EXPECT_FALSE( res ); } diff --git a/test/unit/utD3MFImportExport.cpp b/test/unit/utD3MFImportExport.cpp index 3aefeba84..c5fdd003b 100644 --- a/test/unit/utD3MFImportExport.cpp +++ b/test/unit/utD3MFImportExport.cpp @@ -86,4 +86,12 @@ TEST_F( utD3MFImporterExporter, export3MFtoMemTest ) { EXPECT_TRUE( exporterTest() ); } +TEST_F( utD3MFImporterExporter, roundtrip3MFtoMemTest ) { + EXPECT_TRUE( exporterTest() ); + + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( "test.3mf", 0 ); + EXPECT_NE( nullptr, scene ); +} + #endif // ASSIMP_BUILD_NO_EXPORT diff --git a/test/unit/utFBXImporterExporter.cpp b/test/unit/utFBXImporterExporter.cpp index d5a6e4354..d2576cfa1 100644 --- a/test/unit/utFBXImporterExporter.cpp +++ b/test/unit/utFBXImporterExporter.cpp @@ -101,12 +101,12 @@ TEST_F( utFBXImporterExporter, importPhongMaterial ) { TEST_F(utFBXImporterExporter, importUnitScaleFactor) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/spider.fbx", aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/global_settings.fbx", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); EXPECT_NE(nullptr, scene->mMetaData); double factor(0.0); scene->mMetaData->Get("UnitScaleFactor", factor); - EXPECT_DOUBLE_EQ(1.0, factor); + EXPECT_DOUBLE_EQ(500.0, factor); } diff --git a/test/unit/utIFCImportExport.cpp b/test/unit/utIFCImportExport.cpp index fa27bb7a8..01132435b 100644 --- a/test/unit/utIFCImportExport.cpp +++ b/test/unit/utIFCImportExport.cpp @@ -62,3 +62,24 @@ public: TEST_F( utIFCImportExport, importIFCFromFileTest ) { EXPECT_TRUE( importerTest() ); } + +TEST_F( utIFCImportExport, importComplextypeAsColor ) { + std::string asset = + "ISO-10303-21;\n" + "HEADER;\n" + "FILE_DESCRIPTION( ( 'ViewDefinition [CoordinationView, SpaceBoundary2ndLevelAddOnView]', 'Option [Filter: ]' ), '2;1' );\n" + "FILE_NAME( 'S:\\[IFC]\\[COMPLETE-BUILDINGS]\\FZK-MODELS\\FZK-Haus\\ArchiCAD-14\\AC14-FZK-Haus.ifc', '2010-10-07T13:40:52', ( 'Architect' ), ( 'Building Designer Office' ), 'PreProc - EDM 5.0', 'ArchiCAD 14.00 Release 1. Windows Build Number of the Ifc 2x3 interface: 3427', 'The authorising person' );\n" + "FILE_SCHEMA( ( 'IFC2X3' ) );\n" + "ENDSEC;\n" + "\n" + "DATA;\n" + "#1 = IFCORGANIZATION( 'GS', 'Graphisoft', 'Graphisoft', $, $ );\n" + "#2 = IFCPROPERTYSINGLEVALUE( 'Red', $, IFCINTEGER( 255 ), $ );\n" + "#3 = IFCPROPERTYSINGLEVALUE( 'Green', $, IFCINTEGER( 255 ), $ );\n" + "#4 = IFCPROPERTYSINGLEVALUE( 'Blue', $, IFCINTEGER( 255 ), $ );\n" + "#5 = IFCCOMPLEXPROPERTY( 'Color', $, 'Color', ( #19, #20, #21 ) );\n"; + Assimp::Importer importer; + const aiScene *scene = importer.ReadFileFromMemory( asset.c_str(), asset.size(), 0 ); + EXPECT_EQ( nullptr, scene ); + +} diff --git a/test/unit/utMetadata.cpp b/test/unit/utMetadata.cpp index 7fda143b7..0801ffd3f 100644 --- a/test/unit/utMetadata.cpp +++ b/test/unit/utMetadata.cpp @@ -181,3 +181,74 @@ TEST_F( utMetadata, get_set_aiVector3D_Test ) { EXPECT_TRUE( success ); } +TEST_F( utMetadata, copy_test ) { + m_data = aiMetadata::Alloc( AI_META_MAX ); + bool bv = true; + m_data->Set( 0, "bool", bv ); + int32_t i32v = -10; + m_data->Set( 1, "int32", i32v ); + uint64_t ui64v = static_cast( 10 ); + m_data->Set( 2, "uint64", ui64v ); + float fv = 1.0f; + m_data->Set( 3, "float", fv ); + double dv = 2.0; + m_data->Set( 4, "double", dv ); + const aiString strVal( std::string( "test" ) ); + m_data->Set( 5, "aiString", strVal ); + aiVector3D vecVal( 1, 2, 3 ); + m_data->Set( 6, "aiVector3D", vecVal ); + + aiMetadata copy( *m_data ); + EXPECT_EQ( 7u, copy.mNumProperties ); + + // bool test + { + bool v; + EXPECT_TRUE( copy.Get( "bool", v ) ); + EXPECT_EQ( bv, v ); + } + + // int32_t test + { + int32_t v; + bool ok = copy.Get( "int32", v ); + EXPECT_TRUE( ok ); + EXPECT_EQ( i32v, v ); + } + + // uint64_t test + { + uint64_t v; + bool ok = copy.Get( "uint64", v ); + EXPECT_TRUE( ok ); + EXPECT_EQ( ui64v, v ); + } + + // float test + { + float v; + EXPECT_TRUE( copy.Get( "float", v ) ); + EXPECT_EQ( fv, v ); + } + + // double test + { + double v; + EXPECT_TRUE( copy.Get( "double", v ) ); + EXPECT_EQ( dv, v ); + } + + // bool test + { + aiString v; + EXPECT_TRUE( copy.Get( "aiString", v ) ); + EXPECT_EQ( strVal, v ); + } + + // bool test + { + aiVector3D v; + EXPECT_TRUE( copy.Get( "aiVector3D", v ) ); + EXPECT_EQ( vecVal, v ); + } +} diff --git a/test/unit/utObjImportExport.cpp b/test/unit/utObjImportExport.cpp index 5a2e42350..8aec9c443 100644 --- a/test/unit/utObjImportExport.cpp +++ b/test/unit/utObjImportExport.cpp @@ -354,3 +354,16 @@ TEST_F(utObjImportExport, 0based_array_Test) { const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), 0); EXPECT_EQ(nullptr, scene); } + +TEST_F( utObjImportExport, mtllib_after_g ) { + ::Assimp::Importer importer; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_mtllib_after_g.obj", aiProcess_ValidateDataStructure ); + ASSERT_NE( nullptr, scene ); + + EXPECT_EQ(scene->mNumMeshes, 1U); + const aiMesh *mesh = scene->mMeshes[0]; + const aiMaterial* mat = scene->mMaterials[mesh->mMaterialIndex]; + aiString name; + ASSERT_EQ(aiReturn_SUCCESS, mat->Get(AI_MATKEY_NAME, name)); + EXPECT_STREQ("MyMaterial", name.C_Str()); +} diff --git a/test/unit/utPLYImportExport.cpp b/test/unit/utPLYImportExport.cpp index fdd20008d..c009bda39 100644 --- a/test/unit/utPLYImportExport.cpp +++ b/test/unit/utPLYImportExport.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include "AbstractImportExportBase.h" +#include using namespace ::Assimp; @@ -52,7 +53,7 @@ class utPLYImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure); EXPECT_EQ( 1u, scene->mNumMeshes ); EXPECT_NE( nullptr, scene->mMeshes[0] ); EXPECT_EQ( 8u, scene->mMeshes[0]->mNumVertices ); @@ -65,7 +66,7 @@ public: virtual bool exporterTest() { Importer importer; Exporter exporter; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "ply", ASSIMP_TEST_MODELS_DIR "/PLY/cube_test.ply")); @@ -89,17 +90,90 @@ TEST_F(utPLYImportExport, exportTest_Success ) { //Test issue 1623, crash when loading two PLY files in a row TEST_F(utPLYImportExport, importerMultipleTest) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); - scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", 0); + scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube.ply", aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); + EXPECT_NE(nullptr, scene->mMeshes[0]); + EXPECT_EQ(6u, scene->mMeshes[0]->mNumFaces); +} + +TEST_F(utPLYImportExport, importPLYwithUV) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube_uv.ply", aiProcess_ValidateDataStructure); + + EXPECT_NE(nullptr, scene); + EXPECT_NE(nullptr, scene->mMeshes[0]); + //This test model is using n-gons, so 6 faces instead of 12 tris + EXPECT_EQ(6u, scene->mMeshes[0]->mNumFaces); + EXPECT_EQ(aiPrimitiveType_POLYGON, scene->mMeshes[0]->mPrimitiveTypes); + EXPECT_EQ(true, scene->mMeshes[0]->HasTextureCoords(0)); +} + +TEST_F(utPLYImportExport, importBinaryPLY) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/cube_binary.ply", aiProcess_ValidateDataStructure); + + EXPECT_NE(nullptr, scene); + EXPECT_NE(nullptr, scene->mMeshes[0]); + //This test model is double sided, so 12 faces instead of 6 + EXPECT_EQ(12u, scene->mMeshes[0]->mNumFaces); } TEST_F( utPLYImportExport, vertexColorTest ) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/float-color.ply", 0 ); + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/PLY/float-color.ply", aiProcess_ValidateDataStructure); + EXPECT_NE( nullptr, scene ); + EXPECT_EQ(1u, scene->mMeshes[0]->mNumFaces); + EXPECT_EQ(aiPrimitiveType_TRIANGLE, scene->mMeshes[0]->mPrimitiveTypes); + EXPECT_EQ(true, scene->mMeshes[0]->HasVertexColors(0)); + + auto first_face = scene->mMeshes[0]->mFaces[0]; + EXPECT_EQ(3u, first_face.mNumIndices); + EXPECT_EQ(0u, first_face.mIndices[0]); + EXPECT_EQ(1u, first_face.mIndices[1]); + EXPECT_EQ(2u, first_face.mIndices[2]); +} + +//Test issue #623, PLY importer should not automatically create faces +TEST_F(utPLYImportExport, pointcloudTest) { + Assimp::Importer importer; + //Could not use aiProcess_ValidateDataStructure since it's missing faces. + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/PLY/issue623.ply", 0); + EXPECT_NE(nullptr, scene); + + EXPECT_EQ(1u, scene->mNumMeshes); + EXPECT_NE(nullptr, scene->mMeshes[0]); + EXPECT_EQ(24u, scene->mMeshes[0]->mNumVertices); + EXPECT_EQ(aiPrimitiveType::aiPrimitiveType_POINT, scene->mMeshes[0]->mPrimitiveTypes); + EXPECT_EQ(0u, scene->mMeshes[0]->mNumFaces); +} + +static const char *test_file = + "ply\n" + "format ascii 1.0\n" + "element vertex 4\n" + "property float x\n" + "property float y\n" + "property float z\n" + "property uchar red\n" + "property uchar green\n" + "property uchar blue\n" + "property float nx\n" + "property float ny\n" + "property float nz\n" + "end_header\n" + "0.0 0.0 0.0 255 255 255 0.0 1.0 0.0\n" + "0.0 0.0 1.0 255 0 255 0.0 0.0 1.0\n" + "0.0 1.0 0.0 255 255 0 1.0 0.0 0.0\n" + "0.0 1.0 1.0 0 255 255 1.0 1.0 0.0\n"; + +TEST_F( utPLYImportExport, parseErrorTest ) { + Assimp::Importer importer; + //Could not use aiProcess_ValidateDataStructure since it's missing faces. + const aiScene *scene = importer.ReadFileFromMemory( test_file, strlen( test_file ), 0); EXPECT_NE( nullptr, scene ); } diff --git a/test/unit/utRemoveComponent.cpp b/test/unit/utRemoveComponent.cpp index 7905ff565..5fb9656d1 100644 --- a/test/unit/utRemoveComponent.cpp +++ b/test/unit/utRemoveComponent.cpp @@ -111,7 +111,7 @@ void RemoveVCProcessTest::SetUp() char check[sizeof(aiMaterial) == sizeof(aiMaterial) ? 10 : -1]; check[0] = 0; // to remove compiler warning - EXPECT_TRUE( check ); + EXPECT_EQ( 0, check[0] ); } // ------------------------------------------------------------------------------------------------ diff --git a/test/unit/utSimd.cpp b/test/unit/utSimd.cpp new file mode 100644 index 000000000..33358c0da --- /dev/null +++ b/test/unit/utSimd.cpp @@ -0,0 +1,62 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2018, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#include "UnitTestPCH.h" + +#include "simd.h" + +using namespace ::Assimp; + +class utSimd : public ::testing::Test { +protected: + // empty +}; + +TEST_F( utSimd, SSE2SupportedTest ) { + bool isSupported; + + isSupported = CPUSupportsSSE2(); + if ( isSupported ) { + std::cout << "Supported" << std::endl; + } else { + std::cout << "Not supported" << std::endl; + } +} diff --git a/test/unit/utXImporterExporter.cpp b/test/unit/utXImporterExporter.cpp index 770e2be47..d4c742e6f 100644 --- a/test/unit/utXImporterExporter.cpp +++ b/test/unit/utXImporterExporter.cpp @@ -62,3 +62,8 @@ public: TEST_F( utXImporterExporter, importXFromFileTest ) { EXPECT_TRUE( importerTest() ); } + +TEST_F( utXImporterExporter, heap_overflow_in_tokenizer ) { + Assimp::Importer importer; + EXPECT_NO_THROW( importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/X/OV_GetNextToken", 0 ) ); +} diff --git a/tools/assimp_cmd/Info.cpp b/tools/assimp_cmd/Info.cpp index e19746db6..ae858bfe8 100644 --- a/tools/assimp_cmd/Info.cpp +++ b/tools/assimp_cmd/Info.cpp @@ -46,10 +46,30 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Main.h" +#include +#include +#include + const char* AICMD_MSG_INFO_HELP_E = -"assimp info [-r]\n" +"assimp info [-r] [-v]\n" "\tPrint basic structure of a 3D model\n" -"\t-r,--raw: No postprocessing, do a raw import\n"; +"\t-r,--raw: No postprocessing, do a raw import\n" +"\t-v,--verbose: Print verbose info such as node transform data\n"; + +const std::string TREE_BRANCH_ASCII = "|-"; +const std::string TREE_BRANCH_UTF8 = "\xe2\x94\x9c\xe2\x95\xb4"; +const std::string TREE_STOP_ASCII = "'-"; +const std::string TREE_STOP_UTF8 = "\xe2\x94\x94\xe2\x95\xb4"; +const std::string TREE_CONTINUE_ASCII = "| "; +const std::string TREE_CONTINUE_UTF8 = "\xe2\x94\x82 "; + +// note: by default this is outputing utf-8 text. +// this is well supported on pretty much any linux terminal. +// if this causes problems on some platform, +// put an #ifdef to use the ascii version for that platform. +const std::string TREE_BRANCH = TREE_BRANCH_UTF8; +const std::string TREE_STOP = TREE_STOP_UTF8; +const std::string TREE_CONTINUE = TREE_CONTINUE_UTF8; // ----------------------------------------------------------------------------------- @@ -183,25 +203,77 @@ std::string FindPTypes(const aiScene* scene) } // ----------------------------------------------------------------------------------- -void PrintHierarchy(const aiNode* root, unsigned int maxnest, unsigned int maxline, - unsigned int cline, unsigned int cnest=0) -{ - if (cline++ >= maxline || cnest >= maxnest) { - return; +// Prettily print the node graph to stdout +void PrintHierarchy( + const aiNode* node, + const std::string &indent, + bool verbose, + bool last = false, + bool first = true +){ + // tree visualization + std::string branchchar; + if (first) { branchchar = ""; } + else if (last) { branchchar = TREE_STOP; } // "'-" + else { branchchar = TREE_BRANCH; } // "|-" + + // print the indent and the branch character and the name + std::cout << indent << branchchar << node->mName.C_Str(); + + // if there are meshes attached, indicate this + if (node->mNumMeshes) { + std::cout << " (mesh "; + bool sep = false; + for (size_t i=0; i < node->mNumMeshes; ++i) { + unsigned int mesh_index = node->mMeshes[i]; + if (sep) { std::cout << ", "; } + std::cout << mesh_index; + sep = true; + } + std::cout << ")"; } - for(unsigned int i = 0; i < cnest; ++i) { - printf("-- "); - } - printf("\'%s\', meshes: %u\n",root->mName.data,root->mNumMeshes); - for (unsigned int i = 0; i < root->mNumChildren; ++i ) { - PrintHierarchy(root->mChildren[i],maxnest,maxline,cline,cnest+1); - if(i == root->mNumChildren-1) { - for(unsigned int i = 0; i < cnest; ++i) { - printf(" "); - } - printf("<--\n"); + // finish the line + std::cout << std::endl; + + // in verbose mode, print the transform data as well + if (verbose) { + // indent to use + std::string indentadd; + if (last) { indentadd += " "; } + else { indentadd += TREE_CONTINUE; } // "| ".. + if (node->mNumChildren == 0) { indentadd += " "; } + else { indentadd += TREE_CONTINUE; } // .."| " + aiVector3D s, r, t; + node->mTransformation.Decompose(s, r, t); + if (s.x != 1.0 || s.y != 1.0 || s.z != 1.0) { + std::cout << indent << indentadd; + printf(" S:[%f %f %f]\n", s.x, s.y, s.z); } + if (r.x || r.y || r.z) { + std::cout << indent << indentadd; + printf(" R:[%f %f %f]\n", r.x, r.y, r.z); + } + if (t.x || t.y || t.z) { + std::cout << indent << indentadd; + printf(" T:[%f %f %f]\n", t.x, t.y, t.z); + } + } + + // and recurse + std::string nextIndent; + if (first) { nextIndent = indent; } + else if (last) { nextIndent = indent + " "; } + else { nextIndent = indent + TREE_CONTINUE; } // "| " + for (size_t i = 0; i < node->mNumChildren; ++i) { + bool lastone = (i == node->mNumChildren - 1); + PrintHierarchy( + node->mChildren[i], + nextIndent, + verbose, + lastone, + false + ); } } @@ -230,10 +302,23 @@ int Assimp_Info (const char* const* params, unsigned int num) const std::string in = std::string(params[0]); + // get -r and -v arguments + bool raw = false; + bool verbose = false; + for(unsigned int i = 1; i < num; ++i) { + if (!strcmp(params[i],"--raw")||!strcmp(params[i],"-r")) { + raw = true; + } + if (!strcmp(params[i],"--verbose")||!strcmp(params[i],"-v")) { + verbose = true; + } + } + // do maximum post-processing unless -r was specified ImportData import; - import.ppFlags = num>1&&(!strcmp(params[1],"--raw")||!strcmp(params[1],"-r")) ? 0 - : aiProcessPreset_TargetRealtime_MaxQuality; + if (!raw) { + import.ppFlags = aiProcessPreset_TargetRealtime_MaxQuality; + } // import the main model const aiScene* scene = ImportModel(import,in); @@ -294,6 +379,29 @@ int Assimp_Info (const char* const* params, unsigned int num) special_points[2][0],special_points[2][1],special_points[2][2] ) ; + + // meshes + if (scene->mNumMeshes) { + printf("\nMeshes: (name) [vertices / bones / faces | primitive_types]\n"); + } + for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { + const aiMesh* mesh = scene->mMeshes[i]; + printf(" %d (%s)", i, mesh->mName.C_Str()); + printf( + ": [%d / %d / %d |", + mesh->mNumVertices, + mesh->mNumBones, + mesh->mNumFaces + ); + const unsigned int ptypes = mesh->mPrimitiveTypes; + if (ptypes & aiPrimitiveType_POINT) { printf(" point"); } + if (ptypes & aiPrimitiveType_LINE) { printf(" line"); } + if (ptypes & aiPrimitiveType_TRIANGLE) { printf(" triangle"); } + if (ptypes & aiPrimitiveType_POLYGON) { printf(" polygon"); } + printf("]\n"); + } + + // materials unsigned int total=0; for(unsigned int i = 0;i < scene->mNumMaterials; ++i) { aiString name; @@ -305,6 +413,7 @@ int Assimp_Info (const char* const* params, unsigned int num) printf("\n"); } + // textures total=0; for(unsigned int i = 0;i < scene->mNumMaterials; ++i) { aiString name; @@ -334,6 +443,7 @@ int Assimp_Info (const char* const* params, unsigned int num) printf("\n"); } + // animations total=0; for(unsigned int i = 0;i < scene->mNumAnimations; ++i) { if (scene->mAnimations[i]->mName.length) { @@ -344,9 +454,9 @@ int Assimp_Info (const char* const* params, unsigned int num) printf("\n"); } + // node hierarchy printf("\nNode hierarchy:\n"); - unsigned int cline=0; - PrintHierarchy(scene->mRootNode,20,1000,cline); + PrintHierarchy(scene->mRootNode,"",verbose); printf("\n"); return 0; diff --git a/tools/assimp_cmd/Main.cpp b/tools/assimp_cmd/Main.cpp index 1b4e5db45..14d00e1e1 100644 --- a/tools/assimp_cmd/Main.cpp +++ b/tools/assimp_cmd/Main.cpp @@ -334,7 +334,8 @@ bool ExportModel(const aiScene* pOut, PrintHorBar(); } if (res != AI_SUCCESS) { - printf("ERROR: Failed to write file\n"); + printf("Failed to write file\n"); + printf("ERROR: %s\n", globalExporter->GetErrorString()); return false; } diff --git a/tools/assimp_qt_viewer/glview.cpp b/tools/assimp_qt_viewer/glview.cpp index 5b755d456..a9a60a6fc 100644 --- a/tools/assimp_qt_viewer/glview.cpp +++ b/tools/assimp_qt_viewer/glview.cpp @@ -560,6 +560,30 @@ void CGLView::Enable_Textures(const bool pEnable) } } +void CGLView::Enable_Axes(const bool pEnable){ + if(pEnable) + { + this->mAxesEnabled = true; + } + else + { + this->mAxesEnabled = false; + } +} + +void CGLView::Enable_Reload_Textures(const bool pEnable) +{ + if(pEnable) + { + this->mReloadTexturesEnabled = true; +// this->mScene->ImportTextures(this->mScene->pScenePath); + } + else + { + this->mReloadTexturesEnabled = false; + } +} + /********************************************************************/ /*********************** Override functions ************************/ /********************************************************************/ @@ -609,6 +633,7 @@ void CGLView::drawCoordSystem() { // Z, -Z qglColor(QColor(Qt::blue)), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 0.0, 100000.0); qglColor(QColor(Qt::yellow)), glVertex3f(0.0, 0.0, 0.0), glVertex3f(0.0, 0.0, -100000.0); + qglColor(QColor(Qt::white)); glEnd(); } @@ -629,7 +654,10 @@ void CGLView::paintGL() if ( mLightingEnabled ) { glDisable( GL_LIGHTING );///TODO: display list } - drawCoordSystem(); + if (this->mAxesEnabled == true) + { + drawCoordSystem(); + } glDisable(GL_COLOR_MATERIAL); if(mLightingEnabled) glEnable(GL_LIGHTING); diff --git a/tools/assimp_qt_viewer/glview.hpp b/tools/assimp_qt_viewer/glview.hpp index ecd7c6b0f..1c397f13f 100644 --- a/tools/assimp_qt_viewer/glview.hpp +++ b/tools/assimp_qt_viewer/glview.hpp @@ -75,7 +75,9 @@ private: }; public: - + bool mAxesEnabled = true; + // Textures + bool mReloadTexturesEnabled = false; // If true then textures will reload when the window is activated. /// \enum ELightType /// Type of light source. enum class ELightType { Directional, Point, Spot }; @@ -155,7 +157,6 @@ private: GLdouble mCamera_Viewport_AspectRatio;///< Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height). // Lighting bool mLightingEnabled = false;///< If true then OpenGL lighting is enabled (glEnable(GL_LIGHTING)), if false - disabled. - // Textures ///TODO: map is goooood, but not for case when one image can be used in different materials with difference in: texture transformation, targeting of the /// texture (ambient or emission, or even height map), texture properties. QMap mTexture_IDMap;///< Map image filenames to textures ID's. @@ -306,6 +307,12 @@ public: /// \param [in] pEnable - if true then enable textures, false - disable textures. void Enable_Textures(const bool pEnable); + void Enable_Axes(const bool pEnable); + /// \fn void Enable_Textures(const bool pEnable) + /// Control textures drawing. + /// \param [in] pEnable - if true then enable textures, false - disable textures. + void Enable_Reload_Textures(const bool pEnable); + /********************************************************************/ /******************** Lighting control functions ********************/ /********************************************************************/ diff --git a/tools/assimp_qt_viewer/mainwindow.cpp b/tools/assimp_qt_viewer/mainwindow.cpp index acfc8e589..d7d809086 100644 --- a/tools/assimp_qt_viewer/mainwindow.cpp +++ b/tools/assimp_qt_viewer/mainwindow.cpp @@ -48,6 +48,7 @@ QTime time_begin = QTime::currentTime(); ui->cbxLighting->setChecked(true); mGLView->Lighting_Enable(); ui->cbxBBox->setChecked(false); mGLView->Enable_SceneBBox(false); ui->cbxTextures->setChecked(true); mGLView->Enable_Textures(true); + ui->cbxReloadTextures->setChecked(true); mGLView->Enable_Reload_Textures(false); // // Fill info labels // @@ -194,6 +195,13 @@ GLfloat step; /********************************************************************/ /********************** Constructor/Destructor **********************/ /********************************************************************/ +bool MainWindow::event(QEvent *e) +{ + if (e->type() == QEvent::WindowActivate && this->mGLView->mReloadTexturesEnabled == true) { + qInfo() << "Window Activated"; + } + return QWidget::event(e); +} MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), @@ -364,6 +372,18 @@ void MainWindow::on_cbxBBox_clicked(bool checked) mGLView->updateGL(); } +void MainWindow::on_cbxDrawAxes_clicked(bool checked) +{ + mGLView->Enable_Axes(checked); + mGLView->updateGL(); +} + +void MainWindow::on_cbxReloadTextures_clicked(bool checked) +{ + mGLView->Enable_Reload_Textures(checked); + mGLView->updateGL(); +} + void MainWindow::on_cbxTextures_clicked(bool checked) { mGLView->Enable_Textures(checked); diff --git a/tools/assimp_qt_viewer/mainwindow.hpp b/tools/assimp_qt_viewer/mainwindow.hpp index 350b37abf..da8a852ac 100644 --- a/tools/assimp_qt_viewer/mainwindow.hpp +++ b/tools/assimp_qt_viewer/mainwindow.hpp @@ -90,7 +90,7 @@ protected: /// \param [in] pEvent - pointer to event data. void keyPressEvent(QKeyEvent* pEvent) override; - + bool event(QEvent*); public: /********************************************************************/ @@ -133,4 +133,6 @@ private slots: void on_lstCamera_clicked(const QModelIndex &index); void on_cbxBBox_clicked(bool checked); void on_cbxTextures_clicked(bool checked); + void on_cbxDrawAxes_clicked(bool checked); + void on_cbxReloadTextures_clicked(bool checked); }; diff --git a/tools/assimp_qt_viewer/mainwindow.ui b/tools/assimp_qt_viewer/mainwindow.ui index 105a470e0..9b139bafd 100644 --- a/tools/assimp_qt_viewer/mainwindow.ui +++ b/tools/assimp_qt_viewer/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 641 - 734 + 778 @@ -501,6 +501,23 @@ + + + + Show Axes + + + true + + + + + + + Live Reload Textures + + + @@ -513,4 +530,7 @@ + + installEventFilter() +