diff --git a/code/Common/Exporter.cpp b/code/Common/Exporter.cpp index 20cbb05d6..ebcc955df 100644 --- a/code/Common/Exporter.cpp +++ b/code/Common/Exporter.cpp @@ -343,9 +343,11 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha delete pimpl->blob; pimpl->blob = nullptr; } + + auto baseName = pProperties ? pProperties->GetPropertyString(AI_CONFIG_EXPORT_BLOB_NAME, AI_BLOBIO_MAGIC) : AI_BLOBIO_MAGIC; std::shared_ptr old = pimpl->mIOSystem; - BlobIOSystem* blobio = new BlobIOSystem(); + BlobIOSystem *blobio = new BlobIOSystem(baseName); pimpl->mIOSystem = std::shared_ptr( blobio ); if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) { diff --git a/include/assimp/BlobIOSystem.h b/include/assimp/BlobIOSystem.h index 4e3d5c2a3..0abf166bc 100644 --- a/include/assimp/BlobIOSystem.h +++ b/include/assimp/BlobIOSystem.h @@ -194,8 +194,14 @@ class BlobIOSystem : public IOSystem { friend class BlobIOStream; typedef std::pair BlobEntry; + public: - BlobIOSystem() { + BlobIOSystem() : + baseName{AI_BLOBIO_MAGIC} { + } + + BlobIOSystem(const std::string &baseName) : + baseName(baseName) { } virtual ~BlobIOSystem() { @@ -207,27 +213,32 @@ public: public: // ------------------------------------------------------------------- const char *GetMagicFileName() const { - return AI_BLOBIO_MAGIC; + return baseName.c_str(); } // ------------------------------------------------------------------- aiExportDataBlob *GetBlobChain() { + const auto magicName = std::string(this->GetMagicFileName()); + const bool hasBaseName = baseName != AI_BLOBIO_MAGIC; + // one must be the master aiExportDataBlob *master = nullptr, *cur; + for (const BlobEntry &blobby : blobs) { - if (blobby.first == AI_BLOBIO_MAGIC) { + if (blobby.first == magicName) { master = blobby.second; + master->name.Set(hasBaseName ? blobby.first : ""); break; } } + if (!master) { ASSIMP_LOG_ERROR("BlobIOSystem: no data written or master file was not closed properly."); return nullptr; } - master->name.Set(""); - cur = master; + for (const BlobEntry &blobby : blobs) { if (blobby.second == master) { continue; @@ -236,9 +247,13 @@ public: cur->next = blobby.second; cur = cur->next; - // extract the file extension from the file written - const std::string::size_type s = blobby.first.find_first_of('.'); - cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s + 1)); + if (hasBaseName) { + cur->name.Set(blobby.first); + } else { + // extract the file extension from the file written + const std::string::size_type s = blobby.first.find_first_of('.'); + cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s + 1)); + } } // give up blob ownership @@ -283,6 +298,7 @@ private: } private: + std::string baseName; std::set created; std::vector blobs; }; diff --git a/include/assimp/cexport.h b/include/assimp/cexport.h index 2e84b1f30..44843af0e 100644 --- a/include/assimp/cexport.h +++ b/include/assimp/cexport.h @@ -205,16 +205,22 @@ struct aiExportDataBlob { void *data; /** Name of the blob. An empty string always - indicates the first (and primary) blob, - which contains the actual file data. - Any other blobs are auxiliary files produced - by exporters (i.e. material files). Existence - of such files depends on the file format. Most - formats don't split assets across multiple files. - - If used, blob names usually contain the file - extension that should be used when writing - the data to disc. + * indicates the first (and primary) blob, + * which contains the actual file data. + * Any other blobs are auxiliary files produced + * by exporters (i.e. material files). Existence + * of such files depends on the file format. Most + * formats don't split assets across multiple files. + * + * If used, blob names usually contain the file + * extension that should be used when writing + * the data to disc. + * + * The blob names generated can be influenced by + * setting the #AI_CONFIG_EXPORT_BLOB_NAME export + * property to the name that is used for the master + * blob. All other names are typically derived from + * the base name, by the file format exporter. */ C_STRUCT aiString name; diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index 016cabbad..a89d4e837 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -1075,6 +1075,23 @@ enum aiComponent */ #define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS" +/** + * @brief Specifies the blob name, assimp uses for exporting. + * + * Some formats require auxiliary files to be written, that need to be linked back into + * the original file. For example, OBJ files export materials to a separate MTL file and + * use the `mtllib` keyword to reference this file. + * + * When exporting blobs using #ExportToBlob, assimp does not know the name of the blob + * file and thus outputs `mtllib $blobfile.mtl`, which might not be desired, since the + * MTL file might be called differently. + * + * This property can be used to give the exporter a hint on how to use the magic + * `$blobfile` keyword. If the exporter detects the keyword and is provided with a name + * for the blob, it instead uses this name. + */ +#define AI_CONFIG_EXPORT_BLOB_NAME "EXPORT_BLOB_NAME" + /** * @brief Specifies a gobal key factor for scale, float value */