From 575ef4d9276c4755ce168e116e8248d123b93d2a Mon Sep 17 00:00:00 2001 From: Matias Date: Thu, 9 May 2019 14:50:22 +0200 Subject: [PATCH 1/4] Added support for embedded textures to the FBXExporter (both binary and ASCII). Also made the FBX-namespaces more consistent (we had both Assimp::FBX:: and FBX::). Since we seem to support two types of embedded texture references (both asterisk+texture_id and filepath) I made the exporter use aiScene::GetEmbeddedTexture for looking up texture reference and integrated @loebl 's modification of the function to support the old ("*1") type of references (https://github.com/loebl/assimp/commit/e217358) --- code/FBXCommon.h | 4 +-- code/FBXExportNode.cpp | 11 ++++--- code/FBXExportNode.h | 3 +- code/FBXExportProperty.cpp | 4 +-- code/FBXExportProperty.h | 3 +- code/FBXExporter.cpp | 48 +++++++++++++++++++++++------- code/FBXUtil.cpp | 60 ++++++++++++++++++++++++++++++++++++++ code/FBXUtil.h | 9 ++++++ include/assimp/scene.h | 8 +++++ 9 files changed, 130 insertions(+), 20 deletions(-) diff --git a/code/FBXCommon.h b/code/FBXCommon.h index fcb20a5ca..e51644913 100644 --- a/code/FBXCommon.h +++ b/code/FBXCommon.h @@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER - +namespace Assimp { namespace FBX { const std::string NULL_RECORD = { // 13 null bytes @@ -80,7 +80,7 @@ namespace FBX TransformInheritance_MAX // end-of-enum sentinel }; } - +} #endif // ASSIMP_BUILD_NO_FBX_EXPORTER #endif // AI_FBXCOMMON_H_INC diff --git a/code/FBXExportNode.cpp b/code/FBXExportNode.cpp index e5215466a..06c89cee4 100644 --- a/code/FBXExportNode.cpp +++ b/code/FBXExportNode.cpp @@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // ostringstream #include // shared_ptr +namespace Assimp { // AddP70 helpers... there's no usable pattern here, // so all are defined as separate functions. // Even "animatable" properties are often completely different @@ -252,7 +253,8 @@ void FBX::Node::DumpChildren( } else { std::ostringstream ss; DumpChildrenAscii(ss, indent); - s.PutString(ss.str()); + if (ss.tellp() > 0) + s.PutString(ss.str()); } } @@ -266,7 +268,8 @@ void FBX::Node::End( } else { std::ostringstream ss; EndAscii(ss, indent, has_children); - s.PutString(ss.str()); + if (ss.tellp() > 0) + s.PutString(ss.str()); } } @@ -367,7 +370,7 @@ void FBX::Node::EndBinary( bool has_children ) { // if there were children, add a null record - if (has_children) { s.PutString(FBX::NULL_RECORD); } + if (has_children) { s.PutString(Assimp::FBX::NULL_RECORD); } // now go back and write initial pos this->end_pos = s.Tell(); @@ -563,6 +566,6 @@ void FBX::Node::WritePropertyNode( FBX::Node::WritePropertyNodeAscii(name, v, s, indent); } } - +} #endif // ASSIMP_BUILD_NO_FBX_EXPORTER #endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/FBXExportNode.h b/code/FBXExportNode.h index e1ebc3696..b388a4f86 100644 --- a/code/FBXExportNode.h +++ b/code/FBXExportNode.h @@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +namespace Assimp { namespace FBX { class Node; } @@ -264,7 +265,7 @@ private: // static helper functions ); }; - +} #endif // ASSIMP_BUILD_NO_FBX_EXPORTER diff --git a/code/FBXExportProperty.cpp b/code/FBXExportProperty.cpp index 9981d6b1c..ac464c806 100644 --- a/code/FBXExportProperty.cpp +++ b/code/FBXExportProperty.cpp @@ -52,7 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include // ostringstream - +namespace Assimp { // constructors for single element properties FBX::Property::Property(bool v) @@ -359,6 +359,6 @@ void FBX::Property::DumpAscii(std::ostream& s, int indent) throw runtime_error(err.str()); } } - +} #endif // ASSIMP_BUILD_NO_FBX_EXPORTER #endif // ASSIMP_BUILD_NO_EXPORT diff --git a/code/FBXExportProperty.h b/code/FBXExportProperty.h index 9c9d37c36..424bf8b32 100644 --- a/code/FBXExportProperty.h +++ b/code/FBXExportProperty.h @@ -56,6 +56,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include // is_void +namespace Assimp { namespace FBX { class Property; } @@ -123,7 +124,7 @@ private: char type; std::vector data; }; - +} #endif // ASSIMP_BUILD_NO_FBX_EXPORTER #endif // AI_FBXEXPORTPROPERTY_H_INC diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 6b1ac0e5e..24ae88e6b 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXExportNode.h" #include "FBXExportProperty.h" #include "FBXCommon.h" +#include "FBXUtil.h" #include // aiGetVersion #include @@ -73,7 +74,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. const ai_real DEG = ai_real( 57.29577951308232087679815481 ); // degrees per radian +using namespace Assimp; +using namespace Assimp::FBX; + // some constants that we'll use for writing metadata +namespace Assimp { namespace FBX { const std::string EXPORT_VERSION_STR = "7.4.0"; const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015 @@ -92,11 +97,6 @@ namespace FBX { ";------------------------------------------------------------------"; } -using namespace Assimp; -using namespace FBX; - -namespace Assimp { - // --------------------------------------------------------------------- // Worker function for exporting a scene to binary FBX. // Prototyped and registered in Exporter.cpp @@ -121,6 +121,7 @@ namespace Assimp { IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties + ){ // initialize the exporter FBXExporter exporter(pScene, pProperties); @@ -1393,10 +1394,6 @@ void FBXExporter::WriteObjects () // FbxVideo - stores images used by textures. for (const auto &it : uid_by_image) { - if (it.first.compare(0, 1, "*") == 0) { - // TODO: embedded textures - continue; - } FBX::Node n("Video"); const int64_t& uid = it.second; const std::string name = ""; // TODO: ... name??? @@ -1406,7 +1403,38 @@ void FBXExporter::WriteObjects () // TODO: get full path... relative path... etc... ugh... // for now just use the same path for everything, // and hopefully one of them will work out. - const std::string& path = it.first; + std::string path = it.first; + // try get embedded texture + const aiTexture* embedded_texture = mScene->GetEmbeddedTexture(it.first.c_str()); + if (embedded_texture != nullptr) + { + // change the path (use original filename, if available. If name is ampty, concatenate texture index with file extension) + std::stringstream newPath; + if (embedded_texture->mFilename.length > 0) + newPath << embedded_texture->mFilename.C_Str(); + else if (embedded_texture->achFormatHint[0]) + { + int texture_index = std::stoi(path.substr(1, path.size() - 1)); + newPath << texture_index << "." << embedded_texture->achFormatHint; + } + path = newPath.str(); + // embed the texture + size_t texture_size = static_cast(embedded_texture->mWidth * std::max(embedded_texture->mHeight, 1u)); + if (binary) + { + // embed texture as binary data + std::vector tex_data; + tex_data.resize(texture_size); + memcpy(&tex_data[0], (char*)embedded_texture->pcData, texture_size); + n.AddChild("Content", tex_data); + } + else + { + // embed texture in base64 encoding + std::string encoded_texture = FBX::Util::EncodeBase64((char*)embedded_texture->pcData, texture_size); + n.AddChild("Content", encoded_texture); + } + } p.AddP70("Path", "KString", "XRefUrl", "", path); n.AddChild(p); n.AddChild("UseMipMap", int32_t(0)); diff --git a/code/FBXUtil.cpp b/code/FBXUtil.cpp index fb483161b..068683f7d 100644 --- a/code/FBXUtil.cpp +++ b/code/FBXUtil.cpp @@ -157,6 +157,66 @@ size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out) return outLength; } +static const char to_base64_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +char EncodeBase64(char byte) +{ + return to_base64_string[(size_t)byte]; +} + +/** Encodes a block of 4 bytes to base64 encoding +* +* @param bytes Bytes to encode. +* @param out_string String to write encoded values to. +* @param string_pos Position in out_string.*/ +void EncodeByteBlock(const char* bytes, std::string& out_string, size_t string_pos) +{ + char b0 = (bytes[0] & 0b11111100) >> 2; + char b1 = (bytes[0] & 0b00000011) << 4 | ((bytes[1] & 0b11110000) >> 4); + char b2 = (bytes[1] & 0b00001111) << 2 | ((bytes[2] & 0b11000000) >> 6); + char b3 = (bytes[2] & 0b00111111); + + out_string[string_pos + 0] = EncodeBase64(b0); + out_string[string_pos + 1] = EncodeBase64(b1); + out_string[string_pos + 2] = EncodeBase64(b2); + out_string[string_pos + 3] = EncodeBase64(b3); +} + +std::string EncodeBase64(const char* data, size_t length) +{ + // calculate extra bytes needed to get a multiple of 3 + size_t extraBytes = 3 - length % 3; + + // number of base64 bytes + size_t encodedBytes = 4 * (length + extraBytes) / 3; + + std::string encoded_string(encodedBytes, '='); + + // read blocks of 3 bytes + for (size_t ib3 = 0; ib3 < length / 3; ib3++) + { + const size_t iByte = ib3 * 3; + const size_t iEncodedByte = ib3 * 4; + const char* currData = &data[iByte]; + + EncodeByteBlock(currData, encoded_string, iEncodedByte); + } + + // if size of data is not a multiple of 3, also encode the final bytes (and add zeros where needed) + if (extraBytes > 0) + { + char finalBytes[4] = { 0,0,0,0 }; + memcpy(&finalBytes[0], &data[length - length % 3], length % 3); + + const size_t iEncodedByte = encodedBytes - 4; + EncodeByteBlock(&finalBytes[0], encoded_string, iEncodedByte); + + // add '=' at the end + for (size_t i = 0; i < 4 * extraBytes / 3; i++) + encoded_string[encodedBytes - i - 1] = '='; + } + return encoded_string; +} + } // !Util } // !FBX } // !Assimp diff --git a/code/FBXUtil.h b/code/FBXUtil.h index 6890e015b..b26eba5d5 100644 --- a/code/FBXUtil.h +++ b/code/FBXUtil.h @@ -113,6 +113,15 @@ uint8_t DecodeBase64(char ch); * @return size of the decoded data (number of bytes)*/ size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out); +char EncodeBase64(char byte); + +/** Encode bytes in base64-encoding +* +* @param data Binary data to encode. +* @param inLength Number of bytes to encode. +* @return base64-encoded string*/ +std::string EncodeBase64(const char* data, size_t length); + } } } diff --git a/include/assimp/scene.h b/include/assimp/scene.h index de0239702..e973f6c5b 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -389,6 +389,14 @@ struct aiScene //! Returns an embedded texture const aiTexture* GetEmbeddedTexture(const char* filename) const { + // lookup using texture ID (if referenced like: "*1", "*2", etc.) + if ('*' == *filename) { + int index = std::atoi(filename + 1); + if (0 > index || mNumTextures <= static_cast(index)) + return nullptr; + return mTextures[index]; + } + // lookup using filename const char* shortFilename = GetShortFilename(filename); for (unsigned int i = 0; i < mNumTextures; i++) { const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str()); From 9903504c42f62c8caf1a7ed8a852e1ff8d7ca97f Mon Sep 17 00:00:00 2001 From: Matias Date: Thu, 9 May 2019 14:57:18 +0200 Subject: [PATCH 2/4] Removed some code that I didn't mean to fix (I already submitted this in another PR: https://github.com/assimp/assimp/pull/2450). --- code/FBXExportNode.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/code/FBXExportNode.cpp b/code/FBXExportNode.cpp index 06c89cee4..8cb12d619 100644 --- a/code/FBXExportNode.cpp +++ b/code/FBXExportNode.cpp @@ -253,8 +253,7 @@ void FBX::Node::DumpChildren( } else { std::ostringstream ss; DumpChildrenAscii(ss, indent); - if (ss.tellp() > 0) - s.PutString(ss.str()); + s.PutString(ss.str()); } } @@ -268,8 +267,7 @@ void FBX::Node::End( } else { std::ostringstream ss; EndAscii(ss, indent, has_children); - if (ss.tellp() > 0) - s.PutString(ss.str()); + s.PutString(ss.str()); } } From a44354498690f41d3174b48e0cab75499c70ed19 Mon Sep 17 00:00:00 2001 From: Matias Date: Thu, 9 May 2019 15:28:00 +0200 Subject: [PATCH 3/4] Replaced binary literals with hex literals (since binary literals was introduced in C++14) --- code/FBXUtil.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/FBXUtil.cpp b/code/FBXUtil.cpp index 068683f7d..557d0d843 100644 --- a/code/FBXUtil.cpp +++ b/code/FBXUtil.cpp @@ -170,10 +170,10 @@ char EncodeBase64(char byte) * @param string_pos Position in out_string.*/ void EncodeByteBlock(const char* bytes, std::string& out_string, size_t string_pos) { - char b0 = (bytes[0] & 0b11111100) >> 2; - char b1 = (bytes[0] & 0b00000011) << 4 | ((bytes[1] & 0b11110000) >> 4); - char b2 = (bytes[1] & 0b00001111) << 2 | ((bytes[2] & 0b11000000) >> 6); - char b3 = (bytes[2] & 0b00111111); + char b0 = (bytes[0] & 0xFC) >> 2; + char b1 = (bytes[0] & 0x03) << 4 | ((bytes[1] & 0xF0) >> 4); + char b2 = (bytes[1] & 0x0F) << 2 | ((bytes[2] & 0xC0) >> 6); + char b3 = (bytes[2] & 0x3F); out_string[string_pos + 0] = EncodeBase64(b0); out_string[string_pos + 1] = EncodeBase64(b1); From 700954c11542035a479052ad036ea5ff000e9143 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 10 May 2019 09:09:47 +0200 Subject: [PATCH 4/4] Update FBXExporter.cpp Just fixed some typos. --- code/FBXExporter.cpp | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/code/FBXExporter.cpp b/code/FBXExporter.cpp index 24ae88e6b..ddfac6d34 100644 --- a/code/FBXExporter.cpp +++ b/code/FBXExporter.cpp @@ -1406,30 +1406,25 @@ void FBXExporter::WriteObjects () std::string path = it.first; // try get embedded texture const aiTexture* embedded_texture = mScene->GetEmbeddedTexture(it.first.c_str()); - if (embedded_texture != nullptr) - { - // change the path (use original filename, if available. If name is ampty, concatenate texture index with file extension) + if (embedded_texture != nullptr) { + // change the path (use original filename, if available. If name is empty, concatenate texture index with file extension) std::stringstream newPath; - if (embedded_texture->mFilename.length > 0) + if (embedded_texture->mFilename.length > 0) { newPath << embedded_texture->mFilename.C_Str(); - else if (embedded_texture->achFormatHint[0]) - { + } else if (embedded_texture->achFormatHint[0]) { int texture_index = std::stoi(path.substr(1, path.size() - 1)); newPath << texture_index << "." << embedded_texture->achFormatHint; } path = newPath.str(); // embed the texture size_t texture_size = static_cast(embedded_texture->mWidth * std::max(embedded_texture->mHeight, 1u)); - if (binary) - { + if (binary) { // embed texture as binary data std::vector tex_data; tex_data.resize(texture_size); memcpy(&tex_data[0], (char*)embedded_texture->pcData, texture_size); n.AddChild("Content", tex_data); - } - else - { + } else { // embed texture in base64 encoding std::string encoded_texture = FBX::Util::EncodeBase64((char*)embedded_texture->pcData, texture_size); n.AddChild("Content", encoded_texture); @@ -1447,17 +1442,17 @@ void FBXExporter::WriteObjects () // referenced by material_index/texture_type pairs. std::map,int64_t> texture_uids; const std::map prop_name_by_tt = { - {aiTextureType_DIFFUSE, "DiffuseColor"}, - {aiTextureType_SPECULAR, "SpecularColor"}, - {aiTextureType_AMBIENT, "AmbientColor"}, - {aiTextureType_EMISSIVE, "EmissiveColor"}, - {aiTextureType_HEIGHT, "Bump"}, - {aiTextureType_NORMALS, "NormalMap"}, - {aiTextureType_SHININESS, "ShininessExponent"}, - {aiTextureType_OPACITY, "TransparentColor"}, + {aiTextureType_DIFFUSE, "DiffuseColor"}, + {aiTextureType_SPECULAR, "SpecularColor"}, + {aiTextureType_AMBIENT, "AmbientColor"}, + {aiTextureType_EMISSIVE, "EmissiveColor"}, + {aiTextureType_HEIGHT, "Bump"}, + {aiTextureType_NORMALS, "NormalMap"}, + {aiTextureType_SHININESS, "ShininessExponent"}, + {aiTextureType_OPACITY, "TransparentColor"}, {aiTextureType_DISPLACEMENT, "DisplacementColor"}, //{aiTextureType_LIGHTMAP, "???"}, - {aiTextureType_REFLECTION, "ReflectionColor"} + {aiTextureType_REFLECTION, "ReflectionColor"} //{aiTextureType_UNKNOWN, ""} }; for (size_t i = 0; i < mScene->mNumMaterials; ++i) {