diff --git a/CMakeLists.txt b/CMakeLists.txt index dcafb649f..693d6f16a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -253,7 +253,7 @@ ELSEIF(MSVC) IF(MSVC12) ADD_COMPILE_OPTIONS(/wd4351) ENDIF() - SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob2 /DEBUG:FULL /Zi") + SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob2 /Zi") ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) IF(NOT HUNTER_ENABLED) SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}") @@ -391,6 +391,11 @@ IF(HUNTER_ENABLED) ) ELSE(HUNTER_ENABLED) # cmake configuration files + if(${BUILD_SHARED_LIBS}) + set(BUILD_LIB_TYPE SHARED) + else() + set(BUILD_LIB_TYPE STATIC) + endif() 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}/assimpTargets.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake" @ONLY IMMEDIATE) IF (is_multi_config) diff --git a/appveyor.yml b/appveyor.yml index df16431bd..fa0a125f0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,6 +14,7 @@ matrix: fast_finish: true image: + - Visual Studio 2013 - Visual Studio 2015 - Visual Studio 2017 - Visual Studio 2019 @@ -29,11 +30,13 @@ install: - set PATH=C:\Ruby24-x64\bin;%PATH% - set CMAKE_DEFINES -DASSIMP_WERROR=ON - if [%COMPILER%]==[MinGW] set PATH=C:\MinGW\bin;%PATH% + - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set CMAKE_GENERATOR_NAME=Visual Studio 12 2013 - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015 - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017 - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" set CMAKE_GENERATOR_NAME=Visual Studio 16 2019 - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" -A %platform% . - # Rename sh.exe as sh.exe in PATH interferes with MinGW + # Rename sh.exe as sh.exe in PATH interferes with MinGW - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015 + - rename "C:\Program Files\Git\usr\bin\sh.exe" "sh2.exe" - set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5" - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe diff --git a/assimpTargets-debug.cmake.in b/assimpTargets-debug.cmake.in index ed98e777b..466759929 100644 --- a/assimpTargets-debug.cmake.in +++ b/assimpTargets-debug.cmake.in @@ -63,10 +63,13 @@ if(MSVC) else() set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" ) if(ASSIMP_BUILD_SHARED_LIBS) - if(APPLE) - set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@") - else(APPLE) - set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@") + if(WIN32) + # Handle MinGW compiler. + set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@") + elseif(APPLE) + set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@") + else() + set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@") endif() set_target_properties(assimp::assimp PROPERTIES IMPORTED_SONAME_DEBUG "${sharedLibraryName}" diff --git a/assimpTargets-release.cmake.in b/assimpTargets-release.cmake.in index a5fe74933..95253a4eb 100644 --- a/assimpTargets-release.cmake.in +++ b/assimpTargets-release.cmake.in @@ -63,10 +63,13 @@ if(MSVC) else() set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" ) if(ASSIMP_BUILD_SHARED_LIBS) - if(APPLE) - set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@") - else(APPLE) - set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@") + if(WIN32) + # Handle MinGW compiler. + set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@") + elseif(APPLE) + set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@") + else() + set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@") endif() set_target_properties(assimp::assimp PROPERTIES IMPORTED_SONAME_RELEASE "${sharedLibraryName}" diff --git a/assimpTargets.cmake.in b/assimpTargets.cmake.in index afef9cbd1..b1c618c3a 100644 --- a/assimpTargets.cmake.in +++ b/assimpTargets.cmake.in @@ -54,11 +54,7 @@ if(_IMPORT_PREFIX STREQUAL "/") endif() # Create imported target assimp::assimp -if(@BUILD_SHARED_LIBS@) - add_library(assimp::assimp SHARED IMPORTED) -else() - add_library(assimp::assimp STATIC IMPORTED) -endif() +add_library(assimp::assimp @BUILD_LIB_TYPE@ IMPORTED) set_target_properties(assimp::assimp PROPERTIES COMPATIBLE_INTERFACE_STRING "assimp_MAJOR_VERSION" diff --git a/code/3DS/3DSConverter.cpp b/code/3DS/3DSConverter.cpp index 2176b75fc..3c3da36a3 100644 --- a/code/3DS/3DSConverter.cpp +++ b/code/3DS/3DSConverter.cpp @@ -72,7 +72,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() unsigned int idx( NotSet ); for (unsigned int i = 0; i < mScene->mMaterials.size();++i) { - std::string &s = mScene->mMaterials[i].mName; + std::string s = mScene->mMaterials[i].mName; for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) { *it = static_cast< char >( ::tolower( *it ) ); } diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index eec805b54..2c030abbb 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -670,6 +670,8 @@ SET( PostProcessing_SRCS PostProcessing/MakeVerboseFormat.h PostProcessing/ScaleProcess.cpp PostProcessing/ScaleProcess.h + PostProcessing/ArmaturePopulate.cpp + PostProcessing/ArmaturePopulate.h PostProcessing/GenBoundingBoxesProcess.cpp PostProcessing/GenBoundingBoxesProcess.h ) diff --git a/code/Collada/ColladaHelper.h b/code/Collada/ColladaHelper.h index 66cff2d20..b7d47da15 100644 --- a/code/Collada/ColladaHelper.h +++ b/code/Collada/ColladaHelper.h @@ -583,7 +583,7 @@ struct Image /** Embedded image data */ std::vector mImageData; - /** File format hint ofembedded image data */ + /** File format hint of embedded image data */ std::string mEmbeddedFormat; }; diff --git a/code/Collada/ColladaLoader.cpp b/code/Collada/ColladaLoader.cpp index 0fbc7d599..37529bc98 100644 --- a/code/Collada/ColladaLoader.cpp +++ b/code/Collada/ColladaLoader.cpp @@ -1735,6 +1735,7 @@ void ColladaLoader::BuildMaterials(ColladaParser& pParser, aiScene* /*pScene*/) // ------------------------------------------------------------------------------------------------ // Resolves the texture name for the given effect texture entry +// and loads the texture data aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParser, const Collada::Effect& pEffect, const std::string& pName) { @@ -1762,7 +1763,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse //set default texture file name result.Set(name + ".jpg"); - ConvertPath(result); + ColladaParser::UriDecodePath(result); return result; } @@ -1781,7 +1782,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse // setup format hint - if (imIt->second.mEmbeddedFormat.length() > 3) { + if (imIt->second.mEmbeddedFormat.length() >= HINTMAXTEXTURELEN) { ASSIMP_LOG_WARN("Collada: texture format hint is too long, truncating to 3 characters"); } strncpy(tex->achFormatHint, imIt->second.mEmbeddedFormat.c_str(), 3); @@ -1802,61 +1803,10 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse } result.Set(imIt->second.mFileName); - ConvertPath(result); } return result; } -// ------------------------------------------------------------------------------------------------ -// Convert a path read from a collada file to the usual representation -void ColladaLoader::ConvertPath(aiString& ss) -{ - // TODO: collada spec, p 22. Handle URI correctly. - // For the moment we're just stripping the file:// away to make it work. - // Windows doesn't seem to be able to find stuff like - // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg' - if (0 == strncmp(ss.data, "file://", 7)) - { - ss.length -= 7; - memmove(ss.data, ss.data + 7, ss.length); - ss.data[ss.length] = '\0'; - } - - // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... - // I need to filter it without destroying linux paths starting with "/somewhere" -#if defined( _MSC_VER ) - if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { -#else - if (ss.data[0] == '/' && isalpha(ss.data[1]) && ss.data[2] == ':') { -#endif - --ss.length; - ::memmove(ss.data, ss.data + 1, ss.length); - ss.data[ss.length] = 0; - } - - // find and convert all %xy special chars - char* out = ss.data; - for (const char* it = ss.data; it != ss.data + ss.length; /**/) - { - if (*it == '%' && (it + 3) < ss.data + ss.length) - { - // separate the number to avoid dragging in chars from behind into the parsing - char mychar[3] = { it[1], it[2], 0 }; - size_t nbr = strtoul16(mychar); - it += 3; - *out++ = (char)(nbr & 0xFF); - } - else - { - *out++ = *it++; - } - } - - // adjust length and terminator of the shortened string - *out = 0; - ss.length = (ptrdiff_t)(out - ss.data); -} - // ------------------------------------------------------------------------------------------------ // Reads a float value from an accessor and its data array. ai_real ColladaLoader::ReadFloat(const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const diff --git a/code/Collada/ColladaLoader.h b/code/Collada/ColladaLoader.h index dce46e06f..d8d3f4f52 100644 --- a/code/Collada/ColladaLoader.h +++ b/code/Collada/ColladaLoader.h @@ -94,7 +94,7 @@ public: public: /** Returns whether the class can handle the format of the given file. * See BaseImporter::CanRead() for details. */ - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override; + bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override; protected: /** Return importer meta information. @@ -184,9 +184,6 @@ protected: aiString FindFilenameForEffectTexture( const ColladaParser& pParser, const Collada::Effect& pEffect, const std::string& pName); - /** Converts a path read from a collada file to the usual representation */ - void ConvertPath( aiString& ss); - /** Reads a float value from an accessor and its data array. * @param pAccessor The accessor to use for reading * @param pData The data array to read from diff --git a/code/Collada/ColladaParser.cpp b/code/Collada/ColladaParser.cpp index 646ec473d..1a7b96189 100644 --- a/code/Collada/ColladaParser.cpp +++ b/code/Collada/ColladaParser.cpp @@ -183,13 +183,67 @@ std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) { if (filepath == nullptr) return std::string(); - return std::string(filepath); + aiString ai_str(filepath); + UriDecodePath(ai_str); + + return std::string(ai_str.C_Str()); } } } return std::string(); } +// ------------------------------------------------------------------------------------------------ +// Convert a path read from a collada file to the usual representation +void ColladaParser::UriDecodePath(aiString& ss) +{ + // TODO: collada spec, p 22. Handle URI correctly. + // For the moment we're just stripping the file:// away to make it work. + // Windows doesn't seem to be able to find stuff like + // 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg' + if (0 == strncmp(ss.data, "file://", 7)) + { + ss.length -= 7; + memmove(ss.data, ss.data + 7, ss.length); + ss.data[ss.length] = '\0'; + } + + // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... + // I need to filter it without destroying linux paths starting with "/somewhere" +#if defined( _MSC_VER ) + if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { +#else + if (ss.data[0] == '/' && isalpha(ss.data[1]) && ss.data[2] == ':') { +#endif + --ss.length; + ::memmove(ss.data, ss.data + 1, ss.length); + ss.data[ss.length] = 0; + } + + // find and convert all %xy special chars + char* out = ss.data; + for (const char* it = ss.data; it != ss.data + ss.length; /**/) + { + if (*it == '%' && (it + 3) < ss.data + ss.length) + { + // separate the number to avoid dragging in chars from behind into the parsing + char mychar[3] = { it[1], it[2], 0 }; + size_t nbr = strtoul16(mychar); + it += 3; + *out++ = (char)(nbr & 0xFF); + } + else + { + *out++ = *it++; + } + } + + // adjust length and terminator of the shortened string + *out = 0; + ai_assert(out > ss.data); + ss.length = static_cast(out - ss.data); +} + // ------------------------------------------------------------------------------------------------ // Read bool from text contents of current element bool ColladaParser::ReadBoolFromTextContent() @@ -1120,7 +1174,12 @@ void ColladaParser::ReadImage(Collada::Image& pImage) if (!mReader->isEmptyElement()) { // element content is filename - hopefully const char* sz = TestTextContent(); - if (sz)pImage.mFileName = sz; + if (sz) + { + aiString filepath(sz); + UriDecodePath(filepath); + pImage.mFileName = filepath.C_Str(); + } TestClosing("init_from"); } if (!pImage.mFileName.length()) { @@ -1153,7 +1212,12 @@ void ColladaParser::ReadImage(Collada::Image& pImage) { // element content is filename - hopefully const char* sz = TestTextContent(); - if (sz)pImage.mFileName = sz; + if (sz) + { + aiString filepath(sz); + UriDecodePath(filepath); + pImage.mFileName = filepath.C_Str(); + } TestClosing("ref"); } else if (IsElement("hex") && !pImage.mFileName.length()) @@ -3056,7 +3120,7 @@ void ColladaParser::ReadMaterialVertexInputBinding(Collada::SemanticMappingTable } } -void Assimp::ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem& zip_archive) +void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem& zip_archive) { // Attempt to load any undefined Collada::Image in ImageLibrary for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) { diff --git a/code/Collada/ColladaParser.h b/code/Collada/ColladaParser.h index a2c9a4ff2..4e8c8fccf 100644 --- a/code/Collada/ColladaParser.h +++ b/code/Collada/ColladaParser.h @@ -66,12 +66,15 @@ namespace Assimp { friend class ColladaLoader; + /** Converts a path read from a collada file to the usual representation */ + static void UriDecodePath(aiString& ss); + protected: /** Map for generic metadata as aiString */ typedef std::map StringMetaData; /** Constructor from XML file */ - ColladaParser( IOSystem* pIOHandler, const std::string& pFile); + ColladaParser(IOSystem* pIOHandler, const std::string& pFile); /** Destructor */ ~ColladaParser(); diff --git a/code/Common/PostStepRegistry.cpp b/code/Common/PostStepRegistry.cpp index ef58f8ddf..8ff4af040 100644 --- a/code/Common/PostStepRegistry.cpp +++ b/code/Common/PostStepRegistry.cpp @@ -131,11 +131,15 @@ corresponding preprocessor flag to selectively disable steps. #if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) # include "PostProcessing/ScaleProcess.h" #endif +#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS) +# include "PostProcessing/ArmaturePopulate.h" +#endif #if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS) # include "PostProcessing/GenBoundingBoxesProcess.h" #endif + namespace Assimp { // ------------------------------------------------------------------------------------------------ @@ -180,6 +184,9 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out) #if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) out.push_back( new ScaleProcess()); #endif +#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS) + out.push_back( new ArmaturePopulate()); +#endif #if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS) out.push_back( new PretransformVertices()); #endif diff --git a/code/Common/Version.cpp b/code/Common/Version.cpp index 868cfb06a..cf1da7d5b 100644 --- a/code/Common/Version.cpp +++ b/code/Common/Version.cpp @@ -46,8 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include "ScenePrivate.h" -static const unsigned int MajorVersion = 5; -static const unsigned int MinorVersion = 0; +#include "revision.h" // -------------------------------------------------------------------------------- // Legal information string - don't remove this. @@ -56,9 +55,9 @@ static const char* LEGAL_INFORMATION = "Open Asset Import Library (Assimp).\n" "A free C/C++ library to import various 3D file formats into applications\n\n" -"(c) 2008-2017, assimp team\n" +"(c) 2006-2019, assimp team\n" "License under the terms and conditions of the 3-clause BSD license\n" -"http://assimp.sourceforge.net\n" +"http://assimp.org\n" ; // ------------------------------------------------------------------------------------------------ @@ -70,13 +69,13 @@ ASSIMP_API const char* aiGetLegalString () { // ------------------------------------------------------------------------------------------------ // Get Assimp minor version ASSIMP_API unsigned int aiGetVersionMinor () { - return MinorVersion; + return VER_MINOR; } // ------------------------------------------------------------------------------------------------ // Get Assimp major version ASSIMP_API unsigned int aiGetVersionMajor () { - return MajorVersion; + return VER_MAJOR; } // ------------------------------------------------------------------------------------------------ @@ -104,9 +103,6 @@ ASSIMP_API unsigned int aiGetCompileFlags () { return flags; } -// include current build revision, which is even updated from time to time -- :-) -#include "revision.h" - // ------------------------------------------------------------------------------------------------ ASSIMP_API unsigned int aiGetVersionRevision() { return GitVersion; diff --git a/code/Common/scene.cpp b/code/Common/scene.cpp index 2acb348d8..d15619acf 100644 --- a/code/Common/scene.cpp +++ b/code/Common/scene.cpp @@ -44,23 +44,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. aiNode::aiNode() : mName("") -, mParent(NULL) +, mParent(nullptr) , mNumChildren(0) -, mChildren(NULL) +, mChildren(nullptr) , mNumMeshes(0) -, mMeshes(NULL) -, mMetaData(NULL) { +, mMeshes(nullptr) +, mMetaData(nullptr) { // empty } aiNode::aiNode(const std::string& name) : mName(name) -, mParent(NULL) +, mParent(nullptr) , mNumChildren(0) -, mChildren(NULL) +, mChildren(nullptr) , mNumMeshes(0) -, mMeshes(NULL) -, mMetaData(NULL) { +, mMeshes(nullptr) +, mMetaData(nullptr) { // empty } @@ -68,7 +68,7 @@ aiNode::aiNode(const std::string& name) aiNode::~aiNode() { // delete all children recursively // to make sure we won't crash if the data is invalid ... - if (mChildren && mNumChildren) + if (mNumChildren && mChildren) { for (unsigned int a = 0; a < mNumChildren; a++) delete mChildren[a]; diff --git a/code/FBX/FBXCompileConfig.h b/code/FBX/FBXCompileConfig.h index 3a3841fa5..03536a182 100644 --- a/code/FBX/FBXCompileConfig.h +++ b/code/FBX/FBXCompileConfig.h @@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define INCLUDED_AI_FBX_COMPILECONFIG_H #include +#include // #if _MSC_VER > 1500 || (defined __GNUC___) @@ -54,16 +55,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # else # define fbx_unordered_map map # define fbx_unordered_multimap multimap +# define fbx_unordered_set set +# define fbx_unordered_multiset multiset #endif #ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP # include +# include # if _MSC_VER > 1600 # define fbx_unordered_map unordered_map # define fbx_unordered_multimap unordered_multimap +# define fbx_unordered_set unordered_set +# define fbx_unordered_multiset unordered_multiset # else # define fbx_unordered_map tr1::unordered_map # define fbx_unordered_multimap tr1::unordered_multimap +# define fbx_unordered_set tr1::unordered_set +# define fbx_unordered_multiset tr1::unordered_multiset # endif #endif diff --git a/code/FBX/FBXConverter.cpp b/code/FBX/FBXConverter.cpp index 152be3277..d8a22d9f7 100644 --- a/code/FBX/FBXConverter.cpp +++ b/code/FBX/FBXConverter.cpp @@ -68,7 +68,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include - +#include +#include namespace Assimp { namespace FBX { @@ -77,7 +78,7 @@ namespace Assimp { #define MAGIC_NODE_TAG "_$AssimpFbx$" -#define CONVERT_FBX_TIME(time) static_cast(time) / 46186158000L +#define CONVERT_FBX_TIME(time) static_cast(time) / 46186158000LL FBXConverter::FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones ) : defaultMaterialIndex() @@ -96,6 +97,14 @@ namespace Assimp { // populate the node_anim_chain_bits map, which is needed // to determine which nodes need to be generated. ConvertAnimations(); + // Embedded textures in FBX could be connected to nothing but to itself, + // for instance Texture -> Video connection only but not to the main graph, + // The idea here is to traverse all objects to find these Textures and convert them, + // so later during material conversion it will find converted texture in the textures_converted array. + if (doc.Settings().readTextures) + { + ConvertOrphantEmbeddedTextures(); + } ConvertRootNode(); if (doc.Settings().readAllMaterials) { @@ -145,7 +154,7 @@ namespace Assimp { out->mRootNode->mName.Set(unique_name); // root has ID 0 - ConvertNodes(0L, *out->mRootNode); + ConvertNodes(0L, out->mRootNode, out->mRootNode); } static std::string getAncestorBaseName(const aiNode* node) @@ -179,8 +188,11 @@ namespace Assimp { GetUniqueName(original_name, unique_name); return unique_name; } - - void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) { + /// todo: pre-build node hierarchy + /// todo: get bone from stack + /// todo: make map of aiBone* to aiNode* + /// then update convert clusters to the new format + void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) { const std::vector& conns = doc.GetConnectionsByDestinationSequenced(id, "Model"); std::vector nodes; @@ -191,62 +203,69 @@ namespace Assimp { try { for (const Connection* con : conns) { - // ignore object-property links if (con->PropertyName().length()) { - continue; + // really important we document why this is ignored. + FBXImporter::LogInfo("ignoring property link - no docs on why this is ignored"); + continue; //? } + // convert connection source object into Object base class const Object* const object = con->SourceObject(); if (nullptr == object) { - FBXImporter::LogWarn("failed to convert source object for Model link"); + FBXImporter::LogError("failed to convert source object for Model link"); continue; } + // FBX Model::Cube, Model::Bone001, etc elements + // This detects if we can cast the object into this model structure. const Model* const model = dynamic_cast(object); if (nullptr != model) { nodes_chain.clear(); post_nodes_chain.clear(); - aiMatrix4x4 new_abs_transform = parent_transform; - - std::string unique_name = MakeUniqueNodeName(model, parent); - + aiMatrix4x4 new_abs_transform = parent->mTransformation; + std::string node_name = FixNodeName(model->Name()); // even though there is only a single input node, the design of // assimp (or rather: the complicated transformation chain that // is employed by fbx) means that we may need multiple aiNode's // to represent a fbx node's transformation. - const bool need_additional_node = GenerateTransformationNodeChain(*model, unique_name, nodes_chain, post_nodes_chain); + + // generate node transforms - this includes pivot data + // if need_additional_node is true then you t + const bool need_additional_node = GenerateTransformationNodeChain(*model, node_name, nodes_chain, post_nodes_chain); + + // assert that for the current node we must have at least a single transform ai_assert(nodes_chain.size()); if (need_additional_node) { - nodes_chain.push_back(new aiNode(unique_name)); + nodes_chain.push_back(new aiNode(node_name)); } //setup metadata on newest node SetupNodeMetadata(*model, *nodes_chain.back()); // link all nodes in a row - aiNode* last_parent = &parent; - for (aiNode* prenode : nodes_chain) { - ai_assert(prenode); + aiNode* last_parent = parent; + for (aiNode* child : nodes_chain) { + ai_assert(child); - if (last_parent != &parent) { + if (last_parent != parent) { last_parent->mNumChildren = 1; last_parent->mChildren = new aiNode*[1]; - last_parent->mChildren[0] = prenode; + last_parent->mChildren[0] = child; } - prenode->mParent = last_parent; - last_parent = prenode; + child->mParent = last_parent; + last_parent = child; - new_abs_transform *= prenode->mTransformation; + new_abs_transform *= child->mTransformation; } // attach geometry - ConvertModel(*model, *nodes_chain.back(), new_abs_transform); + ConvertModel(*model, nodes_chain.back(), root_node, new_abs_transform); // check if there will be any child nodes const std::vector& child_conns @@ -258,7 +277,7 @@ namespace Assimp { for (aiNode* postnode : post_nodes_chain) { ai_assert(postnode); - if (last_parent != &parent) { + if (last_parent != parent) { last_parent->mNumChildren = 1; last_parent->mChildren = new aiNode*[1]; last_parent->mChildren[0] = postnode; @@ -280,15 +299,15 @@ namespace Assimp { ); } - // attach sub-nodes (if any) - ConvertNodes(model->ID(), *last_parent, new_abs_transform); + // recursion call - child nodes + ConvertNodes(model->ID(), last_parent, root_node); if (doc.Settings().readLights) { - ConvertLights(*model, unique_name); + ConvertLights(*model, node_name); } if (doc.Settings().readCameras) { - ConvertCameras(*model, unique_name); + ConvertCameras(*model, node_name); } nodes.push_back(nodes_chain.front()); @@ -297,11 +316,17 @@ namespace Assimp { } if (nodes.size()) { - parent.mChildren = new aiNode*[nodes.size()](); - parent.mNumChildren = static_cast(nodes.size()); + parent->mChildren = new aiNode*[nodes.size()](); + parent->mNumChildren = static_cast(nodes.size()); - std::swap_ranges(nodes.begin(), nodes.end(), parent.mChildren); + std::swap_ranges(nodes.begin(), nodes.end(), parent->mChildren); } + else + { + parent->mNumChildren = 0; + parent->mChildren = nullptr; + } + } catch (std::exception&) { Util::delete_fun deleter; @@ -803,7 +828,7 @@ namespace Assimp { // is_complex needs to be consistent with NeedsComplexTransformationChain() // or the interplay between this code and the animation converter would // not be guaranteed. - ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0)); + //ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0)); // now, if we have more than just Translation, Scaling and Rotation, // we need to generate a full node chain to accommodate for assimp's @@ -905,7 +930,8 @@ namespace Assimp { } } - void FBXConverter::ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform) + void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform) { const std::vector& geos = model.GetGeometry(); @@ -917,11 +943,12 @@ namespace Assimp { const MeshGeometry* const mesh = dynamic_cast(geo); const LineGeometry* const line = dynamic_cast(geo); if (mesh) { - const std::vector& indices = ConvertMesh(*mesh, model, node_global_transform, nd); + const std::vector& indices = ConvertMesh(*mesh, model, parent, root_node, + absolute_transform); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); } else if (line) { - const std::vector& indices = ConvertLine(*line, model, node_global_transform, nd); + const std::vector& indices = ConvertLine(*line, model, parent, root_node); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); } else { @@ -930,15 +957,16 @@ namespace Assimp { } if (meshes.size()) { - nd.mMeshes = new unsigned int[meshes.size()](); - nd.mNumMeshes = static_cast(meshes.size()); + parent->mMeshes = new unsigned int[meshes.size()](); + parent->mNumMeshes = static_cast(meshes.size()); - std::swap_ranges(meshes.begin(), meshes.end(), nd.mMeshes); + std::swap_ranges(meshes.begin(), meshes.end(), parent->mMeshes); } } - std::vector FBXConverter::ConvertMesh(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) + std::vector + FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform) { std::vector temp; @@ -962,18 +990,18 @@ namespace Assimp { const MatIndexArray::value_type base = mindices[0]; for (MatIndexArray::value_type index : mindices) { if (index != base) { - return ConvertMeshMultiMaterial(mesh, model, node_global_transform, nd); + return ConvertMeshMultiMaterial(mesh, model, parent, root_node, absolute_transform); } } } // faster code-path, just copy the data - temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform, nd)); + temp.push_back(ConvertMeshSingleMaterial(mesh, model, absolute_transform, parent, root_node)); return temp; } std::vector FBXConverter::ConvertLine(const LineGeometry& line, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) + aiNode *parent, aiNode *root_node) { std::vector temp; @@ -984,7 +1012,7 @@ namespace Assimp { return temp; } - aiMesh* const out_mesh = SetupEmptyMesh(line, nd); + aiMesh* const out_mesh = SetupEmptyMesh(line, root_node); out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; // copy vertices @@ -1019,7 +1047,7 @@ namespace Assimp { return temp; } - aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode& nd) + aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode *parent) { aiMesh* const out_mesh = new aiMesh(); meshes.push_back(out_mesh); @@ -1036,17 +1064,18 @@ namespace Assimp { } else { - out_mesh->mName = nd.mName; + out_mesh->mName = parent->mName; } return out_mesh; } - unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) + unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, + const aiMatrix4x4 &absolute_transform, aiNode *parent, + aiNode *root_node) { const MatIndexArray& mindices = mesh.GetMaterialIndices(); - aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); + aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent); const std::vector& vertices = mesh.GetVertices(); const std::vector& faces = mesh.GetFaceIndexCounts(); @@ -1113,7 +1142,7 @@ namespace Assimp { binormals = &tempBinormals; } else { - binormals = NULL; + binormals = nullptr; } } @@ -1163,8 +1192,9 @@ namespace Assimp { ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]); } - if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) { - ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION); + if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr) { + ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, NO_MATERIAL_SEPARATION, + nullptr); } std::vector animMeshes; @@ -1209,8 +1239,10 @@ namespace Assimp { return static_cast(meshes.size() - 1); } - std::vector FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd) + std::vector + FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, + aiNode *root_node, + const aiMatrix4x4 &absolute_transform) { const MatIndexArray& mindices = mesh.GetMaterialIndices(); ai_assert(mindices.size()); @@ -1221,7 +1253,7 @@ namespace Assimp { for (MatIndexArray::value_type index : mindices) { if (had.find(index) == had.end()) { - indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform, nd)); + indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, parent, root_node, absolute_transform)); had.insert(index); } } @@ -1229,18 +1261,18 @@ namespace Assimp { return indices; } - unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - MatIndexArray::value_type index, - const aiMatrix4x4& node_global_transform, - aiNode& nd) + unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, + MatIndexArray::value_type index, + aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform) { - aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); + aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent); const MatIndexArray& mindices = mesh.GetMaterialIndices(); const std::vector& vertices = mesh.GetVertices(); const std::vector& faces = mesh.GetFaceIndexCounts(); - const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL; + const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != nullptr; unsigned int count_faces = 0; unsigned int count_vertices = 0; @@ -1300,7 +1332,7 @@ namespace Assimp { binormals = &tempBinormals; } else { - binormals = NULL; + binormals = nullptr; } } @@ -1399,7 +1431,7 @@ namespace Assimp { ConvertMaterialForMesh(out_mesh, model, mesh, index); if (process_weights) { - ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping); + ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, index, &reverseMapping); } std::vector animMeshes; @@ -1449,10 +1481,10 @@ namespace Assimp { return static_cast(meshes.size() - 1); } - void FBXConverter::ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, - const aiMatrix4x4& node_global_transform, - unsigned int materialIndex, - std::vector* outputVertStartIndices) + void FBXConverter::ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo, + const aiMatrix4x4 &absolute_transform, + aiNode *parent, aiNode *root_node, unsigned int materialIndex, + std::vector *outputVertStartIndices) { ai_assert(geo.DeformerSkin()); @@ -1463,13 +1495,12 @@ namespace Assimp { const Skin& sk = *geo.DeformerSkin(); std::vector bones; - bones.reserve(sk.Clusters().size()); const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION; ai_assert(no_mat_check || outputVertStartIndices); try { - + // iterate over the sub deformers for (const Cluster* cluster : sk.Clusters()) { ai_assert(cluster); @@ -1483,15 +1514,16 @@ namespace Assimp { index_out_indices.clear(); out_indices.clear(); + // now check if *any* of these weights is contained in the output mesh, // taking notes so we don't need to do it twice. for (WeightIndexArray::value_type index : indices) { unsigned int count = 0; const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count); - // ToOutputVertexIndex only returns NULL if index is out of bounds + // ToOutputVertexIndex only returns nullptr if index is out of bounds // which should never happen - ai_assert(out_idx != NULL); + ai_assert(out_idx != nullptr); index_out_indices.push_back(no_index_sentinel); count_out_indices.push_back(0); @@ -1520,68 +1552,107 @@ namespace Assimp { } } } - + // if we found at least one, generate the output bones // XXX this could be heavily simplified by collecting the bone // data in a single step. - ConvertCluster(bones, model, *cluster, out_indices, index_out_indices, - count_out_indices, node_global_transform); + ConvertCluster(bones, cluster, out_indices, index_out_indices, + count_out_indices, absolute_transform, parent, root_node); } + + bone_map.clear(); } - catch (std::exception&) { + catch (std::exception&e) { std::for_each(bones.begin(), bones.end(), Util::delete_fun()); throw; } if (bones.empty()) { + out->mBones = nullptr; + out->mNumBones = 0; return; + } else { + out->mBones = new aiBone *[bones.size()](); + out->mNumBones = static_cast(bones.size()); + + std::swap_ranges(bones.begin(), bones.end(), out->mBones); } - - out->mBones = new aiBone*[bones.size()](); - out->mNumBones = static_cast(bones.size()); - - std::swap_ranges(bones.begin(), bones.end(), out->mBones); } - void FBXConverter::ConvertCluster(std::vector& bones, const Model& /*model*/, const Cluster& cl, - std::vector& out_indices, - std::vector& index_out_indices, - std::vector& count_out_indices, - const aiMatrix4x4& node_global_transform) + const aiNode* FBXConverter::GetNodeByName( const aiString& name, aiNode *current_node ) { + aiNode * iter = current_node; + //printf("Child count: %d", iter->mNumChildren); + return iter; + } - aiBone* const bone = new aiBone(); - bones.push_back(bone); + void FBXConverter::ConvertCluster(std::vector &local_mesh_bones, const Cluster *cl, + std::vector &out_indices, std::vector &index_out_indices, + std::vector &count_out_indices, const aiMatrix4x4 &absolute_transform, + aiNode *parent, aiNode *root_node) { + ai_assert(cl); // make sure cluster valid + std::string deformer_name = cl->TargetNode()->Name(); + aiString bone_name = aiString(FixNodeName(deformer_name)); - bone->mName = FixNodeName(cl.TargetNode()->Name()); + aiBone *bone = nullptr; - bone->mOffsetMatrix = cl.TransformLink(); - bone->mOffsetMatrix.Inverse(); + if (bone_map.count(deformer_name)) { + std::cout << "retrieved bone from lookup " << bone_name.C_Str() << ". Deformer: " << deformer_name + << std::endl; + bone = bone_map[deformer_name]; + } else { + std::cout << "created new bone " << bone_name.C_Str() << ". Deformer: " << deformer_name << std::endl; + bone = new aiBone(); + bone->mName = bone_name; - bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform; + // store local transform link for post processing + bone->mOffsetMatrix = cl->TransformLink(); + bone->mOffsetMatrix.Inverse(); - bone->mNumWeights = static_cast(out_indices.size()); - aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()]; + aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform; - const size_t no_index_sentinel = std::numeric_limits::max(); - const WeightArray& weights = cl.GetWeights(); + bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset - const size_t c = index_out_indices.size(); - for (size_t i = 0; i < c; ++i) { - const size_t index_index = index_out_indices[i]; - if (index_index == no_index_sentinel) { - continue; + // + // Now calculate the aiVertexWeights + // + + aiVertexWeight *cursor = nullptr; + + bone->mNumWeights = static_cast(out_indices.size()); + cursor = bone->mWeights = new aiVertexWeight[out_indices.size()]; + + const size_t no_index_sentinel = std::numeric_limits::max(); + const WeightArray& weights = cl->GetWeights(); + + const size_t c = index_out_indices.size(); + for (size_t i = 0; i < c; ++i) { + const size_t index_index = index_out_indices[i]; + + if (index_index == no_index_sentinel) { + continue; + } + + const size_t cc = count_out_indices[i]; + for (size_t j = 0; j < cc; ++j) { + // cursor runs from first element relative to the start + // or relative to the start of the next indexes. + aiVertexWeight& out_weight = *cursor++; + + out_weight.mVertexId = static_cast(out_indices[index_index + j]); + out_weight.mWeight = weights[i]; + } } - const size_t cc = count_out_indices[i]; - for (size_t j = 0; j < cc; ++j) { - aiVertexWeight& out_weight = *cursor++; - - out_weight.mVertexId = static_cast(out_indices[index_index + j]); - out_weight.mWeight = weights[i]; - } + bone_map.insert(std::pair(deformer_name, bone)); } + + std::cout << "bone research: Indicies size: " << out_indices.size() << std::endl; + + // lookup must be populated in case something goes wrong + // this also allocates bones to mesh instance outside + local_mesh_bones.push_back(bone); } void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, @@ -1711,7 +1782,7 @@ namespace Assimp { bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found) unsigned int index; - VideoMap::const_iterator it = textures_converted.find(media); + VideoMap::const_iterator it = textures_converted.find(*media); if (it != textures_converted.end()) { index = (*it).second; textureReady = true; @@ -1719,7 +1790,7 @@ namespace Assimp { else { if (media->ContentLength() > 0) { index = ConvertVideo(*media); - textures_converted[media] = index; + textures_converted[*media] = index; textureReady = true; } } @@ -2243,13 +2314,13 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa if (media != nullptr && media->ContentLength() > 0) { unsigned int index; - VideoMap::const_iterator it = textures_converted.find(media); + VideoMap::const_iterator it = textures_converted.find(*media); if (it != textures_converted.end()) { index = (*it).second; } else { index = ConvertVideo(*media); - textures_converted[media] = index; + textures_converted[*media] = index; } // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) @@ -2677,7 +2748,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa // sanity check whether the input is ok static void validateAnimCurveNodes(const std::vector& curves, bool strictMode) { - const Object* target(NULL); + const Object* target(nullptr); for (const AnimationCurveNode* node : curves) { if (!target) { target = node->Target(); @@ -2708,7 +2779,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa #ifdef ASSIMP_BUILD_DEBUG validateAnimCurveNodes(curves, doc.Settings().strictMode); #endif - const AnimationCurveNode* curve_node = NULL; + const AnimationCurveNode* curve_node = nullptr; for (const AnimationCurveNode* node : curves) { ai_assert(node); @@ -3556,7 +3627,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa ai_assert(!out->mMeshes); ai_assert(!out->mNumMeshes); - // note: the trailing () ensures initialization with NULL - not + // note: the trailing () ensures initialization with nullptr - not // many C++ users seem to know this, so pointing it out to avoid // confusion why this code works. @@ -3603,6 +3674,47 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa } } + void FBXConverter::ConvertOrphantEmbeddedTextures() + { + // in C++14 it could be: + // for (auto&& [id, object] : objects) + for (auto&& id_and_object : doc.Objects()) + { + auto&& id = std::get<0>(id_and_object); + auto&& object = std::get<1>(id_and_object); + // If an object doesn't have parent + if (doc.ConnectionsBySource().count(id) == 0) + { + const Texture* realTexture = nullptr; + try + { + const auto& element = object->GetElement(); + const Token& key = element.KeyToken(); + const char* obtype = key.begin(); + const size_t length = static_cast(key.end() - key.begin()); + if (strncmp(obtype, "Texture", length) == 0) + { + const Texture* texture = static_cast(object->Get()); + if (texture->Media() && texture->Media()->ContentLength() > 0) + { + realTexture = texture; + } + } + } + catch (...) + { + // do nothing + } + if (realTexture) + { + const Video* media = realTexture->Media(); + unsigned int index = ConvertVideo(*media); + textures_converted[*media] = index; + } + } + } + } + // ------------------------------------------------------------------------------------------------ void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones) { diff --git a/code/FBX/FBXConverter.h b/code/FBX/FBXConverter.h index 77ced1950..46693bdca 100644 --- a/code/FBX/FBXConverter.h +++ b/code/FBX/FBXConverter.h @@ -123,7 +123,7 @@ private: // ------------------------------------------------------------------------------------------------ // collect and assign child nodes - void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4()); + void ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node); // ------------------------------------------------------------------------------------------------ void ConvertLights(const Model& model, const std::string &orig_name ); @@ -179,32 +179,35 @@ private: void SetupNodeMetadata(const Model& model, aiNode& nd); // ------------------------------------------------------------------------------------------------ - void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform); + void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform); // ------------------------------------------------------------------------------------------------ // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed - std::vector ConvertMesh(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); + std::vector + ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform); // ------------------------------------------------------------------------------------------------ std::vector ConvertLine(const LineGeometry& line, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); + aiNode *parent, aiNode *root_node); // ------------------------------------------------------------------------------------------------ - aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode& nd); + aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode *parent); // ------------------------------------------------------------------------------------------------ - unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); + unsigned int ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model, + const aiMatrix4x4 &absolute_transform, aiNode *parent, + aiNode *root_node); // ------------------------------------------------------------------------------------------------ - std::vector ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - const aiMatrix4x4& node_global_transform, aiNode& nd); + std::vector + ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node, + const aiMatrix4x4 &absolute_transform); // ------------------------------------------------------------------------------------------------ - unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, - MatIndexArray::value_type index, - const aiMatrix4x4& node_global_transform, aiNode& nd); + unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, MatIndexArray::value_type index, + aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform); // ------------------------------------------------------------------------------------------------ static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits::max() */ @@ -217,17 +220,17 @@ private: * - outputVertStartIndices is only used when a material index is specified, it gives for * each output vertex the DOM index it maps to. */ - void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, - const aiMatrix4x4& node_global_transform = aiMatrix4x4(), - unsigned int materialIndex = NO_MATERIAL_SEPARATION, - std::vector* outputVertStartIndices = NULL); - + void ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform, + aiNode *parent = NULL, aiNode *root_node = NULL, + unsigned int materialIndex = NO_MATERIAL_SEPARATION, + std::vector *outputVertStartIndices = NULL); + // lookup + static const aiNode* GetNodeByName( const aiString& name, aiNode *current_node ); // ------------------------------------------------------------------------------------------------ - void ConvertCluster(std::vector& bones, const Model& /*model*/, const Cluster& cl, - std::vector& out_indices, - std::vector& index_out_indices, - std::vector& count_out_indices, - const aiMatrix4x4& node_global_transform); + void ConvertCluster(std::vector &local_mesh_bones, const Cluster *cl, + std::vector &out_indices, std::vector &index_out_indices, + std::vector &count_out_indices, const aiMatrix4x4 &absolute_transform, + aiNode *parent, aiNode *root_node); // ------------------------------------------------------------------------------------------------ void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, @@ -424,6 +427,10 @@ private: // copy generated meshes, animations, lights, cameras and textures to the output scene void TransferDataToScene(); + // ------------------------------------------------------------------------------------------------ + // FBX file could have embedded textures not connected to anything + void ConvertOrphantEmbeddedTextures(); + private: // 0: not assigned yet, others: index is value - 1 unsigned int defaultMaterialIndex; @@ -435,27 +442,47 @@ private: std::vector cameras; std::vector textures; - using MaterialMap = std::map; + using MaterialMap = std::fbx_unordered_map; MaterialMap materials_converted; - using VideoMap = std::map; + using VideoMap = std::fbx_unordered_map; VideoMap textures_converted; - using MeshMap = std::map >; + using MeshMap = std::fbx_unordered_map >; MeshMap meshes_converted; // fixed node name -> which trafo chain components have animations? - using NodeAnimBitMap = std::map ; + using NodeAnimBitMap = std::fbx_unordered_map ; NodeAnimBitMap node_anim_chain_bits; // number of nodes with the same name - using NodeNameCache = std::unordered_map; + using NodeNameCache = std::fbx_unordered_map; NodeNameCache mNodeNames; + // Deformer name is not the same as a bone name - it does contain the bone name though :) + // Deformer names in FBX are always unique in an FBX file. + std::map bone_map; + double anim_fps; aiScene* const out; const FBX::Document& doc; + + static void BuildBoneList(aiNode *current_node, const aiNode *root_node, const aiScene *scene, + std::vector& bones); + + void BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene, + const std::vector &bones, + std::map &bone_stack, + std::vector &node_stack ); + + static void BuildNodeList(aiNode *current_node, std::vector &nodes); + + static aiNode *GetNodeFromStack(const aiString &node_name, std::vector &nodes); + + static aiNode *GetArmatureRoot(aiNode *bone_node, std::vector &bone_list); + + static bool IsBoneNode(const aiString &bone_name, std::vector &bones); }; } diff --git a/code/FBX/FBXDocument.h b/code/FBX/FBXDocument.h index 18e5c38f1..a60d7d9ef 100644 --- a/code/FBX/FBXDocument.h +++ b/code/FBX/FBXDocument.h @@ -637,6 +637,20 @@ public: return ptr; } + bool operator==(const Video& other) const + { + return ( + type == other.type + && relativeFileName == other.relativeFileName + && fileName == other.fileName + ); + } + + bool operator<(const Video& other) const + { + return std::tie(type, relativeFileName, fileName) < std::tie(other.type, other.relativeFileName, other.fileName); + } + private: std::string type; std::string relativeFileName; @@ -1005,10 +1019,10 @@ public: // during their entire lifetime (Document). FBX files have // up to many thousands of objects (most of which we never use), // so the memory overhead for them should be kept at a minimum. -typedef std::map ObjectMap; +typedef std::fbx_unordered_map ObjectMap; typedef std::fbx_unordered_map > PropertyTemplateMap; -typedef std::multimap ConnectionMap; +typedef std::fbx_unordered_multimap ConnectionMap; /** DOM class for global document settings, a single instance per document can * be accessed via Document.Globals(). */ @@ -1177,4 +1191,25 @@ private: } // Namespace FBX } // Namespace Assimp +namespace std +{ + template <> + struct hash + { + std::size_t operator()(const Assimp::FBX::Video& video) const + { + using std::size_t; + using std::hash; + using std::string; + + size_t res = 17; + res = res * 31 + hash()(video.Name()); + res = res * 31 + hash()(video.RelativeFilename()); + res = res * 31 + hash()(video.Type()); + + return res; + } + }; +} + #endif // INCLUDED_AI_FBX_DOCUMENT_H diff --git a/code/FBX/FBXImporter.cpp b/code/FBX/FBXImporter.cpp index c8c1a6853..afcc1ddc7 100644 --- a/code/FBX/FBXImporter.cpp +++ b/code/FBX/FBXImporter.cpp @@ -48,26 +48,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXImporter.h" -#include "FBXTokenizer.h" -#include "FBXParser.h" -#include "FBXUtil.h" -#include "FBXDocument.h" #include "FBXConverter.h" +#include "FBXDocument.h" +#include "FBXParser.h" +#include "FBXTokenizer.h" +#include "FBXUtil.h" -#include #include -#include +#include #include +#include namespace Assimp { -template<> -const char* LogFunctions::Prefix() { - static auto prefix = "FBX: "; - return prefix; +template <> +const char *LogFunctions::Prefix() { + static auto prefix = "FBX: "; + return prefix; } -} +} // namespace Assimp using namespace Assimp; using namespace Assimp::Formatter; @@ -76,131 +76,123 @@ using namespace Assimp::FBX; namespace { static const aiImporterDesc desc = { - "Autodesk FBX Importer", - "", - "", - "", - aiImporterFlags_SupportTextFlavour, - 0, - 0, - 0, - 0, - "fbx" + "Autodesk FBX Importer", + "", + "", + "", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "fbx" }; } // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by #Importer -FBXImporter::FBXImporter() -{ +FBXImporter::FBXImporter() { } // ------------------------------------------------------------------------------------------------ // Destructor, private as well -FBXImporter::~FBXImporter() -{ +FBXImporter::~FBXImporter() { } // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const -{ - const std::string& extension = GetExtension(pFile); - if (extension == std::string( desc.mFileExtensions ) ) { - return true; - } +bool FBXImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { + const std::string &extension = GetExtension(pFile); + if (extension == std::string(desc.mFileExtensions)) { + return true; + } - else if ((!extension.length() || checkSig) && pIOHandler) { - // at least ASCII-FBX files usually have a 'FBX' somewhere in their head - const char* tokens[] = {"fbx"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); - } - return false; + else if ((!extension.length() || checkSig) && pIOHandler) { + // at least ASCII-FBX files usually have a 'FBX' somewhere in their head + const char *tokens[] = { "fbx" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); + } + return false; } // ------------------------------------------------------------------------------------------------ // List all extensions handled by this loader -const aiImporterDesc* FBXImporter::GetInfo () const -{ - return &desc; +const aiImporterDesc *FBXImporter::GetInfo() const { + return &desc; } // ------------------------------------------------------------------------------------------------ // Setup configuration properties for the loader -void FBXImporter::SetupProperties(const Importer* pImp) -{ - settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); - settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); - settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); - settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true); - settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true); - settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true); - settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true); - settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false); - settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); - settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true); - settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false); - settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true); - settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false); +void FBXImporter::SetupProperties(const Importer *pImp) { + settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); + settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); + settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); + settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true); + settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true); + settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true); + settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true); + settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false); + settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); + settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true); + settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false); + settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true); + settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false); } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. -void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) -{ - std::unique_ptr stream(pIOHandler->Open(pFile,"rb")); - if (!stream) { - ThrowException("Could not open file for reading"); - } +void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { + std::unique_ptr stream(pIOHandler->Open(pFile, "rb")); + if (!stream) { + ThrowException("Could not open file for reading"); + } - // read entire file into memory - no streaming for this, fbx - // files can grow large, but the assimp output data structure - // then becomes very large, too. Assimp doesn't support - // streaming for its output data structures so the net win with - // streaming input data would be very low. - std::vector contents; - contents.resize(stream->FileSize()+1); - stream->Read( &*contents.begin(), 1, contents.size()-1 ); - contents[ contents.size() - 1 ] = 0; - const char* const begin = &*contents.begin(); + // read entire file into memory - no streaming for this, fbx + // files can grow large, but the assimp output data structure + // then becomes very large, too. Assimp doesn't support + // streaming for its output data structures so the net win with + // streaming input data would be very low. + std::vector contents; + contents.resize(stream->FileSize() + 1); + stream->Read(&*contents.begin(), 1, contents.size() - 1); + contents[contents.size() - 1] = 0; + const char *const begin = &*contents.begin(); - // broadphase tokenizing pass in which we identify the core - // syntax elements of FBX (brackets, commas, key:value mappings) - TokenList tokens; - try { + // broadphase tokenizing pass in which we identify the core + // syntax elements of FBX (brackets, commas, key:value mappings) + TokenList tokens; + try { - bool is_binary = false; - if (!strncmp(begin,"Kaydara FBX Binary",18)) { - is_binary = true; - TokenizeBinary(tokens,begin,contents.size()); - } - else { - Tokenize(tokens,begin); - } + bool is_binary = false; + if (!strncmp(begin, "Kaydara FBX Binary", 18)) { + is_binary = true; + TokenizeBinary(tokens, begin, contents.size()); + } else { + Tokenize(tokens, begin); + } - // use this information to construct a very rudimentary - // parse-tree representing the FBX scope structure - Parser parser(tokens, is_binary); + // use this information to construct a very rudimentary + // parse-tree representing the FBX scope structure + Parser parser(tokens, is_binary); - // take the raw parse-tree and convert it to a FBX DOM - Document doc(parser,settings); + // take the raw parse-tree and convert it to a FBX DOM + Document doc(parser, settings); - // convert the FBX DOM to aiScene - ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones); + // convert the FBX DOM to aiScene + ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones); - // size relative to cm - float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor(); + // size relative to cm + float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor(); - // Set FBX file scale is relative to CM must be converted to M for - // assimp universal format (M) - SetFileScale( size_relative_to_cm * 0.01f); + // Set FBX file scale is relative to CM must be converted to M for + // assimp universal format (M) + SetFileScale(size_relative_to_cm * 0.01f); - std::for_each(tokens.begin(),tokens.end(),Util::delete_fun()); - } - catch(std::exception&) { - std::for_each(tokens.begin(),tokens.end(),Util::delete_fun()); - throw; - } + std::for_each(tokens.begin(), tokens.end(), Util::delete_fun()); + } catch (std::exception &) { + std::for_each(tokens.begin(), tokens.end(), Util::delete_fun()); + throw; + } } #endif // !ASSIMP_BUILD_NO_FBX_IMPORTER diff --git a/code/PostProcessing/ArmaturePopulate.cpp b/code/PostProcessing/ArmaturePopulate.cpp new file mode 100644 index 000000000..75daeb6b5 --- /dev/null +++ b/code/PostProcessing/ArmaturePopulate.cpp @@ -0,0 +1,268 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 "ArmaturePopulate.h" + +#include +#include +#include +#include +#include + +namespace Assimp { + +/// The default class constructor. +ArmaturePopulate::ArmaturePopulate() : BaseProcess() +{} + +/// The class destructor. +ArmaturePopulate::~ArmaturePopulate() +{} + +bool ArmaturePopulate::IsActive(unsigned int pFlags) const { + return (pFlags & aiProcess_PopulateArmatureData) != 0; +} + +void ArmaturePopulate::SetupProperties(const Importer *pImp) { + // do nothing +} + +void ArmaturePopulate::Execute(aiScene *out) { + + // Now convert all bone positions to the correct mOffsetMatrix + std::vector bones; + std::vector nodes; + std::map bone_stack; + BuildBoneList(out->mRootNode, out->mRootNode, out, bones); + BuildNodeList(out->mRootNode, nodes); + + BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes); + + ASSIMP_LOG_DEBUG_F("Bone stack size: ", bone_stack.size()); + + for (std::pair kvp : bone_stack) { + aiBone *bone = kvp.first; + aiNode *bone_node = kvp.second; + ASSIMP_LOG_DEBUG_F("active node lookup: ", bone->mName.C_Str()); + // lcl transform grab - done in generate_nodes :) + + // bone->mOffsetMatrix = bone_node->mTransformation; + aiNode *armature = GetArmatureRoot(bone_node, bones); + + ai_assert(armature); + + // set up bone armature id + bone->mArmature = armature; + + // set this bone node to be referenced properly + ai_assert(bone_node); + bone->mNode = bone_node; + } +} + + +/* Reprocess all nodes to calculate bone transforms properly based on the REAL + * mOffsetMatrix not the local. */ +/* Before this would use mesh transforms which is wrong for bone transforms */ +/* Before this would work for simple character skeletons but not complex meshes + * with multiple origins */ +/* Source: sketch fab log cutter fbx */ +void ArmaturePopulate::BuildBoneList(aiNode *current_node, + const aiNode *root_node, + const aiScene *scene, + std::vector &bones) { + ai_assert(scene); + for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { + aiNode *child = current_node->mChildren[nodeId]; + ai_assert(child); + + // check for bones + for (unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId) { + ai_assert(child->mMeshes); + unsigned int mesh_index = child->mMeshes[meshId]; + aiMesh *mesh = scene->mMeshes[mesh_index]; + ai_assert(mesh); + + for (unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) { + aiBone *bone = mesh->mBones[boneId]; + ai_assert(bone); + + // duplicate meshes exist with the same bones sometimes :) + // so this must be detected + if (std::find(bones.begin(), bones.end(), bone) == bones.end()) { + // add the element once + bones.push_back(bone); + } + } + + // find mesh and get bones + // then do recursive lookup for bones in root node hierarchy + } + + BuildBoneList(child, root_node, scene, bones); + } +} + +/* Prepare flat node list which can be used for non recursive lookups later */ +void ArmaturePopulate::BuildNodeList(const aiNode *current_node, + std::vector &nodes) { + ai_assert(current_node); + + for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) { + aiNode *child = current_node->mChildren[nodeId]; + ai_assert(child); + + nodes.push_back(child); + + BuildNodeList(child, nodes); + } +} + +/* A bone stack allows us to have multiple armatures, with the same bone names + * A bone stack allows us also to retrieve bones true transform even with + * duplicate names :) + */ +void ArmaturePopulate::BuildBoneStack(aiNode *current_node, + const aiNode *root_node, + const aiScene *scene, + const std::vector &bones, + std::map &bone_stack, + std::vector &node_stack) { + ai_assert(scene); + ai_assert(root_node); + ai_assert(!node_stack.empty()); + + for (aiBone *bone : bones) { + ai_assert(bone); + aiNode *node = GetNodeFromStack(bone->mName, node_stack); + if (node == nullptr) { + node_stack.clear(); + BuildNodeList(root_node, node_stack); + ASSIMP_LOG_DEBUG_F("Resetting bone stack: nullptr element ", bone->mName.C_Str()); + + node = GetNodeFromStack(bone->mName, node_stack); + + if (!node) { + ASSIMP_LOG_ERROR("serious import issue node for bone was not detected"); + continue; + } + } + + ASSIMP_LOG_DEBUG_F("Successfully added bone[", bone->mName.C_Str(), "] to stack and bone node is: ", node->mName.C_Str()); + + bone_stack.insert(std::pair(bone, node)); + } +} + + +/* Returns the armature root node */ +/* This is required to be detected for a bone initially, it will recurse up + * until it cannot find another bone and return the node No known failure + * points. (yet) + */ +aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, + std::vector &bone_list) { + while (bone_node) { + if (!IsBoneNode(bone_node->mName, bone_list)) { + ASSIMP_LOG_DEBUG_F("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str()); + return bone_node; + } + + bone_node = bone_node->mParent; + } + + ASSIMP_LOG_ERROR("GetArmatureRoot() can't find armature!"); + + return nullptr; +} + + + +/* Simple IsBoneNode check if this could be a bone */ +bool ArmaturePopulate::IsBoneNode(const aiString &bone_name, + std::vector &bones) { + for (aiBone *bone : bones) { + if (bone->mName == bone_name) { + return true; + } + } + + return false; +} + +/* Pop this node by name from the stack if found */ +/* Used in multiple armature situations with duplicate node / bone names */ +/* Known flaw: cannot have nodes with bone names, will be fixed in later release + */ +/* (serious to be fixed) Known flaw: nodes which have more than one bone could + * be prematurely dropped from stack */ +aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name, + std::vector &nodes) { + std::vector::iterator iter; + aiNode *found = nullptr; + for (iter = nodes.begin(); iter < nodes.end(); ++iter) { + aiNode *element = *iter; + ai_assert(element); + // node valid and node name matches + if (element->mName == node_name) { + found = element; + break; + } + } + + if (found != nullptr) { + ASSIMP_LOG_INFO_F("Removed node from stack: ", found->mName.C_Str()); + // now pop the element from the node list + nodes.erase(iter); + + return found; + } + + // unique names can cause this problem + ASSIMP_LOG_ERROR("[Serious] GetNodeFromStack() can't find node from stack!"); + + return nullptr; +} + + + + +} // Namespace Assimp diff --git a/code/PostProcessing/ArmaturePopulate.h b/code/PostProcessing/ArmaturePopulate.h new file mode 100644 index 000000000..aa1ad7c80 --- /dev/null +++ b/code/PostProcessing/ArmaturePopulate.h @@ -0,0 +1,112 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ +#ifndef ARMATURE_POPULATE_H_ +#define ARMATURE_POPULATE_H_ + +#include "Common/BaseProcess.h" +#include +#include +#include + + +struct aiNode; +struct aiBone; + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** Armature Populate: This is a post process designed + * To save you time when importing models into your game engines + * This was originally designed only for fbx but will work with other formats + * it is intended to auto populate aiBone data with armature and the aiNode + * This is very useful when dealing with skinned meshes + * or when dealing with many different skeletons + * It's off by default but recommend that you try it and use it + * It should reduce down any glue code you have in your + * importers + * You can contact RevoluPowered + * For more info about this +*/ +class ASSIMP_API ArmaturePopulate : public BaseProcess { +public: + /// The default class constructor. + ArmaturePopulate(); + + /// The class destructor. + virtual ~ArmaturePopulate(); + + /// Overwritten, @see BaseProcess + virtual bool IsActive( unsigned int pFlags ) const; + + /// Overwritten, @see BaseProcess + virtual void SetupProperties( const Importer* pImp ); + + /// Overwritten, @see BaseProcess + virtual void Execute( aiScene* pScene ); + + static aiNode *GetArmatureRoot(aiNode *bone_node, + std::vector &bone_list); + + static bool IsBoneNode(const aiString &bone_name, + std::vector &bones); + + static aiNode *GetNodeFromStack(const aiString &node_name, + std::vector &nodes); + + static void BuildNodeList(const aiNode *current_node, + std::vector &nodes); + + static void BuildBoneList(aiNode *current_node, const aiNode *root_node, + const aiScene *scene, + std::vector &bones); + + static void BuildBoneStack(aiNode *current_node, const aiNode *root_node, + const aiScene *scene, + const std::vector &bones, + std::map &bone_stack, + std::vector &node_stack); +}; + +} // Namespace Assimp + + +#endif // SCALE_PROCESS_H_ \ No newline at end of file diff --git a/code/glTF2/glTF2AssetWriter.inl b/code/glTF2/glTF2AssetWriter.inl index 5d1b22064..44a1d3e73 100644 --- a/code/glTF2/glTF2AssetWriter.inl +++ b/code/glTF2/glTF2AssetWriter.inl @@ -358,7 +358,7 @@ namespace glTF2 { WriteVec(pbrSpecularGlossiness, pbrSG.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl); if (pbrSG.glossinessFactor != 1) { - WriteFloat(obj, pbrSG.glossinessFactor, "glossinessFactor", w.mAl); + WriteFloat(pbrSpecularGlossiness, pbrSG.glossinessFactor, "glossinessFactor", w.mAl); } WriteTex(pbrSpecularGlossiness, pbrSG.diffuseTexture, "diffuseTexture", w.mAl); diff --git a/code/glTF2/glTF2Exporter.cpp b/code/glTF2/glTF2Exporter.cpp index 4724c2ef4..dc0b76443 100644 --- a/code/glTF2/glTF2Exporter.cpp +++ b/code/glTF2/glTF2Exporter.cpp @@ -320,7 +320,9 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe if (path[0] == '*') { // embedded aiTexture* tex = mScene->mTextures[atoi(&path[1])]; - uint8_t* data = reinterpret_cast(tex->pcData); + // copy data since lifetime control is handed over to the asset + uint8_t* data = new uint8_t[tex->mWidth]; + memcpy(data, tex->pcData, tex->mWidth); texture->source->SetData(data, tex->mWidth, *mAsset); if (tex->achFormatHint[0]) { diff --git a/contrib/irrXML/CXMLReaderImpl.h b/contrib/irrXML/CXMLReaderImpl.h index 6f3bec5fa..a125312a1 100644 --- a/contrib/irrXML/CXMLReaderImpl.h +++ b/contrib/irrXML/CXMLReaderImpl.h @@ -15,6 +15,9 @@ #include //using namespace Assimp; +// For locale independent number conversion +#include +#include #ifdef _DEBUG #define IRR_DEBUGPRINT(x) printf((x)); @@ -178,8 +181,11 @@ public: return 0; core::stringc c = attrvalue; - return static_cast(atof(c.c_str())); - //return fast_atof(c.c_str()); + std::istringstream sstr(c.c_str()); + sstr.imbue(std::locale("C")); // Locale free number convert + float fNum; + sstr >> fNum; + return fNum; } diff --git a/include/assimp/defs.h b/include/assimp/defs.h index 71579a5c7..6f2f8ae88 100644 --- a/include/assimp/defs.h +++ b/include/assimp/defs.h @@ -128,16 +128,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * GENBOUNDINGBOXES */ ////////////////////////////////////////////////////////////////////////// -#ifdef _MSC_VER +#ifdef _WIN32 # undef ASSIMP_API - ////////////////////////////////////////////////////////////////////////// /* Define 'ASSIMP_BUILD_DLL_EXPORT' to build a DLL of the library */ ////////////////////////////////////////////////////////////////////////// # ifdef ASSIMP_BUILD_DLL_EXPORT # define ASSIMP_API __declspec(dllexport) # define ASSIMP_API_WINONLY __declspec(dllexport) -# pragma warning (disable : 4251) ////////////////////////////////////////////////////////////////////////// /* Define 'ASSIMP_DLL' before including Assimp to link to ASSIMP in @@ -150,7 +148,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # define ASSIMP_API # define ASSIMP_API_WINONLY # endif +#elif defined(SWIG) + /* Do nothing, the relevant defines are all in AssimpSwigPort.i */ + +#else +# define ASSIMP_API __attribute__ ((visibility("default"))) +# define ASSIMP_API_WINONLY +#endif + +#ifdef _MSC_VER +# ifdef ASSIMP_BUILD_DLL_EXPORT +# pragma warning (disable : 4251) +# endif /* Force the compiler to inline a function, if possible */ # define AI_FORCE_INLINE __forceinline @@ -158,17 +168,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* Tells the compiler that a function never returns. Used in code analysis * to skip dead paths (e.g. after an assertion evaluated to false). */ # define AI_WONT_RETURN __declspec(noreturn) - #elif defined(SWIG) /* Do nothing, the relevant defines are all in AssimpSwigPort.i */ #else - # define AI_WONT_RETURN - -# define ASSIMP_API __attribute__ ((visibility("default"))) -# define ASSIMP_API_WINONLY # define AI_FORCE_INLINE inline #endif // (defined _MSC_VER) diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index eb30ad5df..fbf2a857a 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -252,6 +252,9 @@ struct aiVertexWeight { }; +// Forward declare aiNode (pointer use only) +struct aiNode; + // --------------------------------------------------------------------------- /** @brief A single bone of a mesh. * @@ -268,6 +271,16 @@ struct aiBone { //! The maximum value for this member is #AI_MAX_BONE_WEIGHTS. unsigned int mNumWeights; +#ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS + // The bone armature node - used for skeleton conversion + // you must enable aiProcess_PopulateArmatureData to populate this + C_STRUCT aiNode* mArmature; + + // The bone node in the scene - used for skeleton conversion + // you must enable aiProcess_PopulateArmatureData to populate this + C_STRUCT aiNode* mNode; + +#endif //! The influence weights of this bone, by vertex index. C_STRUCT aiVertexWeight* mWeights; @@ -422,11 +435,11 @@ struct aiAnimMesh /**Anim Mesh name */ C_STRUCT aiString mName; - /** Replacement for aiMesh::mVertices. If this array is non-NULL, + /** Replacement for aiMesh::mVertices. If this array is non-nullptr, * it *must* contain mNumVertices entries. The corresponding - * array in the host mesh must be non-NULL as well - animation + * array in the host mesh must be non-nullptr as well - animation * meshes may neither add or nor remove vertex components (if - * a replacement array is NULL and the corresponding source + * a replacement array is nullptr and the corresponding source * array is not, the source data is taken instead)*/ C_STRUCT aiVector3D* mVertices; @@ -600,7 +613,7 @@ struct aiMesh C_STRUCT aiVector3D* mVertices; /** Vertex normals. - * The array contains normalized vectors, NULL if not present. + * The array contains normalized vectors, nullptr if not present. * The array is mNumVertices in size. Normals are undefined for * point and line primitives. A mesh consisting of points and * lines only may not have normal vectors. Meshes with mixed @@ -623,7 +636,7 @@ struct aiMesh /** Vertex tangents. * The tangent of a vertex points in the direction of the positive - * X texture axis. The array contains normalized vectors, NULL if + * X texture axis. The array contains normalized vectors, nullptr if * not present. The array is mNumVertices in size. A mesh consisting * of points and lines only may not have normal vectors. Meshes with * mixed primitive types (i.e. lines and triangles) may have @@ -637,7 +650,7 @@ struct aiMesh /** Vertex bitangents. * The bitangent of a vertex points in the direction of the positive - * Y texture axis. The array contains normalized vectors, NULL if not + * Y texture axis. The array contains normalized vectors, nullptr if not * present. The array is mNumVertices in size. * @note If the mesh contains tangents, it automatically also contains * bitangents. @@ -646,14 +659,14 @@ struct aiMesh /** Vertex color sets. * A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex - * colors per vertex. NULL if not present. Each array is + * colors per vertex. nullptr if not present. Each array is * mNumVertices in size if present. */ C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; /** Vertex texture coords, also known as UV channels. * A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per - * vertex. NULL if not present. The array is mNumVertices in size. + * vertex. nullptr if not present. The array is mNumVertices in size. */ C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; @@ -675,7 +688,7 @@ struct aiMesh C_STRUCT aiFace* mFaces; /** The number of bones this mesh contains. - * Can be 0, in which case the mBones array is NULL. + * Can be 0, in which case the mBones array is nullptr. */ unsigned int mNumBones; @@ -773,7 +786,10 @@ struct aiMesh // DO NOT REMOVE THIS ADDITIONAL CHECK if (mNumBones && mBones) { for( unsigned int a = 0; a < mNumBones; a++) { - delete mBones[a]; + if(mBones[a]) + { + delete mBones[a]; + } } delete [] mBones; } diff --git a/include/assimp/postprocess.h b/include/assimp/postprocess.h index 77d387c7e..4b6732e80 100644 --- a/include/assimp/postprocess.h +++ b/include/assimp/postprocess.h @@ -320,6 +320,19 @@ enum aiPostProcessSteps */ aiProcess_FixInfacingNormals = 0x2000, + + + // ------------------------------------------------------------------------- + /** + * This step generically populates aiBone->mArmature and aiBone->mNode generically + * The point of these is it saves you later having to calculate these elements + * This is useful when handling rest information or skin information + * If you have multiple armatures on your models we strongly recommend enabling this + * Instead of writing your own multi-root, multi-armature lookups we have done the + * hard work for you :) + */ + aiProcess_PopulateArmatureData = 0x4000, + // ------------------------------------------------------------------------- /**
This step splits meshes with more than one primitive type in * homogeneous sub-meshes. @@ -537,6 +550,8 @@ enum aiPostProcessSteps */ aiProcess_Debone = 0x4000000, + + // ------------------------------------------------------------------------- /**
This step will perform a global scale of the model. * diff --git a/include/assimp/scene.h b/include/assimp/scene.h index e69c81803..b76709eb1 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -110,13 +110,13 @@ struct ASSIMP_API aiNode /** The transformation relative to the node's parent. */ C_STRUCT aiMatrix4x4 mTransformation; - /** Parent node. NULL if this node is the root node. */ + /** Parent node. nullptr if this node is the root node. */ C_STRUCT aiNode* mParent; /** The number of child nodes of this node. */ unsigned int mNumChildren; - /** The child nodes of this node. NULL if mNumChildren is 0. */ + /** The child nodes of this node. nullptr if mNumChildren is 0. */ C_STRUCT aiNode** mChildren; /** The number of meshes of this node. */ @@ -127,7 +127,7 @@ struct ASSIMP_API aiNode */ unsigned int* mMeshes; - /** Metadata associated with this node or NULL if there is no metadata. + /** Metadata associated with this node or nullptr if there is no metadata. * Whether any metadata is generated depends on the source file format. See the * @link importer_notes @endlink page for more information on every source file * format. Importers that don't document any metadata don't write any. @@ -149,7 +149,7 @@ struct ASSIMP_API aiNode * of the scene. * * @param name Name to search for - * @return NULL or a valid Node if the search was successful. + * @return nullptr or a valid Node if the search was successful. */ inline const aiNode* FindNode(const aiString& name) const { @@ -344,7 +344,7 @@ struct aiScene #ifdef __cplusplus - //! Default constructor - set everything to 0/NULL + //! Default constructor - set everything to 0/nullptr ASSIMP_API aiScene(); //! Destructor @@ -353,33 +353,33 @@ struct aiScene //! Check whether the scene contains meshes //! Unless no special scene flags are set this will always be true. inline bool HasMeshes() const { - return mMeshes != NULL && mNumMeshes > 0; + return mMeshes != nullptr && mNumMeshes > 0; } //! Check whether the scene contains materials //! Unless no special scene flags are set this will always be true. inline bool HasMaterials() const { - return mMaterials != NULL && mNumMaterials > 0; + return mMaterials != nullptr && mNumMaterials > 0; } //! Check whether the scene contains lights inline bool HasLights() const { - return mLights != NULL && mNumLights > 0; + return mLights != nullptr && mNumLights > 0; } //! Check whether the scene contains textures inline bool HasTextures() const { - return mTextures != NULL && mNumTextures > 0; + return mTextures != nullptr && mNumTextures > 0; } //! Check whether the scene contains cameras inline bool HasCameras() const { - return mCameras != NULL && mNumCameras > 0; + return mCameras != nullptr && mNumCameras > 0; } //! Check whether the scene contains animations inline bool HasAnimations() const { - return mAnimations != NULL && mNumAnimations > 0; + return mAnimations != nullptr && mNumAnimations > 0; } //! Returns a short filename from a full path diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 913813c3b..f59ce000a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -148,6 +148,7 @@ SET( POST_PROCESSES unit/utRemoveRedundantMaterials.cpp unit/utRemoveVCProcess.cpp unit/utScaleProcess.cpp + unit/utArmaturePopulate.cpp unit/utJoinVertices.cpp unit/utRemoveComments.cpp unit/utRemoveComponent.cpp diff --git a/test/models/Collada/duck.zae b/test/models/Collada/duck.zae index 978235355..7e6748cb0 100644 Binary files a/test/models/Collada/duck.zae and b/test/models/Collada/duck.zae differ diff --git a/test/models/FBX/box_orphant_embedded_texture.fbx b/test/models/FBX/box_orphant_embedded_texture.fbx new file mode 100644 index 000000000..d874751d0 --- /dev/null +++ b/test/models/FBX/box_orphant_embedded_texture.fbx @@ -0,0 +1,723 @@ +; FBX 7.5.0 project file +; ---------------------------------------------------- + +FBXHeaderExtension: { + FBXHeaderVersion: 1003 + FBXVersion: 7500 + CreationTimeStamp: { + Version: 1000 + Year: 2019 + Month: 3 + Day: 1 + Hour: 12 + Minute: 46 + Second: 3 + Millisecond: 995 + } + Creator: "FBX SDK/FBX Plugins version 2018.1.1" + SceneInfo: "SceneInfo::GlobalInfo", "UserData" { + Type: "UserData" + Version: 100 + MetaData: { + Version: 100 + Title: "" + Subject: "" + Author: "" + Keywords: "" + Revision: "" + Comment: "" + } + Properties70: { + P: "DocumentUrl", "KString", "Url", "", "U:\Some\Absolute\Path\box_embedded_texture.fbx" + P: "SrcDocumentUrl", "KString", "Url", "", "U:\Some\Absolute\Path\box_embedded_texture.fbx" + P: "Original", "Compound", "", "" + P: "Original|ApplicationVendor", "KString", "", "", "Autodesk" + P: "Original|ApplicationName", "KString", "", "", "Maya" + P: "Original|ApplicationVersion", "KString", "", "", "201800" + P: "Original|DateTime_GMT", "DateTime", "", "", "01/03/2019 12:46:03.994" + P: "Original|FileName", "KString", "", "", "U:\Some\Absolute\Path\box_embedded_texture.fbx" + P: "LastSaved", "Compound", "", "" + P: "LastSaved|ApplicationVendor", "KString", "", "", "Autodesk" + P: "LastSaved|ApplicationName", "KString", "", "", "Maya" + P: "LastSaved|ApplicationVersion", "KString", "", "", "201800" + P: "LastSaved|DateTime_GMT", "DateTime", "", "", "01/03/2019 12:46:03.994" + P: "Original|ApplicationActiveProject", "KString", "", "", "U:\Some\Absolute\Path" + } + } +} +GlobalSettings: { + Version: 1000 + Properties70: { + P: "UpAxis", "int", "Integer", "",1 + P: "UpAxisSign", "int", "Integer", "",1 + P: "FrontAxis", "int", "Integer", "",2 + P: "FrontAxisSign", "int", "Integer", "",1 + P: "CoordAxis", "int", "Integer", "",0 + P: "CoordAxisSign", "int", "Integer", "",1 + P: "OriginalUpAxis", "int", "Integer", "",1 + P: "OriginalUpAxisSign", "int", "Integer", "",1 + P: "UnitScaleFactor", "double", "Number", "",100 + P: "OriginalUnitScaleFactor", "double", "Number", "",1 + P: "AmbientColor", "ColorRGB", "Color", "",0,0,0 + P: "DefaultCamera", "KString", "", "", "Producer Perspective" + P: "TimeMode", "enum", "", "",6 + P: "TimeProtocol", "enum", "", "",2 + P: "SnapOnFrameMode", "enum", "", "",0 + P: "TimeSpanStart", "KTime", "Time", "",0 + P: "TimeSpanStop", "KTime", "Time", "",153953860000 + P: "CustomFrameRate", "double", "Number", "",-1 + P: "TimeMarker", "Compound", "", "" + P: "CurrentTimeMarker", "int", "Integer", "",-1 + } +} + +; Documents Description +;------------------------------------------------------------------ + +Documents: { + Count: 1 + Document: 2957686739424, "", "Scene" { + Properties70: { + P: "SourceObject", "object", "", "" + P: "ActiveAnimStackName", "KString", "", "", "Take 001" + } + RootNode: 0 + } +} + +; Document References +;------------------------------------------------------------------ + +References: { +} + +; Object definitions +;------------------------------------------------------------------ + +Definitions: { + Version: 100 + Count: 17 + ObjectType: "GlobalSettings" { + Count: 1 + } + ObjectType: "AnimationStack" { + Count: 1 + PropertyTemplate: "FbxAnimStack" { + Properties70: { + P: "Description", "KString", "", "", "" + P: "LocalStart", "KTime", "Time", "",0 + P: "LocalStop", "KTime", "Time", "",0 + P: "ReferenceStart", "KTime", "Time", "",0 + P: "ReferenceStop", "KTime", "Time", "",0 + } + } + } + ObjectType: "AnimationLayer" { + Count: 1 + PropertyTemplate: "FbxAnimLayer" { + Properties70: { + P: "Weight", "Number", "", "A",100 + P: "Mute", "bool", "", "",0 + P: "Solo", "bool", "", "",0 + P: "Lock", "bool", "", "",0 + P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8 + P: "BlendMode", "enum", "", "",0 + P: "RotationAccumulationMode", "enum", "", "",0 + P: "ScaleAccumulationMode", "enum", "", "",0 + P: "BlendModeBypass", "ULongLong", "", "",0 + } + } + } + ObjectType: "Geometry" { + Count: 1 + PropertyTemplate: "FbxMesh" { + Properties70: { + P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8 + P: "BBoxMin", "Vector3D", "Vector", "",0,0,0 + P: "BBoxMax", "Vector3D", "Vector", "",0,0,0 + P: "Primary Visibility", "bool", "", "",1 + P: "Casts Shadows", "bool", "", "",1 + P: "Receive Shadows", "bool", "", "",1 + } + } + } + ObjectType: "Material" { + Count: 1 + PropertyTemplate: "FbxSurfacePhong" { + Properties70: { + P: "ShadingModel", "KString", "", "", "Phong" + P: "MultiLayer", "bool", "", "",0 + P: "EmissiveColor", "Color", "", "A",0,0,0 + P: "EmissiveFactor", "Number", "", "A",1 + P: "AmbientColor", "Color", "", "A",0.2,0.2,0.2 + P: "AmbientFactor", "Number", "", "A",1 + P: "DiffuseColor", "Color", "", "A",0.8,0.8,0.8 + P: "DiffuseFactor", "Number", "", "A",1 + P: "Bump", "Vector3D", "Vector", "",0,0,0 + P: "NormalMap", "Vector3D", "Vector", "",0,0,0 + P: "BumpFactor", "double", "Number", "",1 + P: "TransparentColor", "Color", "", "A",0,0,0 + P: "TransparencyFactor", "Number", "", "A",0 + P: "DisplacementColor", "ColorRGB", "Color", "",0,0,0 + P: "DisplacementFactor", "double", "Number", "",1 + P: "VectorDisplacementColor", "ColorRGB", "Color", "",0,0,0 + P: "VectorDisplacementFactor", "double", "Number", "",1 + P: "SpecularColor", "Color", "", "A",0.2,0.2,0.2 + P: "SpecularFactor", "Number", "", "A",1 + P: "ShininessExponent", "Number", "", "A",20 + P: "ReflectionColor", "Color", "", "A",0,0,0 + P: "ReflectionFactor", "Number", "", "A",1 + } + } + } + ObjectType: "Texture" { + Count: 1 + PropertyTemplate: "FbxFileTexture" { + Properties70: { + P: "TextureTypeUse", "enum", "", "",0 + P: "Texture alpha", "Number", "", "A",1 + P: "CurrentMappingType", "enum", "", "",0 + P: "WrapModeU", "enum", "", "",0 + P: "WrapModeV", "enum", "", "",0 + P: "UVSwap", "bool", "", "",0 + P: "PremultiplyAlpha", "bool", "", "",1 + P: "Translation", "Vector", "", "A",0,0,0 + P: "Rotation", "Vector", "", "A",0,0,0 + P: "Scaling", "Vector", "", "A",1,1,1 + P: "TextureRotationPivot", "Vector3D", "Vector", "",0,0,0 + P: "TextureScalingPivot", "Vector3D", "Vector", "",0,0,0 + P: "CurrentTextureBlendMode", "enum", "", "",1 + P: "UVSet", "KString", "", "", "default" + P: "UseMaterial", "bool", "", "",0 + P: "UseMipMap", "bool", "", "",0 + } + } + } + ObjectType: "Model" { + Count: 1 + PropertyTemplate: "FbxNode" { + Properties70: { + P: "QuaternionInterpolate", "enum", "", "",0 + P: "RotationOffset", "Vector3D", "Vector", "",0,0,0 + P: "RotationPivot", "Vector3D", "Vector", "",0,0,0 + P: "ScalingOffset", "Vector3D", "Vector", "",0,0,0 + P: "ScalingPivot", "Vector3D", "Vector", "",0,0,0 + P: "TranslationActive", "bool", "", "",0 + P: "TranslationMin", "Vector3D", "Vector", "",0,0,0 + P: "TranslationMax", "Vector3D", "Vector", "",0,0,0 + P: "TranslationMinX", "bool", "", "",0 + P: "TranslationMinY", "bool", "", "",0 + P: "TranslationMinZ", "bool", "", "",0 + P: "TranslationMaxX", "bool", "", "",0 + P: "TranslationMaxY", "bool", "", "",0 + P: "TranslationMaxZ", "bool", "", "",0 + P: "RotationOrder", "enum", "", "",0 + P: "RotationSpaceForLimitOnly", "bool", "", "",0 + P: "RotationStiffnessX", "double", "Number", "",0 + P: "RotationStiffnessY", "double", "Number", "",0 + P: "RotationStiffnessZ", "double", "Number", "",0 + P: "AxisLen", "double", "Number", "",10 + P: "PreRotation", "Vector3D", "Vector", "",0,0,0 + P: "PostRotation", "Vector3D", "Vector", "",0,0,0 + P: "RotationActive", "bool", "", "",0 + P: "RotationMin", "Vector3D", "Vector", "",0,0,0 + P: "RotationMax", "Vector3D", "Vector", "",0,0,0 + P: "RotationMinX", "bool", "", "",0 + P: "RotationMinY", "bool", "", "",0 + P: "RotationMinZ", "bool", "", "",0 + P: "RotationMaxX", "bool", "", "",0 + P: "RotationMaxY", "bool", "", "",0 + P: "RotationMaxZ", "bool", "", "",0 + P: "InheritType", "enum", "", "",0 + P: "ScalingActive", "bool", "", "",0 + P: "ScalingMin", "Vector3D", "Vector", "",0,0,0 + P: "ScalingMax", "Vector3D", "Vector", "",1,1,1 + P: "ScalingMinX", "bool", "", "",0 + P: "ScalingMinY", "bool", "", "",0 + P: "ScalingMinZ", "bool", "", "",0 + P: "ScalingMaxX", "bool", "", "",0 + P: "ScalingMaxY", "bool", "", "",0 + P: "ScalingMaxZ", "bool", "", "",0 + P: "GeometricTranslation", "Vector3D", "Vector", "",0,0,0 + P: "GeometricRotation", "Vector3D", "Vector", "",0,0,0 + P: "GeometricScaling", "Vector3D", "Vector", "",1,1,1 + P: "MinDampRangeX", "double", "Number", "",0 + P: "MinDampRangeY", "double", "Number", "",0 + P: "MinDampRangeZ", "double", "Number", "",0 + P: "MaxDampRangeX", "double", "Number", "",0 + P: "MaxDampRangeY", "double", "Number", "",0 + P: "MaxDampRangeZ", "double", "Number", "",0 + P: "MinDampStrengthX", "double", "Number", "",0 + P: "MinDampStrengthY", "double", "Number", "",0 + P: "MinDampStrengthZ", "double", "Number", "",0 + P: "MaxDampStrengthX", "double", "Number", "",0 + P: "MaxDampStrengthY", "double", "Number", "",0 + P: "MaxDampStrengthZ", "double", "Number", "",0 + P: "PreferedAngleX", "double", "Number", "",0 + P: "PreferedAngleY", "double", "Number", "",0 + P: "PreferedAngleZ", "double", "Number", "",0 + P: "LookAtProperty", "object", "", "" + P: "UpVectorProperty", "object", "", "" + P: "Show", "bool", "", "",1 + P: "NegativePercentShapeSupport", "bool", "", "",1 + P: "DefaultAttributeIndex", "int", "Integer", "",-1 + P: "Freeze", "bool", "", "",0 + P: "LODBox", "bool", "", "",0 + P: "Lcl Translation", "Lcl Translation", "", "A",0,0,0 + P: "Lcl Rotation", "Lcl Rotation", "", "A",0,0,0 + P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1 + P: "Visibility", "Visibility", "", "A",1 + P: "Visibility Inheritance", "Visibility Inheritance", "", "",1 + } + } + } + ObjectType: "AnimationCurveNode" { + Count: 8 + PropertyTemplate: "FbxAnimCurveNode" { + Properties70: { + P: "d", "Compound", "", "" + } + } + } + ObjectType: "CollectionExclusive" { + Count: 1 + PropertyTemplate: "FbxDisplayLayer" { + Properties70: { + P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8 + P: "Show", "bool", "", "",1 + P: "Freeze", "bool", "", "",0 + P: "LODBox", "bool", "", "",0 + } + } + } + ObjectType: "Video" { + Count: 1 + PropertyTemplate: "FbxVideo" { + Properties70: { + P: "Path", "KString", "XRefUrl", "", "" + P: "RelPath", "KString", "XRefUrl", "", "" + P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8 + P: "ClipIn", "KTime", "Time", "",0 + P: "ClipOut", "KTime", "Time", "",0 + P: "Offset", "KTime", "Time", "",0 + P: "PlaySpeed", "double", "Number", "",0 + P: "FreeRunning", "bool", "", "",0 + P: "Loop", "bool", "", "",0 + P: "Mute", "bool", "", "",0 + P: "AccessMode", "enum", "", "",0 + P: "ImageSequence", "bool", "", "",0 + P: "ImageSequenceOffset", "int", "Integer", "",0 + P: "FrameRate", "double", "Number", "",0 + P: "LastFrame", "int", "Integer", "",0 + P: "Width", "int", "Integer", "",0 + P: "Height", "int", "Integer", "",0 + P: "StartFrame", "int", "Integer", "",0 + P: "StopFrame", "int", "Integer", "",0 + P: "InterlaceMode", "enum", "", "",0 + } + } + } +} + +; Object properties +;------------------------------------------------------------------ + +Objects: { + Geometry: 2957764348544, "Geometry::", "Mesh" { + Vertices: *24 { + a: -0.5,-0.5,-0.5,0.5,-0.50000011920929,-0.5,-0.5,0.50000011920929,-0.5,0.5,0.50000011920929,-0.5,-0.5,-0.500000059604645,0.5,0.5,-0.500000059604645,0.5,-0.5,0.500000059604645,0.5,0.5,0.500000059604645,0.5 + } + PolygonVertexIndex: *24 { + a: 0,2,3,-2,4,5,7,-7,0,1,5,-5,1,3,7,-6,3,2,6,-8,2,0,4,-7 + } + Edges: *12 { + a: 0,1,2,3,4,5,6,7,9,11,13,17 + } + GeometryVersion: 124 + LayerElementNormal: 0 { + Version: 102 + Name: "" + MappingInformationType: "ByPolygonVertex" + ReferenceInformationType: "Direct" + Normals: *72 { + a: 0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,-5.96046447753906e-08,-1,0,-5.96046447753906e-08,-1,0,-5.96046447753906e-08,-1,0,-5.96046447753906e-08,-1,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,5.96046447753906e-08,0,1,5.96046447753906e-08,0,1,5.96046447753906e-08,0,1,5.96046447753906e-08,-1,0,0,-1,0,0,-1,0,0,-1,0,0 + } + NormalsW: *24 { + a: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + } + } + LayerElementBinormal: 0 { + Version: 102 + Name: "UVChannel_1" + MappingInformationType: "ByPolygonVertex" + ReferenceInformationType: "Direct" + Binormals: *72 { + a: 1.19209289550781e-07,1,0,5.96046447753906e-08,1,0,0,1,0,5.96046447753906e-08,1,0,0,1,-0,0,1,-0,0,1,-0,0,1,-0,0,0,1,0,0,1,0,0,1,0,0,1,-0,0,1,-0,0,1,-0,0,1,-0,0,1,-0,-5.96046447753906e-08,1,-0,-5.96046447753906e-08,1,-0,-5.96046447753906e-08,1,-0,-5.96046447753906e-08,1,0,0,1,0,0,1,0,0,1,0,0,1 + } + BinormalsW: *24 { + a: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + } + + } + LayerElementBinormal: 1 { + Version: 102 + Name: "UVChannel_3" + MappingInformationType: "ByPolygonVertex" + ReferenceInformationType: "Direct" + Binormals: *72 { + a: -1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-0,0,-1,-0,0,-1,0,-0,-1,-0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,5.96046447753906e-08,-1,0,5.96046447753906e-08,-1,0,5.96046447753906e-08,-1,0,5.96046447753906e-08,-1,-0,0,-1,-0,0,-1,-0,0,-1,-0,0,-1 + } + BinormalsW: *24 { + a: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + } + + } + LayerElementTangent: 0 { + Version: 102 + Name: "UVChannel_1" + MappingInformationType: "ByPolygonVertex" + ReferenceInformationType: "Direct" + Tangents: *72 { + a: -1,1.19209289550781e-07,0,-1,5.96046447753906e-08,0,-1,-0,0,-1,5.96046447753906e-08,0,1,-0,-0,1,-0,0,1,-0,0,1,-0,0,1,-5.96046447753906e-08,-0,1,-5.96046447753906e-08,0,1,-5.96046447753906e-08,0,1,-5.96046447753906e-08,0,-0,1,-0,0,1,-0,0,1,-0,0,1,-0,-1,0,-0,-1,0,-0,-1,0,-0,-1,0,-0,0,-1,-0,0,-1,0,0,-1,0,0,-1,0 + } + TangentsW: *24 { + a: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + } + } + LayerElementTangent: 1 { + Version: 102 + Name: "UVChannel_3" + MappingInformationType: "ByPolygonVertex" + ReferenceInformationType: "Direct" + Tangents: *72 { + a: -0,-1,0,-0,-1,0,-0,-1,0,-0,-1,0,0,1,0,0,1,0,-0,1,-0,0,1,0,-1,5.96046447753906e-08,0,-1,5.96046447753906e-08,0,-1,5.96046447753906e-08,-0,-1,5.96046447753906e-08,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,1,-0,-0,1,-0,-0,1,-0,-0,1,-0,-0,0,1,-0,0,1,-0,0,1,-0,0,1,-0 + } + TangentsW: *24 { + a: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + } + } + LayerElementUV: 0 { + Version: 101 + Name: "UVChannel_1" + MappingInformationType: "ByPolygonVertex" + ReferenceInformationType: "IndexToDirect" + UV: *48 { + a: 1,0,0,0,1,1,0,1,0,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1 + } + UVIndex: *24 { + a: 0,2,3,1,4,5,7,6,8,9,11,10,12,13,15,14,16,17,19,18,20,21,23,22 + } + } + LayerElementUV: 1 { + Version: 101 + Name: "UVChannel_3" + MappingInformationType: "ByPolygonVertex" + ReferenceInformationType: "IndexToDirect" + UV: *48 { + a: 0.28125,0.28125,0,0.28125,0,0,0.28125,0,0.34375,1,0.34375,0.71875,0.625,0.71875,0.625,1,0.28125,0.65625,0,0.65625,0,0.375,0.28125,0.375,0.625,0.28125,0.34375,0.28125,0.34375,0,0.625,0,0.28125,1,0,1,0,0.71875,0.28125,0.71875,0.625,0.65625,0.34375,0.65625,0.34375,0.375,0.625,0.375 + } + UVIndex: *24 { + a: 0,1,2,3,4,5,6,7,20,21,22,23,8,9,10,11,16,17,18,19,12,13,14,15 + } + } + LayerElementSmoothing: 0 { + Version: 102 + Name: "" + MappingInformationType: "ByEdge" + ReferenceInformationType: "Direct" + Smoothing: *12 { + a: 0,0,0,0,0,0,0,0,0,0,0,0 + } + } + LayerElementMaterial: 0 { + Version: 101 + Name: "" + MappingInformationType: "AllSame" + ReferenceInformationType: "IndexToDirect" + Materials: *1 { + a: 0 + } + } + Layer: 0 { + Version: 100 + LayerElement: { + Type: "LayerElementNormal" + TypedIndex: 0 + } + LayerElement: { + Type: "LayerElementBinormal" + TypedIndex: 0 + } + LayerElement: { + Type: "LayerElementTangent" + TypedIndex: 0 + } + LayerElement: { + Type: "LayerElementMaterial" + TypedIndex: 0 + } + LayerElement: { + Type: "LayerElementSmoothing" + TypedIndex: 0 + } + LayerElement: { + Type: "LayerElementUV" + TypedIndex: 0 + } + } + Layer: 1 { + Version: 100 + LayerElement: { + Type: "LayerElementBinormal" + TypedIndex: 1 + } + LayerElement: { + Type: "LayerElementTangent" + TypedIndex: 1 + } + LayerElement: { + Type: "LayerElementUV" + TypedIndex: 1 + } + } + } + Model: 2957618625584, "Model::Box", "Mesh" { + Version: 232 + Properties70: { + P: "RotationActive", "bool", "", "",1 + P: "InheritType", "enum", "", "",1 + P: "ScalingMax", "Vector3D", "Vector", "",0,0,0 + P: "DefaultAttributeIndex", "int", "Integer", "",0 + P: "Lcl Translation", "Lcl Translation", "", "A",0,0.5,2.18556946492754e-06 + P: "Lcl Rotation", "Lcl Rotation", "", "A",-90,0,0 + P: "currentUVSet", "KString", "", "U", "UVChannel_1" + P: "mr displacement use global settings", "Bool", "", "A+U",1 + P: "mr displacement view dependent", "Bool", "", "A+U",1 + P: "mr displacement method", "Integer", "", "A+U",6,6,6 + P: "mr displacement smoothing on", "Bool", "", "A+U",1 + P: "mr displacement edge length", "Number", "", "A+U",2,2,2 + P: "mr displacement max displace", "Number", "", "A+U",20,20,20 + P: "mr displacement parametric subdivision level", "Integer", "", "A+U",5,5,5 + P: "MaxHandle", "Integer", "", "A+UH",1,0,0 + } + Shading: T + Culling: "CullingOff" + } + Material: 2957776713840, "Material::Default", "" { + Version: 102 + ShadingModel: "phong" + MultiLayer: 0 + Properties70: { + P: "AmbientColor", "Color", "", "A",0,0,0 + P: "DiffuseColor", "Color", "", "A",1,1,1 + P: "TransparencyFactor", "Number", "", "A",1 + P: "SpecularColor", "Color", "", "A",0,0,0 + P: "ShininessExponent", "Number", "", "A",2 + P: "Emissive", "Vector3D", "Vector", "",0,0,0 + P: "Ambient", "Vector3D", "Vector", "",0,0,0 + P: "Diffuse", "Vector3D", "Vector", "",1,1,1 + P: "Specular", "Vector3D", "Vector", "",0,0,0 + P: "Shininess", "double", "Number", "",2 + P: "Opacity", "double", "Number", "",1 + P: "Reflectivity", "double", "Number", "",0 + } + } + Video: 2957776707120, "Video::Map #2", "Clip" { + Type: "Clip" + Properties70: { + P: "Path", "KString", "XRefUrl", "", "U:/Some/Absolute/Primitives/GridGrey.tga" + P: "RelPath", "KString", "XRefUrl", "", "..\Primitives\GridGrey.tga" + } + UseMipMap: 0 + Filename: "U:/Some/Absolute/Primitives/GridGrey.tga" + RelativeFilename: "..\Primitives\GridGrey.tga" + } + Video: 2957776707121, "Video::Map #2", "Clip" { + Type: "Clip" + Properties70: { + P: "Path", "KString", "XRefUrl", "", "U:/Some/Absolute/Primitives/GridGrey.tga" + P: "RelPath", "KString", "XRefUrl", "", "..\Primitives\GridGrey.tga" + } + UseMipMap: 0 + Filename: "U:/Some/Absolute/Primitives/GridGrey.tga" + RelativeFilename: "..\Primitives\GridGrey.tga" + Content: , + "AAAKAAAAAAAAAAAAAAEAARgAh+rq6v+Pj4/wj4+Phurq6ofq6uq2dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+3dHR0hurq6oHq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6oHq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6oHq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6oHq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6oHq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6oHq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6gCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+Pj/+Pj4//j4+P/4+Pj/+Pj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj48Aj4+PvXR0dIGPj4+9dHR0gY+Pj710dHSBj4+PvXR0dACPj4//j4+P/4+Pj/+Pj4//j4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+PAI+Pj710dHSBj4+PvXR0dIGPj4+9dHR0gY+Pj710dHQAj4+P/4+Pj/+Pj4//j4+P/4+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+PjwCPj4+9dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+9dHR0AI+Pj4Hq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6oHq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6oHq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6oHq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6oHq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6oHq6uq8dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+8dHR0gerq6ofq6uq2dHR0gY+Pj710dHSBj4+PvXR0dIGPj4+3dHR0hurq6ofq6ur/j4+P8I+Pj4bq6uo=" + } + Texture: 2957776714320, "Texture::Map #2", "" { + Type: "TextureVideoClip" + Version: 202 + TextureName: "Texture::Map #2" + Properties70: { + P: "CurrentTextureBlendMode", "enum", "", "",0 + P: "UVSet", "KString", "", "", "UVChannel_1" + P: "UseMaterial", "bool", "", "",1 + } + Media: "Video::Map #2" + FileName: "U:/Some/Absolute/Primitives/GridGrey.tga" + RelativeFilename: "..\Primitives\GridGrey.tga" + ModelUVTranslation: 0,0 + ModelUVScaling: 1,1 + Texture_Alpha_Source: "None" + Cropping: 0,0,0,0 + } + Texture: 2957776714321, "Texture::Map #2", "" { + Type: "TextureVideoClip" + Version: 202 + TextureName: "Texture::Map #2" + Properties70: { + P: "CurrentTextureBlendMode", "enum", "", "",0 + P: "UVSet", "KString", "", "", "UVChannel_1" + P: "UseMaterial", "bool", "", "",1 + } + Media: "Video::Map #2" + FileName: "U:/Some/Absolute/Primitives/GridGrey.tga" + RelativeFilename: "..\Primitives\GridGrey.tga" + ModelUVTranslation: 0,0 + ModelUVScaling: 1,1 + Texture_Alpha_Source: "None" + Cropping: 0,0,0,0 + } + AnimationStack: 2957627494560, "AnimStack::Take 001", "" { + Properties70: { + P: "LocalStop", "KTime", "Time", "",153953860000 + P: "ReferenceStop", "KTime", "Time", "",153953860000 + } + } + AnimationCurveNode: 2957627500176, "AnimCurveNode::mr displacement use global settings", "" { + Properties70: { + P: "d|mr displacement use global settings", "Bool", "", "A",1 + } + } + AnimationCurveNode: 2957627490192, "AnimCurveNode::mr displacement view dependent", "" { + Properties70: { + P: "d|mr displacement view dependent", "Bool", "", "A",1 + } + } + AnimationCurveNode: 2957627487904, "AnimCurveNode::mr displacement method", "" { + Properties70: { + P: "d|mr displacement method", "Integer", "", "A",6 + } + } + AnimationCurveNode: 2957627492688, "AnimCurveNode::mr displacement smoothing on", "" { + Properties70: { + P: "d|mr displacement smoothing on", "Bool", "", "A",1 + } + } + AnimationCurveNode: 2957627492064, "AnimCurveNode::mr displacement edge length", "" { + Properties70: { + P: "d|mr displacement edge length", "Number", "", "A",2 + } + } + AnimationCurveNode: 2957627492896, "AnimCurveNode::mr displacement max displace", "" { + Properties70: { + P: "d|mr displacement max displace", "Number", "", "A",20 + } + } + AnimationCurveNode: 2957627492272, "AnimCurveNode::mr displacement parametric subdivision level", "" { + Properties70: { + P: "d|mr displacement parametric subdivision level", "Integer", "", "A",5 + } + } + AnimationCurveNode: 2957627488320, "AnimCurveNode::MaxHandle", "" { + Properties70: { + P: "d|MaxHandle", "Integer", "", "A",1 + } + } + AnimationLayer: 2957566198176, "AnimLayer::BaseLayer", "" { + } + CollectionExclusive: 2959111896352, "DisplayLayer::Box", "DisplayLayer" { + Properties70: { + P: "Color", "ColorRGB", "Color", "",0.607999980449677,0,0.157000005245209 + } + } +} + +; Object connections +;------------------------------------------------------------------ + +Connections: { + + ;Model::Box, Model::RootNode + C: "OO",2957618625584,0 + + ;AnimLayer::BaseLayer, AnimStack::Take 001 + C: "OO",2957566198176,2957627494560 + + ;AnimCurveNode::mr displacement use global settings, AnimLayer::BaseLayer + C: "OO",2957627500176,2957566198176 + + ;AnimCurveNode::mr displacement view dependent, AnimLayer::BaseLayer + C: "OO",2957627490192,2957566198176 + + ;AnimCurveNode::mr displacement method, AnimLayer::BaseLayer + C: "OO",2957627487904,2957566198176 + + ;AnimCurveNode::mr displacement smoothing on, AnimLayer::BaseLayer + C: "OO",2957627492688,2957566198176 + + ;AnimCurveNode::mr displacement edge length, AnimLayer::BaseLayer + C: "OO",2957627492064,2957566198176 + + ;AnimCurveNode::mr displacement max displace, AnimLayer::BaseLayer + C: "OO",2957627492896,2957566198176 + + ;AnimCurveNode::mr displacement parametric subdivision level, AnimLayer::BaseLayer + C: "OO",2957627492272,2957566198176 + + ;AnimCurveNode::MaxHandle, AnimLayer::BaseLayer + C: "OO",2957627488320,2957566198176 + + ;Texture::Map #2, Material::Default + C: "OP",2957776714320,2957776713840, "DiffuseColor" + + ;Video::Map #2, Texture::Map #2 + C: "OO",2957776707120,2957776714320 + + ;Video::Map #2, Texture::Map #2 + C: "OO",2957776707121,2957776714321 + + ;Geometry::, Model::Box + C: "OO",2957764348544,2957618625584 + + ;Material::Default, Model::Box + C: "OO",2957776713840,2957618625584 + + ;AnimCurveNode::mr displacement use global settings, Model::Box + C: "OP",2957627500176,2957618625584, "mr displacement use global settings" + + ;AnimCurveNode::mr displacement view dependent, Model::Box + C: "OP",2957627490192,2957618625584, "mr displacement view dependent" + + ;AnimCurveNode::mr displacement method, Model::Box + C: "OP",2957627487904,2957618625584, "mr displacement method" + + ;AnimCurveNode::mr displacement smoothing on, Model::Box + C: "OP",2957627492688,2957618625584, "mr displacement smoothing on" + + ;AnimCurveNode::mr displacement edge length, Model::Box + C: "OP",2957627492064,2957618625584, "mr displacement edge length" + + ;AnimCurveNode::mr displacement max displace, Model::Box + C: "OP",2957627492896,2957618625584, "mr displacement max displace" + + ;AnimCurveNode::mr displacement parametric subdivision level, Model::Box + C: "OP",2957627492272,2957618625584, "mr displacement parametric subdivision level" + + ;AnimCurveNode::MaxHandle, Model::Box + C: "OP",2957627488320,2957618625584, "MaxHandle" + + ;Model::Box, DisplayLayer::Box + C: "OO",2957618625584,2959111896352 +} +;Takes section +;---------------------------------------------------- + +Takes: { + Current: "Take 001" + Take: "Take 001" { + FileName: "Take_001.tak" + LocalTime: 0,153953860000 + ReferenceTime: 0,153953860000 + } +} diff --git a/test/models/FBX/huesitos.fbx b/test/models/FBX/huesitos.fbx new file mode 100644 index 000000000..646271392 Binary files /dev/null and b/test/models/FBX/huesitos.fbx differ diff --git a/test/unit/Main.cpp b/test/unit/Main.cpp index 5ba5c487d..333fd655d 100644 --- a/test/unit/Main.cpp +++ b/test/unit/Main.cpp @@ -16,7 +16,7 @@ int main(int argc, char* argv[]) // create a logger from both CPP Assimp::DefaultLogger::create("AssimpLog_Cpp.txt",Assimp::Logger::VERBOSE, - aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE); + aiDefaultLogStream_STDOUT | aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE); // .. and C. They should smoothly work together aiEnableVerboseLogging(AI_TRUE); diff --git a/test/unit/utArmaturePopulate.cpp b/test/unit/utArmaturePopulate.cpp new file mode 100644 index 000000000..8eb577d61 --- /dev/null +++ b/test/unit/utArmaturePopulate.cpp @@ -0,0 +1,83 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 "TestModelFactory.h" + + +#include "SceneDiffer.h" +#include "AbstractImportExportBase.h" + +#include +#include +#include +#include +#include + +#include "PostProcessing/ArmaturePopulate.h" + +namespace Assimp { +namespace UnitTest { + +class utArmaturePopulate : public ::testing::Test { + // empty +}; + +TEST_F( utArmaturePopulate, importCheckForArmatureTest) { + Assimp::Importer importer; + unsigned int mask = aiProcess_PopulateArmatureData | aiProcess_ValidateDataStructure; + const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/FBX/huesitos.fbx", mask); + EXPECT_NE( nullptr, scene ); + EXPECT_EQ(scene->mNumMeshes, 1u); + aiMesh* mesh = scene->mMeshes[0]; + EXPECT_EQ(mesh->mNumFaces, 68u); + EXPECT_EQ(mesh->mNumVertices, 256u); + EXPECT_GT(mesh->mNumBones, 0u); + + aiBone* exampleBone = mesh->mBones[0]; + EXPECT_NE(exampleBone, nullptr); + EXPECT_NE(exampleBone->mArmature, nullptr); + EXPECT_NE(exampleBone->mNode, nullptr); +} + +} // Namespace UnitTest +} // Namespace Assimp diff --git a/test/unit/utColladaImportExport.cpp b/test/unit/utColladaImportExport.cpp index 1a2a6bc9d..8bce3a3dd 100644 --- a/test/unit/utColladaImportExport.cpp +++ b/test/unit/utColladaImportExport.cpp @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include +#include #include using namespace Assimp; @@ -52,8 +53,19 @@ class utColladaImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/Collada/duck.dae", aiProcess_ValidateDataStructure ); - return nullptr != scene; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck.dae", aiProcess_ValidateDataStructure); + if (scene == nullptr) + return false; + + // Expected number of items + EXPECT_EQ(scene->mNumMeshes, 1u); + EXPECT_EQ(scene->mNumMaterials, 1u); + EXPECT_EQ(scene->mNumAnimations, 0u); + EXPECT_EQ(scene->mNumTextures, 0u); + EXPECT_EQ(scene->mNumLights, 1u); + EXPECT_EQ(scene->mNumCameras, 1u); + + return true; } }; @@ -64,9 +76,37 @@ TEST_F(utColladaImportExport, importBlenFromFileTest) { class utColladaZaeImportExport : public AbstractImportExportBase { public: virtual bool importerTest() { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck.zae", aiProcess_ValidateDataStructure); - return nullptr != scene; + { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck.zae", aiProcess_ValidateDataStructure); + if (scene == nullptr) + return false; + + // Expected number of items + EXPECT_EQ(scene->mNumMeshes, 1u); + EXPECT_EQ(scene->mNumMaterials, 1u); + EXPECT_EQ(scene->mNumAnimations, 0u); + EXPECT_EQ(scene->mNumTextures, 1u); + EXPECT_EQ(scene->mNumLights, 1u); + EXPECT_EQ(scene->mNumCameras, 1u); + } + + { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/duck_nomanifest.zae", aiProcess_ValidateDataStructure); + if (scene == nullptr) + return false; + + // Expected number of items + EXPECT_EQ(scene->mNumMeshes, 1u); + EXPECT_EQ(scene->mNumMaterials, 1u); + EXPECT_EQ(scene->mNumAnimations, 0u); + EXPECT_EQ(scene->mNumTextures, 1u); + EXPECT_EQ(scene->mNumLights, 1u); + EXPECT_EQ(scene->mNumCameras, 1u); + } + + return true; } }; diff --git a/test/unit/utFBXImporterExporter.cpp b/test/unit/utFBXImporterExporter.cpp index 1445e3c3e..43dc40e88 100644 --- a/test/unit/utFBXImporterExporter.cpp +++ b/test/unit/utFBXImporterExporter.cpp @@ -76,6 +76,7 @@ TEST_F( utFBXImporterExporter, importBareBoxWithoutColorsAndTextureCoords ) { EXPECT_EQ(mesh->mNumVertices, 36u); } + TEST_F(utFBXImporterExporter, importCubesWithNoNames) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/cubes_nonames.fbx", aiProcess_ValidateDataStructure); @@ -86,26 +87,6 @@ TEST_F(utFBXImporterExporter, importCubesWithNoNames) { ASSERT_STREQ(root->mName.C_Str(), "RootNode"); ASSERT_TRUE(root->mChildren); ASSERT_EQ(root->mNumChildren, 2u); - - const auto child0 = root->mChildren[0]; - ASSERT_TRUE(child0); - ASSERT_STREQ(child0->mName.C_Str(), "RootNode001"); - ASSERT_TRUE(child0->mChildren); - ASSERT_EQ(child0->mNumChildren, 1u); - - const auto child00 = child0->mChildren[0]; - ASSERT_TRUE(child00); - ASSERT_STREQ(child00->mName.C_Str(), "RootNode001001"); - - const auto child1 = root->mChildren[1]; - ASSERT_TRUE(child1); - ASSERT_STREQ(child1->mName.C_Str(), "RootNode002"); - ASSERT_TRUE(child1->mChildren); - ASSERT_EQ(child1->mNumChildren, 1u); - - const auto child10 = child1->mChildren[0]; - ASSERT_TRUE(child10); - ASSERT_STREQ(child10->mName.C_Str(), "RootNode002001"); } TEST_F(utFBXImporterExporter, importCubesWithUnicodeDuplicatedNames) { @@ -137,7 +118,7 @@ TEST_F(utFBXImporterExporter, importCubesWithUnicodeDuplicatedNames) { const auto child10 = child1->mChildren[0]; ASSERT_TRUE(child10); - ASSERT_STREQ(child10->mName.C_Str(), "\xd0\x9a\xd1\x83\xd0\xb1\x31""001"); + ASSERT_STREQ(child10->mName.C_Str(), "\xd0\x9a\xd1\x83\xd0\xb1\x31"); } TEST_F(utFBXImporterExporter, importCubesComplexTransform) { @@ -168,14 +149,14 @@ TEST_F(utFBXImporterExporter, importCubesComplexTransform) { auto parent = child1; const size_t chain_length = 8u; const char* chainStr[chain_length] = { - "Cube1001_$AssimpFbx$_Translation", - "Cube1001_$AssimpFbx$_RotationPivot", - "Cube1001_$AssimpFbx$_RotationPivotInverse", - "Cube1001_$AssimpFbx$_ScalingOffset", - "Cube1001_$AssimpFbx$_ScalingPivot", - "Cube1001_$AssimpFbx$_Scaling", - "Cube1001_$AssimpFbx$_ScalingPivotInverse", - "Cube1001" + "Cube1_$AssimpFbx$_Translation", + "Cube1_$AssimpFbx$_RotationPivot", + "Cube1_$AssimpFbx$_RotationPivotInverse", + "Cube1_$AssimpFbx$_ScalingOffset", + "Cube1_$AssimpFbx$_ScalingPivot", + "Cube1_$AssimpFbx$_Scaling", + "Cube1_$AssimpFbx$_ScalingPivotInverse", + "Cube1" }; for (size_t i = 0; i < chain_length; ++i) { ASSERT_TRUE(parent->mChildren); @@ -282,3 +263,23 @@ TEST_F(utFBXImporterExporter, fbxTokenizeTestTest) { //const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/transparentTest2.fbx", aiProcess_ValidateDataStructure); //EXPECT_NE(nullptr, scene); } + +TEST_F(utFBXImporterExporter, importOrphantEmbeddedTextureTest) { + // see https://github.com/assimp/assimp/issues/1957 + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/box_orphant_embedded_texture.fbx", aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); + + EXPECT_EQ(1u, scene->mNumMaterials); + aiMaterial *mat = scene->mMaterials[0]; + ASSERT_NE(nullptr, mat); + + aiString path; + aiTextureMapMode modes[2]; + ASSERT_EQ(aiReturn_SUCCESS, mat->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, nullptr, nullptr, modes)); + ASSERT_STREQ(path.C_Str(), "..\\Primitives\\GridGrey.tga"); + + ASSERT_EQ(1u, scene->mNumTextures); + ASSERT_TRUE(scene->mTextures[0]->pcData); + ASSERT_EQ(9026u, scene->mTextures[0]->mWidth) << "FBX ASCII base64 compression used for a texture."; +} diff --git a/test/unit/utVersion.cpp b/test/unit/utVersion.cpp index 233b2fb0b..66e832baa 100644 --- a/test/unit/utVersion.cpp +++ b/test/unit/utVersion.cpp @@ -48,7 +48,7 @@ TEST_F( utVersion, aiGetLegalStringTest ) { EXPECT_NE( lv, nullptr ); std::string text( lv ); - size_t pos( text.find( std::string( "2017" ) ) ); + size_t pos( text.find( std::string( "2019" ) ) ); EXPECT_NE( pos, std::string::npos ); }