diff options
Diffstat (limited to 'libs/assimp/code/AssetLib/Blender/BlenderDNA.h')
-rw-r--r-- | libs/assimp/code/AssetLib/Blender/BlenderDNA.h | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderDNA.h b/libs/assimp/code/AssetLib/Blender/BlenderDNA.h new file mode 100644 index 0000000..b2158f2 --- /dev/null +++ b/libs/assimp/code/AssetLib/Blender/BlenderDNA.h @@ -0,0 +1,808 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file BlenderDNA.h + * @brief Blender `DNA` (file format specification embedded in + * blend file itself) loader. + */ +#ifndef INCLUDED_AI_BLEND_DNA_H +#define INCLUDED_AI_BLEND_DNA_H + +#include <assimp/BaseImporter.h> +#include <assimp/StreamReader.h> +#include <stdint.h> +#include <assimp/DefaultLogger.hpp> +#include <map> +#include <memory> + +// enable verbose log output. really verbose, so be careful. +#ifdef ASSIMP_BUILD_DEBUG +#define ASSIMP_BUILD_BLENDER_DEBUG +#endif + +// set this to non-zero to dump BlenderDNA stuff to dna.txt. +// you could set it on the assimp build command line too without touching it here. +// !!! please make sure this is set to 0 in the repo !!! +#ifndef ASSIMP_BUILD_BLENDER_DEBUG_DNA +#define ASSIMP_BUILD_BLENDER_DEBUG_DNA 0 +#endif + +// #define ASSIMP_BUILD_BLENDER_NO_STATS + +namespace Assimp { + +template <bool, bool> +class StreamReader; +typedef StreamReader<true, true> StreamReaderAny; + +namespace Blender { + +class FileDatabase; +struct FileBlockHead; + +template <template <typename> class TOUT> +class ObjectCache; + +// ------------------------------------------------------------------------------- +/** Exception class used by the blender loader to selectively catch exceptions + * thrown in its own code (DeadlyImportErrors thrown in general utility + * functions are untouched then). If such an exception is not caught by + * the loader itself, it will still be caught by Assimp due to its + * ancestry. */ +// ------------------------------------------------------------------------------- +struct Error : DeadlyImportError { + template <typename... T> + explicit Error(T &&...args) : + DeadlyImportError(args...) { + } +}; + +// ------------------------------------------------------------------------------- +/** The only purpose of this structure is to feed a virtual dtor into its + * descendents. It serves as base class for all data structure fields. */ +// ------------------------------------------------------------------------------- +struct ElemBase { + ElemBase() : + dna_type(nullptr) { + // empty + } + + virtual ~ElemBase() { + // empty + } + + /** Type name of the element. The type + * string points is the `c_str` of the `name` attribute of the + * corresponding `Structure`, that is, it is only valid as long + * as the DNA is not modified. The dna_type is only set if the + * data type is not static, i.e. a std::shared_ptr<ElemBase> + * in the scene description would have its type resolved + * at runtime, so this member is always set. */ + const char *dna_type; +}; + +// ------------------------------------------------------------------------------- +/** Represents a generic pointer to a memory location, which can be either 32 + * or 64 bits. These pointers are loaded from the BLEND file and finally + * fixed to point to the real, converted representation of the objects + * they used to point to.*/ +// ------------------------------------------------------------------------------- +struct Pointer { + Pointer() : + val() { + // empty + } + uint64_t val; +}; + +// ------------------------------------------------------------------------------- +/** Represents a generic offset within a BLEND file */ +// ------------------------------------------------------------------------------- +struct FileOffset { + FileOffset() : + val() { + // empty + } + uint64_t val; +}; + +// ------------------------------------------------------------------------------- +/** Dummy derivate of std::vector to be able to use it in templates simultaenously + * with std::shared_ptr, which takes only one template argument + * while std::vector takes three. Also we need to provide some special member + * functions of shared_ptr */ +// ------------------------------------------------------------------------------- +template <typename T> +class vector : public std::vector<T> { +public: + using std::vector<T>::resize; + using std::vector<T>::empty; + + void reset() { + resize(0); + } + + operator bool() const { + return !empty(); + } +}; + +// ------------------------------------------------------------------------------- +/** Mixed flags for use in #Field */ +// ------------------------------------------------------------------------------- +enum FieldFlags { + FieldFlag_Pointer = 0x1, + FieldFlag_Array = 0x2 +}; + +// ------------------------------------------------------------------------------- +/** Represents a single member of a data structure in a BLEND file */ +// ------------------------------------------------------------------------------- +struct Field { + std::string name; + std::string type; + + size_t size; + size_t offset; + + /** Size of each array dimension. For flat arrays, + * the second dimension is set to 1. */ + size_t array_sizes[2]; + + /** Any of the #FieldFlags enumerated values */ + unsigned int flags; +}; + +// ------------------------------------------------------------------------------- +/** Range of possible behaviors for fields absence in the input file. Some are + * mission critical so we need them, while others can silently be default + * initialized and no animations are harmed. */ +// ------------------------------------------------------------------------------- +enum ErrorPolicy { + /** Substitute default value and ignore */ + ErrorPolicy_Igno, + /** Substitute default value and write to log */ + ErrorPolicy_Warn, + /** Substitute a massive error message and crash the whole matrix. Its time for another zion */ + ErrorPolicy_Fail +}; + +#ifdef ASSIMP_BUILD_BLENDER_DEBUG +#define ErrorPolicy_Igno ErrorPolicy_Warn +#endif + +// ------------------------------------------------------------------------------- +/** Represents a data structure in a BLEND file. A Structure defines n fields + * and their locations and encodings the input stream. Usually, every + * Structure instance pertains to one equally-named data structure in the + * BlenderScene.h header. This class defines various utilities to map a + * binary `blob` read from the file to such a structure instance with + * meaningful contents. */ +// ------------------------------------------------------------------------------- +class Structure { + template <template <typename> class> + friend class ObjectCache; + +public: + Structure() : + cache_idx(static_cast<size_t>(-1)) { + // empty + } + + // publicly accessible members + std::string name; + vector<Field> fields; + std::map<std::string, size_t> indices; + + size_t size; + + // -------------------------------------------------------- + /** Access a field of the structure by its canonical name. The pointer version + * returns nullptr on failure while the reference version raises an import error. */ + inline const Field &operator[](const std::string &ss) const; + inline const Field *Get(const std::string &ss) const; + + // -------------------------------------------------------- + /** Access a field of the structure by its index */ + inline const Field &operator[](const size_t i) const; + + // -------------------------------------------------------- + inline bool operator==(const Structure &other) const { + return name == other.name; // name is meant to be an unique identifier + } + + // -------------------------------------------------------- + inline bool operator!=(const Structure &other) const { + return name != other.name; + } + + // -------------------------------------------------------- + /** Try to read an instance of the structure from the stream + * and attempt to convert to `T`. This is done by + * an appropriate specialization. If none is available, + * a compiler complain is the result. + * @param dest Destination value to be written + * @param db File database, including input stream. */ + template <typename T> + void Convert(T &dest, const FileDatabase &db) const; + + // -------------------------------------------------------- + // generic converter + template <typename T> + void Convert(std::shared_ptr<ElemBase> in, const FileDatabase &db) const; + + // -------------------------------------------------------- + // generic allocator + template <typename T> + std::shared_ptr<ElemBase> Allocate() const; + + // -------------------------------------------------------- + // field parsing for 1d arrays + template <int error_policy, typename T, size_t M> + void ReadFieldArray(T (&out)[M], const char *name, + const FileDatabase &db) const; + + // -------------------------------------------------------- + // field parsing for 2d arrays + template <int error_policy, typename T, size_t M, size_t N> + void ReadFieldArray2(T (&out)[M][N], const char *name, + const FileDatabase &db) const; + + // -------------------------------------------------------- + // field parsing for pointer or dynamic array types + // (std::shared_ptr) + // The return value indicates whether the data was already cached. + template <int error_policy, template <typename> class TOUT, typename T> + bool ReadFieldPtr(TOUT<T> &out, const char *name, + const FileDatabase &db, + bool non_recursive = false) const; + + // -------------------------------------------------------- + // field parsing for static arrays of pointer or dynamic + // array types (std::shared_ptr[]) + // The return value indicates whether the data was already cached. + template <int error_policy, template <typename> class TOUT, typename T, size_t N> + bool ReadFieldPtr(TOUT<T> (&out)[N], const char *name, + const FileDatabase &db) const; + + // -------------------------------------------------------- + // field parsing for `normal` values + // The return value indicates whether the data was already cached. + template <int error_policy, typename T> + void ReadField(T &out, const char *name, + const FileDatabase &db) const; + + // -------------------------------------------------------- + /** + * @brief field parsing for dynamic vectors + * @param[in] out vector of struct to be filled + * @param[in] name of field + * @param[in] db to access the file, dna, ... + * @return true when read was successful + */ + template <int error_policy, template <typename> class TOUT, typename T> + bool ReadFieldPtrVector(vector<TOUT<T>> &out, const char *name, const FileDatabase &db) const; + + /** + * @brief parses raw customdata + * @param[in] out shared_ptr to be filled + * @param[in] cdtype customdata type to read + * @param[in] name of field ptr + * @param[in] db to access the file, dna, ... + * @return true when read was successful + */ + template <int error_policy> + bool ReadCustomDataPtr(std::shared_ptr<ElemBase> &out, int cdtype, const char *name, const FileDatabase &db) const; + +private: + // -------------------------------------------------------- + template <template <typename> class TOUT, typename T> + bool ResolvePointer(TOUT<T> &out, const Pointer &ptrval, + const FileDatabase &db, const Field &f, + bool non_recursive = false) const; + + // -------------------------------------------------------- + template <template <typename> class TOUT, typename T> + bool ResolvePointer(vector<TOUT<T>> &out, const Pointer &ptrval, + const FileDatabase &db, const Field &f, bool) const; + + // -------------------------------------------------------- + bool ResolvePointer(std::shared_ptr<FileOffset> &out, const Pointer &ptrval, + const FileDatabase &db, const Field &f, bool) const; + + // -------------------------------------------------------- + inline const FileBlockHead *LocateFileBlockForAddress( + const Pointer &ptrval, + const FileDatabase &db) const; + +private: + // ------------------------------------------------------------------------------ + template <typename T> + T *_allocate(std::shared_ptr<T> &out, size_t &s) const { + out = std::shared_ptr<T>(new T()); + s = 1; + return out.get(); + } + + template <typename T> + T *_allocate(vector<T> &out, size_t &s) const { + out.resize(s); + return s ? &out.front() : nullptr; + } + + // -------------------------------------------------------- + template <int error_policy> + struct _defaultInitializer { + + template <typename T, unsigned int N> + void operator()(T (&out)[N], const char * = nullptr) { + for (unsigned int i = 0; i < N; ++i) { + out[i] = T(); + } + } + + template <typename T, unsigned int N, unsigned int M> + void operator()(T (&out)[N][M], const char * = nullptr) { + for (unsigned int i = 0; i < N; ++i) { + for (unsigned int j = 0; j < M; ++j) { + out[i][j] = T(); + } + } + } + + template <typename T> + void operator()(T &out, const char * = nullptr) { + out = T(); + } + }; + +private: + mutable size_t cache_idx; +}; + +// -------------------------------------------------------- +template <> +struct Structure::_defaultInitializer<ErrorPolicy_Warn> { + + template <typename T> + void operator()(T &out, const char *reason = "<add reason>") { + ASSIMP_LOG_WARN(reason); + + // ... and let the show go on + _defaultInitializer<0 /*ErrorPolicy_Igno*/>()(out); + } +}; + +template <> +struct Structure::_defaultInitializer<ErrorPolicy_Fail> { + + template <typename T> + void operator()(T & /*out*/, const char * = "") { + // obviously, it is crucial that _DefaultInitializer is used + // only from within a catch clause. + throw DeadlyImportError("Constructing BlenderDNA Structure encountered an error"); + } +}; + +// ------------------------------------------------------------------------------------------------------- +template <> +inline bool Structure ::ResolvePointer<std::shared_ptr, ElemBase>(std::shared_ptr<ElemBase> &out, + const Pointer &ptrval, + const FileDatabase &db, + const Field &f, + bool) const; + +// ------------------------------------------------------------------------------- +/** Represents the full data structure information for a single BLEND file. + * This data is extracted from the DNA1 chunk in the file. + * #DNAParser does the reading and represents currently the only place where + * DNA is altered.*/ +// ------------------------------------------------------------------------------- +class DNA { +public: + typedef void (Structure::*ConvertProcPtr)( + std::shared_ptr<ElemBase> in, + const FileDatabase &) const; + + typedef std::shared_ptr<ElemBase> ( + Structure::*AllocProcPtr)() const; + + typedef std::pair<AllocProcPtr, ConvertProcPtr> FactoryPair; + +public: + std::map<std::string, FactoryPair> converters; + vector<Structure> structures; + std::map<std::string, size_t> indices; + +public: + // -------------------------------------------------------- + /** Access a structure by its canonical name, the pointer version returns nullptr on failure + * while the reference version raises an error. */ + inline const Structure &operator[](const std::string &ss) const; + inline const Structure *Get(const std::string &ss) const; + + // -------------------------------------------------------- + /** Access a structure by its index */ + inline const Structure &operator[](const size_t i) const; + +public: + // -------------------------------------------------------- + /** Add structure definitions for all the primitive types, + * i.e. integer, short, char, float */ + void AddPrimitiveStructures(); + + // -------------------------------------------------------- + /** Fill the @c converters member with converters for all + * known data types. The implementation of this method is + * in BlenderScene.cpp and is machine-generated. + * Converters are used to quickly handle objects whose + * exact data type is a runtime-property and not yet + * known at compile time (consider Object::data).*/ + void RegisterConverters(); + + // -------------------------------------------------------- + /** Take an input blob from the stream, interpret it according to + * a its structure name and convert it to the intermediate + * representation. + * @param structure Destination structure definition + * @param db File database. + * @return A null pointer if no appropriate converter is available.*/ + std::shared_ptr<ElemBase> ConvertBlobToStructure( + const Structure &structure, + const FileDatabase &db) const; + + // -------------------------------------------------------- + /** Find a suitable conversion function for a given Structure. + * Such a converter function takes a blob from the input + * stream, reads as much as it needs, and builds up a + * complete object in intermediate representation. + * @param structure Destination structure definition + * @param db File database. + * @return A null pointer in .first if no appropriate converter is available.*/ + FactoryPair GetBlobToStructureConverter( + const Structure &structure, + const FileDatabase &db) const; + +#if ASSIMP_BUILD_BLENDER_DEBUG_DNA + // -------------------------------------------------------- + /** Dump the DNA to a text file. This is for debugging purposes. + * The output file is `dna.txt` in the current working folder*/ + void DumpToFile(); +#endif + + // -------------------------------------------------------- + /** Extract array dimensions from a C array declaration, such + * as `...[4][6]`. Returned string would be `...[][]`. + * @param out + * @param array_sizes Receive maximally two array dimensions, + * the second element is set to 1 if the array is flat. + * Both are set to 1 if the input is not an array. + * @throw DeadlyImportError if more than 2 dimensions are + * encountered. */ + static void ExtractArraySize( + const std::string &out, + size_t array_sizes[2]); +}; + +// special converters for primitive types +template <> +inline void Structure ::Convert<int>(int &dest, const FileDatabase &db) const; +template <> +inline void Structure ::Convert<short>(short &dest, const FileDatabase &db) const; +template <> +inline void Structure ::Convert<char>(char &dest, const FileDatabase &db) const; +template <> +inline void Structure ::Convert<float>(float &dest, const FileDatabase &db) const; +template <> +inline void Structure ::Convert<double>(double &dest, const FileDatabase &db) const; +template <> +inline void Structure ::Convert<Pointer>(Pointer &dest, const FileDatabase &db) const; + +// ------------------------------------------------------------------------------- +/** Describes a master file block header. Each master file sections holds n + * elements of a certain SDNA structure (or otherwise unspecified data). */ +// ------------------------------------------------------------------------------- +struct FileBlockHead { + // points right after the header of the file block + StreamReaderAny::pos start; + + std::string id; + size_t size; + + // original memory address of the data + Pointer address; + + // index into DNA + unsigned int dna_index; + + // number of structure instances to follow + size_t num; + + // file blocks are sorted by address to quickly locate specific memory addresses + bool operator<(const FileBlockHead &o) const { + return address.val < o.address.val; + } + + // for std::upper_bound + operator const Pointer &() const { + return address; + } +}; + +// for std::upper_bound +inline bool operator<(const Pointer &a, const Pointer &b) { + return a.val < b.val; +} + +// ------------------------------------------------------------------------------- +/** Utility to read all master file blocks in turn. */ +// ------------------------------------------------------------------------------- +class SectionParser { +public: + // -------------------------------------------------------- + /** @param stream Inout stream, must point to the + * first section in the file. Call Next() once + * to have it read. + * @param ptr64 Pointer size in file is 64 bits? */ + SectionParser(StreamReaderAny &stream, bool ptr64) : + stream(stream), ptr64(ptr64) { + current.size = current.start = 0; + } + +public: + // -------------------------------------------------------- + const FileBlockHead &GetCurrent() const { + return current; + } + +public: + // -------------------------------------------------------- + /** Advance to the next section. + * @throw DeadlyImportError if the last chunk was passed. */ + void Next(); + +public: + FileBlockHead current; + StreamReaderAny &stream; + bool ptr64; +}; + +#ifndef ASSIMP_BUILD_BLENDER_NO_STATS +// ------------------------------------------------------------------------------- +/** Import statistics, i.e. number of file blocks read*/ +// ------------------------------------------------------------------------------- +class Statistics { + +public: + Statistics() : + fields_read(), pointers_resolved(), cache_hits() + // , blocks_read () + , + cached_objects() {} + +public: + /** total number of fields we read */ + unsigned int fields_read; + + /** total number of resolved pointers */ + unsigned int pointers_resolved; + + /** number of pointers resolved from the cache */ + unsigned int cache_hits; + + /** number of blocks (from FileDatabase::entries) + we did actually read from. */ + // unsigned int blocks_read; + + /** objects in FileData::cache */ + unsigned int cached_objects; +}; +#endif + +// ------------------------------------------------------------------------------- +/** The object cache - all objects addressed by pointers are added here. This + * avoids circular references and avoids object duplication. */ +// ------------------------------------------------------------------------------- +template <template <typename> class TOUT> +class ObjectCache { +public: + typedef std::map<Pointer, TOUT<ElemBase>> StructureCache; + +public: + ObjectCache(const FileDatabase &db) : + db(db) { + // currently there are only ~400 structure records per blend file. + // we read only a small part of them and don't cache objects + // which we don't need, so this should suffice. + caches.reserve(64); + } + +public: + // -------------------------------------------------------- + /** Check whether a specific item is in the cache. + * @param s Data type of the item + * @param out Output pointer. Unchanged if the + * cache doesn't know the item yet. + * @param ptr Item address to look for. */ + template <typename T> + void get( + const Structure &s, + TOUT<T> &out, + const Pointer &ptr) const; + + // -------------------------------------------------------- + /** Add an item to the cache after the item has + * been fully read. Do not insert anything that + * may be faulty or might cause the loading + * to abort. + * @param s Data type of the item + * @param out Item to insert into the cache + * @param ptr address (cache key) of the item. */ + template <typename T> + void set(const Structure &s, + const TOUT<T> &out, + const Pointer &ptr); + +private: + mutable vector<StructureCache> caches; + const FileDatabase &db; +}; + +// ------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------- +template <> +class ObjectCache<Blender::vector> { +public: + ObjectCache(const FileDatabase &) {} + + template <typename T> + void get(const Structure &, vector<T> &, const Pointer &) {} + template <typename T> + void set(const Structure &, const vector<T> &, const Pointer &) {} +}; + +#ifdef _MSC_VER +#pragma warning(disable : 4355) +#endif + +// ------------------------------------------------------------------------------- +/** Memory representation of a full BLEND file and all its dependencies. The + * output aiScene is constructed from an instance of this data structure. */ +// ------------------------------------------------------------------------------- +class FileDatabase { + template <template <typename> class TOUT> + friend class ObjectCache; + +public: + FileDatabase() : + _cacheArrays(*this), _cache(*this), next_cache_idx() {} + +public: + // publicly accessible fields + bool i64bit; + bool little; + + DNA dna; + std::shared_ptr<StreamReaderAny> reader; + vector<FileBlockHead> entries; + +public: + Statistics &stats() const { + return _stats; + } + + // For all our templates to work on both shared_ptr's and vector's + // using the same code, a dummy cache for arrays is provided. Actually, + // arrays of objects are never cached because we can't easily + // ensure their proper destruction. + template <typename T> + ObjectCache<std::shared_ptr> &cache(std::shared_ptr<T> & /*in*/) const { + return _cache; + } + + template <typename T> + ObjectCache<vector> &cache(vector<T> & /*in*/) const { + return _cacheArrays; + } + +private: +#ifndef ASSIMP_BUILD_BLENDER_NO_STATS + mutable Statistics _stats; +#endif + + mutable ObjectCache<vector> _cacheArrays; + mutable ObjectCache<std::shared_ptr> _cache; + + mutable size_t next_cache_idx; +}; + +#ifdef _MSC_VER +#pragma warning(default : 4355) +#endif + +// ------------------------------------------------------------------------------- +/** Factory to extract a #DNA from the DNA1 file block in a BLEND file. */ +// ------------------------------------------------------------------------------- +class DNAParser { + +public: + /** Bind the parser to a empty DNA and an input stream */ + DNAParser(FileDatabase &db) : + db(db) {} + +public: + // -------------------------------------------------------- + /** Locate the DNA in the file and parse it. The input + * stream is expected to point to the beginning of the DN1 + * chunk at the time this method is called and is + * undefined afterwards. + * @throw DeadlyImportError if the DNA cannot be read. + * @note The position of the stream pointer is undefined + * afterwards.*/ + void Parse(); + +public: + /** Obtain a reference to the extracted DNA information */ + const Blender::DNA &GetDNA() const { + return db.dna; + } + +private: + FileDatabase &db; +}; + +/** +* @brief read CustomData's data to ptr to mem +* @param[out] out memory ptr to set +* @param[in] cdtype to read +* @param[in] cnt cnt of elements to read +* @param[in] db to read elements from +* @return true when ok +*/ +bool readCustomData(std::shared_ptr<ElemBase> &out, int cdtype, size_t cnt, const FileDatabase &db); + +} // namespace Blender +} // namespace Assimp + +#include "BlenderDNA.inl" + +#endif |