Browse Source

Initial support for FBX embedded textures

Otger 9 years ago
parent
commit
f94bc8d66e

+ 61 - 0
code/FBXConverter.cpp

@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "FBXUtil.h"
 #include "FBXProperties.h"
 #include "FBXImporter.h"
+#include "StringComparison.h"
 #include "../include/assimp/scene.h"
 #include <boost/foreach.hpp>
 #include <boost/scoped_array.hpp>
@@ -148,6 +149,7 @@ public:
         std::for_each(animations.begin(),animations.end(),Util::delete_fun<aiAnimation>());
         std::for_each(lights.begin(),lights.end(),Util::delete_fun<aiLight>());
         std::for_each(cameras.begin(),cameras.end(),Util::delete_fun<aiCamera>());
+        std::for_each(textures.begin(),textures.end(),Util::delete_fun<aiTexture>());
     }
 
 
@@ -1449,6 +1451,36 @@ private:
         return static_cast<unsigned int>(materials.size() - 1);
     }
 
+    // ------------------------------------------------------------------------------------------------
+    // Video -> aiTexture
+    unsigned int ConvertVideo(const Video& video)
+    {
+        // generate empty output texture
+        aiTexture* out_tex = new aiTexture();
+        textures.push_back(out_tex);
+
+        // assuming the texture is compressed
+        out_tex->mWidth = static_cast<unsigned int>(video.ContentLength()); // total data size
+        out_tex->mHeight = 0; // fixed to 0
+
+        // steal the data from the Video to avoid an additional copy
+        out_tex->pcData = reinterpret_cast<aiTexel*>( const_cast<Video&>(video).RelinquishContent() );
+
+        // try to extract a hint from the file extension
+        const std::string& filename = video.FileName().empty() ? video.RelativeFilename() : video.FileName();
+        std::string ext = BaseImporter::GetExtension(filename);
+
+        if(ext == "jpeg") {
+            ext = "jpg";
+        }
+
+        if(ext.size() <= 3) {
+            memcpy(out_tex->achFormatHint, ext.c_str(), ext.size());
+        }
+
+        return static_cast<unsigned int>(textures.size() - 1);
+    }
+
 
     // ------------------------------------------------------------------------------------------------
     void TrySetTextureProperties(aiMaterial* out_mat, const TextureMap& textures,
@@ -1466,6 +1498,24 @@ private:
             aiString path;
             path.Set(tex->RelativeFilename());
 
+            const Video* media = tex->Media();
+            if(media != 0) {
+                unsigned int index;
+
+                VideoMap::const_iterator it = textures_converted.find(media);
+                if(it != textures_converted.end()) {
+                    index = (*it).second;
+                }
+                else {
+                    index = ConvertVideo(*media);
+                    textures_converted[media] = index;
+                }
+
+                // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
+                path.data[0] = '*';
+                path.length = 1 + ASSIMP_itoa10(path.data + 1, MAXLEN - 1, index);
+            }
+
             out_mat->AddProperty(&path,_AI_MATKEY_TEXTURE_BASE,target,0);
 
             aiUVTransform uvTrafo;
@@ -3024,6 +3074,13 @@ private:
 
             std::swap_ranges(cameras.begin(),cameras.end(),out->mCameras);
         }
+
+        if(textures.size()) {
+            out->mTextures = new aiTexture*[textures.size()]();
+            out->mNumTextures = static_cast<unsigned int>(textures.size());
+
+            std::swap_ranges(textures.begin(),textures.end(),out->mTextures);
+        }
     }
 
 
@@ -3037,10 +3094,14 @@ private:
     std::vector<aiAnimation*> animations;
     std::vector<aiLight*> lights;
     std::vector<aiCamera*> cameras;
+    std::vector<aiTexture*> textures;
 
     typedef std::map<const Material*, unsigned int> MaterialMap;
     MaterialMap materials_converted;
 
+    typedef std::map<const Video*, unsigned int> VideoMap;
+    VideoMap textures_converted;
+
     typedef std::map<const Geometry*, std::vector<unsigned int> > MeshMap;
     MeshMap meshes_converted;
 

+ 3 - 0
code/FBXDocument.cpp

@@ -180,6 +180,9 @@ const Object* LazyObject::Get(bool dieOnError)
         else if (!strncmp(obtype,"LayeredTexture",length)) {
             object.reset(new LayeredTexture(id,element,doc,name));
         }
+        else if (!strncmp(obtype,"Video",length)) {
+            object.reset(new Video(id,element,doc,name));
+        }
         else if (!strncmp(obtype,"AnimationStack",length)) {
             object.reset(new AnimationStack(id,element,name,doc));
         }

+ 62 - 0
code/FBXDocument.h

@@ -73,6 +73,8 @@ namespace FBX {
     class Material;
     class Geometry;
 
+    class Video;
+
     class AnimationCurve;
     class AnimationCurveNode;
     class AnimationLayer;
@@ -571,6 +573,10 @@ public:
         return crop;
     }
 
+    const Video* Media() const {
+        return media;
+    }
+
 private:
 
     aiVector2D uvTrans;
@@ -583,6 +589,8 @@ private:
     boost::shared_ptr<const PropertyTable> props;
 
     unsigned int crop[4];
+
+    const Video* media;
 };
 
 /** DOM class for layered FBX textures */
@@ -654,6 +662,60 @@ typedef std::fbx_unordered_map<std::string, const Texture*> TextureMap;
 typedef std::fbx_unordered_map<std::string, const LayeredTexture*> LayeredTextureMap;
 
 
