diff --git a/code/FBXConverter.cpp b/code/FBXConverter.cpp index e0c6b9ca2..27d43e753 100644 --- a/code/FBXConverter.cpp +++ b/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 #include @@ -148,6 +149,7 @@ public: std::for_each(animations.begin(),animations.end(),Util::delete_fun()); std::for_each(lights.begin(),lights.end(),Util::delete_fun()); std::for_each(cameras.begin(),cameras.end(),Util::delete_fun()); + std::for_each(textures.begin(),textures.end(),Util::delete_fun()); } @@ -1449,6 +1451,36 @@ private: return static_cast(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(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( const_cast(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(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 && media->ContentLength() > 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(textures.size()); + + std::swap_ranges(textures.begin(),textures.end(),out->mTextures); + } } @@ -3037,10 +3094,14 @@ private: std::vector animations; std::vector lights; std::vector cameras; + std::vector textures; typedef std::map MaterialMap; MaterialMap materials_converted; + typedef std::map VideoMap; + VideoMap textures_converted; + typedef std::map > MeshMap; MeshMap meshes_converted; diff --git a/code/FBXDocument.cpp b/code/FBXDocument.cpp index 67ca99466..52f4c81bd 100644 --- a/code/FBXDocument.cpp +++ b/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)); } @@ -483,6 +486,11 @@ void Document::ReadConnections() for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) { const Element& el = *(*it).second; const std::string& type = ParseTokenAsString(GetRequiredToken(el,0)); + + // PP = property-property connection, ignored for now + // (tokens: "PP", ID1, "Property1", ID2, "Property2") + if(type == "PP") continue; + const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1)); const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2)); diff --git a/code/FBXDocument.h b/code/FBXDocument.h index 6a0c71c8a..3d8f18801 100644 --- a/code/FBXDocument.h +++ b/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 props; unsigned int crop[4]; + + const Video* media; }; /** DOM class for layered FBX textures */ @@ -654,6 +662,59 @@ typedef std::fbx_unordered_map TextureMap; typedef std::fbx_unordered_map 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; + return ptr; + } + +private: + + std::string type; + std::string relativeFileName; + std::string fileName; + boost::shared_ptr props; + + uint32_t contentLength; + uint8_t* content; +}; + /** DOM class for generic FBX materials */ class Material : public Object { diff --git a/code/FBXImportSettings.h b/code/FBXImportSettings.h index 13c266ada..484241c52 100644 --- a/code/FBXImportSettings.h +++ b/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; diff --git a/code/FBXImporter.cpp b/code/FBXImporter.cpp index d7e4926aa..9bfc4fd2a 100644 --- a/code/FBXImporter.cpp +++ b/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); diff --git a/code/FBXMaterial.cpp b/code/FBXMaterial.cpp index 03f292168..ced7247c6 100644 --- a/code/FBXMaterial.cpp +++ b/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 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& 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(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(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 diff --git a/include/assimp/config.h b/include/assimp/config.h index 4b2546760..1f145bfc0 100644 --- a/include/assimp/config.h +++ b/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. * diff --git a/include/assimp/texture.h b/include/assimp/texture.h index 40faf497e..1ec3bbf31 100644 --- a/include/assimp/texture.h +++ b/include/assimp/texture.h @@ -116,6 +116,10 @@ struct aiTexel * 2. Compressed textures stored in a file format like png or jpg. The raw file * bytes are given so the application must utilize an image decoder (e.g. DevIL) to * get access to the actual color data. + * + * Embedded textures are referenced from materials using strings like "*0", "*1", etc. + * as the texture paths (a single asterisk character followed by the + * zero-based index of the texture in the aiScene::mTextures array). */ struct aiTexture { diff --git a/test/regression/db.zip b/test/regression/db.zip index c94598840..c7631c240 100644 Binary files a/test/regression/db.zip and b/test/regression/db.zip differ