diff --git a/CMakeLists.txt b/CMakeLists.txt index 0026dcb7f..83e1be081 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,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 @@ -161,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) @@ -179,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" ) @@ -192,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 ) @@ -212,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" ) @@ -229,39 +220,39 @@ ELSEIF( CMAKE_COMPILER_IS_MINGW ) ADD_DEFINITIONS( -U__STRICT_ANSI__ ) 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 (IOS) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3") +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 (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) +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) @@ -294,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) @@ -371,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") @@ -412,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 ) @@ -476,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}" ) @@ -507,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) @@ -520,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) @@ -548,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/Readme.md b/Readme.md index 4ee8a0e90..c78a0c1c8 100644 --- a/Readme.md +++ b/Readme.md @@ -35,29 +35,29 @@ Please check our Wiki as well: https://github.com/assimp/assimp/wiki __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__: 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/3DSLoader.cpp b/code/3DSLoader.cpp index 92a64e3d8..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" }; @@ -127,7 +127,7 @@ Discreet3DSImporter::~Discreet3DSImporter() { // Returns whether the class can handle the format of the given file. bool Discreet3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { std::string extension = GetExtension(pFile); - if(extension == "3ds" || extension == "prj" ) { + if(extension == "3ds" || extension == "3DS" || extension == "prj"|| extension == "PRJ" ) { return true; } diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 7ae1a7e63..d5d05db18 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -186,6 +186,8 @@ SET( Common_SRCS Bitmap.cpp Version.cpp CreateAnimMesh.cpp + simd.h + simd.cpp ) SOURCE_GROUP(Common FILES ${Common_SRCS}) 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/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/Exporter.cpp b/code/Exporter.cpp index 49523e658..b7478c0be 100644 --- a/code/Exporter.cpp +++ b/code/Exporter.cpp @@ -98,7 +98,7 @@ void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportPrope 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 ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* ); // ------------------------------------------------------------------------------------------------ @@ -173,7 +173,7 @@ Exporter::ExportFormatEntry gExporters[] = #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ), - //Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ), + Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ), #endif #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index f3a908dd4..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,9 +135,7 @@ 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; @@ -153,14 +153,14 @@ 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(); @@ -174,7 +174,7 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa 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 @@ -189,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 @@ -250,11 +258,11 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa 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() ); @@ -278,34 +286,31 @@ void Converter::ConvertNodes( uint64_t id, aiNode& parent, const aiMatrix4x4& pa } -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(); @@ -378,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(); @@ -397,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 ) { @@ -738,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 @@ -786,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; @@ -2005,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 ) diff --git a/code/FBXConverter.h b/code/FBXConverter.h index 26ba3cb4e..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 @@ -117,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 - @@ -258,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 @@ -281,7 +274,6 @@ private: // XXX: better use multi_map .. typedef std::map > NodeMap; - // ------------------------------------------------------------------------------------------------ void ConvertAnimationStack(const AnimationStack& st); @@ -432,19 +424,11 @@ 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; const FBX::Document& doc; - - std::vector mLightNames; }; } diff --git a/code/FBXExportNode.cpp b/code/FBXExportNode.cpp index 596901989..a1171eca8 100644 --- a/code/FBXExportNode.cpp +++ b/code/FBXExportNode.cpp @@ -45,9 +45,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #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, @@ -145,33 +149,174 @@ void FBX::Node::AddP70time( } +// 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::Dump(std::shared_ptr outfile) -{ - Assimp::StreamWriterLE outstream(outfile); - Dump(outstream); -} - -void FBX::Node::Dump(Assimp::StreamWriterLE &s) +void FBX::Node::DumpBinary(Assimp::StreamWriterLE &s) { // write header section (with placeholders for some things) - Begin(s); + BeginBinary(s); // write properties - DumpProperties(s); + DumpPropertiesBinary(s); // go back and fill in property related placeholders - EndProperties(s, properties.size()); + EndPropertiesBinary(s, properties.size()); // write children - DumpChildren(s); + DumpChildrenBinary(s); // finish, filling in end offset placeholder - End(s, !children.empty()); + EndBinary(s, force_has_children || !children.empty()); } -void FBX::Node::Begin(Assimp::StreamWriterLE &s) + +// 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(); @@ -189,26 +334,14 @@ void FBX::Node::Begin(Assimp::StreamWriterLE &s) this->property_start = s.Tell(); } -void FBX::Node::DumpProperties(Assimp::StreamWriterLE& s) +void FBX::Node::DumpPropertiesBinary(Assimp::StreamWriterLE& s) { for (auto &p : properties) { - p.Dump(s); + p.DumpBinary(s); } } -void FBX::Node::DumpChildren(Assimp::StreamWriterLE& s) -{ - for (FBX::Node& child : children) { - child.Dump(s); - } -} - -void FBX::Node::EndProperties(Assimp::StreamWriterLE &s) -{ - EndProperties(s, properties.size()); -} - -void FBX::Node::EndProperties( +void FBX::Node::EndPropertiesBinary( Assimp::StreamWriterLE &s, size_t num_properties ) { @@ -222,7 +355,14 @@ void FBX::Node::EndProperties( s.Seek(pos); } -void FBX::Node::End( +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 ) { @@ -237,48 +377,192 @@ void FBX::Node::End( } -// static member functions +void FBX::Node::BeginAscii(std::ostream& s, int indent) +{ + s << '\n'; + for (int i = 0; i < indent; ++i) { s << '\t'; } + s << name << ": "; +} -// 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::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::WritePropertyNode( +void FBX::Node::WritePropertyNodeBinary( const std::string& name, const std::vector& v, Assimp::StreamWriterLE& s ){ - Node node(name); - node.Begin(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.EndProperties(s, 1); - node.End(s, false); + node.EndPropertiesBinary(s, 1); + node.EndBinary(s, false); } -// 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. +// binary property node from vector of int32_t // TODO: optional zip compression! -void FBX::Node::WritePropertyNode( +void FBX::Node::WritePropertyNodeBinary( const std::string& name, const std::vector& v, Assimp::StreamWriterLE& s ){ - Node node(name); - node.Begin(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.EndProperties(s, 1); - node.End(s, false); + 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 index edce8f700..5ddd8c77b 100644 --- a/code/FBXExportNode.h +++ b/code/FBXExportNode.h @@ -66,14 +66,18 @@ public: // public data members 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) {} - Node(const std::string& n, const FBX::Property &p) + + // convenience template to construct with properties directly + template + Node(const std::string& n, const More... more) : name(n) - { properties.push_back(p); } - Node(const std::string& n, const std::vector &pv) - : name(n), properties(pv) {} + { AddProperties(more...); } public: // functions to add properties or children // add a single property to the node @@ -138,19 +142,48 @@ public: // support specifically for dealing with Properties70 nodes public: // member functions for writing data to a file or stream - // write the full node as binary data to the given file or stream - void Dump(std::shared_ptr outfile); - void Dump(Assimp::StreamWriterLE &s); + // 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); - void DumpProperties(Assimp::StreamWriterLE& s); - void EndProperties(Assimp::StreamWriterLE &s); - void EndProperties(Assimp::StreamWriterLE &s, size_t num_properties); - void DumpChildren(Assimp::StreamWriterLE& s); - void End(Assimp::StreamWriterLE &s, bool has_children); + 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 @@ -165,11 +198,12 @@ public: // static member functions static void WritePropertyNode( const std::string& name, const T value, - Assimp::StreamWriterLE& s + Assimp::StreamWriterLE& s, + bool binary, int indent ) { FBX::Property p(value); FBX::Node node(name, p); - node.Dump(s); + node.Dump(s, binary, indent); } // convenience function to create and write a property node, @@ -178,7 +212,8 @@ public: // static member functions static void WritePropertyNode( const std::string& name, const std::vector& v, - Assimp::StreamWriterLE& s + Assimp::StreamWriterLE& s, + bool binary, int indent ); // convenience function to create and write a property node, @@ -187,8 +222,34 @@ public: // static member functions 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 + ); + }; diff --git a/code/FBXExportProperty.cpp b/code/FBXExportProperty.cpp index e139bb95a..431750274 100644 --- a/code/FBXExportProperty.cpp +++ b/code/FBXExportProperty.cpp @@ -48,7 +48,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include // stringstream +#include +#include +#include // ostringstream // constructors for single element properties @@ -164,18 +166,18 @@ size_t FBX::Property::size() } } -void FBX::Property::Dump(Assimp::StreamWriterLE &s) +void FBX::Property::DumpBinary(Assimp::StreamWriterLE &s) { s.PutU1(type); - uint8_t* d; + uint8_t* d = data.data(); size_t N; switch (type) { - case 'C': s.PutU1(*(reinterpret_cast(data.data()))); return; - case 'Y': s.PutI2(*(reinterpret_cast(data.data()))); return; - case 'I': s.PutI4(*(reinterpret_cast(data.data()))); return; - case 'F': s.PutF4(*(reinterpret_cast(data.data()))); return; - case 'D': s.PutF8(*(reinterpret_cast(data.data()))); return; - case 'L': s.PutI8(*(reinterpret_cast(data.data()))); return; + 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())); @@ -187,7 +189,6 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s) s.PutU4(0); // no encoding (1 would be zip-compressed) // TODO: compress if large? s.PutU4(uint32_t(data.size())); // data size - d = data.data(); for (size_t i = 0; i < N; ++i) { s.PutI4((reinterpret_cast(d))[i]); } @@ -198,7 +199,6 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s) s.PutU4(0); // no encoding (1 would be zip-compressed) // TODO: compress if large? s.PutU4(uint32_t(data.size())); // data size - d = data.data(); for (size_t i = 0; i < N; ++i) { s.PutI8((reinterpret_cast(d))[i]); } @@ -209,7 +209,6 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s) s.PutU4(0); // no encoding (1 would be zip-compressed) // TODO: compress if large? s.PutU4(uint32_t(data.size())); // data size - d = data.data(); for (size_t i = 0; i < N; ++i) { s.PutF4((reinterpret_cast(d))[i]); } @@ -220,18 +219,146 @@ void FBX::Property::Dump(Assimp::StreamWriterLE &s) s.PutU4(0); // no encoding (1 would be zip-compressed) // TODO: compress if large? s.PutU4(uint32_t(data.size())); // data size - d = data.data(); for (size_t i = 0; i < N; ++i) { s.PutF8((reinterpret_cast(d))[i]); } return; default: - std::stringstream err; + 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 index 40a020688..cb3b0113f 100644 --- a/code/FBXExportProperty.h +++ b/code/FBXExportProperty.h @@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include // is_void namespace FBX { @@ -113,7 +114,10 @@ public: size_t size(); // write this property node as binary data to the given stream - void Dump(Assimp::StreamWriterLE &s); + 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; diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 7dcf752da..c524e3911 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -66,7 +66,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include // endl // RESOURCES: // https://code.blender.org/2013/08/fbx-binary-file-format-specification/ @@ -89,6 +88,8 @@ namespace FBX { "\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; @@ -115,7 +116,7 @@ namespace Assimp { // --------------------------------------------------------------------- // Worker function for exporting a scene to ASCII FBX. // Prototyped and registered in Exporter.cpp - /*void ExportSceneFBXA ( + void ExportSceneFBXA ( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, @@ -126,7 +127,7 @@ namespace Assimp { // perform ascii export exporter.ExportAscii(pFile, pIOSystem); - }*/ // TODO + } } // end of namespace Assimp @@ -194,27 +195,43 @@ void FBXExporter::ExportAscii ( ); } - // this isn't really necessary, - // but the Autodesk FBX SDK puts a similar comment at the top of the file. - // Theirs declares that the file copyright is owned by Autodesk... - std::stringstream head; - using std::endl; - head << "; FBX " << EXPORT_VERSION_STR << " project file" << endl; - head << "; Created by the Open Asset Import Library (Assimp)" << endl; - head << "; http://assimp.org" << endl; - head << "; -------------------------------------------------" << endl; - head << endl; - const std::string ascii_header = head.str(); - outfile->Write(ascii_header.c_str(), ascii_header.size(), 1); + // 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 @@ -294,28 +311,39 @@ void FBXExporter::WriteAllNodes () //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); + n.Begin(outstream, binary, indent); // write properties // (none) // finish properties - n.EndProperties(outstream, 0); + 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 + "FBXHeaderVersion", int32_t(1003), outstream, binary, indent ); FBX::Node::WritePropertyNode( - "FBXVersion", int32_t(EXPORT_VERSION_INT), outstream - ); - FBX::Node::WritePropertyNode( - "EncryptionType", int32_t(0), outstream + "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; @@ -329,36 +357,50 @@ void FBXExporter::WriteHeaderExtension () 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); + CreationTimeStamp.Dump(outstream, binary, indent); std::stringstream creator; creator << "Open Asset Import Library (Assimp) " << aiGetVersionMajor() << "." << aiGetVersionMinor() << "." << aiGetVersionRevision(); - FBX::Node::WritePropertyNode("Creator", creator.str(), outstream); + FBX::Node::WritePropertyNode( + "Creator", creator.str(), outstream, binary, indent + ); - FBX::Node sceneinfo("SceneInfo"); + //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); + //sceneinfo.Dump(outstream, binary, indent); + + indent = 0; // finish node - n.End(outstream, true); + 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); - FBX::Node::WritePropertyNode("CreationTime", GENERIC_CTIME, outstream); - FBX::Node::WritePropertyNode("Creator", creator.str(), outstream); + 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)); @@ -385,11 +427,15 @@ void FBXExporter::WriteGlobalSettings () p.AddP70int("CurrentTimeMarker", -1); gs.AddChild(p); - gs.Dump(outfile); + 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"); @@ -411,15 +457,19 @@ void FBXExporter::WriteDocuments () doc.AddChild("RootNode", int64_t(0)); docs.AddChild(doc); - docs.Dump(outfile); + 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.Dump(outfile); + n.force_has_children = true; + n.Dump(outfile, binary, 0); } @@ -468,9 +518,6 @@ size_t count_images(const aiScene* scene) { } } } - //for (auto &s : images) { - // std::cout << "found image: " << s << std::endl; - //} return images.size(); } @@ -510,6 +557,11 @@ void FBXExporter::WriteDefinitions () // 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; @@ -520,7 +572,7 @@ void FBXExporter::WriteDefinitions () // GlobalSettings // this seems to always be here in Maya exports - n = FBX::Node("ObjectType", Property("GlobalSettings")); + n = FBX::Node("ObjectType", "GlobalSettings"); count = 1; n.AddChild("Count", count); object_nodes.push_back(n); @@ -531,9 +583,9 @@ void FBXExporter::WriteDefinitions () // but no harm seems to come of leaving it out. count = mScene->mNumAnimations; if (count) { - n = FBX::Node("ObjectType", Property("AnimationStack")); + n = FBX::Node("ObjectType", "AnimationStack"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxAnimStack")); + pt = FBX::Node("PropertyTemplate", "FbxAnimStack"); p = FBX::Node("Properties70"); p.AddP70string("Description", ""); p.AddP70time("LocalStart", 0); @@ -553,9 +605,9 @@ void FBXExporter::WriteDefinitions () // so there will be one per aiAnimation count = mScene->mNumAnimations; if (count) { - n = FBX::Node("ObjectType", Property("AnimationLayer")); + n = FBX::Node("ObjectType", "AnimationLayer"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FBXAnimLayer")); + pt = FBX::Node("PropertyTemplate", "FBXAnimLayer"); p = FBX::Node("Properties70"); p.AddP70("Weight", "Number", "", "A", double(100)); p.AddP70bool("Mute", 0); @@ -583,9 +635,9 @@ void FBXExporter::WriteDefinitions () count = 1; // TODO: select properly if (count) { // FbxSkeleton - n = FBX::Node("ObjectType", Property("NodeAttribute")); + n = FBX::Node("ObjectType", "NodeAttribute"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxSkeleton")); + pt = FBX::Node("PropertyTemplate", "FbxSkeleton"); p = FBX::Node("Properties70"); p.AddP70color("Color", 0.8, 0.8, 0.8); p.AddP70double("Size", 33.333333333333); @@ -601,9 +653,9 @@ void FBXExporter::WriteDefinitions () // <~~ node heirarchy count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) if (count) { - n = FBX::Node("ObjectType", Property("Model")); + n = FBX::Node("ObjectType", "Model"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxNode")); + pt = FBX::Node("PropertyTemplate", "FbxNode"); p = FBX::Node("Properties70"); p.AddP70enum("QuaternionInterpolate", 0); p.AddP70vector("RotationOffset", 0.0, 0.0, 0.0); @@ -698,9 +750,9 @@ void FBXExporter::WriteDefinitions () // <~~ aiMesh count = mScene->mNumMeshes; if (count) { - n = FBX::Node("ObjectType", Property("Geometry")); + n = FBX::Node("ObjectType", "Geometry"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxMesh")); + pt = FBX::Node("PropertyTemplate", "FbxMesh"); p = FBX::Node("Properties70"); p.AddP70color("Color", 0, 0, 0); p.AddP70vector("BBoxMin", 0, 0, 0); @@ -724,7 +776,7 @@ void FBXExporter::WriteDefinitions () count = mScene->mNumMaterials; if (count) { bool has_phong = has_phong_mat(mScene); - n = FBX::Node("ObjectType", Property("Material")); + n = FBX::Node("ObjectType", "Material"); n.AddChild("Count", count); pt = FBX::Node("PropertyTemplate"); if (has_phong) { @@ -771,9 +823,9 @@ void FBXExporter::WriteDefinitions () // one for each image file. count = int32_t(count_images(mScene)); if (count) { - n = FBX::Node("ObjectType", Property("Video")); + n = FBX::Node("ObjectType", "Video"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxVideo")); + pt = FBX::Node("PropertyTemplate", "FbxVideo"); p = FBX::Node("Properties70"); p.AddP70bool("ImageSequence", 0); p.AddP70int("ImageSequenceOffset", 0); @@ -800,9 +852,9 @@ void FBXExporter::WriteDefinitions () // <~~ aiTexture count = int32_t(count_textures(mScene)); if (count) { - n = FBX::Node("ObjectType", Property("Texture")); + n = FBX::Node("ObjectType", "Texture"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxFileTexture")); + pt = FBX::Node("PropertyTemplate", "FbxFileTexture"); p = FBX::Node("Properties70"); p.AddP70enum("TextureTypeUse", 0); p.AddP70numberA("Texture alpha", 1.0); @@ -829,9 +881,9 @@ void FBXExporter::WriteDefinitions () // AnimationCurveNode / FbxAnimCurveNode count = mScene->mNumAnimations * 3; if (count) { - n = FBX::Node("ObjectType", Property("AnimationCurveNode")); + n = FBX::Node("ObjectType", "AnimationCurveNode"); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("FbxAnimCurveNode")); + pt = FBX::Node("PropertyTemplate", "FbxAnimCurveNode"); p = FBX::Node("Properties70"); p.AddP70("d", "Compound", "", ""); pt.AddChild(p); @@ -843,7 +895,7 @@ void FBXExporter::WriteDefinitions () // AnimationCurve / FbxAnimCurve count = mScene->mNumAnimations * 9; if (count) { - n = FBX::Node("ObjectType", Property("AnimationCurve")); + n = FBX::Node("ObjectType", "AnimationCurve"); n.AddChild("Count", count); object_nodes.push_back(n); total_count += count; @@ -856,7 +908,7 @@ void FBXExporter::WriteDefinitions () if (mesh->HasBones()) { ++count; } } if (count) { - n = FBX::Node("ObjectType", Property("Pose")); + n = FBX::Node("ObjectType", "Pose"); n.AddChild("Count", count); object_nodes.push_back(n); total_count += count; @@ -865,7 +917,7 @@ void FBXExporter::WriteDefinitions () // Deformer count = int32_t(count_deformers(mScene)); if (count) { - n = FBX::Node("ObjectType", Property("Deformer")); + n = FBX::Node("ObjectType", "Deformer"); n.AddChild("Count", count); object_nodes.push_back(n); total_count += count; @@ -874,9 +926,9 @@ void FBXExporter::WriteDefinitions () // (template) count = 0; if (count) { - n = FBX::Node("ObjectType", Property("")); + n = FBX::Node("ObjectType", ""); n.AddChild("Count", count); - pt = FBX::Node("PropertyTemplate", Property("")); + pt = FBX::Node("PropertyTemplate", ""); p = FBX::Node("Properties70"); pt.AddChild(p); n.AddChild(pt); @@ -889,7 +941,7 @@ void FBXExporter::WriteDefinitions () defs.AddChild("Version", int32_t(100)); defs.AddChild("Count", int32_t(total_count)); for (auto &n : object_nodes) { defs.AddChild(n); } - defs.Dump(outfile); + defs.Dump(outfile, binary, 0); } @@ -935,14 +987,20 @@ int64_t to_ktime(double ticks, const aiAnimation* anim) { 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"); - object_node.Begin(outstream); - object_node.EndProperties(outstream); + 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]; @@ -954,9 +1012,11 @@ void FBXExporter::WriteObjects () n.AddProperty(uid); n.AddProperty(FBX::SEPARATOR + "Geometry"); n.AddProperty("Mesh"); - n.Begin(outstream); - n.DumpProperties(outstream); - n.EndProperties(outstream); + 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; @@ -980,7 +1040,7 @@ void FBXExporter::WriteObjects () } } FBX::Node::WritePropertyNode( - "Vertices", flattened_vertices, outstream + "Vertices", flattened_vertices, outstream, binary, indent ); // output polygon data as a flattened array of vertex indices. @@ -996,30 +1056,38 @@ void FBXExporter::WriteObjects () ); } FBX::Node::WritePropertyNode( - "PolygonVertexIndex", polygon_data, outstream + "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 + "GeometryVersion", int32_t(124), outstream, binary, indent ); // normals, if any if (m->HasNormals()) { - FBX::Node normals("LayerElementNormal", Property(int32_t(0))); - normals.Begin(outstream); - normals.DumpProperties(outstream); - normals.EndProperties(outstream); - FBX::Node::WritePropertyNode("Version", int32_t(101), outstream); - FBX::Node::WritePropertyNode("Name", "", outstream); + 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( - "MappingInformationType", "ByPolygonVertex", outstream + "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 + "ReferenceInformationType", "Direct", + outstream, binary, indent ); std::vector normal_data; normal_data.reserve(3 * polygon_data.size()); @@ -1032,10 +1100,13 @@ void FBXExporter::WriteObjects () normal_data.push_back(n.z); } } - FBX::Node::WritePropertyNode("Normals", normal_data, outstream); + 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. - normals.End(outstream, true); + indent = 2; + normals.End(outstream, binary, indent, true); } // uvs, if any @@ -1055,19 +1126,27 @@ void FBXExporter::WriteObjects () err << " but may be incorrectly interpreted on load."; DefaultLogger::get()->warn(err.str()); } - FBX::Node uv("LayerElementUV", Property(int32_t(uvi))); - uv.Begin(outstream); - uv.DumpProperties(outstream); - uv.EndProperties(outstream); - FBX::Node::WritePropertyNode("Version", int32_t(101), outstream); + 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); FBX::Node::WritePropertyNode( - "MappingInformationType", "ByPolygonVertex", outstream + "Name", "", outstream, binary, indent ); FBX::Node::WritePropertyNode( - "ReferenceInformationType", "IndexToDirect", outstream + "MappingInformationType", "ByPolygonVertex", + outstream, binary, indent + ); + FBX::Node::WritePropertyNode( + "ReferenceInformationType", "IndexToDirect", + outstream, binary, indent ); std::vector uv_data; @@ -1092,27 +1171,32 @@ void FBXExporter::WriteObjects () } } } - FBX::Node::WritePropertyNode("UV", uv_data, outstream); - FBX::Node::WritePropertyNode("UVIndex", uv_indices, outstream); - uv.End(outstream, true); + 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", Property(int32_t(0))); + 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); + 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", Property(int32_t(0))); + FBX::Node layer("Layer", int32_t(0)); layer.AddChild("Version", int32_t(100)); FBX::Node le("LayerElement"); le.AddChild("Type", "LayerElementNormal"); @@ -1126,10 +1210,11 @@ void FBXExporter::WriteObjects () le.AddChild("Type", "LayerElementUV"); le.AddChild("TypedIndex", int32_t(0)); layer.AddChild(le); - layer.Dump(outstream); + layer.Dump(outstream, binary, indent); // finish the node record - n.End(outstream, true); + indent = 1; + n.End(outstream, binary, indent, true); } // aiMaterial @@ -1273,7 +1358,7 @@ void FBXExporter::WriteObjects () n.AddChild(p); - n.Dump(outstream); + n.Dump(outstream, binary, indent); } // we need to look up all the images we're using, @@ -1321,7 +1406,7 @@ void FBXExporter::WriteObjects () n.AddChild("UseMipMap", int32_t(0)); n.AddChild("Filename", path); n.AddChild("RelativeFilename", path); - n.Dump(outstream); + n.Dump(outstream, binary, indent); } // Textures @@ -1408,14 +1493,12 @@ void FBXExporter::WriteObjects () const int64_t texture_uid = generate_uid(); // link the texture to the material - FBX::Node c("C"); - c.AddProperties("OP", texture_uid, material_uid, prop_name); - connections.push_back(c); + connections.emplace_back( + "C", "OP", texture_uid, material_uid, prop_name + ); // link the image data to the texture - c = FBX::Node("C"); - c.AddProperties("OO", image_uid, texture_uid); - connections.push_back(c); + connections.emplace_back("C", "OO", image_uid, texture_uid); // now write the actual texture node FBX::Node tnode("Texture"); @@ -1438,11 +1521,11 @@ void FBXExporter::WriteObjects () 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_Soutce", "None"); + tnode.AddChild("Texture_Alpha_Source", "None"); tnode.AddChild( "Cropping", int32_t(0), int32_t(0), int32_t(0), int32_t(0) ); - tnode.Dump(outstream); + tnode.Dump(outstream, binary, indent); } } @@ -1595,12 +1678,10 @@ void FBXExporter::WriteObjects () // "acuracy"... this is not a typo.... dnode.AddChild("Link_DeformAcuracy", double(50)); dnode.AddChild("SkinningType", "Linear"); // TODO: other modes? - dnode.Dump(outstream); + dnode.Dump(outstream, binary, indent); // connect it - FBX::Node c("C"); - c.AddProperties("OO", deformer_uid, mesh_uids[mi]); - connections.push_back(c); // TODO: emplace_back + 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" @@ -1703,7 +1784,7 @@ void FBXExporter::WriteObjects () // 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-5f; // some error is to be expected + 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); @@ -1741,17 +1822,17 @@ void FBXExporter::WriteObjects () // there's not really any way around this at the moment. // done - sdnode.Dump(outstream); + sdnode.Dump(outstream, binary, indent); // lastly, connect to the parent deformer - c = FBX::Node("C"); - c.AddProperties("OO", subdeformer_uid, deformer_uid); - connections.push_back(c); // TODO: emplace_back + connections.emplace_back( + "C", "OO", subdeformer_uid, deformer_uid + ); // we also need to connect the limb node to the subdeformer. - c = FBX::Node("C"); - c.AddProperties("OO", node_uids[bone_node], subdeformer_uid); - connections.push_back(c); // TODO: emplace_back + connections.emplace_back( + "C", "OO", node_uids[bone_node], subdeformer_uid + ); } // if we cannot create a valid FBX file, simply die. @@ -1859,7 +1940,7 @@ void FBXExporter::WriteObjects () } // now write it - bpnode.Dump(outstream); + bpnode.Dump(outstream, binary, indent); }*/ // TODO: cameras, lights @@ -1918,11 +1999,8 @@ void FBXExporter::WriteObjects () // this node absurdly always pretends it has children // (in this case it does, but just in case...) - asnode.Begin(outstream); - asnode.DumpProperties(outstream); - asnode.EndProperties(outstream); - asnode.DumpChildren(outstream); - asnode.End(outstream, true); + asnode.force_has_children = true; + asnode.Dump(outstream, binary, indent); // note: animation stacks are not connected to anything } @@ -1936,16 +2014,13 @@ void FBXExporter::WriteObjects () alnode.AddProperties(animlayer_uid, FBX::SEPARATOR + "AnimLayer", ""); // this node absurdly always pretends it has children - alnode.Begin(outstream); - alnode.DumpProperties(outstream); - alnode.EndProperties(outstream); - alnode.DumpChildren(outstream); - alnode.End(outstream, true); + alnode.force_has_children = true; + alnode.Dump(outstream, binary, indent); // connect to the relevant animstack - FBX::Node c("C"); - c.AddProperties("OO", animlayer_uid, animation_stack_uids[ai]); - connections.push_back(c); // TODO: emplace_back + connections.emplace_back( + "C", "OO", animlayer_uid, animation_stack_uids[ai] + ); } // AnimCurveNode - three per aiNodeAnim @@ -2057,7 +2132,8 @@ void FBXExporter::WriteObjects () } } - object_node.End(outstream, true); + indent = 0; + object_node.End(outstream, binary, indent, true); } // convenience map of magic node name strings to FBX properties, @@ -2083,13 +2159,14 @@ const std::map> transform_types = { }; // write a single model node to the stream -void WriteModelNode( +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=TransformInheritance_RSrs + TransformInheritance inherit_type ){ const aiVector3D zero = {0, 0, 0}; const aiVector3D one = {1, 1, 1}; @@ -2157,7 +2234,7 @@ void WriteModelNode( m.AddChild("Shading", Property(true)); m.AddChild("Culling", Property("CullingOff")); - m.Dump(outstream); + m.Dump(outstream, binary, 1); } // wrapper for WriteModelNodes to create and pass a blank transform chain @@ -2181,15 +2258,6 @@ void FBXExporter::WriteModelNodes( // 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) { - if (node->mNumChildren != 1) { - // this should never happen - std::stringstream err; - err << "FBX transformation node should have exactly 1 child,"; - err << " but " << node->mNumChildren << " found"; - err << " on node \"" << node_name << "\"!"; - throw DeadlyExportError(err.str()); - } - aiNode* next_node = node->mChildren[0]; 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); @@ -2223,10 +2291,16 @@ void FBXExporter::WriteModelNodes( err << elem->second.second; throw DeadlyExportError(err.str()); } - // now just continue to the next node - WriteModelNodes( - outstream, next_node, parent_uid, limbnodes, transform_chain - ); + // 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; } @@ -2240,9 +2314,7 @@ void FBXExporter::WriteModelNodes( node_uid = generate_uid(); node_uids[node] = node_uid; } - FBX::Node c("C"); - c.AddProperties("OO", node_uid, parent_uid); - connections.push_back(c); + connections.emplace_back("C", "OO", node_uid, parent_uid); } // what type of node is this? @@ -2250,21 +2322,23 @@ void FBXExporter::WriteModelNodes( // handled later } else if (node->mNumMeshes == 1) { // connect to child mesh, which should have been written previously - FBX::Node c("C"); - c.AddProperties("OO", mesh_uids[node->mMeshes[0]], node_uid); - connections.push_back(c); + connections.emplace_back( + "C", "OO", mesh_uids[node->mMeshes[0]], node_uid + ); // also connect to the material for the child mesh - c = FBX::Node("C"); - c.AddProperties( - "OO", + connections.emplace_back( + "C", "OO", material_uids[mScene->mMeshes[node->mMeshes[0]]->mMaterialIndex], node_uid ); - connections.push_back(c); // write model node - WriteModelNode(outstream, node, node_uid, "Mesh", transform_chain); + WriteModelNode( + outstream, binary, node, node_uid, "Mesh", transform_chain + ); } else if (limbnodes.count(node)) { - WriteModelNode(outstream, node, node_uid, "LimbNode", transform_chain); + 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"); @@ -2272,14 +2346,14 @@ void FBXExporter::WriteModelNodes( node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode" ); na.AddChild("TypeFlags", Property("Skeleton")); - na.Dump(outstream); + na.Dump(outstream, binary, 1); // and connect them - FBX::Node c("C"); - c.AddProperties("OO", node_attribute_uid, node_uid); - connections.push_back(c); + connections.emplace_back("C", "OO", node_attribute_uid, node_uid); } else { // generate a null node so we can add children to it - WriteModelNode(outstream, node, node_uid, "Null", transform_chain); + WriteModelNode( + outstream, binary, node, node_uid, "Null", transform_chain + ); } // if more than one child mesh, make nodes for each mesh @@ -2288,23 +2362,19 @@ void FBXExporter::WriteModelNodes( // make a new model node int64_t new_node_uid = generate_uid(); // connect to parent node - FBX::Node c("C"); - c.AddProperties("OO", new_node_uid, node_uid); - connections.push_back(c); + connections.emplace_back("C", "OO", new_node_uid, node_uid); // connect to child mesh, which should have been written previously - c = FBX::Node("C"); - c.AddProperties("OO", mesh_uids[node->mMeshes[i]], new_node_uid); - connections.push_back(c); + connections.emplace_back( + "C", "OO", mesh_uids[node->mMeshes[i]], new_node_uid + ); // also connect to the material for the child mesh - c = FBX::Node("C"); - c.AddProperties( - "OO", + connections.emplace_back( + "C", "OO", material_uids[ mScene->mMeshes[node->mMeshes[i]]->mMaterialIndex ], new_node_uid ); - connections.push_back(c); // write model node FBX::Node m("Model"); // take name from mesh name, if it exists @@ -2315,7 +2385,7 @@ void FBXExporter::WriteModelNodes( FBX::Node p("Properties70"); p.AddP70enum("InheritType", 1); m.AddChild(p); - m.Dump(outstream); + m.Dump(outstream, binary, 1); } } @@ -2344,15 +2414,11 @@ void FBXExporter::WriteAnimationCurveNode( p.AddP70numberA("d|Y", default_value.y); p.AddP70numberA("d|Z", default_value.z); n.AddChild(p); - n.Dump(outstream); + n.Dump(outstream, binary, 1); // connect to layer - FBX::Node cl("C"); - cl.AddProperties("OO", uid, layer_uid); - this->connections.push_back(cl); // TODO: emplace_back + this->connections.emplace_back("C", "OO", uid, layer_uid); // connect to bone - FBX::Node cb("C"); - cb.AddProperties("OP", uid, node_uid, property_name); - this->connections.push_back(cb); // TODO: emplace_back + this->connections.emplace_back("C", "OP", uid, node_uid, property_name); } @@ -2379,10 +2445,10 @@ void FBXExporter::WriteAnimationCurve( "KeyAttrRefCount", std::vector{static_cast(times.size())} ); - n.Dump(outstream); - FBX::Node c("C"); - c.AddProperties("OP", curve_uid, curvenode_uid, property_link); - this->connections.push_back(c); // TODO: emplace_back + n.Dump(outstream, binary, 1); + this->connections.emplace_back( + "C", "OP", curve_uid, curvenode_uid, property_link + ); } @@ -2390,13 +2456,18 @@ 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); + conn.Begin(outstream, binary, 0); + conn.BeginChildren(outstream, binary, 0); for (auto &n : connections) { - n.Dump(outstream); + n.Dump(outstream, binary, 1); } - conn.End(outstream, !connections.empty()); + conn.End(outstream, binary, 0, !connections.empty()); connections.clear(); } diff --git a/code/FBXExporter.h b/code/FBXExporter.h index 553cf60fe..3b9de8acb 100644 --- a/code/FBXExporter.h +++ b/code/FBXExporter.h @@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER #include "FBXExportNode.h" // FBX::Node +#include "FBXCommon.h" // FBX::TransformInheritance #include //#include @@ -104,6 +105,9 @@ namespace Assimp 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(); @@ -126,6 +130,7 @@ namespace Assimp // WriteTakes(); // deprecated since at least 2015 (fbx 7.4) // helpers + void WriteAsciiSectionHeader(const std::string& title); void WriteModelNodes( Assimp::StreamWriterLE& s, const aiNode* node, @@ -139,6 +144,15 @@ namespace Assimp 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, 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/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/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/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 6f492df90..deb35fb4d 100644 --- a/include/assimp/StreamWriter.h +++ b/include/assimp/StreamWriter.h @@ -203,6 +203,12 @@ 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) @@ -249,8 +255,6 @@ public: cursor = new_cursor; } -private: - // --------------------------------------------------------------------- /** Generic write method. ByteSwap::Swap(T*) *must* be defined */ template diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 049dbcdd2..7c5ff3706 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -53,6 +53,7 @@ endif() 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/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/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/tools/assimp_cmd/Info.cpp b/tools/assimp_cmd/Info.cpp index c4d5fe189..ae858bfe8 100644 --- a/tools/assimp_cmd/Info.cpp +++ b/tools/assimp_cmd/Info.cpp @@ -46,12 +46,31 @@ 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] [-v]\n" "\tPrint basic structure of a 3D model\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; + // ----------------------------------------------------------------------------------- unsigned int CountNodes(const aiNode* root) @@ -184,46 +203,77 @@ std::string FindPTypes(const aiScene* scene) } // ----------------------------------------------------------------------------------- -void PrintHierarchy(const aiNode* root, unsigned int maxnest, unsigned int maxline, - unsigned int cline, bool verbose, 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); + // finish the line + std::cout << std::endl; + // in verbose mode, print the transform data as well if (verbose) { - // print the actual transform - //printf(","); + // 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; - root->mTransformation.Decompose(s, r, t); + node->mTransformation.Decompose(s, r, t); if (s.x != 1.0 || s.y != 1.0 || s.z != 1.0) { - for(unsigned int i = 0; i < cnest; ++i) { printf(" "); } - printf(" S:[%f %f %f]\n", s.x, s.y, s.z); + std::cout << indent << indentadd; + printf(" S:[%f %f %f]\n", s.x, s.y, s.z); } if (r.x || r.y || r.z) { - for(unsigned int i = 0; i < cnest; ++i) { printf(" "); } - printf(" R:[%f %f %f]\n", 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) { - for(unsigned int i = 0; i < cnest; ++i) { printf(" "); } - printf(" T:[%f %f %f]\n", t.x, t.y, t.z); + std::cout << indent << indentadd; + printf(" T:[%f %f %f]\n", t.x, t.y, t.z); } } - //printf("\n"); - for (unsigned int i = 0; i < root->mNumChildren; ++i ) { - PrintHierarchy(root->mChildren[i],maxnest,maxline,cline,verbose,cnest+1); - if(i == root->mNumChildren-1) { - for(unsigned int i = 0; i < cnest; ++i) { - printf(" "); - } - printf("<--\n"); - } + // 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 + ); } } @@ -406,8 +456,7 @@ int Assimp_Info (const char* const* params, unsigned int num) // node hierarchy printf("\nNode hierarchy:\n"); - unsigned int cline=0; - PrintHierarchy(scene->mRootNode,20,1000,cline,verbose); + PrintHierarchy(scene->mRootNode,"",verbose); printf("\n"); return 0; 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() +