diff --git a/code/AssetLib/Assxml/AssxmlFileWriter.cpp b/code/AssetLib/Assxml/AssxmlFileWriter.cpp index 6d876b3aa..24fda5955 100644 --- a/code/AssetLib/Assxml/AssxmlFileWriter.cpp +++ b/code/AssetLib/Assxml/AssxmlFileWriter.cpp @@ -598,8 +598,11 @@ static void WriteDump(const char *pFile, const char *cmd, const aiScene *scene, if (!mesh->mTextureCoords[a]) break; - ioprintf(io, "\t\t \n", mesh->mNumVertices, - a, mesh->mNumUVComponents[a]); + ioprintf(io, "\t\t \n", + mesh->mNumVertices, + a, + mesh->mTextureCoordsNames[a].C_Str(), + mesh->mNumUVComponents[a]); if (!shortened) { if (mesh->mNumUVComponents[a] == 3) { diff --git a/code/AssetLib/DXF/DXFLoader.cpp b/code/AssetLib/DXF/DXFLoader.cpp index 09c229265..5d32ed121 100644 --- a/code/AssetLib/DXF/DXFLoader.cpp +++ b/code/AssetLib/DXF/DXFLoader.cpp @@ -63,11 +63,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; // AutoCAD Binary DXF -const std::string AI_DXF_BINARY_IDENT = std::string("AutoCAD Binary DXF\r\n\x1a\0"); -const size_t AI_DXF_BINARY_IDENT_LEN = 24u; +static constexpr char AI_DXF_BINARY_IDENT[] = "AutoCAD Binary DXF\r\n\x1a"; +static constexpr size_t AI_DXF_BINARY_IDENT_LEN = sizeof AI_DXF_BINARY_IDENT; // default vertex color that all uncolored vertices will receive -const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f)); +static const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f)); // color indices for DXF - 16 are supported, the table is // taken directly from the DXF spec. @@ -156,10 +156,10 @@ void DXFImporter::InternReadFile( const std::string& filename, aiScene* pScene, } // Check whether this is a binary DXF file - we can't read binary DXF files :-( - char buff[AI_DXF_BINARY_IDENT_LEN+1] = {0}; + char buff[AI_DXF_BINARY_IDENT_LEN] = {0}; file->Read(buff,AI_DXF_BINARY_IDENT_LEN,1); - if (0 == strncmp(AI_DXF_BINARY_IDENT.c_str(),buff,AI_DXF_BINARY_IDENT_LEN)) { + if (0 == memcmp(AI_DXF_BINARY_IDENT,buff,AI_DXF_BINARY_IDENT_LEN)) { throw DeadlyImportError("DXF: Binary files are not supported at the moment"); } diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index 1fcc79c7b..a564b3e9b 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -1126,6 +1126,8 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c *out_uv++ = aiVector3D(v.x, v.y, 0.0f); } + out_mesh->mTextureCoordsNames[i] = mesh.GetTextureCoordChannelName(i); + out_mesh->mNumUVComponents[i] = 2; } diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index 9bafcbd2a..5aecb61b5 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -604,15 +604,15 @@ void MeshGeometry::ReadVertexDataTangents(std::vector& tangents_out, } // ------------------------------------------------------------------------------------------------ -static const std::string BinormalIndexToken = "BinormalIndex"; -static const std::string BinormalsIndexToken = "BinormalsIndex"; +static const char * BinormalIndexToken = "BinormalIndex"; +static const char * BinormalsIndexToken = "BinormalsIndex"; void MeshGeometry::ReadVertexDataBinormals(std::vector& binormals_out, const Scope& source, const std::string& MappingInformationType, const std::string& ReferenceInformationType) { const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal"; - const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken.c_str() : BinormalIndexToken.c_str(); + const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken : BinormalIndexToken; ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType, str, strIdx, diff --git a/code/AssetLib/Ogre/OgreBinarySerializer.cpp b/code/AssetLib/Ogre/OgreBinarySerializer.cpp index 1d20799a1..fc975a5cc 100644 --- a/code/AssetLib/Ogre/OgreBinarySerializer.cpp +++ b/code/AssetLib/Ogre/OgreBinarySerializer.cpp @@ -55,9 +55,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace Ogre { -const std::string MESH_VERSION_1_8 = "[MeshSerializer_v1.8]"; -const std::string SKELETON_VERSION_1_8 = "[Serializer_v1.80]"; -const std::string SKELETON_VERSION_1_1 = "[Serializer_v1.10]"; +static constexpr auto MESH_VERSION_1_8 = "[MeshSerializer_v1.8]"; +static constexpr auto SKELETON_VERSION_1_8 = "[Serializer_v1.80]"; +static constexpr auto SKELETON_VERSION_1_1 = "[Serializer_v1.10]"; const unsigned short HEADER_CHUNK_ID = 0x1000; diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index 9baaa4a12..05507944e 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -749,22 +749,22 @@ enum MeshAttribute { TexCoord }; -static const std::string PosToken = "position"; -static const std::string ColToken = "color"; -static const std::string NormalToken = "normal"; -static const std::string TexCoordToken = "texcoord"; +constexpr auto PosToken = "position"; +constexpr auto ColToken = "color"; +constexpr auto NormalToken = "normal"; +constexpr auto TexCoordToken = "texcoord"; //------------------------------------------------------------------------------------------------ static MeshAttribute getAttributeByName(const char *attribName) { ai_assert(nullptr != attribName); - if (0 == strncmp(PosToken.c_str(), attribName, PosToken.size())) { + if (0 == strcmp(PosToken, attribName)) { return Position; - } else if (0 == strncmp(ColToken.c_str(), attribName, ColToken.size())) { + } else if (0 == strcmp(ColToken, attribName)) { return Color; - } else if (0 == strncmp(NormalToken.c_str(), attribName, NormalToken.size())) { + } else if (0 == strcmp(NormalToken, attribName)) { return Normal; - } else if (0 == strncmp(TexCoordToken.c_str(), attribName, TexCoordToken.size())) { + } else if (0 == strcmp(TexCoordToken, attribName)) { return TexCoord; } diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 26a2f64c3..6aa0f92ed 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -1118,11 +1118,13 @@ public: bool KHR_materials_transmission; bool KHR_draco_mesh_compression; bool FB_ngon_encoding; + bool KHR_texture_basisu; } extensionsUsed; //! Keeps info about the required extensions struct RequiredExtensions { bool KHR_draco_mesh_compression; + bool KHR_texture_basisu; } extensionsRequired; AssetMetadata asset; diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 256fc8931..b51ac20c2 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1146,6 +1146,7 @@ inline Image::Image() : } inline void Image::Read(Value &obj, Asset &r) { + //basisu: no need to handle .ktx2, .basis, load as is if (!mDataLength) { Value *curUri = FindString(obj, "uri"); if (nullptr != curUri) { @@ -2126,6 +2127,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { CHECK_EXT(KHR_materials_clearcoat); CHECK_EXT(KHR_materials_transmission); CHECK_EXT(KHR_draco_mesh_compression); + CHECK_EXT(KHR_texture_basisu); #undef CHECK_EXT } diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index 01a28d4b7..bf7dbbb2e 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -250,6 +250,7 @@ namespace glTF2 { inline void Write(Value& obj, Image& img, AssetWriter& w) { + //basisu: no need to handle .ktx2, .basis, write as is if (img.bufferView) { obj.AddMember("bufferView", img.bufferView->index, w.mAl); obj.AddMember("mimeType", Value(img.mimeType, w.mAl).Move(), w.mAl); @@ -892,10 +893,22 @@ namespace glTF2 { if (this->mAsset.extensionsUsed.FB_ngon_encoding) { exts.PushBack(StringRef("FB_ngon_encoding"), mAl); } + + if (this->mAsset.extensionsUsed.KHR_texture_basisu) { + exts.PushBack(StringRef("KHR_texture_basisu"), mAl); + } } if (!exts.Empty()) mDoc.AddMember("extensionsUsed", exts, mAl); + + //basisu extensionRequired + Value extsReq; + extsReq.SetArray(); + if (this->mAsset.extensionsUsed.KHR_texture_basisu) { + extsReq.PushBack(StringRef("KHR_texture_basisu"), mAl); + mDoc.AddMember("extensionsRequired", extsReq, mAl); + } } template diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 51aef013d..751508225 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -494,7 +494,6 @@ void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) { - if (mat->GetTextureCount(tt) > 0) { aiString tex; @@ -507,6 +506,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe texture = mAsset->textures.Get(it->second); } + bool useBasisUniversal = false; if (!texture) { std::string texId = mAsset->FindUniqueID("", "texture"); texture = mAsset->textures.Create(texId); @@ -519,18 +519,46 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe aiTexture* curTex = mScene->mTextures[atoi(&path[1])]; texture->source->name = curTex->mFilename.C_Str(); - - // The asset has its own buffer, see Image::SetData - texture->source->SetData(reinterpret_cast(curTex->pcData), curTex->mWidth, *mAsset); - + + //basisu: embedded ktx2, bu if (curTex->achFormatHint[0]) { std::string mimeType = "image/"; - mimeType += (memcmp(curTex->achFormatHint, "jpg", 3) == 0) ? "jpeg" : curTex->achFormatHint; + if(memcmp(curTex->achFormatHint, "jpg", 3) == 0) + mimeType += "jpeg"; + else if(memcmp(curTex->achFormatHint, "ktx", 3) == 0) { + useBasisUniversal = true; + mimeType += "ktx"; + } + else if(memcmp(curTex->achFormatHint, "kx2", 3) == 0) { + useBasisUniversal = true; + mimeType += "ktx2"; + } + else if(memcmp(curTex->achFormatHint, "bu", 2) == 0) { + useBasisUniversal = true; + mimeType += "basis"; + } + else + mimeType += curTex->achFormatHint; texture->source->mimeType = mimeType; } + + // The asset has its own buffer, see Image::SetData + //basisu: "image/ktx2", "image/basis" as is + texture->source->SetData(reinterpret_cast(curTex->pcData), curTex->mWidth, *mAsset); } else { texture->source->uri = path; + if(texture->source->uri.find(".ktx")!=std::string::npos || + texture->source->uri.find(".basis")!=std::string::npos) + { + useBasisUniversal = true; + } + } + + //basisu + if(useBasisUniversal) { + mAsset->extensionsUsed.KHR_texture_basisu = true; + mAsset->extensionsRequired.KHR_texture_basisu = true; } GetTexSampler(mat, texture, tt, slot); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index dbc8267d3..c62989c3b 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1263,7 +1263,7 @@ aiMeshMorphAnim *CreateMeshMorphAnim(glTF2::Asset&, Node &node, AnimationSampler static const float kMillisecondsFromSeconds = 1000.f; - if (nullptr != samplers.weight) { + if (samplers.weight && samplers.weight->input && samplers.weight->output) { float *times = nullptr; samplers.weight->input->ExtractData(times); float *values = nullptr; @@ -1476,6 +1476,12 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { if (strcmp(ext, "jpeg") == 0) { ext = "jpg"; } + else if(strcmp(ext, "ktx2") == 0) { //basisu: ktx remains + ext = "kx2"; + } + else if(strcmp(ext, "basis") == 0) { //basisu + ext = "bu"; + } size_t len = strlen(ext); if (len <= 3) { diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index 4fa9c865b..da43a7a7e 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -169,7 +169,7 @@ void Logger::debug(const char *message) { // sometimes importers will include data from the input file // (i.e. node names) in their messages. if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnDebug(""); } return OnDebug(message); } @@ -179,7 +179,7 @@ void Logger::verboseDebug(const char *message) { // SECURITY FIX: see above if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnVerboseDebug(""); } return OnVerboseDebug(message); } @@ -189,7 +189,7 @@ void Logger::info(const char *message) { // SECURITY FIX: see above if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnInfo(""); } return OnInfo(message); } @@ -199,7 +199,7 @@ void Logger::warn(const char *message) { // SECURITY FIX: see above if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnWarn(""); } return OnWarn(message); } @@ -208,7 +208,7 @@ void Logger::warn(const char *message) { void Logger::error(const char *message) { // SECURITY FIX: see above if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnError(""); } return OnError(message); } 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/camera.h b/include/assimp/camera.h index d7324d10d..6a7acadbb 100644 --- a/include/assimp/camera.h +++ b/include/assimp/camera.h @@ -137,7 +137,7 @@ struct aiCamera */ C_STRUCT aiVector3D mLookAt; - /** Half horizontal field of view angle, in radians. + /** Horizontal field of view angle, in radians. * * The field of view angle is the angle between the center * line of the screen and the left or right border. 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 */ diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index 427dba008..989ed3800 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -674,6 +674,10 @@ struct aiMesh { */ C_STRUCT aiVector3D *mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + /** Vertex stream names. + */ + C_STRUCT aiString mTextureCoordsNames[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + /** Specifies the number of components for a given UV channel. * Up to three channels are supported (UVW, for accessing volume * or cube maps). If the value is 2 for a given channel n, the