+/** DOM class for generic FBX videos */
+class Video : public Object
+{
+public:
+
+    Video(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+    ~Video();
+
+public:
+
+    const std::string& Type() const {
+        return type;
+    }
+
+    const std::string& FileName() const {
+        return fileName;
+    }
+
+    const std::string& RelativeFilename() const {
+        return relativeFileName;
+    }
+
+    const PropertyTable& Props() const {
+        ai_assert(props.get());
+        return *props.get();
+    }
+
+    const uint8_t* Content() const {
+        ai_assert(content);
+        return content;
+    }
+
+    const uint32_t ContentLength() const {
+        return contentLength;
+    }
+
+    uint8_t* RelinquishContent() {
+        uint8_t* ptr = content;
+        content = 0;
+        contentLength = 0;
+        return ptr;
+    }
+
+private:
+
+    std::string type;
+    std::string relativeFileName;
+    std::string fileName;
+    boost::shared_ptr<const PropertyTable> props;
+
+    uint32_t contentLength;
+    uint8_t* content;
+};
+
 /** DOM class for generic FBX materials */
 class Material : public Object
 {

+ 4 - 0
code/FBXImportSettings.h

@@ -55,6 +55,7 @@ struct ImportSettings
         , readAllLayers(true)
         , readAllMaterials(false)
         , readMaterials(true)
+        , readTextures(true)
         , readCameras(true)
         , readLights(true)
         , readAnimations(true)
@@ -92,6 +93,9 @@ struct ImportSettings
      *  material. The default value is true.*/
     bool readMaterials;
 
+    /** import embedded textures? Default value is true.*/
+    bool readTextures;
+
     /** import cameras? Default value is true.*/
     bool readCameras;
 

+ 1 - 0
code/FBXImporter.cpp

@@ -126,6 +126,7 @@ void FBXImporter::SetupProperties(const Importer* pImp)
     settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
     settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
     settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
+    settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
     settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
     settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
     settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);

+ 81 - 0
code/FBXMaterial.cpp

@@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "FBXImportSettings.h"
 #include "FBXDocumentUtil.h"
 #include "FBXProperties.h"
+#include "ByteSwapper.h"
 #include <boost/foreach.hpp>
 
 namespace Assimp {
@@ -147,6 +148,7 @@ Material::~Material()
 Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name)
 : Object(id,element,name)
 , uvScaling(1.0f,1.0f)
+, media(0)
 {
     const Scope& sc = GetRequiredScope(element);
 
@@ -199,6 +201,23 @@ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const
     }
 
     props = GetPropertyTable(doc,"Texture.FbxFileTexture",element,sc);
+
+    // resolve video links
+    if(doc.Settings().readTextures) {
+        const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID());
+        BOOST_FOREACH(const Connection* con, conns) {
+            const Object* const ob = con->SourceObject();
+            if(!ob) {
+                DOMWarning("failed to read source object for texture link, ignoring",&element);
+                continue;
+            }
+
+            const Video* const video = dynamic_cast<const Video*>(ob);
+            if(video) {
+                media = video;
+            }
+        }
+    }
 }
 
 
@@ -253,6 +272,68 @@ void LayeredTexture::fillTexture(const Document& doc)
     }
 }
 
+
+// ------------------------------------------------------------------------------------------------
+Video::Video(uint64_t id, const Element& element, const Document& doc, const std::string& name)
+: Object(id,element,name)
+, contentLength(0)
+, content(0)
+{
+    const Scope& sc = GetRequiredScope(element);
+
+    const Element* const Type = sc["Type"];
+    const Element* const FileName = sc["FileName"];
+    const Element* const RelativeFilename = sc["RelativeFilename"];
+    const Element* const Content = sc["Content"];
+
+    if(Type) {
+        type = ParseTokenAsString(GetRequiredToken(*Type,0));
+    }
+
+    if(FileName) {
+        fileName = ParseTokenAsString(GetRequiredToken(*FileName,0));
+    }
+
+    if(RelativeFilename) {
+        relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0));
+    }
+
+    if(Content) {
+        const Token& token = GetRequiredToken(*Content, 0);
+        const char* data = token.begin();
+        if(!token.IsBinary()) {
+            DOMWarning("video content is not binary data, ignoring", &element);
+        }
+        else if(static_cast<size_t>(token.end() - data) < 5) {
+            DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element);
+        }
+        else if(*data != 'R') {
+            DOMWarning("video content is not raw binary data, ignoring", &element);
+        }
+        else {
+            // read number of elements
+            uint32_t len = 0;
+            ::memcpy(&len, data + 1, sizeof(len));
+            AI_SWAP4(len);
+
+            contentLength = len;
+
+            content = new uint8_t[len];
+            ::memcpy(content, data + 5, len);
+        }
+    }
+
+    props = GetPropertyTable(doc,"Video.FbxVideo",element,sc);
+}
+
+
+Video::~Video()
+{
+    if(content) {
+        delete[] content;
+    }
+}
+
 } //!FBX
 } //!Assimp
 

+ 9 - 0
include/assimp/config.h

@@ -561,6 +561,15 @@ enum aiComponent
 #define AI_CONFIG_IMPORT_FBX_READ_MATERIALS \
     "IMPORT_FBX_READ_MATERIALS"
 
+// ---------------------------------------------------------------------------
+/** @brief Set whether the fbx importer will read embedded textures.
+ *
+ * The default value is true (1)
+ * Property type: bool
+ */
+#define AI_CONFIG_IMPORT_FBX_READ_TEXTURES \
+    "IMPORT_FBX_READ_TEXTURES"
+
 // ---------------------------------------------------------------------------
 /** @brief Set whether the fbx importer will read cameras.
  *