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());