Browse Source

Merge pull request #2451 from mlavik1/fbxexporter_embedded_textures

FBXExporter: Embedded texture support
Kim Kulling 6 năm trước cách đây
mục cha
commit
f81b90a469

+ 2 - 2
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

+ 3 - 2
code/FBXExportNode.cpp

@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <sstream> // ostringstream
 #include <memory> // shared_ptr
 
+namespace Assimp {
 // AddP70<type> helpers... there's no usable pattern here,
 // so all are defined as separate functions.
 // Even "animatable" properties are often completely different
@@ -367,7 +368,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 +564,6 @@ void FBX::Node::WritePropertyNode(
         FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
     }
 }
-
+}
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_EXPORT

+ 2 - 1
code/FBXExportNode.h

@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <string>
 #include <vector>
 
+namespace Assimp {
 namespace FBX {
     class Node;
 }
@@ -264,7 +265,7 @@ private: // static helper functions
     );
 
 };
-
+}
 
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 

+ 2 - 2
code/FBXExportProperty.cpp

@@ -52,7 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <locale>
 #include <sstream> // 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

+ 2 - 1
code/FBXExportProperty.h

@@ -56,6 +56,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <ostream>
 #include <type_traits> // is_void
 
+namespace Assimp {
 namespace FBX {
     class Property;
 }
@@ -123,7 +124,7 @@ private:
     char type;
     std::vector<uint8_t> data;
 };
-
+}
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 
 #endif // AI_FBXEXPORTPROPERTY_H_INC

+ 42 - 19
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 <assimp/version.h> // aiGetVersion
 #include <assimp/IOSystem.hpp>
@@ -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,33 @@ 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 empty, 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<size_t>(embedded_texture->mWidth * std::max(embedded_texture->mHeight, 1u));
+            if (binary) {
+                // embed texture as binary data
+                std::vector<uint8_t> 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));
@@ -1419,17 +1442,17 @@ void FBXExporter::WriteObjects ()
     // referenced by material_index/texture_type pairs.
     std::map<std::pair<size_t,size_t>,int64_t> texture_uids;
     const std::map<aiTextureType,std::string> 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) {

+ 60 - 0
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] & 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);
+    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

+ 9 - 0
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);
+
 }
 }
 }

+ 8 - 0
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<unsigned>(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());