diff options
author | sanine <sanine.not@pm.me> | 2023-02-12 23:53:22 -0600 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2023-02-12 23:53:22 -0600 |
commit | f1fe73d1909a2448a004a88362a1a532d0d4f7c3 (patch) | |
tree | ab37ae3837e2f858de2932bcee9f26e69fab3db1 /libs/assimp/code/AssetLib/glTF2 | |
parent | f567ea1e2798fd3156a416e61f083ea3e6b95719 (diff) |
switch to tinyobj and nanovg from assimp and cairo
Diffstat (limited to 'libs/assimp/code/AssetLib/glTF2')
-rw-r--r-- | libs/assimp/code/AssetLib/glTF2/glTF2Asset.h | 1242 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/glTF2/glTF2Asset.inl | 2069 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/glTF2/glTF2AssetWriter.h | 101 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/glTF2/glTF2AssetWriter.inl | 1015 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/glTF2/glTF2Exporter.cpp | 1542 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/glTF2/glTF2Exporter.h | 147 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/glTF2/glTF2Importer.cpp | 1637 | ||||
-rw-r--r-- | libs/assimp/code/AssetLib/glTF2/glTF2Importer.h | 91 |
8 files changed, 0 insertions, 7844 deletions
diff --git a/libs/assimp/code/AssetLib/glTF2/glTF2Asset.h b/libs/assimp/code/AssetLib/glTF2/glTF2Asset.h deleted file mode 100644 index c597fc9..0000000 --- a/libs/assimp/code/AssetLib/glTF2/glTF2Asset.h +++ /dev/null @@ -1,1242 +0,0 @@ -/* -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 glTFAsset.h - * Declares a glTF class to handle gltf/glb files - * - * glTF Extensions Support: - * KHR_materials_pbrSpecularGlossiness full - * KHR_materials_unlit full - * KHR_lights_punctual full - * KHR_materials_sheen full - * KHR_materials_clearcoat full - * KHR_materials_transmission full - * KHR_materials_volume full - * KHR_materials_ior full - */ -#ifndef GLTF2ASSET_H_INC -#define GLTF2ASSET_H_INC - -#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER) - -#include <assimp/Exceptional.h> - -#include <algorithm> -#include <list> -#include <map> -#include <set> -#include <stdexcept> -#include <string> -#include <vector> - -// clang-format off -#if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wclass-memaccess" -#endif - -#include <rapidjson/document.h> -#include <rapidjson/error/en.h> -#include <rapidjson/rapidjson.h> -#include <rapidjson/schema.h> - -#if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0) -# pragma GCC diagnostic pop -#endif - -#ifdef ASSIMP_API -# include <assimp/ByteSwapper.h> -# include <assimp/DefaultIOSystem.h> -# include <memory> -#else -# include <memory> -# define AI_SWAP4(p) -# define ai_assert -#endif - -#if _MSC_VER > 1500 || (defined __GNUC___) -# define ASSIMP_GLTF_USE_UNORDERED_MULTIMAP -#else -# define gltf_unordered_map map -# define gltf_unordered_set set -#endif - -#ifdef ASSIMP_GLTF_USE_UNORDERED_MULTIMAP -# include <unordered_map> -# include <unordered_set> -# if defined(_MSC_VER) && _MSC_VER <= 1600 -# define gltf_unordered_map tr1::unordered_map -# define gltf_unordered_set tr1::unordered_set -# else -# define gltf_unordered_map unordered_map -# define gltf_unordered_set unordered_set -# endif -#endif -// clang-format on - -#include <assimp/StringUtils.h> -#include <assimp/material.h> -#include <assimp/GltfMaterial.h> - -#include "AssetLib/glTF/glTFCommon.h" - -namespace glTF2 { - -using glTFCommon::Nullable; -using glTFCommon::Ref; -using glTFCommon::IOStream; -using glTFCommon::IOSystem; -using glTFCommon::shared_ptr; - -using rapidjson::Document; -using rapidjson::Value; - -class Asset; -class AssetWriter; - -struct BufferView; // here due to cross-reference -struct Texture; -struct Skin; - -using glTFCommon::mat4; -using glTFCommon::vec3; -using glTFCommon::vec4; - -//! Magic number for GLB files -#define AI_GLB_MAGIC_NUMBER "glTF" - -#ifdef ASSIMP_API -#include <assimp/Compiler/pushpack1.h> -#endif - -//! For binary .glb files -//! 12-byte header (+ the JSON + a "body" data section) -struct GLB_Header { - uint8_t magic[4]; //!< Magic number: "glTF" - uint32_t version; //!< Version number (always 2 as of the last update) - uint32_t length; //!< Total length of the Binary glTF, including header, scene, and body, in bytes -} PACK_STRUCT; - -struct GLB_Chunk { - uint32_t chunkLength; - uint32_t chunkType; -} PACK_STRUCT; - -#ifdef ASSIMP_API -#include <assimp/Compiler/poppack1.h> -#endif - -//! Values for the GLB_Chunk::chunkType field -enum ChunkType { - ChunkType_JSON = 0x4E4F534A, - ChunkType_BIN = 0x004E4942 -}; - -//! Values for the mesh primitive modes -enum PrimitiveMode { - PrimitiveMode_POINTS = 0, - PrimitiveMode_LINES = 1, - PrimitiveMode_LINE_LOOP = 2, - PrimitiveMode_LINE_STRIP = 3, - PrimitiveMode_TRIANGLES = 4, - PrimitiveMode_TRIANGLE_STRIP = 5, - PrimitiveMode_TRIANGLE_FAN = 6 -}; - -//! Values for the Accessor::componentType field -enum ComponentType { - ComponentType_BYTE = 5120, - ComponentType_UNSIGNED_BYTE = 5121, - ComponentType_SHORT = 5122, - ComponentType_UNSIGNED_SHORT = 5123, - ComponentType_UNSIGNED_INT = 5125, - ComponentType_FLOAT = 5126 -}; - -inline unsigned int ComponentTypeSize(ComponentType t) { - switch (t) { - case ComponentType_SHORT: - case ComponentType_UNSIGNED_SHORT: - return 2; - - case ComponentType_UNSIGNED_INT: - case ComponentType_FLOAT: - return 4; - - case ComponentType_BYTE: - case ComponentType_UNSIGNED_BYTE: - return 1; - default: - throw DeadlyImportError("GLTF: Unsupported Component Type ", ai_to_string(t)); - } -} - -//! Values for the BufferView::target field -enum BufferViewTarget { - BufferViewTarget_NONE = 0, - BufferViewTarget_ARRAY_BUFFER = 34962, - BufferViewTarget_ELEMENT_ARRAY_BUFFER = 34963 -}; - -//! Values for the Sampler::magFilter field -enum class SamplerMagFilter : unsigned int { - UNSET = 0, - SamplerMagFilter_Nearest = 9728, - SamplerMagFilter_Linear = 9729 -}; - -//! Values for the Sampler::minFilter field -enum class SamplerMinFilter : unsigned int { - UNSET = 0, - SamplerMinFilter_Nearest = 9728, - SamplerMinFilter_Linear = 9729, - SamplerMinFilter_Nearest_Mipmap_Nearest = 9984, - SamplerMinFilter_Linear_Mipmap_Nearest = 9985, - SamplerMinFilter_Nearest_Mipmap_Linear = 9986, - SamplerMinFilter_Linear_Mipmap_Linear = 9987 -}; - -//! Values for the Sampler::wrapS and Sampler::wrapT field -enum class SamplerWrap : unsigned int { - UNSET = 0, - Clamp_To_Edge = 33071, - Mirrored_Repeat = 33648, - Repeat = 10497 -}; - -//! Values for the Texture::format and Texture::internalFormat fields -enum TextureFormat { - TextureFormat_ALPHA = 6406, - TextureFormat_RGB = 6407, - TextureFormat_RGBA = 6408, - TextureFormat_LUMINANCE = 6409, - TextureFormat_LUMINANCE_ALPHA = 6410 -}; - -//! Values for the Texture::target field -enum TextureTarget { - TextureTarget_TEXTURE_2D = 3553 -}; - -//! Values for the Texture::type field -enum TextureType { - TextureType_UNSIGNED_BYTE = 5121, - TextureType_UNSIGNED_SHORT_5_6_5 = 33635, - TextureType_UNSIGNED_SHORT_4_4_4_4 = 32819, - TextureType_UNSIGNED_SHORT_5_5_5_1 = 32820 -}; - -//! Values for the Animation::Target::path field -enum AnimationPath { - AnimationPath_TRANSLATION, - AnimationPath_ROTATION, - AnimationPath_SCALE, - AnimationPath_WEIGHTS, -}; - -//! Values for the Animation::Sampler::interpolation field -enum Interpolation { - Interpolation_LINEAR, - Interpolation_STEP, - Interpolation_CUBICSPLINE, -}; - -//! Values for the Accessor::type field (helper class) -class AttribType { -public: - enum Value { SCALAR, - VEC2, - VEC3, - VEC4, - MAT2, - MAT3, - MAT4 }; - -private: - static const size_t NUM_VALUES = static_cast<size_t>(MAT4) + 1; - - struct Info { - const char *name; - unsigned int numComponents; - }; - - template <int N> - struct data { static const Info infos[NUM_VALUES]; }; - -public: - inline static Value FromString(const char *str) { - for (size_t i = 0; i < NUM_VALUES; ++i) { - if (strcmp(data<0>::infos[i].name, str) == 0) { - return static_cast<Value>(i); - } - } - return SCALAR; - } - - inline static const char *ToString(Value type) { - return data<0>::infos[static_cast<size_t>(type)].name; - } - - inline static unsigned int GetNumComponents(Value type) { - return data<0>::infos[static_cast<size_t>(type)].numComponents; - } -}; - -// must match the order of the AttribTypeTraits::Value enum! -template <int N> -const AttribType::Info - AttribType::data<N>::infos[AttribType::NUM_VALUES] = { - { "SCALAR", 1 }, { "VEC2", 2 }, { "VEC3", 3 }, { "VEC4", 4 }, { "MAT2", 4 }, { "MAT3", 9 }, { "MAT4", 16 } - }; - - -struct CustomExtension { - - // - // A struct containing custom extension data added to a glTF2 file - // Has to contain Object, Array, String, Double, Uint64, and Int64 at a minimum - // String, Double, Uint64, and Int64 are stored in the Nullables - // Object and Array are stored in the std::vector - // - std::string name; - - Nullable<std::string> mStringValue; - Nullable<double> mDoubleValue; - Nullable<uint64_t> mUint64Value; - Nullable<int64_t> mInt64Value; - Nullable<bool> mBoolValue; - - // std::vector<CustomExtension> handles both Object and Array - Nullable<std::vector<CustomExtension>> mValues; - - operator bool() const { - return Size() != 0; - } - - size_t Size() const { - if (mValues.isPresent) { - return mValues.value.size(); - } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) { - return 1; - } - return 0; - } - - CustomExtension() = default; - - ~CustomExtension() = default; - - CustomExtension(const CustomExtension &other) : - name(other.name), - mStringValue(other.mStringValue), - mDoubleValue(other.mDoubleValue), - mUint64Value(other.mUint64Value), - mInt64Value(other.mInt64Value), - mBoolValue(other.mBoolValue), - mValues(other.mValues) { - // empty - } -}; - -//! Base class for all glTF top-level objects -struct Object { - int index; //!< The index of this object within its property container - int oIndex; //!< The original index of this object defined in the JSON - std::string id; //!< The globally unique ID used to reference this object - std::string name; //!< The user-defined name of this object - - CustomExtension customExtensions; - CustomExtension extras; - - //! Objects marked as special are not exported (used to emulate the binary body buffer) - virtual bool IsSpecial() const { return false; } - - virtual ~Object() {} - - //! Maps special IDs to another ID, where needed. Subclasses may override it (statically) - static const char *TranslateId(Asset & /*r*/, const char *id) { return id; } - - inline Value *FindString(Value &val, const char *id); - inline Value *FindNumber(Value &val, const char *id); - inline Value *FindUInt(Value &val, const char *id); - inline Value *FindArray(Value &val, const char *id); - inline Value *FindObject(Value &val, const char *id); - inline Value *FindExtension(Value &val, const char *extensionId); - - inline void ReadExtensions(Value &val); - inline void ReadExtras(Value &val); -}; - -// -// Classes for each glTF top-level object type -// - -//! A buffer points to binary geometry, animation, or skins. -struct Buffer : public Object { - /********************* Types *********************/ -public: - enum Type { - Type_arraybuffer, - Type_text - }; - - /// \struct SEncodedRegion - /// Descriptor of encoded region in "bufferView". - struct SEncodedRegion { - const size_t Offset; ///< Offset from begin of "bufferView" to encoded region, in bytes. - const size_t EncodedData_Length; ///< Size of encoded region, in bytes. - uint8_t *const DecodedData; ///< Cached encoded data. - const size_t DecodedData_Length; ///< Size of decoded region, in bytes. - const std::string ID; ///< ID of the region. - - /// \fn SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string pID) - /// Constructor. - /// \param [in] pOffset - offset from begin of "bufferView" to encoded region, in bytes. - /// \param [in] pEncodedData_Length - size of encoded region, in bytes. - /// \param [in] pDecodedData - pointer to decoded data array. - /// \param [in] pDecodedData_Length - size of encoded region, in bytes. - /// \param [in] pID - ID of the region. - SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) : - Offset(pOffset), - EncodedData_Length(pEncodedData_Length), - DecodedData(pDecodedData), - DecodedData_Length(pDecodedData_Length), - ID(pID) {} - - /// \fn ~SEncodedRegion() - /// Destructor. - ~SEncodedRegion() { delete[] DecodedData; } - }; - - /******************* Variables *******************/ - - //std::string uri; //!< The uri of the buffer. Can be a filepath, a data uri, etc. (required) - size_t byteLength; //!< The length of the buffer in bytes. (default: 0) - //std::string type; //!< XMLHttpRequest responseType (default: "arraybuffer") - size_t capacity = 0; //!< The capacity of the buffer in bytes. (default: 0) - - Type type; - - /// \var EncodedRegion_Current - /// Pointer to currently active encoded region. - /// Why not decoding all regions at once and not to set one buffer with decoded data? - /// Yes, why not? Even "accessor" point to decoded data. I mean that fields "byteOffset", "byteStride" and "count" has values which describes decoded - /// data array. But only in range of mesh while is active parameters from "compressedData". For another mesh accessors point to decoded data too. But - /// offset is counted for another regions is encoded. - /// Example. You have two meshes. For every of it you have 4 bytes of data. That data compressed to 2 bytes. So, you have buffer with encoded data: - /// M1_E0, M1_E1, M2_E0, M2_E1. - /// After decoding you'll get: - /// M1_D0, M1_D1, M1_D2, M1_D3, M2_D0, M2_D1, M2_D2, M2_D3. - /// "accessors" must to use values that point to decoded data - obviously. So, you'll expect "accessors" like - /// "accessor_0" : { byteOffset: 0, byteLength: 4}, "accessor_1" : { byteOffset: 4, byteLength: 4} - /// but in real life you'll get: - /// "accessor_0" : { byteOffset: 0, byteLength: 4}, "accessor_1" : { byteOffset: 2, byteLength: 4} - /// Yes, accessor of next mesh has offset and length which mean: current mesh data is decoded, all other data is encoded. - /// And when before you start to read data of current mesh (with encoded data of course) you must decode region of "bufferView", after read finished - /// delete encoding mark. And after that you can repeat process: decode data of mesh, read, delete decoded data. - /// - /// Remark. Encoding all data at once is good in world with computers which do not has RAM limitation. So, you must use step by step encoding in - /// exporter and importer. And, thanks to such way, there is no need to load whole file into memory. - SEncodedRegion *EncodedRegion_Current; - -private: - shared_ptr<uint8_t> mData; //!< Pointer to the data - bool mIsSpecial; //!< Set to true for special cases (e.g. the body buffer) - - /// \var EncodedRegion_List - /// List of encoded regions. - std::list<SEncodedRegion *> EncodedRegion_List; - - /******************* Functions *******************/ - -public: - Buffer(); - ~Buffer(); - - void Read(Value &obj, Asset &r); - - bool LoadFromStream(IOStream &stream, size_t length = 0, size_t baseOffset = 0); - - /// \fn void EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string& pID) - /// Mark region of "bufferView" as encoded. When data is request from such region then "bufferView" use decoded data. - /// \param [in] pOffset - offset from begin of "bufferView" to encoded region, in bytes. - /// \param [in] pEncodedData_Length - size of encoded region, in bytes. - /// \param [in] pDecodedData - pointer to decoded data array. - /// \param [in] pDecodedData_Length - size of encoded region, in bytes. - /// \param [in] pID - ID of the region. - void EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID); - - /// \fn void EncodedRegion_SetCurrent(const std::string& pID) - /// Select current encoded region by ID. \sa EncodedRegion_Current. - /// \param [in] pID - ID of the region. - void EncodedRegion_SetCurrent(const std::string &pID); - - /// \fn bool ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t* pReplace_Data, const size_t pReplace_Count) - /// Replace part of buffer data. Pay attention that function work with original array of data (\ref mData) not with encoded regions. - /// \param [in] pBufferData_Offset - index of first element in buffer from which new data will be placed. - /// \param [in] pBufferData_Count - count of bytes in buffer which will be replaced. - /// \param [in] pReplace_Data - pointer to array with new data for buffer. - /// \param [in] pReplace_Count - count of bytes in new data. - /// \return true - if successfully replaced, false if input arguments is out of range. - bool ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count); - bool ReplaceData_joint(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count); - - size_t AppendData(uint8_t *data, size_t length); - void Grow(size_t amount); - - uint8_t *GetPointer() { return mData.get(); } - - void MarkAsSpecial() { mIsSpecial = true; } - - bool IsSpecial() const override { return mIsSpecial; } - - std::string GetURI() { return std::string(this->id) + ".bin"; } - - static const char *TranslateId(Asset &r, const char *id); -}; - -//! A view into a buffer generally representing a subset of the buffer. -struct BufferView : public Object { - Ref<Buffer> buffer; //! The ID of the buffer. (required) - size_t byteOffset; //! The offset into the buffer in bytes. (required) - size_t byteLength; //! The length of the bufferView in bytes. (default: 0) - unsigned int byteStride; //!< The stride, in bytes, between attributes referenced by this accessor. (default: 0) - - BufferViewTarget target; //! The target that the WebGL buffer should be bound to. - - void Read(Value &obj, Asset &r); - uint8_t *GetPointer(size_t accOffset); -}; - -//! A typed view into a BufferView. A BufferView contains raw binary data. -//! An accessor provides a typed view into a BufferView or a subset of a BufferView -//! similar to how WebGL's vertexAttribPointer() defines an attribute in a buffer. -struct Accessor : public Object { - struct Sparse; - - Ref<BufferView> bufferView; //!< The ID of the bufferView. (required) - size_t byteOffset; //!< The offset relative to the start of the bufferView in bytes. (required) - ComponentType componentType; //!< The datatype of components in the attribute. (required) - size_t count; //!< The number of attributes referenced by this accessor. (required) - AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required) - std::vector<double> max; //!< Maximum value of each component in this attribute. - std::vector<double> min; //!< Minimum value of each component in this attribute. - std::unique_ptr<Sparse> sparse; - std::unique_ptr<Buffer> decodedBuffer; // Packed decoded data, returned instead of original bufferView if present - - unsigned int GetNumComponents(); - unsigned int GetBytesPerComponent(); - unsigned int GetElementSize(); - - inline uint8_t *GetPointer(); - inline size_t GetStride(); - inline size_t GetMaxByteSize(); - - template <class T> - void ExtractData(T *&outData); - - void WriteData(size_t count, const void *src_buffer, size_t src_stride); - void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride); - void WriteSparseIndices(size_t count, const void *src_idx, size_t src_idxStride); - - //! Helper class to iterate the data - class Indexer { - friend struct Accessor; - - // This field is reported as not used, making it protectd is the easiest way to work around it without going to the bottom of what the problem is: - // ../code/glTF2/glTF2Asset.h:392:19: error: private field 'accessor' is not used [-Werror,-Wunused-private-field] - protected: - Accessor &accessor; - - private: - uint8_t *data; - size_t elemSize, stride; - - Indexer(Accessor &acc); - - public: - //! Accesses the i-th value as defined by the accessor - template <class T> - T GetValue(int i); - - //! Accesses the i-th value as defined by the accessor - inline unsigned int GetUInt(int i) { - return GetValue<unsigned int>(i); - } - - inline bool IsValid() const { - return data != nullptr; - } - }; - - inline Indexer GetIndexer() { - return Indexer(*this); - } - - Accessor() {} - void Read(Value &obj, Asset &r); - - //sparse - struct Sparse { - size_t count; - ComponentType indicesType; - Ref<BufferView> indices; - size_t indicesByteOffset; - Ref<BufferView> values; - size_t valuesByteOffset; - - std::vector<uint8_t> data; //!< Actual data, which may be defaulted to an array of zeros or the original data, with the sparse buffer view applied on top of it. - - void PopulateData(size_t numBytes, uint8_t *bytes); - void PatchData(unsigned int elementSize); - }; -}; - -struct Camera : public Object { - enum Type { - Perspective, - Orthographic - }; - - Type type; - - union { - struct { - float aspectRatio; //!<The floating - point aspect ratio of the field of view. (0 = undefined = use the canvas one) - float yfov; //!<The floating - point vertical field of view in radians. (required) - float zfar; //!<The floating - point distance to the far clipping plane. (required) - float znear; //!< The floating - point distance to the near clipping plane. (required) - } perspective; - - struct { - float xmag; //! The floating-point horizontal magnification of the view. (required) - float ymag; //! The floating-point vertical magnification of the view. (required) - float zfar; //! The floating-point distance to the far clipping plane. (required) - float znear; //! The floating-point distance to the near clipping plane. (required) - } ortographic; - } cameraProperties; - - Camera() : - type(Perspective), - cameraProperties() { - // empty - } - void Read(Value &obj, Asset &r); -}; - -//! A light (from KHR_lights_punctual extension) -struct Light : public Object { - enum Type { - Directional, - Point, - Spot - }; - - Type type; - - vec3 color; - float intensity; - Nullable<float> range; - - float innerConeAngle; - float outerConeAngle; - - Light() {} - void Read(Value &obj, Asset &r); -}; - -//! Image data used to create a texture. -struct Image : public Object { - std::string uri; //! The uri of the image, that can be a file path, a data URI, etc.. (required) - - Ref<BufferView> bufferView; - - std::string mimeType; - - int width, height; - -private: - std::unique_ptr<uint8_t[]> mData; - size_t mDataLength; - -public: - Image(); - void Read(Value &obj, Asset &r); - - inline bool HasData() const { return mDataLength > 0; } - - inline size_t GetDataLength() const { return mDataLength; } - - inline const uint8_t *GetData() const { return mData.get(); } - - inline uint8_t *StealData(); - - inline void SetData(uint8_t *data, size_t length, Asset &r); -}; - -const vec4 defaultBaseColor = { 1, 1, 1, 1 }; -const vec3 defaultEmissiveFactor = { 0, 0, 0 }; -const vec4 defaultDiffuseFactor = { 1, 1, 1, 1 }; -const vec3 defaultSpecularFactor = { 1, 1, 1 }; -const vec3 defaultSheenFactor = { 0, 0, 0 }; -const vec3 defaultAttenuationColor = { 1, 1, 1 }; - -struct TextureInfo { - Ref<Texture> texture; - unsigned int index; - unsigned int texCoord = 0; - - bool textureTransformSupported = false; - struct TextureTransformExt { - float offset[2]; - float rotation; - float scale[2]; - } TextureTransformExt_t; -}; - -struct NormalTextureInfo : TextureInfo { - float scale = 1; -}; - -struct OcclusionTextureInfo : TextureInfo { - float strength = 1; -}; - -struct PbrMetallicRoughness { - vec4 baseColorFactor; - TextureInfo baseColorTexture; - TextureInfo metallicRoughnessTexture; - float metallicFactor; - float roughnessFactor; -}; - -struct PbrSpecularGlossiness { - vec4 diffuseFactor; - vec3 specularFactor; - float glossinessFactor; - TextureInfo diffuseTexture; - TextureInfo specularGlossinessTexture; - - PbrSpecularGlossiness() { SetDefaults(); } - void SetDefaults(); -}; - -struct MaterialSheen { - vec3 sheenColorFactor; - float sheenRoughnessFactor; - TextureInfo sheenColorTexture; - TextureInfo sheenRoughnessTexture; - - MaterialSheen() { SetDefaults(); } - void SetDefaults(); -}; - -struct MaterialClearcoat { - float clearcoatFactor = 0.f; - float clearcoatRoughnessFactor = 0.f; - TextureInfo clearcoatTexture; - TextureInfo clearcoatRoughnessTexture; - NormalTextureInfo clearcoatNormalTexture; -}; - -struct MaterialTransmission { - TextureInfo transmissionTexture; - float transmissionFactor = 0.f; -}; - -struct MaterialVolume { - float thicknessFactor = 0.f; - TextureInfo thicknessTexture; - float attenuationDistance = 0.f; - vec3 attenuationColor; - - MaterialVolume() { SetDefaults(); } - void SetDefaults(); -}; - -struct MaterialIOR { - float ior = 0.f; - - MaterialIOR() { SetDefaults(); } - void SetDefaults(); -}; - -//! The material appearance of a primitive. -struct Material : public Object { - //PBR metallic roughness properties - PbrMetallicRoughness pbrMetallicRoughness; - - //other basic material properties - NormalTextureInfo normalTexture; - OcclusionTextureInfo occlusionTexture; - TextureInfo emissiveTexture; - vec3 emissiveFactor; - std::string alphaMode; - float alphaCutoff; - bool doubleSided; - - //extension: KHR_materials_pbrSpecularGlossiness - Nullable<PbrSpecularGlossiness> pbrSpecularGlossiness; - - //extension: KHR_materials_sheen - Nullable<MaterialSheen> materialSheen; - - //extension: KHR_materials_clearcoat - Nullable<MaterialClearcoat> materialClearcoat; - - //extension: KHR_materials_transmission - Nullable<MaterialTransmission> materialTransmission; - - //extension: KHR_materials_volume - Nullable<MaterialVolume> materialVolume; - - //extension: KHR_materials_ior - Nullable<MaterialIOR> materialIOR; - - //extension: KHR_materials_unlit - bool unlit; - - Material() { SetDefaults(); } - void Read(Value &obj, Asset &r); - void SetDefaults(); - - inline void SetTextureProperties(Asset &r, Value *prop, TextureInfo &out); - inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, TextureInfo &out); - inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, NormalTextureInfo &out); - inline void ReadTextureProperty(Asset &r, Value &vals, const char *propName, OcclusionTextureInfo &out); -}; - -//! A set of primitives to be rendered. A node can contain one or more meshes. A node's transform places the mesh in the scene. -struct Mesh : public Object { - using AccessorList = std::vector<Ref<Accessor>>; - - struct Primitive { - PrimitiveMode mode; - - struct Attributes { - AccessorList position, normal, tangent, texcoord, color, joint, jointmatrix, weight; - } attributes; - - Ref<Accessor> indices; - - Ref<Material> material; - - struct Target { - AccessorList position, normal, tangent; - }; - std::vector<Target> targets; - - // extension: FB_ngon_encoding - bool ngonEncoded; - - Primitive(): ngonEncoded(false) {} - }; - - std::vector<Primitive> primitives; - - std::vector<float> weights; - std::vector<std::string> targetNames; - - Mesh() {} - - /// Get mesh data from JSON-object and place them to root asset. - /// \param [in] pJSON_Object - reference to pJSON-object from which data are read. - /// \param [out] pAsset_Root - reference to root asset where data will be stored. - void Read(Value &pJSON_Object, Asset &pAsset_Root); -}; - -struct Node : public Object { - std::vector<Ref<Node>> children; - std::vector<Ref<Mesh>> meshes; - - Nullable<mat4> matrix; - Nullable<vec3> translation; - Nullable<vec4> rotation; - Nullable<vec3> scale; - - Ref<Camera> camera; - Ref<Light> light; - - std::vector<Ref<Node>> skeletons; //!< The ID of skeleton nodes. Each of which is the root of a node hierarchy. - Ref<Skin> skin; //!< The ID of the skin referenced by this node. - std::string jointName; //!< Name used when this node is a joint in a skin. - - Ref<Node> parent; //!< This is not part of the glTF specification. Used as a helper. - - Node() {} - void Read(Value &obj, Asset &r); -}; - -struct Program : public Object { - Program() {} - void Read(Value &obj, Asset &r); -}; - -struct Sampler : public Object { - SamplerMagFilter magFilter; //!< The texture magnification filter. - SamplerMinFilter minFilter; //!< The texture minification filter. - SamplerWrap wrapS; //!< The texture wrapping in the S direction. - SamplerWrap wrapT; //!< The texture wrapping in the T direction. - - Sampler() { SetDefaults(); } - void Read(Value &obj, Asset &r); - void SetDefaults(); -}; - -struct Scene : public Object { - std::string name; - std::vector<Ref<Node>> nodes; - - Scene() {} - void Read(Value &obj, Asset &r); -}; - -struct Shader : public Object { - Shader() {} - void Read(Value &obj, Asset &r); -}; - -struct Skin : public Object { - Nullable<mat4> bindShapeMatrix; //!< Floating-point 4x4 transformation matrix stored in column-major order. - Ref<Accessor> inverseBindMatrices; //!< The ID of the accessor containing the floating-point 4x4 inverse-bind matrices. - std::vector<Ref<Node>> jointNames; //!< Joint names of the joints (nodes with a jointName property) in this skin. - std::string name; //!< The user-defined name of this object. - - Skin() {} - void Read(Value &obj, Asset &r); -}; - -//! A texture and its sampler. -struct Texture : public Object { - Ref<Sampler> sampler; //!< The ID of the sampler used by this texture. (required) - Ref<Image> source; //!< The ID of the image used by this texture. (required) - - //TextureFormat format; //!< The texture's format. (default: TextureFormat_RGBA) - //TextureFormat internalFormat; //!< The texture's internal format. (default: TextureFormat_RGBA) - - //TextureTarget target; //!< The target that the WebGL texture should be bound to. (default: TextureTarget_TEXTURE_2D) - //TextureType type; //!< Texel datatype. (default: TextureType_UNSIGNED_BYTE) - - Texture() {} - void Read(Value &obj, Asset &r); -}; - -struct Animation : public Object { - struct Sampler { - Sampler() : - interpolation(Interpolation_LINEAR) {} - - Ref<Accessor> input; //!< Accessor reference to the buffer storing the key-frame times. - Ref<Accessor> output; //!< Accessor reference to the buffer storing the key-frame values. - Interpolation interpolation; //!< Type of interpolation algorithm to use between key-frames. - }; - - struct Target { - Target() : - path(AnimationPath_TRANSLATION) {} - - Ref<Node> node; //!< The node to animate. - AnimationPath path; //!< The property of the node to animate. - }; - - struct Channel { - Channel() : - sampler(-1) {} - - int sampler; //!< The sampler index containing the animation data. - Target target; //!< The node and property to animate. - }; - - std::vector<Sampler> samplers; //!< All the key-frame data for this animation. - std::vector<Channel> channels; //!< Data to connect nodes to key-frames. - - Animation() {} - void Read(Value &obj, Asset &r); -}; - -//! Base class for LazyDict that acts as an interface -class LazyDictBase { -public: - virtual ~LazyDictBase() {} - - virtual void AttachToDocument(Document &doc) = 0; - virtual void DetachFromDocument() = 0; - -#if !defined(ASSIMP_BUILD_NO_EXPORT) - virtual void WriteObjects(AssetWriter &writer) = 0; -#endif -}; - -template <class T> -class LazyDict; - -//! (Implemented in glTFAssetWriter.h) -template <class T> -void WriteLazyDict(LazyDict<T> &d, AssetWriter &w); - -//! Manages lazy loading of the glTF top-level objects, and keeps a reference to them by ID -//! It is the owner the loaded objects, so when it is destroyed it also deletes them -template <class T> -class LazyDict : public LazyDictBase { - friend class Asset; - friend class AssetWriter; - - using Dict = typename std::gltf_unordered_map<unsigned int, unsigned int>; - using IdDict = typename std::gltf_unordered_map<std::string, unsigned int>; - - std::vector<T *> mObjs; //! The read objects - Dict mObjsByOIndex; //! The read objects accessible by original index - IdDict mObjsById; //! The read objects accessible by id - const char *mDictId; //! ID of the dictionary object - const char *mExtId; //! ID of the extension defining the dictionary - Value *mDict; //! JSON dictionary object - Asset &mAsset; //! The asset instance - - std::gltf_unordered_set<unsigned int> mRecursiveReferenceCheck; //! Used by Retrieve to prevent recursive lookups - - void AttachToDocument(Document &doc); - void DetachFromDocument(); - -#if !defined(ASSIMP_BUILD_NO_EXPORT) - void WriteObjects(AssetWriter &writer) { WriteLazyDict<T>(*this, writer); } -#endif - - Ref<T> Add(T *obj); - -public: - LazyDict(Asset &asset, const char *dictId, const char *extId = 0); - ~LazyDict(); - - Ref<T> Retrieve(unsigned int i); - - Ref<T> Get(unsigned int i); - Ref<T> Get(const char *id); - - Ref<T> Create(const char *id); - Ref<T> Create(const std::string &id) { return Create(id.c_str()); } - - unsigned int Remove(const char *id); - - inline unsigned int Size() const { return unsigned(mObjs.size()); } - - inline T &operator[](size_t i) { return *mObjs[i]; } -}; - -struct AssetMetadata { - std::string copyright; //!< A copyright message suitable for display to credit the content creator. - std::string generator; //!< Tool that generated this glTF model.Useful for debugging. - - struct { - std::string api; //!< Specifies the target rendering API (default: "WebGL") - std::string version; //!< Specifies the target rendering API (default: "1.0.3") - } profile; //!< Specifies the target rendering API and version, e.g., WebGL 1.0.3. (default: {}) - - std::string version; //!< The glTF format version - - void Read(Document &doc); - - AssetMetadata() : - version() {} -}; - -// -// glTF Asset class -// - -//! Root object for a glTF asset -class Asset { - using IdMap = std::gltf_unordered_map<std::string, int>; - - template <class T> - friend class LazyDict; - friend struct Buffer; // To access OpenFile - friend class AssetWriter; - - std::vector<LazyDictBase *> mDicts; - -public: - //! Keeps info about the enabled extensions - struct Extensions { - bool KHR_materials_pbrSpecularGlossiness; - bool KHR_materials_unlit; - bool KHR_lights_punctual; - bool KHR_texture_transform; - bool KHR_materials_sheen; - bool KHR_materials_clearcoat; - bool KHR_materials_transmission; - bool KHR_materials_volume; - bool KHR_materials_ior; - bool KHR_draco_mesh_compression; - bool FB_ngon_encoding; - bool KHR_texture_basisu; - - Extensions() : - KHR_materials_pbrSpecularGlossiness(false), - KHR_materials_unlit(false), - KHR_lights_punctual(false), - KHR_texture_transform(false), - KHR_materials_sheen(false), - KHR_materials_clearcoat(false), - KHR_materials_transmission(false), - KHR_materials_volume(false), - KHR_materials_ior(false), - KHR_draco_mesh_compression(false), - FB_ngon_encoding(false), - KHR_texture_basisu(false) { - // empty - } - } extensionsUsed; - - //! Keeps info about the required extensions - struct RequiredExtensions { - bool KHR_draco_mesh_compression; - bool KHR_texture_basisu; - - RequiredExtensions() : KHR_draco_mesh_compression(false), KHR_texture_basisu(false) { - // empty - } - } extensionsRequired; - - AssetMetadata asset; - Value *extras; - - // Dictionaries for each type of object - - LazyDict<Accessor> accessors; - LazyDict<Animation> animations; - LazyDict<Buffer> buffers; - LazyDict<BufferView> bufferViews; - LazyDict<Camera> cameras; - LazyDict<Light> lights; - LazyDict<Image> images; - LazyDict<Material> materials; - LazyDict<Mesh> meshes; - LazyDict<Node> nodes; - LazyDict<Sampler> samplers; - LazyDict<Scene> scenes; - LazyDict<Skin> skins; - LazyDict<Texture> textures; - - Ref<Scene> scene; - -public: - Asset(IOSystem *io = nullptr, rapidjson::IRemoteSchemaDocumentProvider *schemaDocumentProvider = nullptr) : - mDicts(), - extensionsUsed(), - extensionsRequired(), - asset(), - extras(nullptr), - accessors(*this, "accessors"), - animations(*this, "animations"), - buffers(*this, "buffers"), - bufferViews(*this, "bufferViews"), - cameras(*this, "cameras"), - lights(*this, "lights", "KHR_lights_punctual"), - images(*this, "images"), - materials(*this, "materials"), - meshes(*this, "meshes"), - nodes(*this, "nodes"), - samplers(*this, "samplers"), - scenes(*this, "scenes"), - skins(*this, "skins"), - textures(*this, "textures") , - mIOSystem(io), - mSchemaDocumentProvider(schemaDocumentProvider) { - // empty - } - - //! Main function - void Load(const std::string &file, bool isBinary = false); - - //! Parse the AssetMetadata and check that the version is 2. - bool CanRead(const std::string &pFile, bool isBinary = false); - - //! Enables binary encoding on the asset - void SetAsBinary(); - - //! Search for an available name, starting from the given strings - std::string FindUniqueID(const std::string &str, const char *suffix); - - Ref<Buffer> GetBodyBuffer() { return mBodyBuffer; } - - Asset(Asset &) = delete; - Asset &operator=(const Asset &) = delete; - -private: - void ReadBinaryHeader(IOStream &stream, std::vector<char> &sceneData); - - /// Obtain a JSON document from the stream. - /// \param second argument is a buffer used by the document. It must be kept - /// alive while the document is in use. - Document ReadDocument(IOStream& stream, bool isBinary, std::vector<char>& sceneData); - - void ReadExtensionsUsed(Document &doc); - void ReadExtensionsRequired(Document &doc); - - IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false); - -private: - IOSystem *mIOSystem; - rapidjson::IRemoteSchemaDocumentProvider *mSchemaDocumentProvider; - std::string mCurrentAssetDir; - size_t mSceneLength; - size_t mBodyOffset; - size_t mBodyLength; - IdMap mUsedIds; - Ref<Buffer> mBodyBuffer; -}; - -inline std::string getContextForErrorMessages(const std::string &id, const std::string &name) { - std::string context = id; - if (!name.empty()) { - context += " (\"" + name + "\")"; - } - return context; -} - -} // namespace glTF2 - -// Include the implementation of the methods -#include "glTF2Asset.inl" - -#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER - -#endif // GLTF2ASSET_H_INC diff --git a/libs/assimp/code/AssetLib/glTF2/glTF2Asset.inl b/libs/assimp/code/AssetLib/glTF2/glTF2Asset.inl deleted file mode 100644 index ec481a7..0000000 --- a/libs/assimp/code/AssetLib/glTF2/glTF2Asset.inl +++ /dev/null @@ -1,2069 +0,0 @@ -/* -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. - ----------------------------------------------------------------------- -*/ - -#include "AssetLib/glTF/glTFCommon.h" - -#include <assimp/MemoryIOWrapper.h> -#include <assimp/StringUtils.h> -#include <assimp/DefaultLogger.hpp> -#include <assimp/Base64.hpp> - -// clang-format off -#ifdef ASSIMP_ENABLE_DRACO - -// Google draco library headers spew many warnings. Bad Google, no cookie -# if _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4018) // Signed/unsigned mismatch -# pragma warning(disable : 4804) // Unsafe use of type 'bool' -# elif defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wsign-compare" -# elif defined(__GNUC__) -# pragma GCC diagnostic push -# if (__GNUC__ > 4) -# pragma GCC diagnostic ignored "-Wbool-compare" -# endif -# pragma GCC diagnostic ignored "-Wsign-compare" -#endif - -#include "draco/compression/decode.h" -#include "draco/core/decoder_buffer.h" - -#if _MSC_VER -# pragma warning(pop) -#elif defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) -# pragma GCC diagnostic pop -#endif -#ifndef DRACO_MESH_COMPRESSION_SUPPORTED -# error glTF: KHR_draco_mesh_compression: draco library must have DRACO_MESH_COMPRESSION_SUPPORTED -#endif -#endif -// clang-format on - -using namespace Assimp; -using namespace glTFCommon; - -namespace glTF2 { - -namespace { - -// -// JSON Value reading helpers -// -inline CustomExtension ReadExtensions(const char *name, Value &obj) { - CustomExtension ret; - ret.name = name; - if (obj.IsObject()) { - ret.mValues.isPresent = true; - for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { - auto &val = it->value; - ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); - } - } else if (obj.IsArray()) { - ret.mValues.value.reserve(obj.Size()); - ret.mValues.isPresent = true; - for (unsigned int i = 0; i < obj.Size(); ++i) { - ret.mValues.value.push_back(ReadExtensions(name, obj[i])); - } - } else if (obj.IsNumber()) { - if (obj.IsUint64()) { - ret.mUint64Value.value = obj.GetUint64(); - ret.mUint64Value.isPresent = true; - } else if (obj.IsInt64()) { - ret.mInt64Value.value = obj.GetInt64(); - ret.mInt64Value.isPresent = true; - } else if (obj.IsDouble()) { - ret.mDoubleValue.value = obj.GetDouble(); - ret.mDoubleValue.isPresent = true; - } - } else if (obj.IsString()) { - ReadValue(obj, ret.mStringValue); - ret.mStringValue.isPresent = true; - } else if (obj.IsBool()) { - ret.mBoolValue.value = obj.GetBool(); - ret.mBoolValue.isPresent = true; - } - return ret; -} - -inline void CopyData(size_t count, const uint8_t *src, size_t src_stride, - uint8_t *dst, size_t dst_stride) { - if (src_stride == dst_stride) { - memcpy(dst, src, count * src_stride); - return; - } - - size_t sz = std::min(src_stride, dst_stride); - for (size_t i = 0; i < count; ++i) { - memcpy(dst, src, sz); - if (sz < dst_stride) { - memset(dst + sz, 0, dst_stride - sz); - } - src += src_stride; - dst += dst_stride; - } -} - -void SetVector(vec4 &v, const float (&in)[4]) { - v[0] = in[0]; - v[1] = in[1]; - v[2] = in[2]; - v[3] = in[3]; -} - -void SetVector(vec3 &v, const float (&in)[3]) { - v[0] = in[0]; - v[1] = in[1]; - v[2] = in[2]; -} - -template <int N> -inline int Compare(const char *attr, const char (&str)[N]) { - return (strncmp(attr, str, N - 1) == 0) ? N - 1 : 0; -} - -#if _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4706) -#endif // _MSC_VER - -inline bool GetAttribVector(Mesh::Primitive &p, const char *attr, Mesh::AccessorList *&v, int &pos) { - if ((pos = Compare(attr, "POSITION"))) { - v = &(p.attributes.position); - } else if ((pos = Compare(attr, "NORMAL"))) { - v = &(p.attributes.normal); - } else if ((pos = Compare(attr, "TANGENT"))) { - v = &(p.attributes.tangent); - } else if ((pos = Compare(attr, "TEXCOORD"))) { - v = &(p.attributes.texcoord); - } else if ((pos = Compare(attr, "COLOR"))) { - v = &(p.attributes.color); - } else if ((pos = Compare(attr, "JOINT"))) { - v = &(p.attributes.joint); - } else if ((pos = Compare(attr, "JOINTMATRIX"))) { - v = &(p.attributes.jointmatrix); - } else if ((pos = Compare(attr, "WEIGHT"))) { - v = &(p.attributes.weight); - } else - return false; - return true; -} - -inline bool GetAttribTargetVector(Mesh::Primitive &p, const int targetIndex, const char *attr, Mesh::AccessorList *&v, int &pos) { - if ((pos = Compare(attr, "POSITION"))) { - v = &(p.targets[targetIndex].position); - } else if ((pos = Compare(attr, "NORMAL"))) { - v = &(p.targets[targetIndex].normal); - } else if ((pos = Compare(attr, "TANGENT"))) { - v = &(p.targets[targetIndex].tangent); - } else - return false; - return true; -} - -} // namespace - -inline Value *Object::FindString(Value &val, const char *memberId) { - return FindStringInContext(val, memberId, id.c_str(), name.c_str()); -} - -inline Value *Object::FindNumber(Value &val, const char *memberId) { - return FindNumberInContext(val, memberId, id.c_str(), name.c_str()); -} - -inline Value *Object::FindUInt(Value &val, const char *memberId) { - return FindUIntInContext(val, memberId, id.c_str(), name.c_str()); -} - -inline Value *Object::FindArray(Value &val, const char *memberId) { - return FindArrayInContext(val, memberId, id.c_str(), name.c_str()); -} - -inline Value *Object::FindObject(Value &val, const char *memberId) { - return FindObjectInContext(val, memberId, id.c_str(), name.c_str()); -} - -inline Value *Object::FindExtension(Value &val, const char *extensionId) { - return FindExtensionInContext(val, extensionId, id.c_str(), name.c_str()); -} - -inline void Object::ReadExtensions(Value &val) { - if (Value *curExtensions = FindObject(val, "extensions")) { - this->customExtensions = glTF2::ReadExtensions("extensions", *curExtensions); - } -} - -inline void Object::ReadExtras(Value &val) { - if (Value *curExtras = FindObject(val, "extras")) { - this->extras = glTF2::ReadExtensions("extras", *curExtras); - } -} - -#ifdef ASSIMP_ENABLE_DRACO - -template <typename T> -inline void CopyFaceIndex_Draco(Buffer &decodedIndexBuffer, const draco::Mesh &draco_mesh) { - const size_t faceStride = sizeof(T) * 3; - for (draco::FaceIndex f(0); f < draco_mesh.num_faces(); ++f) { - const draco::Mesh::Face &face = draco_mesh.face(f); - T indices[3] = { static_cast<T>(face[0].value()), static_cast<T>(face[1].value()), static_cast<T>(face[2].value()) }; - memcpy(decodedIndexBuffer.GetPointer() + (f.value() * faceStride), &indices[0], faceStride); - } -} - -inline void SetDecodedIndexBuffer_Draco(const draco::Mesh &dracoMesh, Mesh::Primitive &prim) { - if (!prim.indices || dracoMesh.num_faces() == 0) - return; - - // Create a decoded Index buffer (if there is one) - size_t componentBytes = prim.indices->GetBytesPerComponent(); - - std::unique_ptr<Buffer> decodedIndexBuffer(new Buffer()); - decodedIndexBuffer->Grow(dracoMesh.num_faces() * 3 * componentBytes); - - // If accessor uses the same size as draco implementation, copy the draco buffer directly - - // Usually uint32_t but shouldn't assume - if (sizeof(dracoMesh.face(draco::FaceIndex(0))[0]) == componentBytes) { - memcpy(decodedIndexBuffer->GetPointer(), &dracoMesh.face(draco::FaceIndex(0))[0], decodedIndexBuffer->byteLength); - return; - } - - // Not same size, convert - switch (componentBytes) { - case sizeof(uint32_t): - CopyFaceIndex_Draco<uint32_t>(*decodedIndexBuffer, dracoMesh); - break; - case sizeof(uint16_t): - CopyFaceIndex_Draco<uint16_t>(*decodedIndexBuffer, dracoMesh); - break; - case sizeof(uint8_t): - CopyFaceIndex_Draco<uint8_t>(*decodedIndexBuffer, dracoMesh); - break; - default: - ai_assert(false); - break; - } - - // Assign this alternate data buffer to the accessor - prim.indices->decodedBuffer.swap(decodedIndexBuffer); -} - -template <typename T> -static bool GetAttributeForAllPoints_Draco(const draco::Mesh &dracoMesh, - const draco::PointAttribute &dracoAttribute, - Buffer &outBuffer) { - size_t byteOffset = 0; - T values[4] = { 0, 0, 0, 0 }; - for (draco::PointIndex i(0); i < dracoMesh.num_points(); ++i) { - const draco::AttributeValueIndex val_index = dracoAttribute.mapped_index(i); - if (!dracoAttribute.ConvertValue<T>(val_index, dracoAttribute.num_components(), values)) { - return false; - } - - memcpy(outBuffer.GetPointer() + byteOffset, &values[0], sizeof(T) * dracoAttribute.num_components()); - byteOffset += sizeof(T) * dracoAttribute.num_components(); - } - - return true; -} - -inline void SetDecodedAttributeBuffer_Draco(const draco::Mesh &dracoMesh, uint32_t dracoAttribId, Accessor &accessor) { - // Create decoded buffer - const draco::PointAttribute *pDracoAttribute = dracoMesh.GetAttributeByUniqueId(dracoAttribId); - if (pDracoAttribute == nullptr) { - throw DeadlyImportError("GLTF: Invalid draco attribute id: ", dracoAttribId); - } - - size_t componentBytes = accessor.GetBytesPerComponent(); - - std::unique_ptr<Buffer> decodedAttribBuffer(new Buffer()); - decodedAttribBuffer->Grow(dracoMesh.num_points() * pDracoAttribute->num_components() * componentBytes); - - switch (accessor.componentType) { - case ComponentType_BYTE: - GetAttributeForAllPoints_Draco<int8_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - case ComponentType_UNSIGNED_BYTE: - GetAttributeForAllPoints_Draco<uint8_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - case ComponentType_SHORT: - GetAttributeForAllPoints_Draco<int16_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - case ComponentType_UNSIGNED_SHORT: - GetAttributeForAllPoints_Draco<uint16_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - case ComponentType_UNSIGNED_INT: - GetAttributeForAllPoints_Draco<uint32_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - case ComponentType_FLOAT: - GetAttributeForAllPoints_Draco<float>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); - break; - default: - ai_assert(false); - break; - } - - // Assign this alternate data buffer to the accessor - accessor.decodedBuffer.swap(decodedAttribBuffer); -} - -#endif // ASSIMP_ENABLE_DRACO - -// -// LazyDict methods -// - -template <class T> -inline LazyDict<T>::LazyDict(Asset &asset, const char *dictId, const char *extId) : - mDictId(dictId), - mExtId(extId), - mDict(0), - mAsset(asset) { - asset.mDicts.push_back(this); // register to the list of dictionaries -} - -template <class T> -inline LazyDict<T>::~LazyDict() { - for (size_t i = 0; i < mObjs.size(); ++i) { - delete mObjs[i]; - } -} - -template <class T> -inline void LazyDict<T>::AttachToDocument(Document &doc) { - Value *container = nullptr; - const char *context = nullptr; - - if (mExtId) { - if (Value *exts = FindObject(doc, "extensions")) { - container = FindObjectInContext(*exts, mExtId, "extensions"); - context = mExtId; - } - } else { - container = &doc; - context = "the document"; - } - - if (container) { - mDict = FindArrayInContext(*container, mDictId, context); - } -} - -template <class T> -inline void LazyDict<T>::DetachFromDocument() { - mDict = nullptr; -} - -template <class T> -unsigned int LazyDict<T>::Remove(const char *id) { - id = T::TranslateId(mAsset, id); - - typename IdDict::iterator objIt = mObjsById.find(id); - - if (objIt == mObjsById.end()) { - throw DeadlyExportError("GLTF: Object with id \"" + std::string(id) + "\" is not found"); - } - - const unsigned int index = objIt->second; - - mAsset.mUsedIds[id] = false; - mObjsById.erase(id); - mObjsByOIndex.erase(index); - delete mObjs[index]; - mObjs.erase(mObjs.begin() + index); - - //update index of object in mObjs; - for (unsigned int i = index; i < mObjs.size(); ++i) { - T *obj = mObjs[i]; - - obj->index = i; - } - - for (IdDict::iterator it = mObjsById.begin(); it != mObjsById.end(); ++it) { - if (it->second <= index) { - continue; - } - - mObjsById[it->first] = it->second - 1; - } - - for (Dict::iterator it = mObjsByOIndex.begin(); it != mObjsByOIndex.end(); ++it) { - if (it->second <= index) { - continue; - } - - mObjsByOIndex[it->first] = it->second - 1; - } - - return index; -} - -template <class T> -Ref<T> LazyDict<T>::Retrieve(unsigned int i) { - - typename Dict::iterator it = mObjsByOIndex.find(i); - if (it != mObjsByOIndex.end()) { // already created? - return Ref<T>(mObjs, it->second); - } - - // read it from the JSON object - if (!mDict) { - throw DeadlyImportError("GLTF: Missing section \"", mDictId, "\""); - } - - if (!mDict->IsArray()) { - throw DeadlyImportError("GLTF: Field \"", mDictId, "\" is not an array"); - } - - if (i >= mDict->Size()) { - throw DeadlyImportError("GLTF: Array index ", i, " is out of bounds (", mDict->Size(), ") for \"", mDictId, "\""); - } - - Value &obj = (*mDict)[i]; - - if (!obj.IsObject()) { - throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" is not a JSON object"); - } - - if (mRecursiveReferenceCheck.find(i) != mRecursiveReferenceCheck.end()) { - throw DeadlyImportError("GLTF: Object at index ", i, " in array \"", mDictId, "\" has recursive reference to itself"); - } - mRecursiveReferenceCheck.insert(i); - - // Unique ptr prevents memory leak in case of Read throws an exception - auto inst = std::unique_ptr<T>(new T()); - // Try to make this human readable so it can be used in error messages. - inst->id = std::string(mDictId) + "[" + ai_to_string(i) + "]"; - inst->oIndex = i; - ReadMember(obj, "name", inst->name); - inst->Read(obj, mAsset); - inst->ReadExtensions(obj); - inst->ReadExtras(obj); - - Ref<T> result = Add(inst.release()); - mRecursiveReferenceCheck.erase(i); - return result; -} - -template <class T> -Ref<T> LazyDict<T>::Get(unsigned int i) { - return Ref<T>(mObjs, i); -} - -template <class T> -Ref<T> LazyDict<T>::Get(const char *id) { - id = T::TranslateId(mAsset, id); - - typename IdDict::iterator it = mObjsById.find(id); - if (it != mObjsById.end()) { // already created? - return Ref<T>(mObjs, it->second); - } - - return Ref<T>(); -} - -template <class T> -Ref<T> LazyDict<T>::Add(T *obj) { - unsigned int idx = unsigned(mObjs.size()); - mObjs.push_back(obj); - mObjsByOIndex[obj->oIndex] = idx; - mObjsById[obj->id] = idx; - mAsset.mUsedIds[obj->id] = true; - return Ref<T>(mObjs, idx); -} - -template <class T> -Ref<T> LazyDict<T>::Create(const char *id) { - Asset::IdMap::iterator it = mAsset.mUsedIds.find(id); - if (it != mAsset.mUsedIds.end()) { - throw DeadlyImportError("GLTF: two objects with the same ID exist"); - } - T *inst = new T(); - unsigned int idx = unsigned(mObjs.size()); - inst->id = id; - inst->index = idx; - inst->oIndex = idx; - return Add(inst); -} - -// -// glTF dictionary objects methods -// -inline Buffer::Buffer() : - byteLength(0), - type(Type_arraybuffer), - EncodedRegion_Current(nullptr), - mIsSpecial(false) {} - -inline Buffer::~Buffer() { - for (SEncodedRegion *reg : EncodedRegion_List) - delete reg; -} - -inline const char *Buffer::TranslateId(Asset & /*r*/, const char *id) { - return id; -} - -inline void Buffer::Read(Value &obj, Asset &r) { - size_t statedLength = MemberOrDefault<size_t>(obj, "byteLength", 0); - byteLength = statedLength; - - Value *it = FindString(obj, "uri"); - if (!it) { - if (statedLength > 0) { - throw DeadlyImportError("GLTF: buffer with non-zero length missing the \"uri\" attribute"); - } - return; - } - - const char *uri = it->GetString(); - - glTFCommon::Util::DataURI dataURI; - if (ParseDataURI(uri, it->GetStringLength(), dataURI)) { - if (dataURI.base64) { - uint8_t *data = nullptr; - this->byteLength = Base64::Decode(dataURI.data, dataURI.dataLength, data); - this->mData.reset(data, std::default_delete<uint8_t[]>()); - - if (statedLength > 0 && this->byteLength != statedLength) { - throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", ai_to_string(statedLength), - " bytes, but found ", ai_to_string(dataURI.dataLength)); - } - } else { // assume raw data - if (statedLength != dataURI.dataLength) { - throw DeadlyImportError("GLTF: buffer \"", id, "\", expected ", ai_to_string(statedLength), - " bytes, but found ", ai_to_string(dataURI.dataLength)); - } - - this->mData.reset(new uint8_t[dataURI.dataLength], std::default_delete<uint8_t[]>()); - memcpy(this->mData.get(), dataURI.data, dataURI.dataLength); - } - } else { // Local file - if (byteLength > 0) { - std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir.back() == '/' ? r.mCurrentAssetDir : r.mCurrentAssetDir + '/') : ""; - - IOStream *file = r.OpenFile(dir + uri, "rb"); - if (file) { - bool ok = LoadFromStream(*file, byteLength); - delete file; - - if (!ok) - throw DeadlyImportError("GLTF: error while reading referenced file \"", uri, "\""); - } else { - throw DeadlyImportError("GLTF: could not open referenced file \"", uri, "\""); - } - } - } -} - -inline bool Buffer::LoadFromStream(IOStream &stream, size_t length, size_t baseOffset) { - byteLength = length ? length : stream.FileSize(); - - if (byteLength > stream.FileSize()) { - throw DeadlyImportError("GLTF: Invalid byteLength exceeds size of actual data."); - } - - if (baseOffset) { - stream.Seek(baseOffset, aiOrigin_SET); - } - - mData.reset(new uint8_t[byteLength], std::default_delete<uint8_t[]>()); - - if (stream.Read(mData.get(), byteLength, 1) != 1) { - return false; - } - return true; -} - -inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) { - // Check pointer to data - if (pDecodedData == nullptr) throw DeadlyImportError("GLTF: for marking encoded region pointer to decoded data must be provided."); - - // Check offset - if (pOffset > byteLength) { - const uint8_t val_size = 32; - - char val[val_size]; - - ai_snprintf(val, val_size, AI_SIZEFMT, pOffset); - throw DeadlyImportError("GLTF: incorrect offset value (", val, ") for marking encoded region."); - } - - // Check length - if ((pOffset + pEncodedData_Length) > byteLength) { - const uint8_t val_size = 64; - - char val[val_size]; - - ai_snprintf(val, val_size, AI_SIZEFMT "/" AI_SIZEFMT, pOffset, pEncodedData_Length); - throw DeadlyImportError("GLTF: encoded region with offset/length (", val, ") is out of range."); - } - - // Add new region - EncodedRegion_List.push_back(new SEncodedRegion(pOffset, pEncodedData_Length, pDecodedData, pDecodedData_Length, pID)); - // And set new value for "byteLength" - byteLength += (pDecodedData_Length - pEncodedData_Length); -} - -inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) { - if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) { - return; - } - - for (SEncodedRegion *reg : EncodedRegion_List) { - if (reg->ID == pID) { - EncodedRegion_Current = reg; - return; - } - } - - throw DeadlyImportError("GLTF: EncodedRegion with ID: \"", pID, "\" not found."); -} - -inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) { - - if ((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) { - return false; - } - - const size_t new_data_size = byteLength + pReplace_Count - pBufferData_Count; - uint8_t *new_data = new uint8_t[new_data_size]; - // Copy data which place before replacing part. - ::memcpy(new_data, mData.get(), pBufferData_Offset); - // Copy new data. - ::memcpy(&new_data[pBufferData_Offset], pReplace_Data, pReplace_Count); - // Copy data which place after replacing part. - ::memcpy(&new_data[pBufferData_Offset + pReplace_Count], &mData.get()[pBufferData_Offset + pBufferData_Count], pBufferData_Offset); - // Apply new data - mData.reset(new_data, std::default_delete<uint8_t[]>()); - byteLength = new_data_size; - - return true; -} - -inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) { - if ((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) { - return false; - } - - const size_t new_data_size = byteLength + pReplace_Count - pBufferData_Count; - uint8_t *new_data = new uint8_t[new_data_size]; - // Copy data which place before replacing part. - memcpy(new_data, mData.get(), pBufferData_Offset); - // Copy new data. - memcpy(&new_data[pBufferData_Offset], pReplace_Data, pReplace_Count); - // Copy data which place after replacing part. - memcpy(&new_data[pBufferData_Offset + pReplace_Count], &mData.get()[pBufferData_Offset + pBufferData_Count], new_data_size - (pBufferData_Offset + pReplace_Count)); - // Apply new data - mData.reset(new_data, std::default_delete<uint8_t[]>()); - byteLength = new_data_size; - - return true; -} - -inline size_t Buffer::AppendData(uint8_t *data, size_t length) { - const size_t offset = this->byteLength; - - // Force alignment to 4 bits - const size_t paddedLength = (length + 3) & ~3; - Grow(paddedLength); - memcpy(mData.get() + offset, data, length); - memset(mData.get() + offset + length, 0, paddedLength - length); - return offset; -} - -inline void Buffer::Grow(size_t amount) { - if (amount <= 0) { - return; - } - - // Capacity is big enough - if (capacity >= byteLength + amount) { - byteLength += amount; - return; - } - - // Just allocate data which we need - capacity = byteLength + amount; - - uint8_t *b = new uint8_t[capacity]; - if (nullptr != mData) { - memcpy(b, mData.get(), byteLength); - } - mData.reset(b, std::default_delete<uint8_t[]>()); - byteLength += amount; -} - -// -// struct BufferView -// -inline void BufferView::Read(Value &obj, Asset &r) { - if (Value *bufferVal = FindUInt(obj, "buffer")) { - buffer = r.buffers.Retrieve(bufferVal->GetUint()); - } - - if (!buffer) { - throw DeadlyImportError("GLTF: Buffer view without valid buffer."); - } - - byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0)); - byteLength = MemberOrDefault(obj, "byteLength", size_t(0)); - byteStride = MemberOrDefault(obj, "byteStride", 0u); - - // Check length - if ((byteOffset + byteLength) > buffer->byteLength) { - throw DeadlyImportError("GLTF: Buffer view with offset/length (", byteOffset, "/", byteLength, ") is out of range."); - } -} - -inline uint8_t *BufferView::GetPointer(size_t accOffset) { - if (!buffer) { - return nullptr; - } - uint8_t *basePtr = buffer->GetPointer(); - if (!basePtr) { - return nullptr; - } - - size_t offset = accOffset + byteOffset; - if (buffer->EncodedRegion_Current != nullptr) { - const size_t begin = buffer->EncodedRegion_Current->Offset; - const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length; - if ((offset >= begin) && (offset < end)) { - return &buffer->EncodedRegion_Current->DecodedData[offset - begin]; - } - } - - return basePtr + offset; -} - -// -// struct Accessor -// -inline void Accessor::Sparse::PopulateData(size_t numBytes, uint8_t *bytes) { - if (bytes) { - data.assign(bytes, bytes + numBytes); - } else { - data.resize(numBytes, 0x00); - } -} - -inline void Accessor::Sparse::PatchData(unsigned int elementSize) { - uint8_t *pIndices = indices->GetPointer(indicesByteOffset); - const unsigned int indexSize = int(ComponentTypeSize(indicesType)); - uint8_t *indicesEnd = pIndices + count * indexSize; - - uint8_t *pValues = values->GetPointer(valuesByteOffset); - while (pIndices != indicesEnd) { - size_t offset; - switch (indicesType) { - case ComponentType_UNSIGNED_BYTE: - offset = *pIndices; - break; - case ComponentType_UNSIGNED_SHORT: - offset = *reinterpret_cast<uint16_t *>(pIndices); - break; - case ComponentType_UNSIGNED_INT: - offset = *reinterpret_cast<uint32_t *>(pIndices); - break; - default: - // have fun with float and negative values from signed types as indices. - throw DeadlyImportError("Unsupported component type in index."); - } - - offset *= elementSize; - - if (offset + elementSize > data.size()) { - throw DeadlyImportError("Invalid sparse accessor. Byte offset for patching points outside allocated memory."); - } - - std::memcpy(data.data() + offset, pValues, elementSize); - - pValues += elementSize; - pIndices += indexSize; - } -} - -inline void Accessor::Read(Value &obj, Asset &r) { - if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { - bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); - } - - byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0)); - componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); - { - const Value *countValue = FindUInt(obj, "count"); - if (!countValue) { - throw DeadlyImportError("A count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); - } - count = countValue->GetUint(); - } - - const char *typestr; - type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR; - - if (bufferView) { - // Check length - unsigned long long byteLength = (unsigned long long)GetBytesPerComponent() * (unsigned long long)count; - - // handle integer overflow - if (byteLength < count) { - throw DeadlyImportError("GLTF: Accessor with offset/count (", byteOffset, "/", count, ") is out of range."); - } - - if ((byteOffset + byteLength) > bufferView->byteLength || (bufferView->byteOffset + byteOffset + byteLength) > bufferView->buffer->byteLength) { - throw DeadlyImportError("GLTF: Accessor with offset/length (", byteOffset, "/", byteLength, ") is out of range."); - } - } - - if (Value *sparseValue = FindObject(obj, "sparse")) { - sparse.reset(new Sparse); - // count - ReadMember(*sparseValue, "count", sparse->count); - - // indices - if (Value *indicesValue = FindObject(*sparseValue, "indices")) { - //indices bufferView - Value *indiceViewID = FindUInt(*indicesValue, "bufferView"); - sparse->indices = r.bufferViews.Retrieve(indiceViewID->GetUint()); - //indices byteOffset - sparse->indicesByteOffset = MemberOrDefault(*indicesValue, "byteOffset", size_t(0)); - //indices componentType - sparse->indicesType = MemberOrDefault(*indicesValue, "componentType", ComponentType_BYTE); - //sparse->indices->Read(*indicesValue, r); - } else { - // indicesType - sparse->indicesType = MemberOrDefault(*sparseValue, "componentType", ComponentType_UNSIGNED_SHORT); - } - - // value - if (Value *valuesValue = FindObject(*sparseValue, "values")) { - //value bufferView - Value *valueViewID = FindUInt(*valuesValue, "bufferView"); - sparse->values = r.bufferViews.Retrieve(valueViewID->GetUint()); - //value byteOffset - sparse->valuesByteOffset = MemberOrDefault(*valuesValue, "byteOffset", size_t(0)); - //sparse->values->Read(*valuesValue, r); - } - - - const unsigned int elementSize = GetElementSize(); - const size_t dataSize = count * elementSize; - sparse->PopulateData(dataSize, bufferView ? bufferView->GetPointer(byteOffset) : 0); - sparse->PatchData(elementSize); - } -} - -inline unsigned int Accessor::GetNumComponents() { - return AttribType::GetNumComponents(type); -} - -inline unsigned int Accessor::GetBytesPerComponent() { - return int(ComponentTypeSize(componentType)); -} - -inline unsigned int Accessor::GetElementSize() { - return GetNumComponents() * GetBytesPerComponent(); -} - -inline uint8_t *Accessor::GetPointer() { - if (decodedBuffer) - return decodedBuffer->GetPointer(); - - if (sparse) - return sparse->data.data(); - - if (!bufferView || !bufferView->buffer) return nullptr; - uint8_t *basePtr = bufferView->buffer->GetPointer(); - if (!basePtr) return nullptr; - - size_t offset = byteOffset + bufferView->byteOffset; - - // Check if region is encoded. - if (bufferView->buffer->EncodedRegion_Current != nullptr) { - const size_t begin = bufferView->buffer->EncodedRegion_Current->Offset; - const size_t end = begin + bufferView->buffer->EncodedRegion_Current->DecodedData_Length; - - if ((offset >= begin) && (offset < end)) - return &bufferView->buffer->EncodedRegion_Current->DecodedData[offset - begin]; - } - - return basePtr + offset; -} - -inline size_t Accessor::GetStride() { - // Decoded buffer is always packed - if (decodedBuffer) - return GetElementSize(); - - // Sparse and normal bufferView - return (bufferView && bufferView->byteStride ? bufferView->byteStride : GetElementSize()); -} - -inline size_t Accessor::GetMaxByteSize() { - if (decodedBuffer) - return decodedBuffer->byteLength; - - return (bufferView ? bufferView->byteLength : sparse->data.size()); -} - -template <class T> -void Accessor::ExtractData(T *&outData) { - uint8_t *data = GetPointer(); - if (!data) { - throw DeadlyImportError("GLTF2: data is null when extracting data from ", getContextForErrorMessages(id, name)); - } - - const size_t elemSize = GetElementSize(); - const size_t totalSize = elemSize * count; - - const size_t stride = GetStride(); - - const size_t targetElemSize = sizeof(T); - - if (elemSize > targetElemSize) { - throw DeadlyImportError("GLTF: elemSize ", elemSize, " > targetElemSize ", targetElemSize, " in ", getContextForErrorMessages(id, name)); - } - - const size_t maxSize = GetMaxByteSize(); - if (count * stride > maxSize) { - throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name)); - } - - outData = new T[count]; - if (stride == elemSize && targetElemSize == elemSize) { - memcpy(outData, data, totalSize); - } else { - for (size_t i = 0; i < count; ++i) { - memcpy(outData + i, data + i * stride, elemSize); - } - } -} - -inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t src_stride) { - uint8_t *buffer_ptr = bufferView->buffer->GetPointer(); - size_t offset = byteOffset + bufferView->byteOffset; - - size_t dst_stride = GetNumComponents() * GetBytesPerComponent(); - - const uint8_t *src = reinterpret_cast<const uint8_t *>(src_buffer); - uint8_t *dst = reinterpret_cast<uint8_t *>(buffer_ptr + offset); - - ai_assert(dst + _count * dst_stride <= buffer_ptr + bufferView->buffer->byteLength); - CopyData(_count, src, src_stride, dst, dst_stride); -} - -inline void Accessor::WriteSparseValues(size_t _count, const void *src_data, size_t src_dataStride) { - if (!sparse) - return; - - // values - uint8_t *value_buffer_ptr = sparse->values->buffer->GetPointer(); - size_t value_offset = sparse->valuesByteOffset + sparse->values->byteOffset; - size_t value_dst_stride = GetNumComponents() * GetBytesPerComponent(); - const uint8_t *value_src = reinterpret_cast<const uint8_t *>(src_data); - uint8_t *value_dst = reinterpret_cast<uint8_t *>(value_buffer_ptr + value_offset); - ai_assert(value_dst + _count * value_dst_stride <= value_buffer_ptr + sparse->values->buffer->byteLength); - CopyData(_count, value_src, src_dataStride, value_dst, value_dst_stride); -} - -inline void Accessor::WriteSparseIndices(size_t _count, const void *src_idx, size_t src_idxStride) { - if (!sparse) - return; - - // indices - uint8_t *indices_buffer_ptr = sparse->indices->buffer->GetPointer(); - size_t indices_offset = sparse->indicesByteOffset + sparse->indices->byteOffset; - size_t indices_dst_stride = 1 * sizeof(unsigned short); - const uint8_t *indices_src = reinterpret_cast<const uint8_t *>(src_idx); - uint8_t *indices_dst = reinterpret_cast<uint8_t *>(indices_buffer_ptr + indices_offset); - ai_assert(indices_dst + _count * indices_dst_stride <= indices_buffer_ptr + sparse->indices->buffer->byteLength); - CopyData(_count, indices_src, src_idxStride, indices_dst, indices_dst_stride); -} - -inline Accessor::Indexer::Indexer(Accessor &acc) : - accessor(acc), - data(acc.GetPointer()), - elemSize(acc.GetElementSize()), - stride(acc.GetStride()) { -} - -//! Accesses the i-th value as defined by the accessor -template <class T> -T Accessor::Indexer::GetValue(int i) { - ai_assert(data); - if (i * stride >= accessor.GetMaxByteSize()) { - throw DeadlyImportError("GLTF: Invalid index ", i, ", count out of range for buffer with stride ", stride, " and size ", accessor.GetMaxByteSize(), "."); - } - // Ensure that the memcpy doesn't overwrite the local. - const size_t sizeToCopy = std::min(elemSize, sizeof(T)); - T value = T(); - // Assume platform endianness matches GLTF binary data (which is little-endian). - memcpy(&value, data + i * stride, sizeToCopy); - return value; -} - -inline Image::Image() : - width(0), - height(0), - mDataLength(0) { -} - -inline void Image::Read(Value &obj, Asset &r) { - //basisu: no need to handle .ktx2, .basis, load as is - if (!mDataLength) { - Value *curUri = FindString(obj, "uri"); - if (nullptr != curUri) { - const char *uristr = curUri->GetString(); - - glTFCommon::Util::DataURI dataURI; - if (ParseDataURI(uristr, curUri->GetStringLength(), dataURI)) { - mimeType = dataURI.mediaType; - if (dataURI.base64) { - uint8_t *ptr = nullptr; - mDataLength = Base64::Decode(dataURI.data, dataURI.dataLength, ptr); - mData.reset(ptr); - } - } else { - this->uri = uristr; - } - } else if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { - this->bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); - if (Value *mtype = FindString(obj, "mimeType")) { - this->mimeType = mtype->GetString(); - } - if (!this->bufferView || this->mimeType.empty()) { - throw DeadlyImportError("GLTF2: ", getContextForErrorMessages(id, name), " does not have a URI, so it must have a valid bufferView and mimetype"); - } - - Ref<Buffer> buffer = this->bufferView->buffer; - - this->mDataLength = this->bufferView->byteLength; - // maybe this memcpy could be avoided if aiTexture does not delete[] pcData at destruction. - - this->mData.reset(new uint8_t[this->mDataLength]); - memcpy(this->mData.get(), buffer->GetPointer() + this->bufferView->byteOffset, this->mDataLength); - } else { - throw DeadlyImportError("GLTF2: ", getContextForErrorMessages(id, name), " should have either a URI of a bufferView and mimetype"); - } - } -} - -inline uint8_t *Image::StealData() { - mDataLength = 0; - return mData.release(); -} - -// Never take over the ownership of data whenever binary or not -inline void Image::SetData(uint8_t *data, size_t length, Asset &r) { - Ref<Buffer> b = r.GetBodyBuffer(); - if (b) { // binary file: append to body - std::string bvId = r.FindUniqueID(this->id, "imgdata"); - bufferView = r.bufferViews.Create(bvId); - - bufferView->buffer = b; - bufferView->byteLength = length; - bufferView->byteOffset = b->AppendData(data, length); - } else { // text file: will be stored as a data uri - uint8_t *temp = new uint8_t[length]; - memcpy(temp, data, length); - this->mData.reset(temp); - this->mDataLength = length; - } -} - -inline void Sampler::Read(Value &obj, Asset & /*r*/) { - SetDefaults(); - - ReadMember(obj, "name", name); - ReadMember(obj, "magFilter", magFilter); - ReadMember(obj, "minFilter", minFilter); - ReadMember(obj, "wrapS", wrapS); - ReadMember(obj, "wrapT", wrapT); -} - -inline void Sampler::SetDefaults() { - //only wrapping modes have defaults - wrapS = SamplerWrap::Repeat; - wrapT = SamplerWrap::Repeat; - magFilter = SamplerMagFilter::UNSET; - minFilter = SamplerMinFilter::UNSET; -} - -inline void Texture::Read(Value &obj, Asset &r) { - if (Value *sourceVal = FindUInt(obj, "source")) { - source = r.images.Retrieve(sourceVal->GetUint()); - } - - if (Value *samplerVal = FindUInt(obj, "sampler")) { - sampler = r.samplers.Retrieve(samplerVal->GetUint()); - } -} - -void Material::SetTextureProperties(Asset &r, Value *prop, TextureInfo &out) { - if (r.extensionsUsed.KHR_texture_transform) { - if (Value *pKHR_texture_transform = FindExtension(*prop, "KHR_texture_transform")) { - out.textureTransformSupported = true; - if (Value *array = FindArray(*pKHR_texture_transform, "offset")) { - out.TextureTransformExt_t.offset[0] = (*array)[0].GetFloat(); - out.TextureTransformExt_t.offset[1] = (*array)[1].GetFloat(); - } else { - out.TextureTransformExt_t.offset[0] = 0; - out.TextureTransformExt_t.offset[1] = 0; - } - - if (!ReadMember(*pKHR_texture_transform, "rotation", out.TextureTransformExt_t.rotation)) { - out.TextureTransformExt_t.rotation = 0; - } - - if (Value *array = FindArray(*pKHR_texture_transform, "scale")) { - out.TextureTransformExt_t.scale[0] = (*array)[0].GetFloat(); - out.TextureTransformExt_t.scale[1] = (*array)[1].GetFloat(); - } else { - out.TextureTransformExt_t.scale[0] = 1; - out.TextureTransformExt_t.scale[1] = 1; - } - } - } - - if (Value *indexProp = FindUInt(*prop, "index")) { - out.texture = r.textures.Retrieve(indexProp->GetUint()); - } - - if (Value *texcoord = FindUInt(*prop, "texCoord")) { - out.texCoord = texcoord->GetUint(); - } -} - -inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, TextureInfo &out) { - if (Value *prop = FindMember(vals, propName)) { - SetTextureProperties(r, prop, out); - } -} - -inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, NormalTextureInfo &out) { - if (Value *prop = FindMember(vals, propName)) { - SetTextureProperties(r, prop, out); - - if (Value *scale = FindNumber(*prop, "scale")) { - out.scale = static_cast<float>(scale->GetDouble()); - } - } -} - -inline void Material::ReadTextureProperty(Asset &r, Value &vals, const char *propName, OcclusionTextureInfo &out) { - if (Value *prop = FindMember(vals, propName)) { - SetTextureProperties(r, prop, out); - - if (Value *strength = FindNumber(*prop, "strength")) { - out.strength = static_cast<float>(strength->GetDouble()); - } - } -} - -inline void Material::Read(Value &material, Asset &r) { - SetDefaults(); - - if (Value *curPbrMetallicRoughness = FindObject(material, "pbrMetallicRoughness")) { - ReadMember(*curPbrMetallicRoughness, "baseColorFactor", this->pbrMetallicRoughness.baseColorFactor); - ReadTextureProperty(r, *curPbrMetallicRoughness, "baseColorTexture", this->pbrMetallicRoughness.baseColorTexture); - ReadTextureProperty(r, *curPbrMetallicRoughness, "metallicRoughnessTexture", this->pbrMetallicRoughness.metallicRoughnessTexture); - ReadMember(*curPbrMetallicRoughness, "metallicFactor", this->pbrMetallicRoughness.metallicFactor); - ReadMember(*curPbrMetallicRoughness, "roughnessFactor", this->pbrMetallicRoughness.roughnessFactor); - } - - ReadTextureProperty(r, material, "normalTexture", this->normalTexture); - ReadTextureProperty(r, material, "occlusionTexture", this->occlusionTexture); - ReadTextureProperty(r, material, "emissiveTexture", this->emissiveTexture); - ReadMember(material, "emissiveFactor", this->emissiveFactor); - - ReadMember(material, "doubleSided", this->doubleSided); - ReadMember(material, "alphaMode", this->alphaMode); - ReadMember(material, "alphaCutoff", this->alphaCutoff); - - if (Value *extensions = FindObject(material, "extensions")) { - if (r.extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - if (Value *curPbrSpecularGlossiness = FindObject(*extensions, "KHR_materials_pbrSpecularGlossiness")) { - PbrSpecularGlossiness pbrSG; - - ReadMember(*curPbrSpecularGlossiness, "diffuseFactor", pbrSG.diffuseFactor); - ReadTextureProperty(r, *curPbrSpecularGlossiness, "diffuseTexture", pbrSG.diffuseTexture); - ReadTextureProperty(r, *curPbrSpecularGlossiness, "specularGlossinessTexture", pbrSG.specularGlossinessTexture); - ReadMember(*curPbrSpecularGlossiness, "specularFactor", pbrSG.specularFactor); - ReadMember(*curPbrSpecularGlossiness, "glossinessFactor", pbrSG.glossinessFactor); - - this->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG); - } - } - - // Extension KHR_texture_transform is handled in ReadTextureProperty - - if (r.extensionsUsed.KHR_materials_sheen) { - if (Value *curMaterialSheen = FindObject(*extensions, "KHR_materials_sheen")) { - MaterialSheen sheen; - - ReadMember(*curMaterialSheen, "sheenColorFactor", sheen.sheenColorFactor); - ReadTextureProperty(r, *curMaterialSheen, "sheenColorTexture", sheen.sheenColorTexture); - ReadMember(*curMaterialSheen, "sheenRoughnessFactor", sheen.sheenRoughnessFactor); - ReadTextureProperty(r, *curMaterialSheen, "sheenRoughnessTexture", sheen.sheenRoughnessTexture); - - this->materialSheen = Nullable<MaterialSheen>(sheen); - } - } - - if (r.extensionsUsed.KHR_materials_clearcoat) { - if (Value *curMaterialClearcoat = FindObject(*extensions, "KHR_materials_clearcoat")) { - MaterialClearcoat clearcoat; - - ReadMember(*curMaterialClearcoat, "clearcoatFactor", clearcoat.clearcoatFactor); - ReadTextureProperty(r, *curMaterialClearcoat, "clearcoatTexture", clearcoat.clearcoatTexture); - ReadMember(*curMaterialClearcoat, "clearcoatRoughnessFactor", clearcoat.clearcoatRoughnessFactor); - ReadTextureProperty(r, *curMaterialClearcoat, "clearcoatRoughnessTexture", clearcoat.clearcoatRoughnessTexture); - ReadTextureProperty(r, *curMaterialClearcoat, "clearcoatNormalTexture", clearcoat.clearcoatNormalTexture); - - this->materialClearcoat = Nullable<MaterialClearcoat>(clearcoat); - } - } - - if (r.extensionsUsed.KHR_materials_transmission) { - if (Value *curMaterialTransmission = FindObject(*extensions, "KHR_materials_transmission")) { - MaterialTransmission transmission; - - ReadMember(*curMaterialTransmission, "transmissionFactor", transmission.transmissionFactor); - ReadTextureProperty(r, *curMaterialTransmission, "transmissionTexture", transmission.transmissionTexture); - - this->materialTransmission = Nullable<MaterialTransmission>(transmission); - } - } - - if (r.extensionsUsed.KHR_materials_volume) { - if (Value *curMaterialVolume = FindObject(*extensions, "KHR_materials_volume")) { - MaterialVolume volume; - - ReadMember(*curMaterialVolume, "thicknessFactor", volume.thicknessFactor); - ReadTextureProperty(r, *curMaterialVolume, "thicknessTexture", volume.thicknessTexture); - ReadMember(*curMaterialVolume, "attenuationDistance", volume.attenuationDistance); - ReadMember(*curMaterialVolume, "attenuationColor", volume.attenuationColor); - - this->materialVolume = Nullable<MaterialVolume>(volume); - } - } - - if (r.extensionsUsed.KHR_materials_ior) { - if (Value *curMaterialIOR = FindObject(*extensions, "KHR_materials_ior")) { - MaterialIOR ior; - - ReadMember(*curMaterialIOR, "ior", ior.ior); - - this->materialIOR = Nullable<MaterialIOR>(ior); - } - } - - unlit = nullptr != FindObject(*extensions, "KHR_materials_unlit"); - } -} - -inline void Material::SetDefaults() { - //pbr materials - SetVector(pbrMetallicRoughness.baseColorFactor, defaultBaseColor); - pbrMetallicRoughness.metallicFactor = 1.0f; - pbrMetallicRoughness.roughnessFactor = 1.0f; - - SetVector(emissiveFactor, defaultEmissiveFactor); - alphaMode = "OPAQUE"; - alphaCutoff = 0.5f; - doubleSided = false; - unlit = false; -} - -inline void PbrSpecularGlossiness::SetDefaults() { - //pbrSpecularGlossiness properties - SetVector(diffuseFactor, defaultDiffuseFactor); - SetVector(specularFactor, defaultSpecularFactor); - glossinessFactor = 1.0f; -} - -inline void MaterialSheen::SetDefaults() { - //KHR_materials_sheen properties - SetVector(sheenColorFactor, defaultSheenFactor); - sheenRoughnessFactor = 0.f; -} - -inline void MaterialVolume::SetDefaults() { - //KHR_materials_volume properties - thicknessFactor = 0.f; - attenuationDistance = INFINITY; - SetVector(attenuationColor, defaultAttenuationColor); -} - -inline void MaterialIOR::SetDefaults() { - //KHR_materials_ior properties - ior = 1.5f; -} - -inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { - Value *curName = FindMember(pJSON_Object, "name"); - if (nullptr != curName && curName->IsString()) { - name = curName->GetString(); - } - - /****************** Mesh primitives ******************/ - Value *curPrimitives = FindArray(pJSON_Object, "primitives"); - if (nullptr != curPrimitives) { - this->primitives.resize(curPrimitives->Size()); - for (unsigned int i = 0; i < curPrimitives->Size(); ++i) { - Value &primitive = (*curPrimitives)[i]; - - Primitive &prim = this->primitives[i]; - prim.mode = MemberOrDefault(primitive, "mode", PrimitiveMode_TRIANGLES); - - if (Value *indices = FindUInt(primitive, "indices")) { - prim.indices = pAsset_Root.accessors.Retrieve(indices->GetUint()); - } - - if (Value *material = FindUInt(primitive, "material")) { - prim.material = pAsset_Root.materials.Retrieve(material->GetUint()); - } - - if (Value *attrs = FindObject(primitive, "attributes")) { - for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) { - if (!it->value.IsUint()) continue; - const char *attr = it->name.GetString(); - // Valid attribute semantics include POSITION, NORMAL, TANGENT, TEXCOORD, COLOR, JOINT, JOINTMATRIX, - // and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc. - - int undPos = 0; - Mesh::AccessorList *vec = nullptr; - if (GetAttribVector(prim, attr, vec, undPos)) { - size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0; - if ((*vec).size() != idx) { - throw DeadlyImportError("GLTF: Invalid attribute in mesh: ", name, " primitive: ", i, "attrib: ", attr, - ". All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc."); - } - (*vec).resize(idx + 1); - (*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint()); - } - } - } - -#ifdef ASSIMP_ENABLE_DRACO - // KHR_draco_mesh_compression spec: Draco can only be used for glTF Triangles or Triangle Strips - if (pAsset_Root.extensionsUsed.KHR_draco_mesh_compression && (prim.mode == PrimitiveMode_TRIANGLES || prim.mode == PrimitiveMode_TRIANGLE_STRIP)) { - // Look for draco mesh compression extension and bufferView - // Skip if any missing - if (Value *dracoExt = FindExtension(primitive, "KHR_draco_mesh_compression")) { - if (Value *bufView = FindUInt(*dracoExt, "bufferView")) { - // Attempt to load indices and attributes using draco compression - auto bufferView = pAsset_Root.bufferViews.Retrieve(bufView->GetUint()); - // Attempt to perform the draco decode on the buffer data - const char *bufferViewData = reinterpret_cast<const char *>(bufferView->buffer->GetPointer() + bufferView->byteOffset); - draco::DecoderBuffer decoderBuffer; - decoderBuffer.Init(bufferViewData, bufferView->byteLength); - draco::Decoder decoder; - auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer); - if (!decodeResult.ok()) { - // A corrupt Draco isn't actually fatal if the primitive data is also provided in a standard buffer, but does anyone do that? - throw DeadlyImportError("GLTF: Invalid Draco mesh compression in mesh: ", name, " primitive: ", i, ": ", decodeResult.status().error_msg_string()); - } - - // Now we have a draco mesh - const std::unique_ptr<draco::Mesh> &pDracoMesh = decodeResult.value(); - - // Redirect the accessors to the decoded data - - // Indices - SetDecodedIndexBuffer_Draco(*pDracoMesh, prim); - - // Vertex attributes - if (Value *attrs = FindObject(*dracoExt, "attributes")) { - for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) { - if (!it->value.IsUint()) continue; - const char *attr = it->name.GetString(); - - int undPos = 0; - Mesh::AccessorList *vec = nullptr; - if (GetAttribVector(prim, attr, vec, undPos)) { - size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0; - if (idx >= (*vec).size()) { - throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr, - ". All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc."); - } - - if (!(*vec)[idx]) { - throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr, - ". All draco-encoded attributes must also define an accessor."); - } - - Accessor &attribAccessor = *(*vec)[idx]; - if (attribAccessor.count == 0) - throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr); - - // Redirect this accessor to the appropriate Draco vertex attribute data - const uint32_t dracoAttribId = it->value.GetUint(); - SetDecodedAttributeBuffer_Draco(*pDracoMesh, dracoAttribId, attribAccessor); - } - } - } - } - } - } -#endif - - Value *targetsArray = FindArray(primitive, "targets"); - if (nullptr != targetsArray) { - prim.targets.resize(targetsArray->Size()); - for (unsigned int j = 0; j < targetsArray->Size(); ++j) { - Value &target = (*targetsArray)[j]; - if (!target.IsObject()) { - continue; - } - for (Value::MemberIterator it = target.MemberBegin(); it != target.MemberEnd(); ++it) { - if (!it->value.IsUint()) { - continue; - } - const char *attr = it->name.GetString(); - // Valid attribute semantics include POSITION, NORMAL, TANGENT - int undPos = 0; - Mesh::AccessorList *vec = nullptr; - if (GetAttribTargetVector(prim, j, attr, vec, undPos)) { - size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0; - if ((*vec).size() <= idx) { - (*vec).resize(idx + 1); - } - (*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint()); - } - } - } - } - } - } - - Value *curWeights = FindArray(pJSON_Object, "weights"); - if (nullptr != curWeights) { - this->weights.resize(curWeights->Size()); - for (unsigned int i = 0; i < curWeights->Size(); ++i) { - Value &weightValue = (*curWeights)[i]; - if (weightValue.IsNumber()) { - this->weights[i] = weightValue.GetFloat(); - } - } - } - - Value *curExtras = FindObject(pJSON_Object, "extras"); - if (nullptr != curExtras) { - if (Value *curTargetNames = FindArray(*curExtras, "targetNames")) { - this->targetNames.resize(curTargetNames->Size()); - for (unsigned int i = 0; i < curTargetNames->Size(); ++i) { - Value &targetNameValue = (*curTargetNames)[i]; - if (targetNameValue.IsString()) { - this->targetNames[i] = targetNameValue.GetString(); - } - } - } - } -} - -inline void Camera::Read(Value &obj, Asset & /*r*/) { - std::string type_string = std::string(MemberOrDefault(obj, "type", "perspective")); - if (type_string == "orthographic") { - type = Camera::Orthographic; - } else { - type = Camera::Perspective; - } - - const char *subobjId = (type == Camera::Orthographic) ? "orthographic" : "perspective"; - - Value *it = FindObject(obj, subobjId); - if (!it) throw DeadlyImportError("GLTF: Camera missing its parameters"); - - if (type == Camera::Perspective) { - cameraProperties.perspective.aspectRatio = MemberOrDefault(*it, "aspectRatio", 0.f); - cameraProperties.perspective.yfov = MemberOrDefault(*it, "yfov", 3.1415f / 2.f); - cameraProperties.perspective.zfar = MemberOrDefault(*it, "zfar", 100.f); - cameraProperties.perspective.znear = MemberOrDefault(*it, "znear", 0.01f); - } else { - cameraProperties.ortographic.xmag = MemberOrDefault(*it, "xmag", 1.f); - cameraProperties.ortographic.ymag = MemberOrDefault(*it, "ymag", 1.f); - cameraProperties.ortographic.zfar = MemberOrDefault(*it, "zfar", 100.f); - cameraProperties.ortographic.znear = MemberOrDefault(*it, "znear", 0.01f); - } -} - -inline void Light::Read(Value &obj, Asset & /*r*/) { -#ifndef M_PI - const float M_PI = 3.14159265358979323846f; -#endif - - std::string type_string; - ReadMember(obj, "type", type_string); - if (type_string == "directional") - type = Light::Directional; - else if (type_string == "point") - type = Light::Point; - else - type = Light::Spot; - - name = MemberOrDefault(obj, "name", ""); - - SetVector(color, vec3{ 1.0f, 1.0f, 1.0f }); - ReadMember(obj, "color", color); - - intensity = MemberOrDefault(obj, "intensity", 1.0f); - - ReadMember(obj, "range", range); - - if (type == Light::Spot) { - Value *spot = FindObject(obj, "spot"); - if (!spot) throw DeadlyImportError("GLTF: Light missing its spot parameters"); - innerConeAngle = MemberOrDefault(*spot, "innerConeAngle", 0.0f); - outerConeAngle = MemberOrDefault(*spot, "outerConeAngle", static_cast<float>(M_PI / 4.0f)); - } -} - -inline void Node::Read(Value &obj, Asset &r) { - if (name.empty()) { - name = id; - } - - Value *curChildren = FindArray(obj, "children"); - if (nullptr != curChildren) { - this->children.reserve(curChildren->Size()); - for (unsigned int i = 0; i < curChildren->Size(); ++i) { - Value &child = (*curChildren)[i]; - if (child.IsUint()) { - // get/create the child node - Ref<Node> chn = r.nodes.Retrieve(child.GetUint()); - if (chn) { - this->children.push_back(chn); - } - } - } - } - - Value *curMatrix = FindArray(obj, "matrix"); - if (nullptr != curMatrix) { - ReadValue(*curMatrix, this->matrix); - } else { - ReadMember(obj, "translation", translation); - ReadMember(obj, "scale", scale); - ReadMember(obj, "rotation", rotation); - } - - Value *curMesh = FindUInt(obj, "mesh"); - if (nullptr != curMesh) { - unsigned int numMeshes = 1; - this->meshes.reserve(numMeshes); - Ref<Mesh> meshRef = r.meshes.Retrieve((*curMesh).GetUint()); - if (meshRef) { - this->meshes.push_back(meshRef); - } - } - - // Do not retrieve a skin here, just take a reference, to avoid infinite recursion - // Skins will be properly loaded later - Value *curSkin = FindUInt(obj, "skin"); - if (nullptr != curSkin) { - this->skin = r.skins.Get(curSkin->GetUint()); - } - - Value *curCamera = FindUInt(obj, "camera"); - if (nullptr != curCamera) { - this->camera = r.cameras.Retrieve(curCamera->GetUint()); - if (this->camera) { - this->camera->id = this->id; - } - } - - Value *curExtensions = FindObject(obj, "extensions"); - if (nullptr != curExtensions) { - if (r.extensionsUsed.KHR_lights_punctual) { - if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) { - Value *curLight = FindUInt(*ext, "light"); - if (nullptr != curLight) { - this->light = r.lights.Retrieve(curLight->GetUint()); - if (this->light) { - this->light->id = this->id; - } - } - } - } - } -} - -inline void Scene::Read(Value &obj, Asset &r) { - if (Value *scene_name = FindString(obj, "name")) { - if (scene_name->IsString()) { - this->name = scene_name->GetString(); - } - } - if (Value *array = FindArray(obj, "nodes")) { - for (unsigned int i = 0; i < array->Size(); ++i) { - if (!(*array)[i].IsUint()) continue; - Ref<Node> node = r.nodes.Retrieve((*array)[i].GetUint()); - if (node) - this->nodes.push_back(node); - } - } -} - -inline void Skin::Read(Value &obj, Asset &r) { - if (Value *matrices = FindUInt(obj, "inverseBindMatrices")) { - inverseBindMatrices = r.accessors.Retrieve(matrices->GetUint()); - } - - if (Value *joints = FindArray(obj, "joints")) { - for (unsigned i = 0; i < joints->Size(); ++i) { - if (!(*joints)[i].IsUint()) continue; - Ref<Node> node = r.nodes.Retrieve((*joints)[i].GetUint()); - if (node) { - this->jointNames.push_back(node); - } - } - } -} - -inline void Animation::Read(Value &obj, Asset &r) { - Value *curSamplers = FindArray(obj, "samplers"); - if (nullptr != curSamplers) { - for (unsigned i = 0; i < curSamplers->Size(); ++i) { - Value &sampler = (*curSamplers)[i]; - - Sampler s; - if (Value *input = FindUInt(sampler, "input")) { - s.input = r.accessors.Retrieve(input->GetUint()); - } - if (Value *output = FindUInt(sampler, "output")) { - s.output = r.accessors.Retrieve(output->GetUint()); - } - s.interpolation = Interpolation_LINEAR; - if (Value *interpolation = FindString(sampler, "interpolation")) { - const std::string interp = interpolation->GetString(); - if (interp == "LINEAR") { - s.interpolation = Interpolation_LINEAR; - } else if (interp == "STEP") { - s.interpolation = Interpolation_STEP; - } else if (interp == "CUBICSPLINE") { - s.interpolation = Interpolation_CUBICSPLINE; - } - } - this->samplers.push_back(s); - } - } - - Value *curChannels = FindArray(obj, "channels"); - if (nullptr != curChannels) { - for (unsigned i = 0; i < curChannels->Size(); ++i) { - Value &channel = (*curChannels)[i]; - - Channel c; - Value *curSampler = FindUInt(channel, "sampler"); - if (nullptr != curSampler) { - c.sampler = curSampler->GetUint(); - } - - if (Value *target = FindObject(channel, "target")) { - if (Value *node = FindUInt(*target, "node")) { - c.target.node = r.nodes.Retrieve(node->GetUint()); - } - if (Value *path = FindString(*target, "path")) { - const std::string p = path->GetString(); - if (p == "translation") { - c.target.path = AnimationPath_TRANSLATION; - } else if (p == "rotation") { - c.target.path = AnimationPath_ROTATION; - } else if (p == "scale") { - c.target.path = AnimationPath_SCALE; - } else if (p == "weights") { - c.target.path = AnimationPath_WEIGHTS; - } - } - } - this->channels.push_back(c); - } - } -} - -inline void AssetMetadata::Read(Document &doc) { - if (Value *obj = FindObject(doc, "asset")) { - ReadMember(*obj, "copyright", copyright); - ReadMember(*obj, "generator", generator); - - if (Value *versionString = FindStringInContext(*obj, "version", "\"asset\"")) { - version = versionString->GetString(); - } - Value *curProfile = FindObjectInContext(*obj, "profile", "\"asset\""); - if (nullptr != curProfile) { - ReadMember(*curProfile, "api", this->profile.api); - ReadMember(*curProfile, "version", this->profile.version); - } - } - - if (version.empty() || version[0] != '2') { - throw DeadlyImportError("GLTF: Unsupported glTF version: ", version); - } -} - -// -// Asset methods implementation -// - -inline void Asset::ReadBinaryHeader(IOStream &stream, std::vector<char> &sceneData) { - ASSIMP_LOG_DEBUG("Reading GLTF2 binary"); - GLB_Header header; - if (stream.Read(&header, sizeof(header), 1) != 1) { - throw DeadlyImportError("GLTF: Unable to read the file header"); - } - - if (strncmp((char *)header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)) != 0) { - throw DeadlyImportError("GLTF: Invalid binary glTF file"); - } - - AI_SWAP4(header.version); - asset.version = ai_to_string(header.version); - if (header.version != 2) { - throw DeadlyImportError("GLTF: Unsupported binary glTF version"); - } - - GLB_Chunk chunk; - if (stream.Read(&chunk, sizeof(chunk), 1) != 1) { - throw DeadlyImportError("GLTF: Unable to read JSON chunk"); - } - - AI_SWAP4(chunk.chunkLength); - AI_SWAP4(chunk.chunkType); - - if (chunk.chunkType != ChunkType_JSON) { - throw DeadlyImportError("GLTF: JSON chunk missing"); - } - - // read the scene data, ensure null termination - static_assert(std::numeric_limits<uint32_t>::max() <= std::numeric_limits<size_t>::max(), "size_t must be at least 32bits"); - mSceneLength = chunk.chunkLength; // Can't be larger than 4GB (max. uint32_t) - sceneData.resize(mSceneLength + 1); - sceneData[mSceneLength] = '\0'; - - if (stream.Read(&sceneData[0], 1, mSceneLength) != mSceneLength) { - throw DeadlyImportError("GLTF: Could not read the file contents"); - } - - uint32_t padding = ((chunk.chunkLength + 3) & ~3) - chunk.chunkLength; - if (padding > 0) { - stream.Seek(padding, aiOrigin_CUR); - } - - AI_SWAP4(header.length); - mBodyOffset = 12 + 8 + chunk.chunkLength + padding + 8; - if (header.length >= mBodyOffset) { - if (stream.Read(&chunk, sizeof(chunk), 1) != 1) { - throw DeadlyImportError("GLTF: Unable to read BIN chunk"); - } - - AI_SWAP4(chunk.chunkLength); - AI_SWAP4(chunk.chunkType); - - if (chunk.chunkType != ChunkType_BIN) { - throw DeadlyImportError("GLTF: BIN chunk missing"); - } - - mBodyLength = chunk.chunkLength; - } else { - mBodyOffset = mBodyLength = 0; - } -} - -inline rapidjson::Document Asset::ReadDocument(IOStream &stream, bool isBinary, std::vector<char> &sceneData) { - ASSIMP_LOG_DEBUG("Loading GLTF2 asset"); - - // is binary? then read the header - if (isBinary) { - SetAsBinary(); // also creates the body buffer - ReadBinaryHeader(stream, sceneData); - } else { - mSceneLength = stream.FileSize(); - mBodyLength = 0; - - // Binary format only supports up to 4GB of JSON, use that as a maximum - if (mSceneLength >= std::numeric_limits<uint32_t>::max()) { - throw DeadlyImportError("GLTF: JSON size greater than 4GB"); - } - - // read the scene data, ensure null termination - sceneData.resize(mSceneLength + 1); - sceneData[mSceneLength] = '\0'; - - if (stream.Read(&sceneData[0], 1, mSceneLength) != mSceneLength) { - throw DeadlyImportError("GLTF: Could not read the file contents"); - } - } - - // Smallest legal JSON file is "{}" Smallest loadable glTF file is larger than that but catch it later - if (mSceneLength < 2) { - throw DeadlyImportError("GLTF: No JSON file contents"); - } - - // parse the JSON document - ASSIMP_LOG_DEBUG("Parsing GLTF2 JSON"); - Document doc; - doc.ParseInsitu(&sceneData[0]); - - if (doc.HasParseError()) { - char buffer[32]; - ai_snprintf(buffer, 32, "%d", static_cast<int>(doc.GetErrorOffset())); - throw DeadlyImportError("GLTF: JSON parse error, offset ", buffer, ": ", GetParseError_En(doc.GetParseError())); - } - - if (!doc.IsObject()) { - throw DeadlyImportError("GLTF: JSON document root must be a JSON object"); - } - - return doc; -} - -inline void Asset::Load(const std::string &pFile, bool isBinary) -{ - mCurrentAssetDir.clear(); - if (0 != strncmp(pFile.c_str(), AI_MEMORYIO_MAGIC_FILENAME, AI_MEMORYIO_MAGIC_FILENAME_LENGTH)) { - mCurrentAssetDir = glTFCommon::getCurrentAssetDir(pFile); - } - - shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true)); - if (!stream) { - throw DeadlyImportError("GLTF: Could not open file for reading"); - } - - std::vector<char> sceneData; - rapidjson::Document doc = ReadDocument(*stream, isBinary, sceneData); - - // If a schemaDocumentProvider is available, see if the glTF schema is present. - // If so, use it to validate the document. - if (mSchemaDocumentProvider) { - if (const rapidjson::SchemaDocument *gltfSchema = mSchemaDocumentProvider->GetRemoteDocument("glTF.schema.json", 16)) { - // The schemas are found here: https://github.com/KhronosGroup/glTF/tree/main/specification/2.0/schema - rapidjson::SchemaValidator validator(*gltfSchema); - if (!doc.Accept(validator)) { - rapidjson::StringBuffer pathBuffer; - validator.GetInvalidSchemaPointer().StringifyUriFragment(pathBuffer); - rapidjson::StringBuffer argumentBuffer; - validator.GetInvalidDocumentPointer().StringifyUriFragment(argumentBuffer); - throw DeadlyImportError("GLTF: The JSON document did not satisfy the glTF2 schema. Schema keyword: ", validator.GetInvalidSchemaKeyword(), ", document path: ", pathBuffer.GetString(), ", argument: ", argumentBuffer.GetString()); - } - } - } - - // Fill the buffer instance for the current file embedded contents - if (mBodyLength > 0) { - if (!mBodyBuffer->LoadFromStream(*stream, mBodyLength, mBodyOffset)) { - throw DeadlyImportError("GLTF: Unable to read gltf file"); - } - } - - // Load the metadata - asset.Read(doc); - ReadExtensionsUsed(doc); - ReadExtensionsRequired(doc); - -#ifndef ASSIMP_ENABLE_DRACO - // Is Draco required? - if (extensionsRequired.KHR_draco_mesh_compression) { - throw DeadlyImportError("GLTF: Draco mesh compression not supported."); - } -#endif - - // Prepare the dictionaries - for (size_t i = 0; i < mDicts.size(); ++i) { - mDicts[i]->AttachToDocument(doc); - } - - // Read the "scene" property, which specifies which scene to load - // and recursively load everything referenced by it - unsigned int sceneIndex = 0; - Value *curScene = FindUInt(doc, "scene"); - if (nullptr != curScene) { - sceneIndex = curScene->GetUint(); - } - - if (Value *scenesArray = FindArray(doc, "scenes")) { - if (sceneIndex < scenesArray->Size()) { - this->scene = scenes.Retrieve(sceneIndex); - } - } - - if (Value *skinsArray = FindArray(doc, "skins")) { - for (unsigned int i = 0; i < skinsArray->Size(); ++i) { - skins.Retrieve(i); - } - } - - if (Value *animsArray = FindArray(doc, "animations")) { - for (unsigned int i = 0; i < animsArray->Size(); ++i) { - animations.Retrieve(i); - } - } - - // Clean up - for (size_t i = 0; i < mDicts.size(); ++i) { - mDicts[i]->DetachFromDocument(); - } -} - -inline bool Asset::CanRead(const std::string &pFile, bool isBinary) { - try { - shared_ptr<IOStream> stream(OpenFile(pFile.c_str(), "rb", true)); - if (!stream) { - return false; - } - std::vector<char> sceneData; - rapidjson::Document doc = ReadDocument(*stream, isBinary, sceneData); - asset.Read(doc); - } catch (...) { - return false; - } - return true; -} - -inline void Asset::SetAsBinary() { - if (!mBodyBuffer) { - mBodyBuffer = buffers.Create("binary_glTF"); - mBodyBuffer->MarkAsSpecial(); - } -} - -// As required extensions are only a concept in glTF 2.0, this is here -// instead of glTFCommon.h -#define CHECK_REQUIRED_EXT(EXT) \ - if (exts.find(#EXT) != exts.end()) extensionsRequired.EXT = true; - -inline void Asset::ReadExtensionsRequired(Document &doc) { - Value *extsRequired = FindArray(doc, "extensionsRequired"); - if (nullptr == extsRequired) { - return; - } - - std::gltf_unordered_map<std::string, bool> exts; - for (unsigned int i = 0; i < extsRequired->Size(); ++i) { - if ((*extsRequired)[i].IsString()) { - exts[(*extsRequired)[i].GetString()] = true; - } - } - - CHECK_REQUIRED_EXT(KHR_draco_mesh_compression); - -#undef CHECK_REQUIRED_EXT -} - -inline void Asset::ReadExtensionsUsed(Document &doc) { - Value *extsUsed = FindArray(doc, "extensionsUsed"); - if (!extsUsed) return; - - std::gltf_unordered_map<std::string, bool> exts; - - for (unsigned int i = 0; i < extsUsed->Size(); ++i) { - if ((*extsUsed)[i].IsString()) { - exts[(*extsUsed)[i].GetString()] = true; - } - } - - CHECK_EXT(KHR_materials_pbrSpecularGlossiness); - CHECK_EXT(KHR_materials_unlit); - CHECK_EXT(KHR_lights_punctual); - CHECK_EXT(KHR_texture_transform); - CHECK_EXT(KHR_materials_sheen); - CHECK_EXT(KHR_materials_clearcoat); - CHECK_EXT(KHR_materials_transmission); - CHECK_EXT(KHR_materials_volume); - CHECK_EXT(KHR_materials_ior); - CHECK_EXT(KHR_draco_mesh_compression); - CHECK_EXT(KHR_texture_basisu); - -#undef CHECK_EXT -} - -inline IOStream *Asset::OpenFile(const std::string &path, const char *mode, bool /*absolute*/) { -#ifdef ASSIMP_API - return mIOSystem->Open(path, mode); -#else - if (path.size() < 2) return nullptr; - if (!absolute && path[1] != ':' && path[0] != '/') { // relative? - path = mCurrentAssetDir + path; - } - FILE *f = fopen(path.c_str(), mode); - return f ? new IOStream(f) : nullptr; -#endif -} - -inline std::string Asset::FindUniqueID(const std::string &str, const char *suffix) { - std::string id = str; - - if (!id.empty()) { - if (mUsedIds.find(id) == mUsedIds.end()) - return id; - - id += "_"; - } - - id += suffix; - - Asset::IdMap::iterator it = mUsedIds.find(id); - if (it == mUsedIds.end()) { - return id; - } - - std::vector<char> buffer; - buffer.resize(id.size() + 16); - int offset = ai_snprintf(buffer.data(), buffer.size(), "%s_", id.c_str()); - for (int i = 0; it != mUsedIds.end(); ++i) { - ai_snprintf(buffer.data() + offset, buffer.size() - offset, "%d", i); - id = buffer.data(); - it = mUsedIds.find(id); - } - - return id; -} - -#if _MSC_VER -# pragma warning(pop) -#endif // _MSC_VER - -} // namespace glTF2 diff --git a/libs/assimp/code/AssetLib/glTF2/glTF2AssetWriter.h b/libs/assimp/code/AssetLib/glTF2/glTF2AssetWriter.h deleted file mode 100644 index 089a158..0000000 --- a/libs/assimp/code/AssetLib/glTF2/glTF2AssetWriter.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -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 glTFWriter.h - * Declares a class to write gltf/glb files - * - * glTF Extensions Support: - * KHR_materials_pbrSpecularGlossiness: full - * KHR_materials_unlit: full - * KHR_materials_sheen: full - * KHR_materials_clearcoat: full - * KHR_materials_transmission: full - * KHR_materials_volume: full - * KHR_materials_ior: full - */ -#ifndef GLTF2ASSETWRITER_H_INC -#define GLTF2ASSETWRITER_H_INC - -#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER) - -#include "glTF2Asset.h" - -namespace glTF2 -{ - -using rapidjson::MemoryPoolAllocator; - -class AssetWriter -{ - template<class T> - friend void WriteLazyDict(LazyDict<T>& d, AssetWriter& w); - -private: - - void WriteBinaryData(IOStream* outfile, size_t sceneLength); - - void WriteMetadata(); - void WriteExtensionsUsed(); - - template<class T> - void WriteObjects(LazyDict<T>& d); - -public: - Document mDoc; - Asset& mAsset; - - MemoryPoolAllocator<>& mAl; - - AssetWriter(Asset& asset); - - void WriteFile(const char* path); - void WriteGLBFile(const char* path); -}; - -} - -// Include the implementation of the methods -#include "glTF2AssetWriter.inl" - -#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER - -#endif // GLTF2ASSETWRITER_H_INC diff --git a/libs/assimp/code/AssetLib/glTF2/glTF2AssetWriter.inl b/libs/assimp/code/AssetLib/glTF2/glTF2AssetWriter.inl deleted file mode 100644 index 0be1395..0000000 --- a/libs/assimp/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ /dev/null @@ -1,1015 +0,0 @@ -/* -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. - ----------------------------------------------------------------------- -*/ - -#include <assimp/Base64.hpp> -#include <rapidjson/stringbuffer.h> -#include <rapidjson/writer.h> -#include <rapidjson/prettywriter.h> - -namespace glTF2 { - - using rapidjson::StringBuffer; - using rapidjson::PrettyWriter; - using rapidjson::Writer; - using rapidjson::StringRef; - using rapidjson::StringRef; - - namespace { - - template<typename T, size_t N> - inline Value& MakeValue(Value& val, T(&r)[N], MemoryPoolAllocator<>& al) { - val.SetArray(); - val.Reserve(N, al); - for (decltype(N) i = 0; i < N; ++i) { - val.PushBack(r[i], al); - } - return val; - } - - template<typename T> - inline Value& MakeValue(Value& val, const std::vector<T> & r, MemoryPoolAllocator<>& al) { - val.SetArray(); - val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al); - for (unsigned int i = 0; i < r.size(); ++i) { - val.PushBack(r[i], al); - } - return val; - } - - template<typename C, typename T> - inline Value& MakeValueCast(Value& val, const std::vector<T> & r, MemoryPoolAllocator<>& al) { - val.SetArray(); - val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al); - for (unsigned int i = 0; i < r.size(); ++i) { - val.PushBack(static_cast<C>(r[i]), al); - } - return val; - } - - template<typename T> - inline Value& MakeValue(Value& val, T r, MemoryPoolAllocator<>& /*al*/) { - val.Set(r); - - return val; - } - - template<class T> - inline void AddRefsVector(Value& obj, const char* fieldId, std::vector< Ref<T> >& v, MemoryPoolAllocator<>& al) { - if (v.empty()) return; - Value lst; - lst.SetArray(); - lst.Reserve(unsigned(v.size()), al); - for (size_t i = 0; i < v.size(); ++i) { - lst.PushBack(v[i]->index, al); - } - obj.AddMember(StringRef(fieldId), lst, al); - } - - - } - - inline void Write(Value& obj, Accessor& a, AssetWriter& w) - { - if (a.bufferView) { - obj.AddMember("bufferView", a.bufferView->index, w.mAl); - obj.AddMember("byteOffset", (unsigned int)a.byteOffset, w.mAl); - } - obj.AddMember("componentType", int(a.componentType), w.mAl); - obj.AddMember("count", (unsigned int)a.count, w.mAl); - obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl); - Value vTmpMax, vTmpMin; - if (a.componentType == ComponentType_FLOAT) { - obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl); - obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl); - } else { - obj.AddMember("max", MakeValueCast<int64_t>(vTmpMax, a.max, w.mAl), w.mAl); - obj.AddMember("min", MakeValueCast<int64_t>(vTmpMin, a.min, w.mAl), w.mAl); - } - - if (a.sparse) { - Value sparseValue; - sparseValue.SetObject(); - - //count - sparseValue.AddMember("count", (unsigned int)a.sparse->count, w.mAl); - - //indices - Value indices; - indices.SetObject(); - indices.AddMember("bufferView", a.sparse->indices->index, w.mAl); - indices.AddMember("byteOffset", (unsigned int)a.sparse->indicesByteOffset, w.mAl); - indices.AddMember("componentType", int(a.sparse->indicesType), w.mAl); - sparseValue.AddMember("indices", indices, w.mAl); - - //values - Value values; - values.SetObject(); - values.AddMember("bufferView", a.sparse->values->index, w.mAl); - values.AddMember("byteOffset", (unsigned int)a.sparse->valuesByteOffset, w.mAl); - sparseValue.AddMember("values", values, w.mAl); - - obj.AddMember("sparse", sparseValue, w.mAl); - } - } - - inline void Write(Value& obj, Animation& a, AssetWriter& w) - { - /****************** Channels *******************/ - Value channels; - channels.SetArray(); - channels.Reserve(unsigned(a.channels.size()), w.mAl); - - for (size_t i = 0; i < unsigned(a.channels.size()); ++i) { - Animation::Channel& c = a.channels[i]; - Value valChannel; - valChannel.SetObject(); - { - valChannel.AddMember("sampler", c.sampler, w.mAl); - - Value valTarget; - valTarget.SetObject(); - { - valTarget.AddMember("node", c.target.node->index, w.mAl); - switch (c.target.path) { - case AnimationPath_TRANSLATION: - valTarget.AddMember("path", "translation", w.mAl); - break; - case AnimationPath_ROTATION: - valTarget.AddMember("path", "rotation", w.mAl); - break; - case AnimationPath_SCALE: - valTarget.AddMember("path", "scale", w.mAl); - break; - case AnimationPath_WEIGHTS: - valTarget.AddMember("path", "weights", w.mAl); - break; - } - } - valChannel.AddMember("target", valTarget, w.mAl); - } - channels.PushBack(valChannel, w.mAl); - } - obj.AddMember("channels", channels, w.mAl); - - /****************** Samplers *******************/ - Value valSamplers; - valSamplers.SetArray(); - - for (size_t i = 0; i < unsigned(a.samplers.size()); ++i) { - Animation::Sampler& s = a.samplers[i]; - Value valSampler; - valSampler.SetObject(); - { - valSampler.AddMember("input", s.input->index, w.mAl); - switch (s.interpolation) { - case Interpolation_LINEAR: - valSampler.AddMember("interpolation", "LINEAR", w.mAl); - break; - case Interpolation_STEP: - valSampler.AddMember("interpolation", "STEP", w.mAl); - break; - case Interpolation_CUBICSPLINE: - valSampler.AddMember("interpolation", "CUBICSPLINE", w.mAl); - break; - } - valSampler.AddMember("output", s.output->index, w.mAl); - } - valSamplers.PushBack(valSampler, w.mAl); - } - obj.AddMember("samplers", valSamplers, w.mAl); - } - - inline void Write(Value& obj, Buffer& b, AssetWriter& w) - { - obj.AddMember("byteLength", static_cast<uint64_t>(b.byteLength), w.mAl); - - const auto uri = b.GetURI(); - const auto relativeUri = uri.substr(uri.find_last_of("/\\") + 1u); - obj.AddMember("uri", Value(relativeUri, w.mAl).Move(), w.mAl); - } - - inline void Write(Value& obj, BufferView& bv, AssetWriter& w) - { - obj.AddMember("buffer", bv.buffer->index, w.mAl); - obj.AddMember("byteOffset", static_cast<uint64_t>(bv.byteOffset), w.mAl); - obj.AddMember("byteLength", static_cast<uint64_t>(bv.byteLength), w.mAl); - if (bv.byteStride != 0) { - obj.AddMember("byteStride", bv.byteStride, w.mAl); - } - if (bv.target != BufferViewTarget_NONE) { - obj.AddMember("target", int(bv.target), w.mAl); - } - } - - inline void Write(Value& /*obj*/, Camera& /*c*/, AssetWriter& /*w*/) - { - - } - - inline void Write(Value& /*obj*/, Light& /*c*/, AssetWriter& /*w*/) - { - - } - - inline void Write(Value& obj, Image& img, AssetWriter& w) - { - //basisu: no need to handle .ktx2, .basis, write as is - if (img.bufferView) { - obj.AddMember("bufferView", img.bufferView->index, w.mAl); - obj.AddMember("mimeType", Value(img.mimeType, w.mAl).Move(), w.mAl); - } - else { - std::string uri; - if (img.HasData()) { - uri = "data:" + (img.mimeType.empty() ? "application/octet-stream" : img.mimeType); - uri += ";base64,"; - Base64::Encode(img.GetData(), img.GetDataLength(), uri); - } - else { - uri = img.uri; - } - - obj.AddMember("uri", Value(uri, w.mAl).Move(), w.mAl); - } - } - - namespace { - inline void SetTexBasic(TextureInfo t, Value& tex, MemoryPoolAllocator<>& al) - { - tex.SetObject(); - tex.AddMember("index", t.texture->index, al); - - if (t.texCoord != 0) { - tex.AddMember("texCoord", t.texCoord, al); - } - } - - inline void WriteTex(Value& obj, TextureInfo t, const char* propName, MemoryPoolAllocator<>& al) - { - - if (t.texture) { - Value tex; - - SetTexBasic(t, tex, al); - - obj.AddMember(StringRef(propName), tex, al); - } - } - - inline void WriteTex(Value& obj, NormalTextureInfo t, const char* propName, MemoryPoolAllocator<>& al) - { - - if (t.texture) { - Value tex; - - SetTexBasic(t, tex, al); - - if (t.scale != 1) { - tex.AddMember("scale", t.scale, al); - } - - obj.AddMember(StringRef(propName), tex, al); - } - } - - inline void WriteTex(Value& obj, OcclusionTextureInfo t, const char* propName, MemoryPoolAllocator<>& al) - { - - if (t.texture) { - Value tex; - - SetTexBasic(t, tex, al); - - if (t.strength != 1) { - tex.AddMember("strength", t.strength, al); - } - - obj.AddMember(StringRef(propName), tex, al); - } - } - - template<size_t N> - inline void WriteVec(Value& obj, float(&prop)[N], const char* propName, MemoryPoolAllocator<>& al) - { - Value arr; - obj.AddMember(StringRef(propName), MakeValue(arr, prop, al), al); - } - - template<size_t N> - inline void WriteVec(Value& obj, float(&prop)[N], const char* propName, const float(&defaultVal)[N], MemoryPoolAllocator<>& al) - { - if (!std::equal(std::begin(prop), std::end(prop), std::begin(defaultVal))) { - WriteVec(obj, prop, propName, al); - } - } - - inline void WriteFloat(Value& obj, float prop, const char* propName, MemoryPoolAllocator<>& al) - { - Value num; - obj.AddMember(StringRef(propName), MakeValue(num, prop, al), al); - } - } - - inline void Write(Value& obj, Material& m, AssetWriter& w) - { - Value pbrMetallicRoughness; - pbrMetallicRoughness.SetObject(); - { - WriteTex(pbrMetallicRoughness, m.pbrMetallicRoughness.baseColorTexture, "baseColorTexture", w.mAl); - WriteTex(pbrMetallicRoughness, m.pbrMetallicRoughness.metallicRoughnessTexture, "metallicRoughnessTexture", w.mAl); - WriteVec(pbrMetallicRoughness, m.pbrMetallicRoughness.baseColorFactor, "baseColorFactor", defaultBaseColor, w.mAl); - - if (m.pbrMetallicRoughness.metallicFactor != 1) { - WriteFloat(pbrMetallicRoughness, m.pbrMetallicRoughness.metallicFactor, "metallicFactor", w.mAl); - } - - if (m.pbrMetallicRoughness.roughnessFactor != 1) { - WriteFloat(pbrMetallicRoughness, m.pbrMetallicRoughness.roughnessFactor, "roughnessFactor", w.mAl); - } - } - - if (!pbrMetallicRoughness.ObjectEmpty()) { - obj.AddMember("pbrMetallicRoughness", pbrMetallicRoughness, w.mAl); - } - - WriteTex(obj, m.normalTexture, "normalTexture", w.mAl); - WriteTex(obj, m.emissiveTexture, "emissiveTexture", w.mAl); - WriteTex(obj, m.occlusionTexture, "occlusionTexture", w.mAl); - WriteVec(obj, m.emissiveFactor, "emissiveFactor", defaultEmissiveFactor, w.mAl); - - if (m.alphaCutoff != 0.5) { - WriteFloat(obj, m.alphaCutoff, "alphaCutoff", w.mAl); - } - - if (m.alphaMode != "OPAQUE") { - obj.AddMember("alphaMode", Value(m.alphaMode, w.mAl).Move(), w.mAl); - } - - if (m.doubleSided) { - obj.AddMember("doubleSided", m.doubleSided, w.mAl); - } - - Value exts; - exts.SetObject(); - - if (m.pbrSpecularGlossiness.isPresent) { - Value pbrSpecularGlossiness; - pbrSpecularGlossiness.SetObject(); - - PbrSpecularGlossiness &pbrSG = m.pbrSpecularGlossiness.value; - - //pbrSpecularGlossiness - WriteVec(pbrSpecularGlossiness, pbrSG.diffuseFactor, "diffuseFactor", defaultDiffuseFactor, w.mAl); - WriteVec(pbrSpecularGlossiness, pbrSG.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl); - - if (pbrSG.glossinessFactor != 1) { - WriteFloat(pbrSpecularGlossiness, pbrSG.glossinessFactor, "glossinessFactor", w.mAl); - } - - WriteTex(pbrSpecularGlossiness, pbrSG.diffuseTexture, "diffuseTexture", w.mAl); - WriteTex(pbrSpecularGlossiness, pbrSG.specularGlossinessTexture, "specularGlossinessTexture", w.mAl); - - if (!pbrSpecularGlossiness.ObjectEmpty()) { - exts.AddMember("KHR_materials_pbrSpecularGlossiness", pbrSpecularGlossiness, w.mAl); - } - } - - if (m.unlit) { - Value unlit; - unlit.SetObject(); - exts.AddMember("KHR_materials_unlit", unlit, w.mAl); - } - - if (m.materialSheen.isPresent) { - Value materialSheen(rapidjson::Type::kObjectType); - - MaterialSheen &sheen = m.materialSheen.value; - - WriteVec(materialSheen, sheen.sheenColorFactor, "sheenColorFactor", defaultSheenFactor, w.mAl); - - if (sheen.sheenRoughnessFactor != 0.f) { - WriteFloat(materialSheen, sheen.sheenRoughnessFactor, "sheenRoughnessFactor", w.mAl); - } - - WriteTex(materialSheen, sheen.sheenColorTexture, "sheenColorTexture", w.mAl); - WriteTex(materialSheen, sheen.sheenRoughnessTexture, "sheenRoughnessTexture", w.mAl); - - if (!materialSheen.ObjectEmpty()) { - exts.AddMember("KHR_materials_sheen", materialSheen, w.mAl); - } - } - - if (m.materialClearcoat.isPresent) { - Value materialClearcoat(rapidjson::Type::kObjectType); - - MaterialClearcoat &clearcoat = m.materialClearcoat.value; - - if (clearcoat.clearcoatFactor != 0.f) { - WriteFloat(materialClearcoat, clearcoat.clearcoatFactor, "clearcoatFactor", w.mAl); - } - - if (clearcoat.clearcoatRoughnessFactor != 0.f) { - WriteFloat(materialClearcoat, clearcoat.clearcoatRoughnessFactor, "clearcoatRoughnessFactor", w.mAl); - } - - WriteTex(materialClearcoat, clearcoat.clearcoatTexture, "clearcoatTexture", w.mAl); - WriteTex(materialClearcoat, clearcoat.clearcoatRoughnessTexture, "clearcoatRoughnessTexture", w.mAl); - WriteTex(materialClearcoat, clearcoat.clearcoatNormalTexture, "clearcoatNormalTexture", w.mAl); - - if (!materialClearcoat.ObjectEmpty()) { - exts.AddMember("KHR_materials_clearcoat", materialClearcoat, w.mAl); - } - } - - if (m.materialTransmission.isPresent) { - Value materialTransmission(rapidjson::Type::kObjectType); - - MaterialTransmission &transmission = m.materialTransmission.value; - - if (transmission.transmissionFactor != 0.f) { - WriteFloat(materialTransmission, transmission.transmissionFactor, "transmissionFactor", w.mAl); - } - - WriteTex(materialTransmission, transmission.transmissionTexture, "transmissionTexture", w.mAl); - - if (!materialTransmission.ObjectEmpty()) { - exts.AddMember("KHR_materials_transmission", materialTransmission, w.mAl); - } - } - - if (m.materialVolume.isPresent) { - Value materialVolume(rapidjson::Type::kObjectType); - - MaterialVolume &volume = m.materialVolume.value; - - if (volume.thicknessFactor != 0.f) { - WriteFloat(materialVolume, volume.thicknessFactor, "thicknessFactor", w.mAl); - } - - WriteTex(materialVolume, volume.thicknessTexture, "thicknessTexture", w.mAl); - - if (volume.attenuationDistance != INFINITY) { - WriteFloat(materialVolume, volume.attenuationDistance, "attenuationDistance", w.mAl); - } - - WriteVec(materialVolume, volume.attenuationColor, "attenuationColor", defaultAttenuationColor, w.mAl); - - if (!materialVolume.ObjectEmpty()) { - exts.AddMember("KHR_materials_volume", materialVolume, w.mAl); - } - } - - if (m.materialIOR.isPresent) { - Value materialIOR(rapidjson::Type::kObjectType); - - MaterialIOR &ior = m.materialIOR.value; - - if (ior.ior != 1.5f) { - WriteFloat(materialIOR, ior.ior, "ior", w.mAl); - } - - if (!materialIOR.ObjectEmpty()) { - exts.AddMember("KHR_materials_ior", materialIOR, w.mAl); - } - } - - if (!exts.ObjectEmpty()) { - obj.AddMember("extensions", exts, w.mAl); - } - } - - namespace { - inline void WriteAttrs(AssetWriter& w, Value& attrs, Mesh::AccessorList& lst, - const char* semantic, bool forceNumber = false) - { - if (lst.empty()) return; - if (lst.size() == 1 && !forceNumber) { - attrs.AddMember(StringRef(semantic), lst[0]->index, w.mAl); - } - else { - for (size_t i = 0; i < lst.size(); ++i) { - char buffer[32]; - ai_snprintf(buffer, 32, "%s_%d", semantic, int(i)); - attrs.AddMember(Value(buffer, w.mAl).Move(), lst[i]->index, w.mAl); - } - } - } - } - - inline void Write(Value& obj, Mesh& m, AssetWriter& w) - { - /****************** Primitives *******************/ - Value primitives; - primitives.SetArray(); - primitives.Reserve(unsigned(m.primitives.size()), w.mAl); - - for (size_t i = 0; i < m.primitives.size(); ++i) { - Mesh::Primitive& p = m.primitives[i]; - Value prim; - prim.SetObject(); - - // Extensions - if (p.ngonEncoded) - { - Value exts; - exts.SetObject(); - - Value FB_ngon_encoding; - FB_ngon_encoding.SetObject(); - - exts.AddMember(StringRef("FB_ngon_encoding"), FB_ngon_encoding, w.mAl); - prim.AddMember("extensions", exts, w.mAl); - } - - { - prim.AddMember("mode", Value(int(p.mode)).Move(), w.mAl); - - if (p.material) - prim.AddMember("material", p.material->index, w.mAl); - - if (p.indices) - prim.AddMember("indices", p.indices->index, w.mAl); - - Value attrs; - attrs.SetObject(); - { - WriteAttrs(w, attrs, p.attributes.position, "POSITION"); - WriteAttrs(w, attrs, p.attributes.normal, "NORMAL"); - WriteAttrs(w, attrs, p.attributes.texcoord, "TEXCOORD", true); - WriteAttrs(w, attrs, p.attributes.color, "COLOR", true); - WriteAttrs(w, attrs, p.attributes.joint, "JOINTS", true); - WriteAttrs(w, attrs, p.attributes.weight, "WEIGHTS", true); - } - prim.AddMember("attributes", attrs, w.mAl); - - // targets for blendshapes - if (p.targets.size() > 0) { - Value tjs; - tjs.SetArray(); - tjs.Reserve(unsigned(p.targets.size()), w.mAl); - for (unsigned int t = 0; t < p.targets.size(); ++t) { - Value tj; - tj.SetObject(); - { - WriteAttrs(w, tj, p.targets[t].position, "POSITION"); - WriteAttrs(w, tj, p.targets[t].normal, "NORMAL"); - WriteAttrs(w, tj, p.targets[t].tangent, "TANGENT"); - } - tjs.PushBack(tj, w.mAl); - } - prim.AddMember("targets", tjs, w.mAl); - } - } - primitives.PushBack(prim, w.mAl); - } - - obj.AddMember("primitives", primitives, w.mAl); - // targetNames - if (m.targetNames.size() > 0) { - Value extras; - extras.SetObject(); - Value targetNames; - targetNames.SetArray(); - targetNames.Reserve(unsigned(m.targetNames.size()), w.mAl); - for (unsigned int n = 0; n < m.targetNames.size(); ++n) { - std::string name = m.targetNames[n]; - Value tname; - tname.SetString(name.c_str(), w.mAl); - targetNames.PushBack(tname, w.mAl); - } - extras.AddMember("targetNames", targetNames, w.mAl); - obj.AddMember("extras", extras, w.mAl); - } - } - - inline void Write(Value& obj, Node& n, AssetWriter& w) - { - if (n.matrix.isPresent) { - Value val; - obj.AddMember("matrix", MakeValue(val, n.matrix.value, w.mAl).Move(), w.mAl); - } - - if (n.translation.isPresent) { - Value val; - obj.AddMember("translation", MakeValue(val, n.translation.value, w.mAl).Move(), w.mAl); - } - - if (n.scale.isPresent) { - Value val; - obj.AddMember("scale", MakeValue(val, n.scale.value, w.mAl).Move(), w.mAl); - } - if (n.rotation.isPresent) { - Value val; - obj.AddMember("rotation", MakeValue(val, n.rotation.value, w.mAl).Move(), w.mAl); - } - - AddRefsVector(obj, "children", n.children, w.mAl); - - if (!n.meshes.empty()) { - obj.AddMember("mesh", n.meshes[0]->index, w.mAl); - } - - if (n.skin) { - obj.AddMember("skin", n.skin->index, w.mAl); - } - - //gltf2 spec does not support "skeletons" under node - if(n.skeletons.size()) { - AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); - } - } - - inline void Write(Value& /*obj*/, Program& /*b*/, AssetWriter& /*w*/) - { - - } - - inline void Write(Value& obj, Sampler& b, AssetWriter& w) - { - if (!b.name.empty()) { - obj.AddMember("name", b.name, w.mAl); - } - - if (b.wrapS != SamplerWrap::UNSET && b.wrapS != SamplerWrap::Repeat) { - obj.AddMember("wrapS", static_cast<unsigned int>(b.wrapS), w.mAl); - } - - if (b.wrapT != SamplerWrap::UNSET && b.wrapT != SamplerWrap::Repeat) { - obj.AddMember("wrapT", static_cast<unsigned int>(b.wrapT), w.mAl); - } - - if (b.magFilter != SamplerMagFilter::UNSET) { - obj.AddMember("magFilter", static_cast<unsigned int>(b.magFilter), w.mAl); - } - - if (b.minFilter != SamplerMinFilter::UNSET) { - obj.AddMember("minFilter", static_cast<unsigned int>(b.minFilter), w.mAl); - } - } - - inline void Write(Value& scene, Scene& s, AssetWriter& w) - { - AddRefsVector(scene, "nodes", s.nodes, w.mAl); - } - - inline void Write(Value& /*obj*/, Shader& /*b*/, AssetWriter& /*w*/) - { - - } - - inline void Write(Value& obj, Skin& b, AssetWriter& w) - { - /****************** jointNames *******************/ - Value vJointNames; - vJointNames.SetArray(); - vJointNames.Reserve(unsigned(b.jointNames.size()), w.mAl); - - for (size_t i = 0; i < unsigned(b.jointNames.size()); ++i) { - vJointNames.PushBack(b.jointNames[i]->index, w.mAl); - } - obj.AddMember("joints", vJointNames, w.mAl); - - if (b.bindShapeMatrix.isPresent) { - Value val; - obj.AddMember("bindShapeMatrix", MakeValue(val, b.bindShapeMatrix.value, w.mAl).Move(), w.mAl); - } - - if (b.inverseBindMatrices) { - obj.AddMember("inverseBindMatrices", b.inverseBindMatrices->index, w.mAl); - } - - } - - inline void Write(Value& obj, Texture& tex, AssetWriter& w) - { - if (tex.source) { - obj.AddMember("source", tex.source->index, w.mAl); - } - if (tex.sampler) { - obj.AddMember("sampler", tex.sampler->index, w.mAl); - } - } - - - inline AssetWriter::AssetWriter(Asset& a) - : mDoc() - , mAsset(a) - , mAl(mDoc.GetAllocator()) - { - mDoc.SetObject(); - - WriteMetadata(); - WriteExtensionsUsed(); - - // Dump the contents of the dictionaries - for (size_t i = 0; i < a.mDicts.size(); ++i) { - a.mDicts[i]->WriteObjects(*this); - } - - // Add the target scene field - if (mAsset.scene) { - mDoc.AddMember("scene", mAsset.scene->index, mAl); - } - - if(mAsset.extras) { - mDoc.AddMember("extras", *mAsset.extras, mAl); - } - } - - inline void AssetWriter::WriteFile(const char* path) - { - std::unique_ptr<IOStream> jsonOutFile(mAsset.OpenFile(path, "wt", true)); - - if (jsonOutFile == 0) { - throw DeadlyExportError("Could not open output file: " + std::string(path)); - } - - StringBuffer docBuffer; - - PrettyWriter<StringBuffer> writer(docBuffer); - if (!mDoc.Accept(writer)) { - throw DeadlyExportError("Failed to write scene data!"); - } - - if (jsonOutFile->Write(docBuffer.GetString(), docBuffer.GetSize(), 1) != 1) { - throw DeadlyExportError("Failed to write scene data!"); - } - - // Write buffer data to separate .bin files - for (unsigned int i = 0; i < mAsset.buffers.Size(); ++i) { - Ref<Buffer> b = mAsset.buffers.Get(i); - - std::string binPath = b->GetURI(); - - std::unique_ptr<IOStream> binOutFile(mAsset.OpenFile(binPath, "wb", true)); - - if (binOutFile == 0) { - throw DeadlyExportError("Could not open output file: " + binPath); - } - - if (b->byteLength > 0) { - if (binOutFile->Write(b->GetPointer(), b->byteLength, 1) != 1) { - throw DeadlyExportError("Failed to write binary file: " + binPath); - } - } - } - } - - inline void AssetWriter::WriteGLBFile(const char* path) - { - std::unique_ptr<IOStream> outfile(mAsset.OpenFile(path, "wb", true)); - - if (outfile == 0) { - throw DeadlyExportError("Could not open output file: " + std::string(path)); - } - - Ref<Buffer> bodyBuffer = mAsset.GetBodyBuffer(); - if (bodyBuffer->byteLength > 0) { - rapidjson::Value glbBodyBuffer; - glbBodyBuffer.SetObject(); - glbBodyBuffer.AddMember("byteLength", static_cast<uint64_t>(bodyBuffer->byteLength), mAl); - mDoc["buffers"].PushBack(glbBodyBuffer, mAl); - } - - // Padding with spaces as required by the spec - uint32_t padding = 0x20202020; - - // - // JSON chunk - // - - StringBuffer docBuffer; - Writer<StringBuffer> writer(docBuffer); - if (!mDoc.Accept(writer)) { - throw DeadlyExportError("Failed to write scene data!"); - } - - uint32_t jsonChunkLength = (docBuffer.GetSize() + 3) & ~3; // Round up to next multiple of 4 - auto paddingLength = jsonChunkLength - docBuffer.GetSize(); - - GLB_Chunk jsonChunk; - jsonChunk.chunkLength = jsonChunkLength; - jsonChunk.chunkType = ChunkType_JSON; - AI_SWAP4(jsonChunk.chunkLength); - - outfile->Seek(sizeof(GLB_Header), aiOrigin_SET); - if (outfile->Write(&jsonChunk, 1, sizeof(GLB_Chunk)) != sizeof(GLB_Chunk)) { - throw DeadlyExportError("Failed to write scene data header!"); - } - if (outfile->Write(docBuffer.GetString(), 1, docBuffer.GetSize()) != docBuffer.GetSize()) { - throw DeadlyExportError("Failed to write scene data!"); - } - if (paddingLength && outfile->Write(&padding, 1, paddingLength) != paddingLength) { - throw DeadlyExportError("Failed to write scene data padding!"); - } - - // - // Binary chunk - // - - int GLB_Chunk_count = 1; - uint32_t binaryChunkLength = 0; - if (bodyBuffer->byteLength > 0) { - binaryChunkLength = (bodyBuffer->byteLength + 3) & ~3; // Round up to next multiple of 4 - - auto curPaddingLength = binaryChunkLength - bodyBuffer->byteLength; - ++GLB_Chunk_count; - - GLB_Chunk binaryChunk; - binaryChunk.chunkLength = binaryChunkLength; - binaryChunk.chunkType = ChunkType_BIN; - AI_SWAP4(binaryChunk.chunkLength); - - size_t bodyOffset = sizeof(GLB_Header) + sizeof(GLB_Chunk) + jsonChunk.chunkLength; - outfile->Seek(bodyOffset, aiOrigin_SET); - if (outfile->Write(&binaryChunk, 1, sizeof(GLB_Chunk)) != sizeof(GLB_Chunk)) { - throw DeadlyExportError("Failed to write body data header!"); - } - if (outfile->Write(bodyBuffer->GetPointer(), 1, bodyBuffer->byteLength) != bodyBuffer->byteLength) { - throw DeadlyExportError("Failed to write body data!"); - } - if (curPaddingLength && outfile->Write(&padding, 1, paddingLength) != paddingLength) { - throw DeadlyExportError("Failed to write body data padding!"); - } - } - - // - // Header - // - - GLB_Header header; - memcpy(header.magic, AI_GLB_MAGIC_NUMBER, sizeof(header.magic)); - - header.version = 2; - AI_SWAP4(header.version); - - header.length = uint32_t(sizeof(GLB_Header) + GLB_Chunk_count * sizeof(GLB_Chunk) + jsonChunkLength + binaryChunkLength); - AI_SWAP4(header.length); - - outfile->Seek(0, aiOrigin_SET); - if (outfile->Write(&header, 1, sizeof(GLB_Header)) != sizeof(GLB_Header)) { - throw DeadlyExportError("Failed to write the header!"); - } - } - - inline void AssetWriter::WriteMetadata() - { - Value asset; - asset.SetObject(); - asset.AddMember("version", Value(mAsset.asset.version, mAl).Move(), mAl); - asset.AddMember("generator", Value(mAsset.asset.generator, mAl).Move(), mAl); - if (!mAsset.asset.copyright.empty()) - asset.AddMember("copyright", Value(mAsset.asset.copyright, mAl).Move(), mAl); - mDoc.AddMember("asset", asset, mAl); - } - - inline void AssetWriter::WriteExtensionsUsed() - { - Value exts; - exts.SetArray(); - { - // This is used to export pbrSpecularGlossiness materials with GLTF 2. - if (this->mAsset.extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - exts.PushBack(StringRef("KHR_materials_pbrSpecularGlossiness"), mAl); - } - - if (this->mAsset.extensionsUsed.KHR_materials_unlit) { - exts.PushBack(StringRef("KHR_materials_unlit"), mAl); - } - - if (this->mAsset.extensionsUsed.KHR_materials_sheen) { - exts.PushBack(StringRef("KHR_materials_sheen"), mAl); - } - - if (this->mAsset.extensionsUsed.KHR_materials_clearcoat) { - exts.PushBack(StringRef("KHR_materials_clearcoat"), mAl); - } - - if (this->mAsset.extensionsUsed.KHR_materials_transmission) { - exts.PushBack(StringRef("KHR_materials_transmission"), mAl); - } - - if (this->mAsset.extensionsUsed.KHR_materials_volume) { - exts.PushBack(StringRef("KHR_materials_volume"), mAl); - } - - if (this->mAsset.extensionsUsed.KHR_materials_ior) { - exts.PushBack(StringRef("KHR_materials_ior"), mAl); - } - - if (this->mAsset.extensionsUsed.FB_ngon_encoding) { - exts.PushBack(StringRef("FB_ngon_encoding"), mAl); - } - - if (this->mAsset.extensionsUsed.KHR_texture_basisu) { - exts.PushBack(StringRef("KHR_texture_basisu"), mAl); - } - } - - if (!exts.Empty()) - mDoc.AddMember("extensionsUsed", exts, mAl); - - //basisu extensionRequired - Value extsReq; - extsReq.SetArray(); - if (this->mAsset.extensionsUsed.KHR_texture_basisu) { - extsReq.PushBack(StringRef("KHR_texture_basisu"), mAl); - mDoc.AddMember("extensionsRequired", extsReq, mAl); - } - } - - template<class T> - void AssetWriter::WriteObjects(LazyDict<T>& d) - { - if (d.mObjs.empty()) return; - - Value* container = &mDoc; - const char* context = "Document"; - - if (d.mExtId) { - Value* exts = FindObject(mDoc, "extensions"); - if (nullptr != exts) { - mDoc.AddMember("extensions", Value().SetObject().Move(), mDoc.GetAllocator()); - exts = FindObject(mDoc, "extensions"); - } - - container = FindObjectInContext(*exts, d.mExtId, "extensions"); - if (nullptr != container) { - exts->AddMember(StringRef(d.mExtId), Value().SetObject().Move(), mDoc.GetAllocator()); - container = FindObjectInContext(*exts, d.mExtId, "extensions"); - context = d.mExtId; - } - } - - Value *dict = FindArrayInContext(*container, d.mDictId, context); - if (nullptr == dict) { - container->AddMember(StringRef(d.mDictId), Value().SetArray().Move(), mDoc.GetAllocator()); - dict = FindArrayInContext(*container, d.mDictId, context); - if (nullptr == dict) { - return; - } - } - - for (size_t i = 0; i < d.mObjs.size(); ++i) { - if (d.mObjs[i]->IsSpecial()) { - continue; - } - - Value obj; - obj.SetObject(); - - if (!d.mObjs[i]->name.empty()) { - obj.AddMember("name", StringRef(d.mObjs[i]->name.c_str()), mAl); - } - - Write(obj, *d.mObjs[i], *this); - - dict->PushBack(obj, mAl); - } - } - - template<class T> - void WriteLazyDict(LazyDict<T>& d, AssetWriter& w) - { - w.WriteObjects(d); - } - -} diff --git a/libs/assimp/code/AssetLib/glTF2/glTF2Exporter.cpp b/libs/assimp/code/AssetLib/glTF2/glTF2Exporter.cpp deleted file mode 100644 index ffd8d22..0000000 --- a/libs/assimp/code/AssetLib/glTF2/glTF2Exporter.cpp +++ /dev/null @@ -1,1542 +0,0 @@ -/* -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. - ----------------------------------------------------------------------- -*/ -#ifndef ASSIMP_BUILD_NO_EXPORT -#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER - -#include "AssetLib/glTF2/glTF2Exporter.h" -#include "AssetLib/glTF2/glTF2AssetWriter.h" -#include "PostProcessing/SplitLargeMeshes.h" - -#include <assimp/ByteSwapper.h> -#include <assimp/Exceptional.h> -#include <assimp/SceneCombiner.h> -#include <assimp/StringComparison.h> -#include <assimp/commonMetaData.h> -#include <assimp/material.h> -#include <assimp/scene.h> -#include <assimp/version.h> -#include <assimp/Exporter.hpp> -#include <assimp/IOSystem.hpp> - -// Header files, standard library. -#include <cinttypes> -#include <limits> -#include <memory> - -using namespace rapidjson; - -using namespace Assimp; -using namespace glTF2; - -namespace Assimp { - -// ------------------------------------------------------------------------------------------------ -// Worker function for exporting a scene to GLTF. Prototyped and registered in Exporter.cpp -void ExportSceneGLTF2(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties *pProperties) { - // invoke the exporter - glTF2Exporter exporter(pFile, pIOSystem, pScene, pProperties, false); -} - -// ------------------------------------------------------------------------------------------------ -// Worker function for exporting a scene to GLB. Prototyped and registered in Exporter.cpp -void ExportSceneGLB2(const char *pFile, IOSystem *pIOSystem, const aiScene *pScene, const ExportProperties *pProperties) { - // invoke the exporter - glTF2Exporter exporter(pFile, pIOSystem, pScene, pProperties, true); -} - -} // end of namespace Assimp - -glTF2Exporter::glTF2Exporter(const char *filename, IOSystem *pIOSystem, const aiScene *pScene, - const ExportProperties *pProperties, bool isBinary) : - mFilename(filename), mIOSystem(pIOSystem), mScene(pScene), mProperties(pProperties), mAsset(new Asset(pIOSystem)) { - // Always on as our triangulation process is aware of this type of encoding - mAsset->extensionsUsed.FB_ngon_encoding = true; - - if (isBinary) { - mAsset->SetAsBinary(); - } - - ExportMetadata(); - - ExportMaterials(); - - if (mScene->mRootNode) { - ExportNodeHierarchy(mScene->mRootNode); - } - - ExportMeshes(); - MergeMeshes(); - - ExportScene(); - - ExportAnimations(); - - // export extras - if (mProperties->HasPropertyCallback("extras")) { - std::function<void *(void *)> ExportExtras = mProperties->GetPropertyCallback("extras"); - mAsset->extras = (rapidjson::Value *)ExportExtras(0); - } - - AssetWriter writer(*mAsset); - - if (isBinary) { - writer.WriteGLBFile(filename); - } else { - writer.WriteFile(filename); - } -} - -glTF2Exporter::~glTF2Exporter() { - // empty -} - -/* - * Copy a 4x4 matrix from struct aiMatrix to typedef mat4. - * Also converts from row-major to column-major storage. - */ -static void CopyValue(const aiMatrix4x4 &v, mat4 &o) { - o[0] = v.a1; - o[1] = v.b1; - o[2] = v.c1; - o[3] = v.d1; - o[4] = v.a2; - o[5] = v.b2; - o[6] = v.c2; - o[7] = v.d2; - o[8] = v.a3; - o[9] = v.b3; - o[10] = v.c3; - o[11] = v.d3; - o[12] = v.a4; - o[13] = v.b4; - o[14] = v.c4; - o[15] = v.d4; -} - -static void CopyValue(const aiMatrix4x4 &v, aiMatrix4x4 &o) { - memcpy(&o, &v, sizeof(aiMatrix4x4)); -} - -static void IdentityMatrix4(mat4 &o) { - o[0] = 1; - o[1] = 0; - o[2] = 0; - o[3] = 0; - o[4] = 0; - o[5] = 1; - o[6] = 0; - o[7] = 0; - o[8] = 0; - o[9] = 0; - o[10] = 1; - o[11] = 0; - o[12] = 0; - o[13] = 0; - o[14] = 0; - o[15] = 1; -} - -static bool IsBoneWeightFitted(vec4 &weight) { - return weight[0] + weight[1] + weight[2] + weight[3] >= 1.f; -} - -static int FitBoneWeight(vec4 &weight, float value) { - int i = 0; - for (; i < 4; ++i) { - if (weight[i] < value) { - weight[i] = value; - return i; - } - } - - return -1; -} - -template <typename T> -void SetAccessorRange(Ref<Accessor> acc, void *data, size_t count, - unsigned int numCompsIn, unsigned int numCompsOut) { - ai_assert(numCompsOut <= numCompsIn); - - // Allocate and initialize with large values. - for (unsigned int i = 0; i < numCompsOut; i++) { - acc->min.push_back(std::numeric_limits<double>::max()); - acc->max.push_back(-std::numeric_limits<double>::max()); - } - - size_t totalComps = count * numCompsIn; - T *buffer_ptr = static_cast<T *>(data); - T *buffer_end = buffer_ptr + totalComps; - - // Search and set extreme values. - for (; buffer_ptr < buffer_end; buffer_ptr += numCompsIn) { - for (unsigned int j = 0; j < numCompsOut; j++) { - double valueTmp = buffer_ptr[j]; - - // Gracefully tolerate rogue NaN's in buffer data - // Any NaNs/Infs introduced in accessor bounds will end up in - // document and prevent rapidjson from writing out valid JSON - if (!std::isfinite(valueTmp)) { - continue; - } - - if (valueTmp < acc->min[j]) { - acc->min[j] = valueTmp; - } - if (valueTmp > acc->max[j]) { - acc->max[j] = valueTmp; - } - } - } -} - -inline void SetAccessorRange(ComponentType compType, Ref<Accessor> acc, void *data, - size_t count, unsigned int numCompsIn, unsigned int numCompsOut) { - switch (compType) { - case ComponentType_SHORT: - SetAccessorRange<short>(acc, data, count, numCompsIn, numCompsOut); - return; - case ComponentType_UNSIGNED_SHORT: - SetAccessorRange<unsigned short>(acc, data, count, numCompsIn, numCompsOut); - return; - case ComponentType_UNSIGNED_INT: - SetAccessorRange<unsigned int>(acc, data, count, numCompsIn, numCompsOut); - return; - case ComponentType_FLOAT: - SetAccessorRange<float>(acc, data, count, numCompsIn, numCompsOut); - return; - case ComponentType_BYTE: - SetAccessorRange<int8_t>(acc, data, count, numCompsIn, numCompsOut); - return; - case ComponentType_UNSIGNED_BYTE: - SetAccessorRange<uint8_t>(acc, data, count, numCompsIn, numCompsOut); - return; - } -} - -// compute the (data-dataBase), store the non-zero data items -template <typename T> -size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn, unsigned int numCompsOut, void *&outputNZDiff, void *&outputNZIdx) { - std::vector<T> vNZDiff; - std::vector<unsigned short> vNZIdx; - size_t totalComps = count * numCompsIn; - T *bufferData_ptr = static_cast<T *>(data); - T *bufferData_end = bufferData_ptr + totalComps; - T *bufferBase_ptr = static_cast<T *>(dataBase); - - // Search and set extreme values. - for (short idx = 0; bufferData_ptr < bufferData_end; idx += 1, bufferData_ptr += numCompsIn) { - bool bNonZero = false; - - //for the data, check any component Non Zero - for (unsigned int j = 0; j < numCompsOut; j++) { - double valueData = bufferData_ptr[j]; - double valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0; - if ((valueData - valueBase) != 0) { - bNonZero = true; - break; - } - } - - //all zeros, continue - if (!bNonZero) - continue; - - //non zero, store the data - for (unsigned int j = 0; j < numCompsOut; j++) { - T valueData = bufferData_ptr[j]; - T valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0; - vNZDiff.push_back(valueData - valueBase); - } - vNZIdx.push_back(idx); - } - - //avoid all-0, put 1 item - if (vNZDiff.size() == 0) { - for (unsigned int j = 0; j < numCompsOut; j++) - vNZDiff.push_back(0); - vNZIdx.push_back(0); - } - - //process data - outputNZDiff = new T[vNZDiff.size()]; - memcpy(outputNZDiff, vNZDiff.data(), vNZDiff.size() * sizeof(T)); - - outputNZIdx = new unsigned short[vNZIdx.size()]; - memcpy(outputNZIdx, vNZIdx.data(), vNZIdx.size() * sizeof(unsigned short)); - return vNZIdx.size(); -} - -inline size_t NZDiff(ComponentType compType, void *data, void *dataBase, size_t count, unsigned int numCompsIn, unsigned int numCompsOut, void *&nzDiff, void *&nzIdx) { - switch (compType) { - case ComponentType_SHORT: - return NZDiff<short>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); - case ComponentType_UNSIGNED_SHORT: - return NZDiff<unsigned short>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); - case ComponentType_UNSIGNED_INT: - return NZDiff<unsigned int>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); - case ComponentType_FLOAT: - return NZDiff<float>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); - case ComponentType_BYTE: - return NZDiff<int8_t>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); - case ComponentType_UNSIGNED_BYTE: - return NZDiff<uint8_t>(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); - } - return 0; -} - -inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffer> &buffer, - size_t count, void *data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, BufferViewTarget target = BufferViewTarget_NONE, void *dataBase = 0) { - if (!count || !data) { - return Ref<Accessor>(); - } - - unsigned int numCompsIn = AttribType::GetNumComponents(typeIn); - unsigned int numCompsOut = AttribType::GetNumComponents(typeOut); - unsigned int bytesPerComp = ComponentTypeSize(compType); - - // accessor - Ref<Accessor> acc = a.accessors.Create(a.FindUniqueID(meshName, "accessor")); - - // if there is a basic data vector - if (dataBase) { - size_t base_offset = buffer->byteLength; - size_t base_padding = base_offset % bytesPerComp; - base_offset += base_padding; - size_t base_length = count * numCompsOut * bytesPerComp; - buffer->Grow(base_length + base_padding); - - Ref<BufferView> bv = a.bufferViews.Create(a.FindUniqueID(meshName, "view")); - bv->buffer = buffer; - bv->byteOffset = base_offset; - bv->byteLength = base_length; //! The target that the WebGL buffer should be bound to. - bv->byteStride = 0; - bv->target = target; - acc->bufferView = bv; - acc->WriteData(count, dataBase, numCompsIn * bytesPerComp); - } - acc->byteOffset = 0; - acc->componentType = compType; - acc->count = count; - acc->type = typeOut; - - if (data) { - void *nzDiff = 0, *nzIdx = 0; - size_t nzCount = NZDiff(compType, data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); - acc->sparse.reset(new Accessor::Sparse); - acc->sparse->count = nzCount; - - //indices - unsigned int bytesPerIdx = sizeof(unsigned short); - size_t indices_offset = buffer->byteLength; - size_t indices_padding = indices_offset % bytesPerIdx; - indices_offset += indices_padding; - size_t indices_length = nzCount * 1 * bytesPerIdx; - buffer->Grow(indices_length + indices_padding); - - Ref<BufferView> indicesBV = a.bufferViews.Create(a.FindUniqueID(meshName, "view")); - indicesBV->buffer = buffer; - indicesBV->byteOffset = indices_offset; - indicesBV->byteLength = indices_length; - indicesBV->byteStride = 0; - acc->sparse->indices = indicesBV; - acc->sparse->indicesType = ComponentType_UNSIGNED_SHORT; - acc->sparse->indicesByteOffset = 0; - acc->WriteSparseIndices(nzCount, nzIdx, 1 * bytesPerIdx); - - //values - size_t values_offset = buffer->byteLength; - size_t values_padding = values_offset % bytesPerComp; - values_offset += values_padding; - size_t values_length = nzCount * numCompsOut * bytesPerComp; - buffer->Grow(values_length + values_padding); - - Ref<BufferView> valuesBV = a.bufferViews.Create(a.FindUniqueID(meshName, "view")); - valuesBV->buffer = buffer; - valuesBV->byteOffset = values_offset; - valuesBV->byteLength = values_length; - valuesBV->byteStride = 0; - acc->sparse->values = valuesBV; - acc->sparse->valuesByteOffset = 0; - acc->WriteSparseValues(nzCount, nzDiff, numCompsIn * bytesPerComp); - - //clear - delete[](char *) nzDiff; - delete[](char *) nzIdx; - } - return acc; -} -inline Ref<Accessor> ExportData(Asset &a, std::string &meshName, Ref<Buffer> &buffer, - size_t count, void *data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, BufferViewTarget target = BufferViewTarget_NONE) { - if (!count || !data) { - return Ref<Accessor>(); - } - - unsigned int numCompsIn = AttribType::GetNumComponents(typeIn); - unsigned int numCompsOut = AttribType::GetNumComponents(typeOut); - unsigned int bytesPerComp = ComponentTypeSize(compType); - - size_t offset = buffer->byteLength; - // make sure offset is correctly byte-aligned, as required by spec - size_t padding = offset % bytesPerComp; - offset += padding; - size_t length = count * numCompsOut * bytesPerComp; - buffer->Grow(length + padding); - - // bufferView - Ref<BufferView> bv = a.bufferViews.Create(a.FindUniqueID(meshName, "view")); - bv->buffer = buffer; - bv->byteOffset = offset; - bv->byteLength = length; //! The target that the WebGL buffer should be bound to. - bv->byteStride = 0; - bv->target = target; - - // accessor - Ref<Accessor> acc = a.accessors.Create(a.FindUniqueID(meshName, "accessor")); - acc->bufferView = bv; - acc->byteOffset = 0; - acc->componentType = compType; - acc->count = count; - acc->type = typeOut; - - // calculate min and max values - SetAccessorRange(compType, acc, data, count, numCompsIn, numCompsOut); - - // copy the data - acc->WriteData(count, data, numCompsIn * bytesPerComp); - - return acc; -} - -inline void SetSamplerWrap(SamplerWrap &wrap, aiTextureMapMode map) { - switch (map) { - case aiTextureMapMode_Clamp: - wrap = SamplerWrap::Clamp_To_Edge; - break; - case aiTextureMapMode_Mirror: - wrap = SamplerWrap::Mirrored_Repeat; - break; - case aiTextureMapMode_Wrap: - case aiTextureMapMode_Decal: - default: - wrap = SamplerWrap::Repeat; - break; - }; -} - -void glTF2Exporter::GetTexSampler(const aiMaterial &mat, Ref<Texture> texture, aiTextureType tt, unsigned int slot) { - aiString aId; - std::string id; - if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { - id = aId.C_Str(); - } - - if (Ref<Sampler> ref = mAsset->samplers.Get(id.c_str())) { - texture->sampler = ref; - } else { - id = mAsset->FindUniqueID(id, "sampler"); - - texture->sampler = mAsset->samplers.Create(id.c_str()); - - aiTextureMapMode mapU, mapV; - SamplerMagFilter filterMag; - SamplerMinFilter filterMin; - - if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int *)&mapU) == AI_SUCCESS) { - SetSamplerWrap(texture->sampler->wrapS, mapU); - } - - if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int *)&mapV) == AI_SUCCESS) { - SetSamplerWrap(texture->sampler->wrapT, mapV); - } - - if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int *)&filterMag) == AI_SUCCESS) { - texture->sampler->magFilter = filterMag; - } - - if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int *)&filterMin) == AI_SUCCESS) { - texture->sampler->minFilter = filterMin; - } - - aiString name; - if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { - texture->sampler->name = name.C_Str(); - } - } -} - -void glTF2Exporter::GetMatTexProp(const aiMaterial &mat, unsigned int &prop, const char *propName, aiTextureType tt, unsigned int slot) { - std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - - mat.Get(textureKey.c_str(), tt, slot, prop); -} - -void glTF2Exporter::GetMatTexProp(const aiMaterial &mat, float &prop, const char *propName, aiTextureType tt, unsigned int slot) { - std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - - mat.Get(textureKey.c_str(), tt, slot, prop); -} - -void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot = 0) { - if (mat.GetTextureCount(tt) == 0) { - return; - } - - aiString tex; - - // Read texcoord (UV map index) - mat.Get(AI_MATKEY_UVWSRC(tt, slot), texCoord); - - if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { - std::string path = tex.C_Str(); - - if (path.size() > 0) { - std::map<std::string, unsigned int>::iterator it = mTexturesByPath.find(path); - if (it != mTexturesByPath.end()) { - texture = mAsset->textures.Get(it->second); - } - - bool useBasisUniversal = false; - if (!texture) { - std::string texId = mAsset->FindUniqueID("", "texture"); - texture = mAsset->textures.Create(texId); - mTexturesByPath[path] = texture.GetIndex(); - - std::string imgId = mAsset->FindUniqueID("", "image"); - texture->source = mAsset->images.Create(imgId); - - const aiTexture *curTex = mScene->GetEmbeddedTexture(path.c_str()); - if (curTex != nullptr) { // embedded - texture->source->name = curTex->mFilename.C_Str(); - - //basisu: embedded ktx2, bu - if (curTex->achFormatHint[0]) { - std::string mimeType = "image/"; - if (memcmp(curTex->achFormatHint, "jpg", 3) == 0) - mimeType += "jpeg"; - else if (memcmp(curTex->achFormatHint, "ktx", 3) == 0) { - useBasisUniversal = true; - mimeType += "ktx"; - } else if (memcmp(curTex->achFormatHint, "kx2", 3) == 0) { - useBasisUniversal = true; - mimeType += "ktx2"; - } else if (memcmp(curTex->achFormatHint, "bu", 2) == 0) { - useBasisUniversal = true; - mimeType += "basis"; - } else - mimeType += curTex->achFormatHint; - texture->source->mimeType = mimeType; - } - - // The asset has its own buffer, see Image::SetData - //basisu: "image/ktx2", "image/basis" as is - texture->source->SetData(reinterpret_cast<uint8_t *>(curTex->pcData), curTex->mWidth, *mAsset); - } else { - texture->source->uri = path; - if (texture->source->uri.find(".ktx") != std::string::npos || - texture->source->uri.find(".basis") != std::string::npos) { - useBasisUniversal = true; - } - } - - //basisu - if (useBasisUniversal) { - mAsset->extensionsUsed.KHR_texture_basisu = true; - mAsset->extensionsRequired.KHR_texture_basisu = true; - } - - GetTexSampler(mat, texture, tt, slot); - } - } - } -} - -void glTF2Exporter::GetMatTex(const aiMaterial &mat, TextureInfo &prop, aiTextureType tt, unsigned int slot = 0) { - Ref<Texture> &texture = prop.texture; - GetMatTex(mat, texture, prop.texCoord, tt, slot); -} - -void glTF2Exporter::GetMatTex(const aiMaterial &mat, NormalTextureInfo &prop, aiTextureType tt, unsigned int slot = 0) { - Ref<Texture> &texture = prop.texture; - - GetMatTex(mat, texture, prop.texCoord, tt, slot); - - if (texture) { - //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); - GetMatTexProp(mat, prop.scale, "scale", tt, slot); - } -} - -void glTF2Exporter::GetMatTex(const aiMaterial &mat, OcclusionTextureInfo &prop, aiTextureType tt, unsigned int slot = 0) { - Ref<Texture> &texture = prop.texture; - - GetMatTex(mat, texture, prop.texCoord, tt, slot); - - if (texture) { - //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); - GetMatTexProp(mat, prop.strength, "strength", tt, slot); - } -} - -aiReturn glTF2Exporter::GetMatColor(const aiMaterial &mat, vec4 &prop, const char *propName, int type, int idx) const { - aiColor4D col; - aiReturn result = mat.Get(propName, type, idx, col); - - if (result == AI_SUCCESS) { - prop[0] = col.r; - prop[1] = col.g; - prop[2] = col.b; - prop[3] = col.a; - } - - return result; -} - -aiReturn glTF2Exporter::GetMatColor(const aiMaterial &mat, vec3 &prop, const char *propName, int type, int idx) const { - aiColor3D col; - aiReturn result = mat.Get(propName, type, idx, col); - - if (result == AI_SUCCESS) { - prop[0] = col.r; - prop[1] = col.g; - prop[2] = col.b; - } - - return result; -} - -bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) { - bool result = false; - // If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension - // NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular - - if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) { - result = true; - } else { - // Don't have explicit glossiness, convert from pbr roughness or legacy shininess - float shininess; - if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way - } else if (mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; - } - } - - if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { - result = true; - } - // Add any appropriate textures - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - - result = result || pbrSG.specularGlossinessTexture.texture; - - if (result) { - // Likely to always have diffuse - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - } - - return result; -} - -bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) { - // Return true if got any valid Sheen properties or textures - if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) { - return false; - } - - // Default Sheen color factor {0,0,0} disables Sheen, so do not export - if (sheen.sheenColorFactor == defaultSheenFactor) { - return false; - } - - mat.Get(AI_MATKEY_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); - - GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_SHEEN_COLOR_TEXTURE); - GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); - - return true; -} - -bool glTF2Exporter::GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat) { - if (mat.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor) != aiReturn_SUCCESS) { - return false; - } - - // Clearcoat factor of zero disables Clearcoat, so do not export - if (clearcoat.clearcoatFactor == 0.0f) - return false; - - mat.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); - - GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_CLEARCOAT_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); - - return true; -} - -bool glTF2Exporter::GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission) { - bool result = mat.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmission.transmissionFactor) == aiReturn_SUCCESS; - GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_TRANSMISSION_TEXTURE); - return result || transmission.transmissionTexture.texture; -} - -bool glTF2Exporter::GetMatVolume(const aiMaterial &mat, glTF2::MaterialVolume &volume) { - bool result = mat.Get(AI_MATKEY_VOLUME_THICKNESS_FACTOR, volume.thicknessFactor) != aiReturn_SUCCESS; - - GetMatTex(mat, volume.thicknessTexture, AI_MATKEY_VOLUME_THICKNESS_TEXTURE); - - result = result || mat.Get(AI_MATKEY_VOLUME_ATTENUATION_DISTANCE, volume.attenuationDistance); - result = result || GetMatColor(mat, volume.attenuationColor, AI_MATKEY_VOLUME_ATTENUATION_COLOR) != aiReturn_SUCCESS; - - // Valid if any of these properties are available - return result || volume.thicknessTexture.texture; -} - -bool glTF2Exporter::GetMatIOR(const aiMaterial &mat, glTF2::MaterialIOR &ior) { - return mat.Get(AI_MATKEY_REFRACTI, ior.ior) == aiReturn_SUCCESS; -} - -void glTF2Exporter::ExportMaterials() { - aiString aiName; - for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { - ai_assert(mScene->mMaterials[i] != nullptr); - - const aiMaterial &mat = *(mScene->mMaterials[i]); - - std::string id = "material_" + ai_to_string(i); - - Ref<Material> m = mAsset->materials.Create(id); - - std::string name; - if (mat.Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { - name = aiName.C_Str(); - } - name = mAsset->FindUniqueID(name, "material"); - - m->name = name; - - GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR); - - if (!m->pbrMetallicRoughness.baseColorTexture.texture) { - //if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture - GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE); - } - - GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - - if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) { - // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material. - //a fallback to any diffuse color should be used instead - GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); - } - - if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { - //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 - m->pbrMetallicRoughness.metallicFactor = 0; - } - - // get roughness if source is gltf2 file - if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { - // otherwise, try to derive and convert from specular + shininess values - aiColor4D specularColor; - ai_real shininess; - - if (mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - // convert specular color to luminance - float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f; - //normalize shininess (assuming max is 1000) with an inverse exponentional curve - float normalizedShininess = std::sqrt(shininess / 1000); - - //clamp the shininess value between 0 and 1 - normalizedShininess = std::min(std::max(normalizedShininess, 0.0f), 1.0f); - // low specular intensity values should produce a rough material even if shininess is high. - normalizedShininess = normalizedShininess * specularIntensity; - - m->pbrMetallicRoughness.roughnessFactor = 1 - normalizedShininess; - } - } - - GetMatTex(mat, m->normalTexture, aiTextureType_NORMALS); - GetMatTex(mat, m->occlusionTexture, aiTextureType_LIGHTMAP); - GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE); - GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE); - - mat.Get(AI_MATKEY_TWOSIDED, m->doubleSided); - mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); - - float opacity; - aiString alphaMode; - - if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { - if (opacity < 1) { - m->alphaMode = "BLEND"; - m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; - } - } - if (mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { - m->alphaMode = alphaMode.C_Str(); - } - - { - // KHR_materials_pbrSpecularGlossiness extension - // NOTE: This extension is being considered for deprecation (Dec 2020) - PbrSpecularGlossiness pbrSG; - if (GetMatSpecGloss(mat, pbrSG)) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - m->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG); - } - } - - // glTFv2 is either PBR or Unlit - aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; - mat.Get(AI_MATKEY_SHADING_MODEL, shadingMode); - if (shadingMode == aiShadingMode_Unlit) { - mAsset->extensionsUsed.KHR_materials_unlit = true; - m->unlit = true; - } else { - // These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness - if (!m->pbrSpecularGlossiness.isPresent) { - // Sheen - MaterialSheen sheen; - if (GetMatSheen(mat, sheen)) { - mAsset->extensionsUsed.KHR_materials_sheen = true; - m->materialSheen = Nullable<MaterialSheen>(sheen); - } - - MaterialClearcoat clearcoat; - if (GetMatClearcoat(mat, clearcoat)) { - mAsset->extensionsUsed.KHR_materials_clearcoat = true; - m->materialClearcoat = Nullable<MaterialClearcoat>(clearcoat); - } - - MaterialTransmission transmission; - if (GetMatTransmission(mat, transmission)) { - mAsset->extensionsUsed.KHR_materials_transmission = true; - m->materialTransmission = Nullable<MaterialTransmission>(transmission); - } - - MaterialVolume volume; - if (GetMatVolume(mat, volume)) { - mAsset->extensionsUsed.KHR_materials_volume = true; - m->materialVolume = Nullable<MaterialVolume>(volume); - } - - MaterialIOR ior; - if (GetMatIOR(mat, ior)) { - mAsset->extensionsUsed.KHR_materials_ior = true; - m->materialIOR = Nullable<MaterialIOR>(ior); - } - } - } - } -} - -/* - * Search through node hierarchy and find the node containing the given meshID. - * Returns true on success, and false otherwise. - */ -bool FindMeshNode(Ref<Node> &nodeIn, Ref<Node> &meshNode, const std::string &meshID) { - for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { - if (meshID.compare(nodeIn->meshes[i]->id) == 0) { - meshNode = nodeIn; - return true; - } - } - - for (unsigned int i = 0; i < nodeIn->children.size(); ++i) { - if (FindMeshNode(nodeIn->children[i], meshNode, meshID)) { - return true; - } - } - - return false; -} - -/* - * Find the root joint of the skeleton. - * Starts will any joint node and traces up the tree, - * until a parent is found that does not have a jointName. - * Returns the first parent Ref<Node> found that does not have a jointName. - */ -Ref<Node> FindSkeletonRootJoint(Ref<Skin> &skinRef) { - Ref<Node> startNodeRef; - Ref<Node> parentNodeRef; - - // Arbitrarily use the first joint to start the search. - startNodeRef = skinRef->jointNames[0]; - parentNodeRef = skinRef->jointNames[0]; - - do { - startNodeRef = parentNodeRef; - parentNodeRef = startNodeRef->parent; - } while (!parentNodeRef->jointName.empty()); - - return parentNodeRef; -} - -void ExportSkin(Asset &mAsset, const aiMesh *aimesh, Ref<Mesh> &meshRef, Ref<Buffer> &bufferRef, Ref<Skin> &skinRef, - std::vector<aiMatrix4x4> &inverseBindMatricesData) { - if (aimesh->mNumBones < 1) { - return; - } - - // Store the vertex joint and weight data. - const size_t NumVerts(aimesh->mNumVertices); - vec4 *vertexJointData = new vec4[NumVerts]; - vec4 *vertexWeightData = new vec4[NumVerts]; - int *jointsPerVertex = new int[NumVerts]; - for (size_t i = 0; i < NumVerts; ++i) { - jointsPerVertex[i] = 0; - for (size_t j = 0; j < 4; ++j) { - vertexJointData[i][j] = 0; - vertexWeightData[i][j] = 0; - } - } - - for (unsigned int idx_bone = 0; idx_bone < aimesh->mNumBones; ++idx_bone) { - const aiBone *aib = aimesh->mBones[idx_bone]; - - // aib->mName =====> skinRef->jointNames - // Find the node with id = mName. - Ref<Node> nodeRef = mAsset.nodes.Get(aib->mName.C_Str()); - nodeRef->jointName = nodeRef->name; - - unsigned int jointNamesIndex = 0; - bool addJointToJointNames = true; - for (unsigned int idx_joint = 0; idx_joint < skinRef->jointNames.size(); ++idx_joint) { - if (skinRef->jointNames[idx_joint]->jointName.compare(nodeRef->jointName) == 0) { - addJointToJointNames = false; - jointNamesIndex = idx_joint; - } - } - - if (addJointToJointNames) { - skinRef->jointNames.push_back(nodeRef); - - // aib->mOffsetMatrix =====> skinRef->inverseBindMatrices - aiMatrix4x4 tmpMatrix4; - CopyValue(aib->mOffsetMatrix, tmpMatrix4); - inverseBindMatricesData.push_back(tmpMatrix4); - jointNamesIndex = static_cast<unsigned int>(inverseBindMatricesData.size() - 1); - } - - // aib->mWeights =====> vertexWeightData - for (unsigned int idx_weights = 0; idx_weights < aib->mNumWeights; ++idx_weights) { - unsigned int vertexId = aib->mWeights[idx_weights].mVertexId; - float vertWeight = aib->mWeights[idx_weights].mWeight; - - // A vertex can only have at most four joint weights, which ideally sum up to 1 - if (IsBoneWeightFitted(vertexWeightData[vertexId])) { - continue; - } - if (jointsPerVertex[vertexId] > 3) { - int boneIndexFitted = FitBoneWeight(vertexWeightData[vertexId], vertWeight); - if (boneIndexFitted != -1) { - vertexJointData[vertexId][boneIndexFitted] = static_cast<float>(jointNamesIndex); - } - }else { - vertexJointData[vertexId][jointsPerVertex[vertexId]] = static_cast<float>(jointNamesIndex); - vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight; - - jointsPerVertex[vertexId] += 1; - } - } - - } // End: for-loop mNumMeshes - - Mesh::Primitive &p = meshRef->primitives.back(); - Ref<Accessor> vertexJointAccessor = ExportData(mAsset, skinRef->id, bufferRef, aimesh->mNumVertices, - vertexJointData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT); - if (vertexJointAccessor) { - size_t offset = vertexJointAccessor->bufferView->byteOffset; - size_t bytesLen = vertexJointAccessor->bufferView->byteLength; - unsigned int s_bytesPerComp = ComponentTypeSize(ComponentType_UNSIGNED_SHORT); - unsigned int bytesPerComp = ComponentTypeSize(vertexJointAccessor->componentType); - size_t s_bytesLen = bytesLen * s_bytesPerComp / bytesPerComp; - Ref<Buffer> buf = vertexJointAccessor->bufferView->buffer; - uint8_t *arrys = new uint8_t[bytesLen]; - unsigned int i = 0; - for (unsigned int j = 0; j < bytesLen; j += bytesPerComp) { - size_t len_p = offset + j; - float f_value = *(float *)&buf->GetPointer()[len_p]; - unsigned short c = static_cast<unsigned short>(f_value); - memcpy(&arrys[i * s_bytesPerComp], &c, s_bytesPerComp); - ++i; - } - buf->ReplaceData_joint(offset, bytesLen, arrys, bytesLen); - vertexJointAccessor->componentType = ComponentType_UNSIGNED_SHORT; - vertexJointAccessor->bufferView->byteLength = s_bytesLen; - - p.attributes.joint.push_back(vertexJointAccessor); - delete[] arrys; - } - - Ref<Accessor> vertexWeightAccessor = ExportData(mAsset, skinRef->id, bufferRef, aimesh->mNumVertices, - vertexWeightData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT); - if (vertexWeightAccessor) { - p.attributes.weight.push_back(vertexWeightAccessor); - } - delete[] jointsPerVertex; - delete[] vertexWeightData; - delete[] vertexJointData; -} - -void glTF2Exporter::ExportMeshes() { - typedef decltype(aiFace::mNumIndices) IndicesType; - - std::string fname = std::string(mFilename); - std::string bufferIdPrefix = fname.substr(0, fname.rfind(".gltf")); - std::string bufferId = mAsset->FindUniqueID("", bufferIdPrefix.c_str()); - - Ref<Buffer> b = mAsset->GetBodyBuffer(); - if (!b) { - b = mAsset->buffers.Create(bufferId); - } - - //---------------------------------------- - // Initialize variables for the skin - bool createSkin = false; - for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) { - const aiMesh *aim = mScene->mMeshes[idx_mesh]; - if (aim->HasBones()) { - createSkin = true; - break; - } - } - - Ref<Skin> skinRef; - std::string skinName = mAsset->FindUniqueID("skin", "skin"); - std::vector<aiMatrix4x4> inverseBindMatricesData; - if (createSkin) { - skinRef = mAsset->skins.Create(skinName); - skinRef->name = skinName; - } - //---------------------------------------- - - for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) { - const aiMesh *aim = mScene->mMeshes[idx_mesh]; - - std::string name = aim->mName.C_Str(); - - std::string meshId = mAsset->FindUniqueID(name, "mesh"); - Ref<Mesh> m = mAsset->meshes.Create(meshId); - m->primitives.resize(1); - Mesh::Primitive &p = m->primitives.back(); - - m->name = name; - - p.material = mAsset->materials.Get(aim->mMaterialIndex); - p.ngonEncoded = (aim->mPrimitiveTypes & aiPrimitiveType_NGONEncodingFlag) != 0; - - /******************* Vertices ********************/ - Ref<Accessor> v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3, - AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER); - if (v) { - p.attributes.position.push_back(v); - } - - /******************** Normals ********************/ - // Normalize all normals as the validator can emit a warning otherwise - if (nullptr != aim->mNormals) { - for (auto i = 0u; i < aim->mNumVertices; ++i) { - aim->mNormals[i].NormalizeSafe(); - } - } - - Ref<Accessor> n = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mNormals, AttribType::VEC3, - AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER); - if (n) { - p.attributes.normal.push_back(n); - } - - /************** Texture coordinates **************/ - for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { - if (!aim->HasTextureCoords(i)) { - continue; - } - - // Flip UV y coords - if (aim->mNumUVComponents[i] > 1) { - for (unsigned int j = 0; j < aim->mNumVertices; ++j) { - aim->mTextureCoords[i][j].y = 1 - aim->mTextureCoords[i][j].y; - } - } - - if (aim->mNumUVComponents[i] > 0) { - AttribType::Value type = (aim->mNumUVComponents[i] == 2) ? AttribType::VEC2 : AttribType::VEC3; - - Ref<Accessor> tc = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mTextureCoords[i], - AttribType::VEC3, type, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER); - if (tc) { - p.attributes.texcoord.push_back(tc); - } - } - } - - /*************** Vertex colors ****************/ - for (unsigned int indexColorChannel = 0; indexColorChannel < aim->GetNumColorChannels(); ++indexColorChannel) { - Ref<Accessor> c = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mColors[indexColorChannel], - AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER); - if (c) { - p.attributes.color.push_back(c); - } - } - - /*************** Vertices indices ****************/ - if (aim->mNumFaces > 0) { - std::vector<IndicesType> indices; - unsigned int nIndicesPerFace = aim->mFaces[0].mNumIndices; - indices.resize(aim->mNumFaces * nIndicesPerFace); - for (size_t i = 0; i < aim->mNumFaces; ++i) { - for (size_t j = 0; j < nIndicesPerFace; ++j) { - indices[i * nIndicesPerFace + j] = IndicesType(aim->mFaces[i].mIndices[j]); - } - } - - p.indices = ExportData(*mAsset, meshId, b, indices.size(), &indices[0], AttribType::SCALAR, AttribType::SCALAR, - ComponentType_UNSIGNED_INT, BufferViewTarget_ELEMENT_ARRAY_BUFFER); - } - - switch (aim->mPrimitiveTypes) { - case aiPrimitiveType_POLYGON: - p.mode = PrimitiveMode_TRIANGLES; - break; // TODO implement this - case aiPrimitiveType_LINE: - p.mode = PrimitiveMode_LINES; - break; - case aiPrimitiveType_POINT: - p.mode = PrimitiveMode_POINTS; - break; - default: // aiPrimitiveType_TRIANGLE - p.mode = PrimitiveMode_TRIANGLES; - break; - } - - /*************** Skins ****************/ - if (aim->HasBones()) { - ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData); - } - - /*************** Targets for blendshapes ****************/ - if (aim->mNumAnimMeshes > 0) { - bool bUseSparse = this->mProperties->HasPropertyBool("GLTF2_SPARSE_ACCESSOR_EXP") && - this->mProperties->GetPropertyBool("GLTF2_SPARSE_ACCESSOR_EXP"); - bool bIncludeNormal = this->mProperties->HasPropertyBool("GLTF2_TARGET_NORMAL_EXP") && - this->mProperties->GetPropertyBool("GLTF2_TARGET_NORMAL_EXP"); - bool bExportTargetNames = this->mProperties->HasPropertyBool("GLTF2_TARGETNAMES_EXP") && - this->mProperties->GetPropertyBool("GLTF2_TARGETNAMES_EXP"); - - p.targets.resize(aim->mNumAnimMeshes); - for (unsigned int am = 0; am < aim->mNumAnimMeshes; ++am) { - aiAnimMesh *pAnimMesh = aim->mAnimMeshes[am]; - if (bExportTargetNames) { - m->targetNames.emplace_back(pAnimMesh->mName.data); - } - // position - if (pAnimMesh->HasPositions()) { - // NOTE: in gltf it is the diff stored - aiVector3D *pPositionDiff = new aiVector3D[pAnimMesh->mNumVertices]; - for (unsigned int vt = 0; vt < pAnimMesh->mNumVertices; ++vt) { - pPositionDiff[vt] = pAnimMesh->mVertices[vt] - aim->mVertices[vt]; - } - Ref<Accessor> vec; - if (bUseSparse) { - vec = ExportDataSparse(*mAsset, meshId, b, - pAnimMesh->mNumVertices, pPositionDiff, - AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); - } else { - vec = ExportData(*mAsset, meshId, b, - pAnimMesh->mNumVertices, pPositionDiff, - AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); - } - if (vec) { - p.targets[am].position.push_back(vec); - } - delete[] pPositionDiff; - } - - // normal - if (pAnimMesh->HasNormals() && bIncludeNormal) { - aiVector3D *pNormalDiff = new aiVector3D[pAnimMesh->mNumVertices]; - for (unsigned int vt = 0; vt < pAnimMesh->mNumVertices; ++vt) { - pNormalDiff[vt] = pAnimMesh->mNormals[vt] - aim->mNormals[vt]; - } - Ref<Accessor> vec; - if (bUseSparse) { - vec = ExportDataSparse(*mAsset, meshId, b, - pAnimMesh->mNumVertices, pNormalDiff, - AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); - } else { - vec = ExportData(*mAsset, meshId, b, - pAnimMesh->mNumVertices, pNormalDiff, - AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); - } - if (vec) { - p.targets[am].normal.push_back(vec); - } - delete[] pNormalDiff; - } - - // tangent? - } - } - } - - //---------------------------------------- - // Finish the skin - // Create the Accessor for skinRef->inverseBindMatrices - bool bAddCustomizedProperty = this->mProperties->HasPropertyBool("GLTF2_CUSTOMIZE_PROPERTY"); - if (createSkin) { - mat4 *invBindMatrixData = new mat4[inverseBindMatricesData.size()]; - for (unsigned int idx_joint = 0; idx_joint < inverseBindMatricesData.size(); ++idx_joint) { - CopyValue(inverseBindMatricesData[idx_joint], invBindMatrixData[idx_joint]); - } - - Ref<Accessor> invBindMatrixAccessor = ExportData(*mAsset, skinName, b, - static_cast<unsigned int>(inverseBindMatricesData.size()), - invBindMatrixData, AttribType::MAT4, AttribType::MAT4, ComponentType_FLOAT); - if (invBindMatrixAccessor) { - skinRef->inverseBindMatrices = invBindMatrixAccessor; - } - - // Identity Matrix =====> skinRef->bindShapeMatrix - // Temporary. Hard-coded identity matrix here - skinRef->bindShapeMatrix.isPresent = bAddCustomizedProperty; - IdentityMatrix4(skinRef->bindShapeMatrix.value); - - // Find nodes that contain a mesh with bones and add "skeletons" and "skin" attributes to those nodes. - Ref<Node> rootNode = mAsset->nodes.Get(unsigned(0)); - Ref<Node> meshNode; - for (unsigned int meshIndex = 0; meshIndex < mAsset->meshes.Size(); ++meshIndex) { - Ref<Mesh> mesh = mAsset->meshes.Get(meshIndex); - bool hasBones = false; - for (unsigned int i = 0; i < mesh->primitives.size(); ++i) { - if (!mesh->primitives[i].attributes.weight.empty()) { - hasBones = true; - break; - } - } - if (!hasBones) { - continue; - } - std::string meshID = mesh->id; - FindMeshNode(rootNode, meshNode, meshID); - Ref<Node> rootJoint = FindSkeletonRootJoint(skinRef); - if (bAddCustomizedProperty) - meshNode->skeletons.push_back(rootJoint); - meshNode->skin = skinRef; - } - delete[] invBindMatrixData; - } -} - -// Merges a node's multiple meshes (with one primitive each) into one mesh with multiple primitives -void glTF2Exporter::MergeMeshes() { - for (unsigned int n = 0; n < mAsset->nodes.Size(); ++n) { - Ref<Node> node = mAsset->nodes.Get(n); - - unsigned int nMeshes = static_cast<unsigned int>(node->meshes.size()); - - //skip if it's 1 or less meshes per node - if (nMeshes > 1) { - Ref<Mesh> firstMesh = node->meshes.at(0); - - //loop backwards to allow easy removal of a mesh from a node once it's merged - for (unsigned int m = nMeshes - 1; m >= 1; --m) { - Ref<Mesh> mesh = node->meshes.at(m); - - //append this mesh's primitives to the first mesh's primitives - firstMesh->primitives.insert( - firstMesh->primitives.end(), - mesh->primitives.begin(), - mesh->primitives.end()); - - //remove the mesh from the list of meshes - unsigned int removedIndex = mAsset->meshes.Remove(mesh->id.c_str()); - - //find the presence of the removed mesh in other nodes - for (unsigned int nn = 0; nn < mAsset->nodes.Size(); ++nn) { - Ref<Node> curNode = mAsset->nodes.Get(nn); - - for (unsigned int mm = 0; mm < curNode->meshes.size(); ++mm) { - Ref<Mesh> &meshRef = curNode->meshes.at(mm); - unsigned int meshIndex = meshRef.GetIndex(); - - if (meshIndex == removedIndex) { - curNode->meshes.erase(curNode->meshes.begin() + mm); - } else if (meshIndex > removedIndex) { - Ref<Mesh> newMeshRef = mAsset->meshes.Get(meshIndex - 1); - - meshRef = newMeshRef; - } - } - } - } - - //since we were looping backwards, reverse the order of merged primitives to their original order - std::reverse(firstMesh->primitives.begin() + 1, firstMesh->primitives.end()); - } - } -} - -/* - * Export the root node of the node hierarchy. - * Calls ExportNode for all children. - */ -unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode *n) { - Ref<Node> node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node")); - - node->name = n->mName.C_Str(); - - if (!n->mTransformation.IsIdentity()) { - node->matrix.isPresent = true; - CopyValue(n->mTransformation, node->matrix.value); - } - - for (unsigned int i = 0; i < n->mNumMeshes; ++i) { - node->meshes.emplace_back(mAsset->meshes.Get(n->mMeshes[i])); - } - - for (unsigned int i = 0; i < n->mNumChildren; ++i) { - unsigned int idx = ExportNode(n->mChildren[i], node); - node->children.emplace_back(mAsset->nodes.Get(idx)); - } - - return node.GetIndex(); -} - -/* - * Export node and recursively calls ExportNode for all children. - * Since these nodes are not the root node, we also export the parent Ref<Node> - */ -unsigned int glTF2Exporter::ExportNode(const aiNode *n, Ref<Node> &parent) { - std::string name = mAsset->FindUniqueID(n->mName.C_Str(), "node"); - Ref<Node> node = mAsset->nodes.Create(name); - - node->parent = parent; - node->name = name; - - if (!n->mTransformation.IsIdentity()) { - if (mScene->mNumAnimations > 0 || (mProperties && mProperties->HasPropertyBool("GLTF2_NODE_IN_TRS"))) { - aiQuaternion quaternion; - n->mTransformation.Decompose(*reinterpret_cast<aiVector3D *>(&node->scale.value), quaternion, *reinterpret_cast<aiVector3D *>(&node->translation.value)); - - aiVector3D vector(static_cast<ai_real>(1.0f), static_cast<ai_real>(1.0f), static_cast<ai_real>(1.0f)); - if (!reinterpret_cast<aiVector3D *>(&node->scale.value)->Equal(vector)) { - node->scale.isPresent = true; - } - if (!reinterpret_cast<aiVector3D *>(&node->translation.value)->Equal(vector)) { - node->translation.isPresent = true; - } - node->rotation.isPresent = true; - node->rotation.value[0] = quaternion.x; - node->rotation.value[1] = quaternion.y; - node->rotation.value[2] = quaternion.z; - node->rotation.value[3] = quaternion.w; - node->matrix.isPresent = false; - } else { - node->matrix.isPresent = true; - CopyValue(n->mTransformation, node->matrix.value); - } - } - - for (unsigned int i = 0; i < n->mNumMeshes; ++i) { - node->meshes.emplace_back(mAsset->meshes.Get(n->mMeshes[i])); - } - - for (unsigned int i = 0; i < n->mNumChildren; ++i) { - unsigned int idx = ExportNode(n->mChildren[i], node); - node->children.emplace_back(mAsset->nodes.Get(idx)); - } - - return node.GetIndex(); -} - -void glTF2Exporter::ExportScene() { - // Use the name of the scene if specified - const std::string sceneName = (mScene->mName.length > 0) ? mScene->mName.C_Str() : "defaultScene"; - - // Ensure unique - Ref<Scene> scene = mAsset->scenes.Create(mAsset->FindUniqueID(sceneName, "")); - - // root node will be the first one exported (idx 0) - if (mAsset->nodes.Size() > 0) { - scene->nodes.emplace_back(mAsset->nodes.Get(0u)); - } - - // set as the default scene - mAsset->scene = scene; -} - -void glTF2Exporter::ExportMetadata() { - AssetMetadata &asset = mAsset->asset; - asset.version = "2.0"; - - char buffer[256]; - ai_snprintf(buffer, 256, "Open Asset Import Library (assimp v%d.%d.%x)", - aiGetVersionMajor(), aiGetVersionMinor(), aiGetVersionRevision()); - - asset.generator = buffer; - - // Copyright - aiString copyright_str; - if (mScene->mMetaData != nullptr && mScene->mMetaData->Get(AI_METADATA_SOURCE_COPYRIGHT, copyright_str)) { - asset.copyright = copyright_str.C_Str(); - } -} - -inline Ref<Accessor> GetSamplerInputRef(Asset &asset, std::string &animId, Ref<Buffer> &buffer, std::vector<ai_real> ×) { - return ExportData(asset, animId, buffer, (unsigned int)times.size(), ×[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); -} - -inline void ExtractTranslationSampler(Asset &asset, std::string &animId, Ref<Buffer> &buffer, const aiNodeAnim *nodeChannel, float ticksPerSecond, Animation::Sampler &sampler) { - const unsigned int numKeyframes = nodeChannel->mNumPositionKeys; - - std::vector<ai_real> times(numKeyframes); - std::vector<ai_real> values(numKeyframes * 3); - for (unsigned int i = 0; i < numKeyframes; ++i) { - const aiVectorKey &key = nodeChannel->mPositionKeys[i]; - // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. - times[i] = static_cast<float>(key.mTime / ticksPerSecond); - values[(i * 3) + 0] = (ai_real) key.mValue.x; - values[(i * 3) + 1] = (ai_real) key.mValue.y; - values[(i * 3) + 2] = (ai_real) key.mValue.z; - } - - sampler.input = GetSamplerInputRef(asset, animId, buffer, times); - sampler.output = ExportData(asset, animId, buffer, numKeyframes, &values[0], AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); - sampler.interpolation = Interpolation_LINEAR; -} - -inline void ExtractScaleSampler(Asset &asset, std::string &animId, Ref<Buffer> &buffer, const aiNodeAnim *nodeChannel, float ticksPerSecond, Animation::Sampler &sampler) { - const unsigned int numKeyframes = nodeChannel->mNumScalingKeys; - - std::vector<ai_real> times(numKeyframes); - std::vector<ai_real> values(numKeyframes * 3); - for (unsigned int i = 0; i < numKeyframes; ++i) { - const aiVectorKey &key = nodeChannel->mScalingKeys[i]; - // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. - times[i] = static_cast<float>(key.mTime / ticksPerSecond); - values[(i * 3) + 0] = (ai_real) key.mValue.x; - values[(i * 3) + 1] = (ai_real) key.mValue.y; - values[(i * 3) + 2] = (ai_real) key.mValue.z; - } - - sampler.input = GetSamplerInputRef(asset, animId, buffer, times); - sampler.output = ExportData(asset, animId, buffer, numKeyframes, &values[0], AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); - sampler.interpolation = Interpolation_LINEAR; -} - -inline void ExtractRotationSampler(Asset &asset, std::string &animId, Ref<Buffer> &buffer, const aiNodeAnim *nodeChannel, float ticksPerSecond, Animation::Sampler &sampler) { - const unsigned int numKeyframes = nodeChannel->mNumRotationKeys; - - std::vector<ai_real> times(numKeyframes); - std::vector<ai_real> values(numKeyframes * 4); - for (unsigned int i = 0; i < numKeyframes; ++i) { - const aiQuatKey &key = nodeChannel->mRotationKeys[i]; - // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. - times[i] = static_cast<float>(key.mTime / ticksPerSecond); - values[(i * 4) + 0] = (ai_real) key.mValue.x; - values[(i * 4) + 1] = (ai_real) key.mValue.y; - values[(i * 4) + 2] = (ai_real) key.mValue.z; - values[(i * 4) + 3] = (ai_real) key.mValue.w; - } - - sampler.input = GetSamplerInputRef(asset, animId, buffer, times); - sampler.output = ExportData(asset, animId, buffer, numKeyframes, &values[0], AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT); - sampler.interpolation = Interpolation_LINEAR; -} - -static void AddSampler(Ref<Animation> &animRef, Ref<Node> &nodeRef, Animation::Sampler &sampler, AnimationPath path) { - Animation::Channel channel; - channel.sampler = static_cast<int>(animRef->samplers.size()); - channel.target.path = path; - channel.target.node = nodeRef; - animRef->channels.push_back(channel); - animRef->samplers.push_back(sampler); -} - -void glTF2Exporter::ExportAnimations() { - Ref<Buffer> bufferRef = mAsset->buffers.Get(unsigned(0)); - - for (unsigned int i = 0; i < mScene->mNumAnimations; ++i) { - const aiAnimation *anim = mScene->mAnimations[i]; - const float ticksPerSecond = static_cast<float>(anim->mTicksPerSecond); - - std::string nameAnim = "anim"; - if (anim->mName.length > 0) { - nameAnim = anim->mName.C_Str(); - } - Ref<Animation> animRef = mAsset->animations.Create(nameAnim); - animRef->name = nameAnim; - - for (unsigned int channelIndex = 0; channelIndex < anim->mNumChannels; ++channelIndex) { - const aiNodeAnim *nodeChannel = anim->mChannels[channelIndex]; - - std::string name = nameAnim + "_" + ai_to_string(channelIndex); - name = mAsset->FindUniqueID(name, "animation"); - - Ref<Node> animNode = mAsset->nodes.Get(nodeChannel->mNodeName.C_Str()); - - if (nodeChannel->mNumPositionKeys > 0) { - Animation::Sampler translationSampler; - ExtractTranslationSampler(*mAsset, name, bufferRef, nodeChannel, ticksPerSecond, translationSampler); - AddSampler(animRef, animNode, translationSampler, AnimationPath_TRANSLATION); - } - - if (nodeChannel->mNumRotationKeys > 0) { - Animation::Sampler rotationSampler; - ExtractRotationSampler(*mAsset, name, bufferRef, nodeChannel, ticksPerSecond, rotationSampler); - AddSampler(animRef, animNode, rotationSampler, AnimationPath_ROTATION); - } - - if (nodeChannel->mNumScalingKeys > 0) { - Animation::Sampler scaleSampler; - ExtractScaleSampler(*mAsset, name, bufferRef, nodeChannel, ticksPerSecond, scaleSampler); - AddSampler(animRef, animNode, scaleSampler, AnimationPath_SCALE); - } - } - } // End: for-loop mNumAnimations -} - -#endif // ASSIMP_BUILD_NO_GLTF_EXPORTER -#endif // ASSIMP_BUILD_NO_EXPORT diff --git a/libs/assimp/code/AssetLib/glTF2/glTF2Exporter.h b/libs/assimp/code/AssetLib/glTF2/glTF2Exporter.h deleted file mode 100644 index 9942522..0000000 --- a/libs/assimp/code/AssetLib/glTF2/glTF2Exporter.h +++ /dev/null @@ -1,147 +0,0 @@ -/* -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 GltfExporter.h -* Declares the exporter class to write a scene to a gltf/glb file -*/ -#ifndef AI_GLTF2EXPORTER_H_INC -#define AI_GLTF2EXPORTER_H_INC - -#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER) - -#include <assimp/material.h> -#include <assimp/types.h> - -#include <map> -#include <memory> -#include <sstream> -#include <vector> - -struct aiScene; -struct aiNode; -struct aiMaterial; - -namespace glTFCommon { -template <class T> -class Ref; - -} - -namespace glTF2 { - -class Asset; - -struct TexProperty; -struct TextureInfo; -struct NormalTextureInfo; -struct OcclusionTextureInfo; -struct Node; -struct Texture; -struct PbrSpecularGlossiness; -struct MaterialSheen; -struct MaterialClearcoat; -struct MaterialTransmission; -struct MaterialVolume; -struct MaterialIOR; - -// Vec/matrix types, as raw float arrays -typedef float(vec2)[2]; -typedef float(vec3)[3]; -typedef float(vec4)[4]; - -} // namespace glTF2 - -namespace Assimp { -class IOSystem; -class IOStream; -class ExportProperties; - -// ------------------------------------------------------------------------------------------------ -/** Helper class to export a given scene to an glTF file. */ -// ------------------------------------------------------------------------------------------------ -class glTF2Exporter { -public: - /// Constructor for a specific scene to export - glTF2Exporter(const char *filename, IOSystem *pIOSystem, const aiScene *pScene, - const ExportProperties *pProperties, bool binary); - ~glTF2Exporter(); - -protected: - void WriteBinaryData(IOStream *outfile, std::size_t sceneLength); - void GetTexSampler(const aiMaterial &mat, glTFCommon::Ref<glTF2::Texture> texture, aiTextureType tt, unsigned int slot); - void GetMatTexProp(const aiMaterial &mat, unsigned int &prop, const char *propName, aiTextureType tt, unsigned int idx); - void GetMatTexProp(const aiMaterial &mat, float &prop, const char *propName, aiTextureType tt, unsigned int idx); - void GetMatTex(const aiMaterial &mat, glTFCommon::Ref<glTF2::Texture> &texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial &mat, glTF2::TextureInfo &prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial &mat, glTF2::NormalTextureInfo &prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial &mat, glTF2::OcclusionTextureInfo &prop, aiTextureType tt, unsigned int slot); - aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec4 &prop, const char *propName, int type, int idx) const; - aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec3 &prop, const char *propName, int type, int idx) const; - bool GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG); - bool GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen); - bool GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat); - bool GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission); - bool GetMatVolume(const aiMaterial &mat, glTF2::MaterialVolume &volume); - bool GetMatIOR(const aiMaterial &mat, glTF2::MaterialIOR &ior); - void ExportMetadata(); - void ExportMaterials(); - void ExportMeshes(); - void MergeMeshes(); - unsigned int ExportNodeHierarchy(const aiNode *n); - unsigned int ExportNode(const aiNode *node, glTFCommon::Ref<glTF2::Node> &parent); - void ExportScene(); - void ExportAnimations(); - -private: - const char *mFilename; - IOSystem *mIOSystem; - const aiScene *mScene; - const ExportProperties *mProperties; - std::map<std::string, unsigned int> mTexturesByPath; - std::shared_ptr<glTF2::Asset> mAsset; - std::vector<unsigned char> mBodyData; -}; - -} // namespace Assimp - -#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER - -#endif // AI_GLTF2EXPORTER_H_INC diff --git a/libs/assimp/code/AssetLib/glTF2/glTF2Importer.cpp b/libs/assimp/code/AssetLib/glTF2/glTF2Importer.cpp deleted file mode 100644 index 1f4cafd..0000000 --- a/libs/assimp/code/AssetLib/glTF2/glTF2Importer.cpp +++ /dev/null @@ -1,1637 +0,0 @@ -/* -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. - ----------------------------------------------------------------------- -*/ - -#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER) - -#include "AssetLib/glTF2/glTF2Importer.h" -#include "PostProcessing/MakeVerboseFormat.h" -#include "AssetLib/glTF2/glTF2Asset.h" - -#if !defined(ASSIMP_BUILD_NO_EXPORT) -#include "AssetLib/glTF2/glTF2AssetWriter.h" -#endif - -#include <assimp/CreateAnimMesh.h> -#include <assimp/StringComparison.h> -#include <assimp/StringUtils.h> -#include <assimp/ai_assert.h> -#include <assimp/importerdesc.h> -#include <assimp/scene.h> -#include <assimp/DefaultLogger.hpp> -#include <assimp/Importer.hpp> -#include <assimp/commonMetaData.h> -#include <assimp/DefaultIOSystem.h> - -#include <memory> -#include <unordered_map> - -#include <rapidjson/document.h> -#include <rapidjson/rapidjson.h> - -using namespace Assimp; -using namespace glTF2; -using namespace glTFCommon; - -namespace { -// generate bi-tangents from normals and tangents according to spec -struct Tangent { - aiVector3D xyz; - ai_real w; -}; -} // namespace - -// -// glTF2Importer -// - -static const aiImporterDesc desc = { - "glTF2 Importer", - "", - "", - "", - aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, - 0, - 0, - 0, - 0, - "gltf glb" -}; - -glTF2Importer::glTF2Importer() : - BaseImporter(), - meshOffsets(), - mEmbeddedTexIdxs(), - mScene(nullptr) { - // empty -} - -glTF2Importer::~glTF2Importer() { - // empty -} - -const aiImporterDesc *glTF2Importer::GetInfo() const { - return &desc; -} - -bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig ) const { - const std::string extension = GetExtension(filename); - if (!checkSig && (extension != "gltf") && (extension != "glb")) { - return false; - } - - if (pIOHandler) { - glTF2::Asset asset(pIOHandler); - return asset.CanRead(filename, extension == "glb"); - } - - return false; -} - -static inline aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) { - switch (gltfWrapMode) { - case SamplerWrap::Mirrored_Repeat: - return aiTextureMapMode_Mirror; - - case SamplerWrap::Clamp_To_Edge: - return aiTextureMapMode_Clamp; - - case SamplerWrap::UNSET: - case SamplerWrap::Repeat: - default: - return aiTextureMapMode_Wrap; - } -} - -static inline void SetMaterialColorProperty(Asset & /*r*/, vec4 &prop, aiMaterial *mat, - const char *pKey, unsigned int type, unsigned int idx) { - aiColor4D col; - CopyValue(prop, col); - mat->AddProperty(&col, 1, pKey, type, idx); -} - -static inline void SetMaterialColorProperty(Asset & /*r*/, vec3 &prop, aiMaterial *mat, - const char *pKey, unsigned int type, unsigned int idx) { - aiColor4D col; - glTFCommon::CopyValue(prop, col); - mat->AddProperty(&col, 1, pKey, type, idx); -} - -static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset & /*r*/, - glTF2::TextureInfo prop, aiMaterial *mat, aiTextureType texType, - unsigned int texSlot = 0) { - if (prop.texture && prop.texture->source) { - aiString uri(prop.texture->source->uri); - - int texIdx = embeddedTexIdxs[prop.texture->source.GetIndex()]; - if (texIdx != -1) { // embedded - // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) - uri.data[0] = '*'; - uri.length = 1 + ASSIMP_itoa10(uri.data + 1, MAXLEN - 1, texIdx); - } - - mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot)); - const int uvIndex = static_cast<int>(prop.texCoord); - mat->AddProperty(&uvIndex, 1, AI_MATKEY_UVWSRC(texType, texSlot)); - - if (prop.textureTransformSupported) { - aiUVTransform transform; - transform.mScaling.x = prop.TextureTransformExt_t.scale[0]; - transform.mScaling.y = prop.TextureTransformExt_t.scale[1]; - transform.mRotation = -prop.TextureTransformExt_t.rotation; // must be negated - - // A change of coordinates is required to map glTF UV transformations into the space used by - // Assimp. In glTF all UV origins are at 0,1 (top left of texture) in Assimp space. In Assimp - // rotation occurs around the image center (0.5,0.5) where as in glTF rotation is around the - // texture origin. All three can be corrected for solely by a change of the translation since - // the transformations available are shape preserving. Note the importer already flips the V - // coordinate of the actual meshes during import. - const ai_real rcos(cos(-transform.mRotation)); - const ai_real rsin(sin(-transform.mRotation)); - transform.mTranslation.x = (static_cast<ai_real>( 0.5 ) * transform.mScaling.x) * (-rcos + rsin + 1) + prop.TextureTransformExt_t.offset[0]; - transform.mTranslation.y = ((static_cast<ai_real>( 0.5 ) * transform.mScaling.y) * (rsin + rcos - 1)) + 1 - transform.mScaling.y - prop.TextureTransformExt_t.offset[1];; - - mat->AddProperty(&transform, 1, _AI_MATKEY_UVTRANSFORM_BASE, texType, texSlot); - } - - if (prop.texture->sampler) { - Ref<Sampler> sampler = prop.texture->sampler; - - aiString name(sampler->name); - aiString id(sampler->id); - - mat->AddProperty(&name, AI_MATKEY_GLTF_MAPPINGNAME(texType, texSlot)); - mat->AddProperty(&id, AI_MATKEY_GLTF_MAPPINGID(texType, texSlot)); - - aiTextureMapMode wrapS = ConvertWrappingMode(sampler->wrapS); - aiTextureMapMode wrapT = ConvertWrappingMode(sampler->wrapT); - mat->AddProperty(&wrapS, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot)); - mat->AddProperty(&wrapT, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot)); - - if (sampler->magFilter != SamplerMagFilter::UNSET) { - mat->AddProperty(&sampler->magFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(texType, texSlot)); - } - - if (sampler->minFilter != SamplerMinFilter::UNSET) { - mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot)); - } - } else { - // Use glTFv2 default sampler - const aiTextureMapMode default_wrap = aiTextureMapMode_Wrap; - mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot)); - mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot)); - } - } -} - -inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset &r, - NormalTextureInfo &prop, aiMaterial *mat, aiTextureType texType, - unsigned int texSlot = 0) { - SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot); - - if (prop.texture && prop.texture->source) { - mat->AddProperty(&prop.scale, 1, AI_MATKEY_GLTF_TEXTURE_SCALE(texType, texSlot)); - } -} - -inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset &r, - OcclusionTextureInfo &prop, aiMaterial *mat, aiTextureType texType, - unsigned int texSlot = 0) { - SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot); - - if (prop.texture && prop.texture->source) { - mat->AddProperty(&prop.strength, 1, AI_MATKEY_GLTF_TEXTURE_STRENGTH(texType, texSlot)); - } -} - -static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, Material &mat) { - aiMaterial *aimat = new aiMaterial(); - - try { - if (!mat.name.empty()) { - aiString str(mat.name); - - aimat->AddProperty(&str, AI_MATKEY_NAME); - } - - // Set Assimp DIFFUSE and BASE COLOR to the pbrMetallicRoughness base color and texture for backwards compatibility - // Technically should not load any pbrMetallicRoughness if extensionsRequired contains KHR_materials_pbrSpecularGlossiness - SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); - SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_BASE_COLOR); - - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR); - - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - - aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR); - aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR); - - float roughnessAsShininess = 1 - mat.pbrMetallicRoughness.roughnessFactor; - roughnessAsShininess *= roughnessAsShininess * 1000; - aimat->AddProperty(&roughnessAsShininess, 1, AI_MATKEY_SHININESS); - - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.normalTexture, aimat, aiTextureType_NORMALS); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.occlusionTexture, aimat, aiTextureType_LIGHTMAP); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.emissiveTexture, aimat, aiTextureType_EMISSIVE); - SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE); - - aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED); - aimat->AddProperty(&mat.pbrMetallicRoughness.baseColorFactor[3], 1, AI_MATKEY_OPACITY); - - aiString alphaMode(mat.alphaMode); - aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE); - aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF); - - //pbrSpecularGlossiness - if (mat.pbrSpecularGlossiness.isPresent) { - PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; - - SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); - SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR); - - float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f; - aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS); - aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLOSSINESS_FACTOR); - - SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE); - - SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR); - } - - // glTFv2 is either PBR or Unlit - aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; - if (mat.unlit) { - aimat->AddProperty(&mat.unlit, 1, "$mat.gltf.unlit", 0, 0); // TODO: Remove this property, it is kept for backwards compatibility with assimp 5.0.1 - shadingMode = aiShadingMode_Unlit; - } - - aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); - - - // KHR_materials_sheen - if (mat.materialSheen.isPresent) { - MaterialSheen &sheen = mat.materialSheen.value; - // Default value {0,0,0} disables Sheen - if (std::memcmp(sheen.sheenColorFactor, defaultSheenFactor, sizeof(glTFCommon::vec3)) != 0) { - SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_SHEEN_COLOR_FACTOR); - aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_SHEEN_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_SHEEN_COLOR_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); - } - } - - // KHR_materials_clearcoat - if (mat.materialClearcoat.isPresent) { - MaterialClearcoat &clearcoat = mat.materialClearcoat.value; - // Default value 0.0 disables clearcoat - if (clearcoat.clearcoatFactor != 0.0f) { - aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_CLEARCOAT_FACTOR); - aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_CLEARCOAT_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); - } - } - - // KHR_materials_transmission - if (mat.materialTransmission.isPresent) { - MaterialTransmission &transmission = mat.materialTransmission.value; - - aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_TRANSMISSION_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_TRANSMISSION_TEXTURE); - } - - // KHR_materials_volume - if (mat.materialVolume.isPresent) { - MaterialVolume &volume = mat.materialVolume.value; - - aimat->AddProperty(&volume.thicknessFactor, 1, AI_MATKEY_VOLUME_THICKNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, volume.thicknessTexture, aimat, AI_MATKEY_VOLUME_THICKNESS_TEXTURE); - aimat->AddProperty(&volume.attenuationDistance, 1, AI_MATKEY_VOLUME_ATTENUATION_DISTANCE); - SetMaterialColorProperty(r, volume.attenuationColor, aimat, AI_MATKEY_VOLUME_ATTENUATION_COLOR); - } - - // KHR_materials_ior - if (mat.materialIOR.isPresent) { - MaterialIOR &ior = mat.materialIOR.value; - - aimat->AddProperty(&ior.ior, 1, AI_MATKEY_REFRACTI); - } - - return aimat; - } catch (...) { - delete aimat; - throw; - } -} - -void glTF2Importer::ImportMaterials(Asset &r) { - const unsigned int numImportedMaterials = unsigned(r.materials.Size()); - ASSIMP_LOG_DEBUG("Importing ", numImportedMaterials, " materials"); - Material defaultMaterial; - - mScene->mNumMaterials = numImportedMaterials + 1; - mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials]; - std::fill(mScene->mMaterials, mScene->mMaterials + mScene->mNumMaterials, nullptr); - mScene->mMaterials[numImportedMaterials] = ImportMaterial(mEmbeddedTexIdxs, r, defaultMaterial); - - for (unsigned int i = 0; i < numImportedMaterials; ++i) { - mScene->mMaterials[i] = ImportMaterial(mEmbeddedTexIdxs, r, r.materials[i]); - } -} - -static inline void SetFaceAndAdvance1(aiFace*& face, unsigned int numVertices, unsigned int a) { - if (a >= numVertices) { - return; - } - face->mNumIndices = 1; - face->mIndices = new unsigned int[1]; - face->mIndices[0] = a; - ++face; -} - -static inline void SetFaceAndAdvance2(aiFace*& face, unsigned int numVertices, - unsigned int a, unsigned int b) { - if ((a >= numVertices) || (b >= numVertices)) { - return; - } - face->mNumIndices = 2; - face->mIndices = new unsigned int[2]; - face->mIndices[0] = a; - face->mIndices[1] = b; - ++face; -} - -static inline void SetFaceAndAdvance3(aiFace*& face, unsigned int numVertices, unsigned int a, - unsigned int b, unsigned int c) { - if ((a >= numVertices) || (b >= numVertices) || (c >= numVertices)) { - return; - } - face->mNumIndices = 3; - face->mIndices = new unsigned int[3]; - face->mIndices[0] = a; - face->mIndices[1] = b; - face->mIndices[2] = c; - ++face; -} - -#ifdef ASSIMP_BUILD_DEBUG -static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsigned nVerts) { - for (unsigned i = 0; i < nFaces; ++i) { - for (unsigned j = 0; j < faces[i].mNumIndices; ++j) { - unsigned idx = faces[i].mIndices[j]; - if (idx >= nVerts) { - return false; - } - } - } - return true; -} -#endif // ASSIMP_BUILD_DEBUG - -template<typename T> -aiColor4D* GetVertexColorsForType(Ref<Accessor> input) { - constexpr float max = std::numeric_limits<T>::max(); - aiColor4t<T>* colors; - input->ExtractData(colors); - auto output = new aiColor4D[input->count]; - for (size_t i = 0; i < input->count; i++) { - output[i] = aiColor4D( - colors[i].r / max, colors[i].g / max, - colors[i].b / max, colors[i].a / max - ); - } - delete[] colors; - return output; -} - -void glTF2Importer::ImportMeshes(glTF2::Asset &r) { - ASSIMP_LOG_DEBUG("Importing ", r.meshes.Size(), " meshes"); - std::vector<std::unique_ptr<aiMesh>> meshes; - - unsigned int k = 0; - meshOffsets.clear(); - - for (unsigned int m = 0; m < r.meshes.Size(); ++m) { - Mesh &mesh = r.meshes[m]; - - meshOffsets.push_back(k); - k += unsigned(mesh.primitives.size()); - - for (unsigned int p = 0; p < mesh.primitives.size(); ++p) { - Mesh::Primitive &prim = mesh.primitives[p]; - - aiMesh *aim = new aiMesh(); - meshes.push_back(std::unique_ptr<aiMesh>(aim)); - - aim->mName = mesh.name.empty() ? mesh.id : mesh.name; - - if (mesh.primitives.size() > 1) { - ai_uint32 &len = aim->mName.length; - aim->mName.data[len] = '-'; - len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p); - } - - switch (prim.mode) { - case PrimitiveMode_POINTS: - aim->mPrimitiveTypes |= aiPrimitiveType_POINT; - break; - - case PrimitiveMode_LINES: - case PrimitiveMode_LINE_LOOP: - case PrimitiveMode_LINE_STRIP: - aim->mPrimitiveTypes |= aiPrimitiveType_LINE; - break; - - case PrimitiveMode_TRIANGLES: - case PrimitiveMode_TRIANGLE_STRIP: - case PrimitiveMode_TRIANGLE_FAN: - aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - break; - } - - Mesh::Primitive::Attributes &attr = prim.attributes; - - if (!attr.position.empty() && attr.position[0]) { - aim->mNumVertices = static_cast<unsigned int>(attr.position[0]->count); - attr.position[0]->ExtractData(aim->mVertices); - } - - if (!attr.normal.empty() && attr.normal[0]) { - if (attr.normal[0]->count != aim->mNumVertices) { - DefaultLogger::get()->warn("Normal count in mesh \"", mesh.name, "\" does not match the vertex count, normals ignored."); - } else { - attr.normal[0]->ExtractData(aim->mNormals); - - // only extract tangents if normals are present - if (!attr.tangent.empty() && attr.tangent[0]) { - if (attr.tangent[0]->count != aim->mNumVertices) { - DefaultLogger::get()->warn("Tangent count in mesh \"", mesh.name, "\" does not match the vertex count, tangents ignored."); - } else { - // generate bitangents from normals and tangents according to spec - Tangent *tangents = nullptr; - - attr.tangent[0]->ExtractData(tangents); - - aim->mTangents = new aiVector3D[aim->mNumVertices]; - aim->mBitangents = new aiVector3D[aim->mNumVertices]; - - for (unsigned int i = 0; i < aim->mNumVertices; ++i) { - aim->mTangents[i] = tangents[i].xyz; - aim->mBitangents[i] = (aim->mNormals[i] ^ tangents[i].xyz) * tangents[i].w; - } - - delete[] tangents; - } - } - } - } - - for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) { - if (attr.color[c]->count != aim->mNumVertices) { - DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name, - "\" does not match the vertex count"); - continue; - } - - auto componentType = attr.color[c]->componentType; - if (componentType == glTF2::ComponentType_FLOAT) { - attr.color[c]->ExtractData(aim->mColors[c]); - } else { - if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) { - aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c]); - } else if (componentType == glTF2::ComponentType_UNSIGNED_SHORT) { - aim->mColors[c] = GetVertexColorsForType<unsigned short>(attr.color[c]); - } - } - } - for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) { - if (!attr.texcoord[tc]) { - DefaultLogger::get()->warn("Texture coordinate accessor not found or non-contiguous texture coordinate sets."); - continue; - } - - if (attr.texcoord[tc]->count != aim->mNumVertices) { - DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name, - "\" does not match the vertex count"); - continue; - } - - attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]); - aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents(); - - aiVector3D *values = aim->mTextureCoords[tc]; - for (unsigned int i = 0; i < aim->mNumVertices; ++i) { - values[i].y = 1 - values[i].y; // Flip Y coords - } - } - - std::vector<Mesh::Primitive::Target> &targets = prim.targets; - if (!targets.empty()) { - aim->mNumAnimMeshes = (unsigned int)targets.size(); - aim->mAnimMeshes = new aiAnimMesh *[aim->mNumAnimMeshes]; - std::fill(aim->mAnimMeshes, aim->mAnimMeshes + aim->mNumAnimMeshes, nullptr); - for (size_t i = 0; i < targets.size(); i++) { - bool needPositions = targets[i].position.size() > 0; - bool needNormals = (targets[i].normal.size() > 0) && aim->HasNormals(); - bool needTangents = (targets[i].tangent.size() > 0) && aim->HasTangentsAndBitangents(); - // GLTF morph does not support colors and texCoords - aim->mAnimMeshes[i] = aiCreateAnimMesh(aim, - needPositions, needNormals, needTangents, false, false); - aiAnimMesh &aiAnimMesh = *(aim->mAnimMeshes[i]); - Mesh::Primitive::Target &target = targets[i]; - - if (needPositions) { - if (target.position[0]->count != aim->mNumVertices) { - ASSIMP_LOG_WARN("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); - } else { - aiVector3D *positionDiff = nullptr; - target.position[0]->ExtractData(positionDiff); - for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) { - aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId]; - } - delete[] positionDiff; - } - } - if (needNormals) { - if (target.normal[0]->count != aim->mNumVertices) { - ASSIMP_LOG_WARN("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); - } else { - aiVector3D *normalDiff = nullptr; - target.normal[0]->ExtractData(normalDiff); - for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) { - aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId]; - } - delete[] normalDiff; - } - } - if (needTangents) { - if (target.tangent[0]->count != aim->mNumVertices) { - ASSIMP_LOG_WARN("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); - } else { - Tangent *tangent = nullptr; - attr.tangent[0]->ExtractData(tangent); - - aiVector3D *tangentDiff = nullptr; - target.tangent[0]->ExtractData(tangentDiff); - - for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) { - tangent[vertexId].xyz += tangentDiff[vertexId]; - aiAnimMesh.mTangents[vertexId] = tangent[vertexId].xyz; - aiAnimMesh.mBitangents[vertexId] = (aiAnimMesh.mNormals[vertexId] ^ tangent[vertexId].xyz) * tangent[vertexId].w; - } - delete[] tangent; - delete[] tangentDiff; - } - } - if (mesh.weights.size() > i) { - aiAnimMesh.mWeight = mesh.weights[i]; - } - if (mesh.targetNames.size() > i) { - aiAnimMesh.mName = mesh.targetNames[i]; - } - } - } - - aiFace *faces = nullptr; - aiFace *facePtr = nullptr; - size_t nFaces = 0; - - if (prim.indices) { - size_t count = prim.indices->count; - - Accessor::Indexer data = prim.indices->GetIndexer(); - if (!data.IsValid()) { - throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name)); - } - - switch (prim.mode) { - case PrimitiveMode_POINTS: { - nFaces = count; - facePtr = faces = new aiFace[nFaces]; - for (unsigned int i = 0; i < count; ++i) { - SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i)); - } - break; - } - - case PrimitiveMode_LINES: { - nFaces = count / 2; - if (nFaces * 2 != count) { - ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped."); - count = nFaces * 2; - } - facePtr = faces = new aiFace[nFaces]; - for (unsigned int i = 0; i < count; i += 2) { - SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1)); - } - break; - } - - case PrimitiveMode_LINE_LOOP: - case PrimitiveMode_LINE_STRIP: { - nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0); - facePtr = faces = new aiFace[nFaces]; - SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1)); - for (unsigned int i = 2; i < count; ++i) { - SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i - 1), data.GetUInt(i)); - } - if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop - SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(static_cast<int>(count) - 1), faces[0].mIndices[0]); - } - break; - } - - case PrimitiveMode_TRIANGLES: { - nFaces = count / 3; - if (nFaces * 3 != count) { - ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped."); - count = nFaces * 3; - } - facePtr = faces = new aiFace[nFaces]; - for (unsigned int i = 0; i < count; i += 3) { - SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2)); - } - break; - } - case PrimitiveMode_TRIANGLE_STRIP: { - nFaces = count - 2; - facePtr = faces = new aiFace[nFaces]; - for (unsigned int i = 0; i < nFaces; ++i) { - //The ordering is to ensure that the triangles are all drawn with the same orientation - if ((i + 1) % 2 == 0) { - //For even n, vertices n + 1, n, and n + 2 define triangle n - SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2)); - } else { - //For odd n, vertices n, n+1, and n+2 define triangle n - SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2)); - } - } - break; - } - case PrimitiveMode_TRIANGLE_FAN: - nFaces = count - 2; - facePtr = faces = new aiFace[nFaces]; - SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1), data.GetUInt(2)); - for (unsigned int i = 1; i < nFaces; ++i) { - SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(i + 1), data.GetUInt(i + 2)); - } - break; - } - } else { // no indices provided so directly generate from counts - - // use the already determined count as it includes checks - unsigned int count = aim->mNumVertices; - - switch (prim.mode) { - case PrimitiveMode_POINTS: { - nFaces = count; - facePtr = faces = new aiFace[nFaces]; - for (unsigned int i = 0; i < count; ++i) { - SetFaceAndAdvance1(facePtr, aim->mNumVertices, i); - } - break; - } - - case PrimitiveMode_LINES: { - nFaces = count / 2; - if (nFaces * 2 != count) { - ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped."); - count = (unsigned int)nFaces * 2; - } - facePtr = faces = new aiFace[nFaces]; - for (unsigned int i = 0; i < count; i += 2) { - SetFaceAndAdvance2(facePtr, aim->mNumVertices, i, i + 1); - } - break; - } - - case PrimitiveMode_LINE_LOOP: - case PrimitiveMode_LINE_STRIP: { - nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0); - facePtr = faces = new aiFace[nFaces]; - SetFaceAndAdvance2(facePtr, aim->mNumVertices, 0, 1); - for (unsigned int i = 2; i < count; ++i) { - SetFaceAndAdvance2(facePtr, aim->mNumVertices, i - 1, i); - } - if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop - SetFaceAndAdvance2(facePtr, aim->mNumVertices, count - 1, 0); - } - break; - } - - case PrimitiveMode_TRIANGLES: { - nFaces = count / 3; - if (nFaces * 3 != count) { - ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped."); - count = (unsigned int)nFaces * 3; - } - facePtr = faces = new aiFace[nFaces]; - for (unsigned int i = 0; i < count; i += 3) { - SetFaceAndAdvance3(facePtr, aim->mNumVertices, i, i + 1, i + 2); - } - break; - } - case PrimitiveMode_TRIANGLE_STRIP: { - nFaces = count - 2; - facePtr = faces = new aiFace[nFaces]; - for (unsigned int i = 0; i < nFaces; ++i) { - //The ordering is to ensure that the triangles are all drawn with the same orientation - if ((i + 1) % 2 == 0) { - //For even n, vertices n + 1, n, and n + 2 define triangle n - SetFaceAndAdvance3(facePtr, aim->mNumVertices, i + 1, i, i + 2); - } else { - //For odd n, vertices n, n+1, and n+2 define triangle n - SetFaceAndAdvance3(facePtr, aim->mNumVertices, i, i + 1, i + 2); - } - } - break; - } - case PrimitiveMode_TRIANGLE_FAN: - nFaces = count - 2; - facePtr = faces = new aiFace[nFaces]; - SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, 1, 2); - for (unsigned int i = 1; i < nFaces; ++i) { - SetFaceAndAdvance3(facePtr, aim->mNumVertices, 0, i + 1, i + 2); - } - break; - } - } - - if (faces) { - aim->mFaces = faces; - const unsigned int actualNumFaces = static_cast<unsigned int>(facePtr - faces); - if (actualNumFaces < nFaces) { - ASSIMP_LOG_WARN("Some faces had out-of-range indices. Those faces were dropped."); - } - if (actualNumFaces == 0) { - throw DeadlyImportError("Mesh \"", aim->mName.C_Str(), "\" has no faces"); - } - aim->mNumFaces = actualNumFaces; - ai_assert(CheckValidFacesIndices(faces, actualNumFaces, aim->mNumVertices)); - } - - if (prim.material) { - aim->mMaterialIndex = prim.material.GetIndex(); - } else { - aim->mMaterialIndex = mScene->mNumMaterials - 1; - } - } - } - - meshOffsets.push_back(k); - - CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes); -} - -void glTF2Importer::ImportCameras(glTF2::Asset &r) { - if (!r.cameras.Size()) { - return; - } - - const unsigned int numCameras = r.cameras.Size(); - ASSIMP_LOG_DEBUG("Importing ", numCameras, " cameras"); - mScene->mNumCameras = numCameras; - mScene->mCameras = new aiCamera *[numCameras]; - std::fill(mScene->mCameras, mScene->mCameras + numCameras, nullptr); - - for (size_t i = 0; i < numCameras; ++i) { - Camera &cam = r.cameras[i]; - - aiCamera *aicam = mScene->mCameras[i] = new aiCamera(); - - // cameras point in -Z by default, rest is specified in node transform - aicam->mLookAt = aiVector3D(0.f, 0.f, -1.f); - - if (cam.type == Camera::Perspective) { - aicam->mAspect = cam.cameraProperties.perspective.aspectRatio; - aicam->mHorizontalFOV = cam.cameraProperties.perspective.yfov * ((aicam->mAspect == 0.f) ? 1.f : aicam->mAspect); - aicam->mClipPlaneFar = cam.cameraProperties.perspective.zfar; - aicam->mClipPlaneNear = cam.cameraProperties.perspective.znear; - } else { - aicam->mClipPlaneFar = cam.cameraProperties.ortographic.zfar; - aicam->mClipPlaneNear = cam.cameraProperties.ortographic.znear; - aicam->mHorizontalFOV = 0.0; - aicam->mOrthographicWidth = cam.cameraProperties.ortographic.xmag; - aicam->mAspect = 1.0f; - if (0.f != cam.cameraProperties.ortographic.ymag) { - aicam->mAspect = cam.cameraProperties.ortographic.xmag / cam.cameraProperties.ortographic.ymag; - } - } - } -} - -void glTF2Importer::ImportLights(glTF2::Asset &r) { - if (!r.lights.Size()) { - return; - } - - const unsigned int numLights = r.lights.Size(); - ASSIMP_LOG_DEBUG("Importing ", numLights, " lights"); - mScene->mNumLights = numLights; - mScene->mLights = new aiLight *[numLights]; - std::fill(mScene->mLights, mScene->mLights + numLights, nullptr); - - for (size_t i = 0; i < numLights; ++i) { - Light &light = r.lights[i]; - - aiLight *ail = mScene->mLights[i] = new aiLight(); - - switch (light.type) { - case Light::Directional: - ail->mType = aiLightSource_DIRECTIONAL; - break; - case Light::Point: - ail->mType = aiLightSource_POINT; - break; - case Light::Spot: - ail->mType = aiLightSource_SPOT; - break; - } - - if (ail->mType != aiLightSource_POINT) { - ail->mDirection = aiVector3D(0.0f, 0.0f, -1.0f); - ail->mUp = aiVector3D(0.0f, 1.0f, 0.0f); - } - - vec3 colorWithIntensity = { light.color[0] * light.intensity, light.color[1] * light.intensity, light.color[2] * light.intensity }; - CopyValue(colorWithIntensity, ail->mColorAmbient); - CopyValue(colorWithIntensity, ail->mColorDiffuse); - CopyValue(colorWithIntensity, ail->mColorSpecular); - - if (ail->mType == aiLightSource_DIRECTIONAL) { - ail->mAttenuationConstant = 1.0; - ail->mAttenuationLinear = 0.0; - ail->mAttenuationQuadratic = 0.0; - } else { - //in PBR attenuation is calculated using inverse square law which can be expressed - //using assimps equation: 1/(att0 + att1 * d + att2 * d*d) with the following parameters - //this is correct equation for the case when range (see - //https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual) - //is not present. When range is not present it is assumed that it is infinite and so numerator is 1. - //When range is present then numerator might be any value in range [0,1] and then assimps equation - //will not suffice. In this case range is added into metadata in ImportNode function - //and its up to implementation to read it when it wants to - ail->mAttenuationConstant = 0.0; - ail->mAttenuationLinear = 0.0; - ail->mAttenuationQuadratic = 1.0; - } - - if (ail->mType == aiLightSource_SPOT) { - ail->mAngleInnerCone = light.innerConeAngle; - ail->mAngleOuterCone = light.outerConeAngle; - } - } -} - -static void GetNodeTransform(aiMatrix4x4 &matrix, const glTF2::Node &node) { - if (node.matrix.isPresent) { - CopyValue(node.matrix.value, matrix); - return; - } - - if (node.translation.isPresent) { - aiVector3D trans; - CopyValue(node.translation.value, trans); - aiMatrix4x4 t; - aiMatrix4x4::Translation(trans, t); - matrix = matrix * t; - } - - if (node.rotation.isPresent) { - aiQuaternion rot; - CopyValue(node.rotation.value, rot); - matrix = matrix * aiMatrix4x4(rot.GetMatrix()); - } - - if (node.scale.isPresent) { - aiVector3D scal(1.f); - CopyValue(node.scale.value, scal); - aiMatrix4x4 s; - aiMatrix4x4::Scaling(scal, s); - matrix = matrix * s; - } -} - -static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std::vector<aiVertexWeight>> &map) { - Mesh::Primitive::Attributes &attr = primitive.attributes; - if (attr.weight.empty() || attr.joint.empty()) { - return; - } - if (attr.weight[0]->count != attr.joint[0]->count) { - return; - } - - size_t num_vertices = attr.weight[0]->count; - - struct Weights { - float values[4]; - }; - Weights *weights = nullptr; - attr.weight[0]->ExtractData(weights); - - struct Indices8 { - uint8_t values[4]; - }; - struct Indices16 { - uint16_t values[4]; - }; - Indices8 *indices8 = nullptr; - Indices16 *indices16 = nullptr; - if (attr.joint[0]->GetElementSize() == 4) { - attr.joint[0]->ExtractData(indices8); - } else { - attr.joint[0]->ExtractData(indices16); - } - // - if (nullptr == indices8 && nullptr == indices16) { - // Something went completely wrong! - ai_assert(false); - return; - } - - for (size_t i = 0; i < num_vertices; ++i) { - for (int j = 0; j < 4; ++j) { - const unsigned int bone = (indices8 != nullptr) ? indices8[i].values[j] : indices16[i].values[j]; - const float weight = weights[i].values[j]; - if (weight > 0 && bone < map.size()) { - map[bone].reserve(8); - map[bone].emplace_back(static_cast<unsigned int>(i), weight); - } - } - } - - delete[] weights; - delete[] indices8; - delete[] indices16; -} - -static std::string GetNodeName(const Node &node) { - return node.name.empty() ? node.id : node.name; -} - -void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) { - if (extension.mStringValue.isPresent) { - metadata->Add(extension.name, aiString(extension.mStringValue.value)); - } else if (extension.mDoubleValue.isPresent) { - metadata->Add(extension.name, extension.mDoubleValue.value); - } else if (extension.mUint64Value.isPresent) { - metadata->Add(extension.name, extension.mUint64Value.value); - } else if (extension.mInt64Value.isPresent) { - metadata->Add(extension.name, static_cast<int32_t>(extension.mInt64Value.value)); - } else if (extension.mBoolValue.isPresent) { - metadata->Add(extension.name, extension.mBoolValue.value); - } else if (extension.mValues.isPresent) { - aiMetadata val; - for (auto const & subExtension : extension.mValues.value) { - ParseExtensions(&val, subExtension); - } - metadata->Add(extension.name, val); - } -} - -void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) { - if (extension.mValues.isPresent) { - for (auto const & subExtension : extension.mValues.value) { - ParseExtensions(metadata, subExtension); - } - } -} - -aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) { - Node &node = *ptr; - - aiNode *ainode = new aiNode(GetNodeName(node)); - - try { - if (!node.children.empty()) { - ainode->mNumChildren = unsigned(node.children.size()); - ainode->mChildren = new aiNode *[ainode->mNumChildren]; - std::fill(ainode->mChildren, ainode->mChildren + ainode->mNumChildren, nullptr); - - for (unsigned int i = 0; i < ainode->mNumChildren; ++i) { - aiNode *child = ImportNode(pScene, r, meshOffsets, node.children[i]); - child->mParent = ainode; - ainode->mChildren[i] = child; - } - } - - if (node.customExtensions || node.extras) { - ainode->mMetaData = new aiMetadata; - if (node.customExtensions) { - ParseExtensions(ainode->mMetaData, node.customExtensions); - } - if (node.extras) { - ParseExtras(ainode->mMetaData, node.extras); - } - } - - GetNodeTransform(ainode->mTransformation, node); - - if (!node.meshes.empty()) { - // GLTF files contain at most 1 mesh per node. - if (node.meshes.size() > 1) - { - throw DeadlyImportError("GLTF: Invalid input, found ", node.meshes.size(), - " meshes in ", getContextForErrorMessages(node.id, node.name), - ", but only 1 mesh per node allowed."); - } - int mesh_idx = node.meshes[0].GetIndex(); - int count = meshOffsets[mesh_idx + 1] - meshOffsets[mesh_idx]; - - ainode->mNumMeshes = count; - ainode->mMeshes = new unsigned int[count]; - - if (node.skin) { - for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) { - aiMesh *mesh = pScene->mMeshes[meshOffsets[mesh_idx] + primitiveNo]; - unsigned int numBones =static_cast<unsigned int>(node.skin->jointNames.size()); - - std::vector<std::vector<aiVertexWeight>> weighting(numBones); - BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting); - - mesh->mNumBones = static_cast<unsigned int>(numBones); - mesh->mBones = new aiBone *[mesh->mNumBones]; - std::fill(mesh->mBones, mesh->mBones + mesh->mNumBones, nullptr); - - // GLTF and Assimp choose to store bone weights differently. - // GLTF has each vertex specify which bones influence the vertex. - // Assimp has each bone specify which vertices it has influence over. - // To convert this data, we first read over the vertex data and pull - // out the bone-to-vertex mapping. Then, when creating the aiBones, - // we copy the bone-to-vertex mapping into the bone. This is unfortunate - // both because it's somewhat slow and because, for many applications, - // we then need to reconvert the data back into the vertex-to-bone - // mapping which makes things doubly-slow. - - mat4 *pbindMatrices = nullptr; - node.skin->inverseBindMatrices->ExtractData(pbindMatrices); - - for (uint32_t i = 0; i < numBones; ++i) { - const std::vector<aiVertexWeight> &weights = weighting[i]; - aiBone *bone = new aiBone(); - - Ref<Node> joint = node.skin->jointNames[i]; - if (!joint->name.empty()) { - bone->mName = joint->name; - } else { - // Assimp expects each bone to have a unique name. - static const std::string kDefaultName = "bone_"; - char postfix[10] = { 0 }; - ASSIMP_itoa10(postfix, i); - bone->mName = (kDefaultName + postfix); - } - GetNodeTransform(bone->mOffsetMatrix, *joint); - CopyValue(pbindMatrices[i], bone->mOffsetMatrix); - bone->mNumWeights = static_cast<uint32_t>(weights.size()); - - if (bone->mNumWeights > 0) { - bone->mWeights = new aiVertexWeight[bone->mNumWeights]; - memcpy(bone->mWeights, weights.data(), bone->mNumWeights * sizeof(aiVertexWeight)); - } else { - // Assimp expects all bones to have at least 1 weight. - bone->mWeights = new aiVertexWeight[1]; - bone->mNumWeights = 1; - bone->mWeights->mVertexId = 0; - bone->mWeights->mWeight = 0.f; - } - mesh->mBones[i] = bone; - } - - if (pbindMatrices) { - delete[] pbindMatrices; - } - } - } - - int k = 0; - for (unsigned int j = meshOffsets[mesh_idx]; j < meshOffsets[mesh_idx + 1]; ++j, ++k) { - ainode->mMeshes[k] = j; - } - } - - if (node.camera) { - pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName; - if (node.translation.isPresent) { - aiVector3D trans; - CopyValue(node.translation.value, trans); - pScene->mCameras[node.camera.GetIndex()]->mPosition = trans; - } - } - - if (node.light) { - pScene->mLights[node.light.GetIndex()]->mName = ainode->mName; - - //range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual - //it is added to meta data of parent node, because there is no other place to put it - if (node.light->range.isPresent) { - if (!ainode->mMetaData) { - ainode->mMetaData = aiMetadata::Alloc(1); - ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value); - } else { - ainode->mMetaData->Add("PBR_LightRange", node.light->range.value); - } - } - } - - return ainode; - } catch (...) { - delete ainode; - throw; - } -} - -void glTF2Importer::ImportNodes(glTF2::Asset &r) { - if (!r.scene) { - throw DeadlyImportError("GLTF: No scene"); - } - ASSIMP_LOG_DEBUG("Importing nodes"); - - std::vector<Ref<Node>> rootNodes = r.scene->nodes; - - // The root nodes - unsigned int numRootNodes = unsigned(rootNodes.size()); - if (numRootNodes == 1) { // a single root node: use it - mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]); - } else if (numRootNodes > 1) { // more than one root node: create a fake root - aiNode *root = mScene->mRootNode = new aiNode("ROOT"); - - root->mChildren = new aiNode *[numRootNodes]; - std::fill(root->mChildren, root->mChildren + numRootNodes, nullptr); - - for (unsigned int i = 0; i < numRootNodes; ++i) { - aiNode *node = ImportNode(mScene, r, meshOffsets, rootNodes[i]); - node->mParent = root; - root->mChildren[root->mNumChildren++] = node; - } - } else { - mScene->mRootNode = new aiNode("ROOT"); - } -} - -struct AnimationSamplers { - AnimationSamplers() : - translation(nullptr), - rotation(nullptr), - scale(nullptr), - weight(nullptr) { - // empty - } - - Animation::Sampler *translation; - Animation::Sampler *rotation; - Animation::Sampler *scale; - Animation::Sampler *weight; -}; - -aiNodeAnim *CreateNodeAnim(glTF2::Asset&, Node &node, AnimationSamplers &samplers) { - aiNodeAnim *anim = new aiNodeAnim(); - - try { - anim->mNodeName = GetNodeName(node); - - static const float kMillisecondsFromSeconds = 1000.f; - - if (samplers.translation && samplers.translation->input && samplers.translation->output) { - float *times = nullptr; - samplers.translation->input->ExtractData(times); - aiVector3D *values = nullptr; - samplers.translation->output->ExtractData(values); - anim->mNumPositionKeys = static_cast<uint32_t>(samplers.translation->input->count); - anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys]; - unsigned int ii = (samplers.translation->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0; - for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) { - anim->mPositionKeys[i].mTime = times[i] * kMillisecondsFromSeconds; - anim->mPositionKeys[i].mValue = values[ii]; - ii += (samplers.translation->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1; - } - delete[] times; - delete[] values; - } else if (node.translation.isPresent) { - anim->mNumPositionKeys = 1; - anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys]; - anim->mPositionKeys->mTime = 0.f; - anim->mPositionKeys->mValue.x = node.translation.value[0]; - anim->mPositionKeys->mValue.y = node.translation.value[1]; - anim->mPositionKeys->mValue.z = node.translation.value[2]; - } - - if (samplers.rotation && samplers.rotation->input && samplers.rotation->output) { - float *times = nullptr; - samplers.rotation->input->ExtractData(times); - aiQuaternion *values = nullptr; - samplers.rotation->output->ExtractData(values); - anim->mNumRotationKeys = static_cast<uint32_t>(samplers.rotation->input->count); - anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys]; - unsigned int ii = (samplers.rotation->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0; - for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) { - anim->mRotationKeys[i].mTime = times[i] * kMillisecondsFromSeconds; - anim->mRotationKeys[i].mValue.x = values[ii].w; - anim->mRotationKeys[i].mValue.y = values[ii].x; - anim->mRotationKeys[i].mValue.z = values[ii].y; - anim->mRotationKeys[i].mValue.w = values[ii].z; - ii += (samplers.rotation->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1; - } - delete[] times; - delete[] values; - } else if (node.rotation.isPresent) { - anim->mNumRotationKeys = 1; - anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys]; - anim->mRotationKeys->mTime = 0.f; - anim->mRotationKeys->mValue.x = node.rotation.value[0]; - anim->mRotationKeys->mValue.y = node.rotation.value[1]; - anim->mRotationKeys->mValue.z = node.rotation.value[2]; - anim->mRotationKeys->mValue.w = node.rotation.value[3]; - } - - if (samplers.scale && samplers.scale->input && samplers.scale->output) { - float *times = nullptr; - samplers.scale->input->ExtractData(times); - aiVector3D *values = nullptr; - samplers.scale->output->ExtractData(values); - anim->mNumScalingKeys = static_cast<uint32_t>(samplers.scale->input->count); - anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys]; - unsigned int ii = (samplers.scale->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0; - for (unsigned int i = 0; i < anim->mNumScalingKeys; ++i) { - anim->mScalingKeys[i].mTime = times[i] * kMillisecondsFromSeconds; - anim->mScalingKeys[i].mValue = values[ii]; - ii += (samplers.scale->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1; - } - delete[] times; - delete[] values; - } else if (node.scale.isPresent) { - anim->mNumScalingKeys = 1; - anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys]; - anim->mScalingKeys->mTime = 0.f; - anim->mScalingKeys->mValue.x = node.scale.value[0]; - anim->mScalingKeys->mValue.y = node.scale.value[1]; - anim->mScalingKeys->mValue.z = node.scale.value[2]; - } - - return anim; - } catch (...) { - delete anim; - throw; - } -} - -aiMeshMorphAnim *CreateMeshMorphAnim(glTF2::Asset&, Node &node, AnimationSamplers &samplers) { - auto *anim = new aiMeshMorphAnim(); - - try { - anim->mName = GetNodeName(node); - - static const float kMillisecondsFromSeconds = 1000.f; - - if (samplers.weight && samplers.weight->input && samplers.weight->output) { - float *times = nullptr; - samplers.weight->input->ExtractData(times); - float *values = nullptr; - samplers.weight->output->ExtractData(values); - anim->mNumKeys = static_cast<uint32_t>(samplers.weight->input->count); - - // for Interpolation_CUBICSPLINE can have more outputs - const unsigned int weightStride = (unsigned int)samplers.weight->output->count / anim->mNumKeys; - const unsigned int numMorphs = (samplers.weight->interpolation == Interpolation_CUBICSPLINE) ? weightStride - 2 : weightStride; - - anim->mKeys = new aiMeshMorphKey[anim->mNumKeys]; - unsigned int ii = (samplers.weight->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0; - for (unsigned int i = 0u; i < anim->mNumKeys; ++i) { - unsigned int k = weightStride * i + ii; - anim->mKeys[i].mTime = times[i] * kMillisecondsFromSeconds; - anim->mKeys[i].mNumValuesAndWeights = numMorphs; - anim->mKeys[i].mValues = new unsigned int[numMorphs]; - anim->mKeys[i].mWeights = new double[numMorphs]; - - for (unsigned int j = 0u; j < numMorphs; ++j, ++k) { - anim->mKeys[i].mValues[j] = j; - anim->mKeys[i].mWeights[j] = (0.f > values[k]) ? 0.f : values[k]; - } - } - - delete[] times; - delete[] values; - } - - return anim; - } catch (...) { - delete anim; - throw; - } -} - -std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation &anim) { - std::unordered_map<unsigned int, AnimationSamplers> samplers; - for (unsigned int c = 0; c < anim.channels.size(); ++c) { - Animation::Channel &channel = anim.channels[c]; - if (channel.sampler < 0 || channel.sampler >= static_cast<int>(anim.samplers.size())) { - continue; - } - - auto& animsampler = anim.samplers[channel.sampler]; - - if (!animsampler.input) { - ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); - continue; - } - - if (!animsampler.output) { - ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping."); - continue; - } - - if (animsampler.input->count > animsampler.output->count) { - ASSIMP_LOG_WARN("Animation ", anim.name, ": Number of keyframes in sampler input ", animsampler.input->count, " exceeds number of keyframes in sampler output ", animsampler.output->count); - continue; - } - - const unsigned int node_index = channel.target.node.GetIndex(); - - AnimationSamplers &sampler = samplers[node_index]; - if (channel.target.path == AnimationPath_TRANSLATION) { - sampler.translation = &anim.samplers[channel.sampler]; - } else if (channel.target.path == AnimationPath_ROTATION) { - sampler.rotation = &anim.samplers[channel.sampler]; - } else if (channel.target.path == AnimationPath_SCALE) { - sampler.scale = &anim.samplers[channel.sampler]; - } else if (channel.target.path == AnimationPath_WEIGHTS) { - sampler.weight = &anim.samplers[channel.sampler]; - } - } - - return samplers; -} - -void glTF2Importer::ImportAnimations(glTF2::Asset &r) { - if (!r.scene) return; - - const unsigned numAnimations = r.animations.Size(); - ASSIMP_LOG_DEBUG("Importing ", numAnimations, " animations"); - mScene->mNumAnimations = numAnimations; - if (mScene->mNumAnimations == 0) { - return; - } - - mScene->mAnimations = new aiAnimation *[numAnimations]; - std::fill(mScene->mAnimations, mScene->mAnimations + numAnimations, nullptr); - - for (unsigned int i = 0; i < numAnimations; ++i) { - aiAnimation *ai_anim = mScene->mAnimations[i] = new aiAnimation(); - - Animation &anim = r.animations[i]; - - ai_anim->mName = anim.name; - ai_anim->mDuration = 0; - ai_anim->mTicksPerSecond = 0; - - std::unordered_map<unsigned int, AnimationSamplers> samplers = GatherSamplers(anim); - - uint32_t numChannels = 0u; - uint32_t numMorphMeshChannels = 0u; - - for (auto &iter : samplers) { - if ((nullptr != iter.second.rotation) || (nullptr != iter.second.scale) || (nullptr != iter.second.translation)) { - ++numChannels; - } - if (nullptr != iter.second.weight) { - ++numMorphMeshChannels; - } - } - - ai_anim->mNumChannels = numChannels; - if (ai_anim->mNumChannels > 0) { - ai_anim->mChannels = new aiNodeAnim *[ai_anim->mNumChannels]; - std::fill(ai_anim->mChannels, ai_anim->mChannels + ai_anim->mNumChannels, nullptr); - int j = 0; - for (auto &iter : samplers) { - if ((nullptr != iter.second.rotation) || (nullptr != iter.second.scale) || (nullptr != iter.second.translation)) { - ai_anim->mChannels[j] = CreateNodeAnim(r, r.nodes[iter.first], iter.second); - ++j; - } - } - } - - ai_anim->mNumMorphMeshChannels = numMorphMeshChannels; - if (ai_anim->mNumMorphMeshChannels > 0) { - ai_anim->mMorphMeshChannels = new aiMeshMorphAnim *[ai_anim->mNumMorphMeshChannels]; - std::fill(ai_anim->mMorphMeshChannels, ai_anim->mMorphMeshChannels + ai_anim->mNumMorphMeshChannels, nullptr); - int j = 0; - for (auto &iter : samplers) { - if (nullptr != iter.second.weight) { - ai_anim->mMorphMeshChannels[j] = CreateMeshMorphAnim(r, r.nodes[iter.first], iter.second); - ++j; - } - } - } - - // Use the latest key-frame for the duration of the animation - double maxDuration = 0; - unsigned int maxNumberOfKeys = 0; - for (unsigned int j = 0; j < ai_anim->mNumChannels; ++j) { - auto chan = ai_anim->mChannels[j]; - if (chan->mNumPositionKeys) { - auto lastPosKey = chan->mPositionKeys[chan->mNumPositionKeys - 1]; - if (lastPosKey.mTime > maxDuration) { - maxDuration = lastPosKey.mTime; - } - maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumPositionKeys); - } - if (chan->mNumRotationKeys) { - auto lastRotKey = chan->mRotationKeys[chan->mNumRotationKeys - 1]; - if (lastRotKey.mTime > maxDuration) { - maxDuration = lastRotKey.mTime; - } - maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumRotationKeys); - } - if (chan->mNumScalingKeys) { - auto lastScaleKey = chan->mScalingKeys[chan->mNumScalingKeys - 1]; - if (lastScaleKey.mTime > maxDuration) { - maxDuration = lastScaleKey.mTime; - } - maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumScalingKeys); - } - } - - for (unsigned int j = 0; j < ai_anim->mNumMorphMeshChannels; ++j) { - const auto *const chan = ai_anim->mMorphMeshChannels[j]; - - if (0u != chan->mNumKeys) { - const auto &lastKey = chan->mKeys[chan->mNumKeys - 1u]; - if (lastKey.mTime > maxDuration) { - maxDuration = lastKey.mTime; - } - maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumKeys); - } - } - - ai_anim->mDuration = maxDuration; - ai_anim->mTicksPerSecond = 1000.0; - } -} - -static unsigned int countEmbeddedTextures(glTF2::Asset &r) { - unsigned int numEmbeddedTexs = 0; - for (size_t i = 0; i < r.images.Size(); ++i) { - if (r.images[i].HasData()) { - numEmbeddedTexs += 1; - } - } - - return numEmbeddedTexs; -} - -void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { - mEmbeddedTexIdxs.resize(r.images.Size(), -1); - const unsigned int numEmbeddedTexs = countEmbeddedTextures(r); - if (numEmbeddedTexs == 0) { - return; - } - - ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); - - mScene->mTextures = new aiTexture *[numEmbeddedTexs]; - std::fill(mScene->mTextures, mScene->mTextures + numEmbeddedTexs, nullptr); - - // Add the embedded textures - for (size_t i = 0; i < r.images.Size(); ++i) { - Image &img = r.images[i]; - if (!img.HasData()) { - continue; - } - - int idx = mScene->mNumTextures++; - mEmbeddedTexIdxs[i] = idx; - - aiTexture *tex = mScene->mTextures[idx] = new aiTexture(); - - size_t length = img.GetDataLength(); - void *data = img.StealData(); - - tex->mFilename = img.name; - tex->mWidth = static_cast<unsigned int>(length); - tex->mHeight = 0; - tex->pcData = reinterpret_cast<aiTexel *>(data); - - if (!img.mimeType.empty()) { - const char *ext = strchr(img.mimeType.c_str(), '/') + 1; - if (ext) { - if (strcmp(ext, "jpeg") == 0) { - ext = "jpg"; - } else if(strcmp(ext, "ktx2") == 0) { //basisu: ktx remains - ext = "kx2"; - } else if(strcmp(ext, "basis") == 0) { //basisu - ext = "bu"; - } - - size_t len = strlen(ext); - if (len <= 3) { - strcpy(tex->achFormatHint, ext); - } - } - } - } -} - -void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { - ASSIMP_LOG_DEBUG("Importing metadata"); - ai_assert(mScene->mMetaData == nullptr); - const bool hasVersion = !a.asset.version.empty(); - const bool hasGenerator = !a.asset.generator.empty(); - const bool hasCopyright = !a.asset.copyright.empty(); - const bool hasSceneMetadata = a.scene->customExtensions; - if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata) { - mScene->mMetaData = new aiMetadata; - if (hasVersion) { - mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version)); - } - if (hasGenerator) { - mScene->mMetaData->Add(AI_METADATA_SOURCE_GENERATOR, aiString(a.asset.generator)); - } - if (hasCopyright) { - mScene->mMetaData->Add(AI_METADATA_SOURCE_COPYRIGHT, aiString(a.asset.copyright)); - } - if (hasSceneMetadata) { - ParseExtensions(mScene->mMetaData, a.scene->customExtensions); - } - } -} - -void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { - ASSIMP_LOG_DEBUG("Reading GLTF2 file"); - - // clean all member arrays - meshOffsets.clear(); - mEmbeddedTexIdxs.clear(); - - this->mScene = pScene; - - // read the asset file - glTF2::Asset asset(pIOHandler, static_cast<rapidjson::IRemoteSchemaDocumentProvider*>(mSchemaDocumentProvider)); - asset.Load(pFile, GetExtension(pFile) == "glb"); - if (asset.scene) { - pScene->mName = asset.scene->name; - } - - // Copy the data out - ImportEmbeddedTextures(asset); - ImportMaterials(asset); - - ImportMeshes(asset); - - ImportCameras(asset); - ImportLights(asset); - - ImportNodes(asset); - - ImportAnimations(asset); - - ImportCommonMetadata(asset); - - if (pScene->mNumMeshes == 0) { - pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; - } -} - -void glTF2Importer::SetupProperties(const Importer *pImp) { - mSchemaDocumentProvider = static_cast<rapidjson::IRemoteSchemaDocumentProvider*>(pImp->GetPropertyPointer(AI_CONFIG_IMPORT_SCHEMA_DOCUMENT_PROVIDER)); -} - -#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER diff --git a/libs/assimp/code/AssetLib/glTF2/glTF2Importer.h b/libs/assimp/code/AssetLib/glTF2/glTF2Importer.h deleted file mode 100644 index 831bcd7..0000000 --- a/libs/assimp/code/AssetLib/glTF2/glTF2Importer.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -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. - ----------------------------------------------------------------------- -*/ -#ifndef AI_GLTF2IMPORTER_H_INC -#define AI_GLTF2IMPORTER_H_INC - -#include <assimp/BaseImporter.h> - -struct aiNode; - -namespace glTF2 { - class Asset; -} - -namespace Assimp { - -/** - * Load the glTF2 format. - * https://github.com/KhronosGroup/glTF/tree/master/specification - */ -class glTF2Importer : public BaseImporter { -public: - glTF2Importer(); - ~glTF2Importer() override; - bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; - -protected: - const aiImporterDesc *GetInfo() const override; - void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; - virtual void SetupProperties(const Importer *pImp) override; - -private: - void ImportEmbeddedTextures(glTF2::Asset &a); - void ImportMaterials(glTF2::Asset &a); - void ImportMeshes(glTF2::Asset &a); - void ImportCameras(glTF2::Asset &a); - void ImportLights(glTF2::Asset &a); - void ImportNodes(glTF2::Asset &a); - void ImportAnimations(glTF2::Asset &a); - void ImportCommonMetadata(glTF2::Asset &a); - -private: - std::vector<unsigned int> meshOffsets; - std::vector<int> mEmbeddedTexIdxs; - aiScene *mScene; - - /// An instance of rapidjson::IRemoteSchemaDocumentProvider - void *mSchemaDocumentProvider = nullptr; -}; - -} // namespace Assimp - -#endif // AI_GLTF2IMPORTER_H_INC |