[+] glTF import/export. Support for Open3DGC-compression of mesh primitives.
authorAlexandr Arutjunov <smal.root@gmail.com>
Wed, 10 Aug 2016 22:04:30 +0000 (01:04 +0300)
committerAlexandr Arutjunov <smal.root@gmail.com>
Wed, 10 Aug 2016 22:04:30 +0000 (01:04 +0300)
code/glTFAsset.h
code/glTFAsset.inl
code/glTFAssetWriter.inl
code/glTFExporter.cpp
code/glTFImporter.cpp

index 5f4f933..90335f5 100644 (file)
@@ -450,88 +450,49 @@ namespace glTF
 
     //! A buffer points to binary geometry, animation, or skins.
     struct Buffer : public Object
-    {
-    public:
-
-        enum Type
-        {
-            Type_arraybuffer,
-            Type_text
-        };
-
-        //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")
-
-        Type type;
-
-    private:
-        shared_ptr<uint8_t> mData; //!< Pointer to the data
-        bool mIsSpecial; //!< Set to true for special cases (e.g. the body buffer)
-
-    public:
-        Buffer();
-
-        void Read(Value& obj, Asset& r);
-
-        bool LoadFromStream(IOStream& stream, size_t length = 0, size_t baseOffset = 0);
+       {
+               /********************* Types *********************/
+       public:
 
-               /// \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. For example: decoded/encoded data.
-               /// \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);
+               enum Type
+               {
+                       Type_arraybuffer,
+                       Type_text
+               };
 
-        size_t AppendData(uint8_t* data, size_t length);
-        void Grow(size_t amount);
+               /// \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.
 
-        uint8_t* GetPointer()
-            { return mData.get(); }
+                       /// \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)
+                       {}
 
-        void MarkAsSpecial()
-            { mIsSpecial = true; }
+                       /// \fn ~SEncodedRegion()
+                       /// Destructor.
+                       ~SEncodedRegion() { delete [] DecodedData; }
+               };
 
-        bool IsSpecial() const
-            { return mIsSpecial; }
+               /******************* Variables *******************/
 
-        static const char* TranslateId(Asset& r, const char* id);
-    };
+               //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")
 
-       /// \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; }
-       };
-
-    //! 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)
+               Type type;
 
                /// \var EncodedRegion_Current
                /// Pointer to currently active encoded region.
@@ -555,19 +516,25 @@ namespace glTF
                /// 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;
 
-        BufferViewTarget target; //! The target that the WebGL buffer should be bound to.
+               /******************* Functions *******************/
 
-               BufferView()
-                       : EncodedRegion_Current(nullptr)
-               {}
+       public:
 
-               ~BufferView() { for(SEncodedRegion* reg : EncodedRegion_List) delete reg; }
+               Buffer();
+               ~Buffer();
 
-        void Read(Value& obj, Asset& r);
+               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.
@@ -582,8 +549,42 @@ namespace glTF
                /// 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);
+
+        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
+            { return mIsSpecial; }
+
+        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)
+
+        BufferViewTarget target; //! The target that the WebGL buffer should be bound to.
+
+        void Read(Value& obj, Asset& r);
+    };
 
     struct Camera : public Object
     {
@@ -739,6 +740,7 @@ namespace glTF
                        std::string Buffer;///< ID of "buffer" used for storing compressed data.
                        size_t Offset;///< Offset in "bufferView" where compressed data are stored.
                        size_t Count;///< Count of elements in compressed data. Is always equivalent to size in bytes: look comments for "Type" and "Component_Type".
+                       bool Binary;///< If true then "binary" mode is used for coding, if false - "ascii" mode.
                        size_t IndicesCount;///< Count of indices in mesh.
                        size_t VerticesCount;///< Count of vertices in mesh.
                        // AttribType::Value Type;///< Is always "SCALAR".
@@ -766,11 +768,11 @@ namespace glTF
                /// \param [out] pAsset_Root - reference to root assed where data will be stored.
                void Read(Value& pJSON_Object, Asset& pAsset_Root);
 
-               /// \fn void Decode_O3DGC(Value& pJSON_Object_CompressedData, Asset& pAsset_Root)
-               /// Decode part of "bufferView" which encoded with Open3DGC algorythm.
-               /// \param [in] pJSON_Object_CompressedData - reference to JSON-object which is "compressedData" block.
+               /// \fn void Decode_O3DGC(const SCompression_Open3DGC& pCompression_Open3DGC, Asset& pAsset_Root)
+               /// Decode part of "buffer" which encoded with Open3DGC algorithm.
+               /// \param [in] pCompression_Open3DGC - reference to structure which describe encoded region.
                /// \param [out] pAsset_Root - reference to root assed where data will be stored.
-               void Decode_O3DGC(Value& pJSON_Object_CompressedData, Asset& pAsset_Root);
+               void Decode_O3DGC(const SCompression_Open3DGC& pCompression_Open3DGC, Asset& pAsset_Root);
     };
 
     struct Node : public Object
@@ -941,6 +943,7 @@ namespace glTF
 
         Ref<T> Get(const char* id);
         Ref<T> Get(unsigned int i);
+               Ref<T> Get(const std::string& pID) { return Get(pID.c_str()); }
 
         Ref<T> Create(const char* id);
         Ref<T> Create(const std::string& id)
index de9c165..3397171 100644 (file)
@@ -247,9 +247,14 @@ Ref<T> LazyDict<T>::Create(const char* id)
 
 
 inline Buffer::Buffer()
-: byteLength(0), type(Type_arraybuffer), mIsSpecial(false)
+       : 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)
 {
     // Compatibility with old spec
@@ -330,6 +335,57 @@ inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseO
     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)
+       {
+               constexpr uint8_t val_size = 32;
+
+               char val[val_size];
+
+               ai_snprintf(val, val_size, "%llu", (long long)pOffset);
+               throw DeadlyImportError(std::string("GLTF: incorrect offset value (") + val + ") for marking encoded region.");
+       }
+
+       // Check length
+       if((pOffset + pEncodedData_Length) > byteLength)
+       {
+               constexpr uint8_t val_size = 64;
+
+               char val[val_size];
+
+               ai_snprintf(val, val_size, "%llu, %llu", (long long)pOffset, (long long)pEncodedData_Length);
+               throw DeadlyImportError(std::string("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)
 {
 const size_t new_data_size = byteLength + pReplace_Count - pBufferData_Count;
@@ -384,58 +440,9 @@ inline void BufferView::Read(Value& obj, Asset& r)
     byteLength = MemberOrDefault(obj, "byteLength", 0u);
 }
 
-inline void BufferView::EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string& pID)
-{
-const size_t last = byteOffset + byteLength;
-
-       // 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 < byteOffset) || (pOffset > last))
-       {
-               constexpr uint8_t val_size = 32;
-
-               char val[val_size];
-
-               ai_snprintf(val, val_size, "%llu", (long long)pOffset);
-               throw DeadlyImportError(std::string("GLTF: incorrect offset value (") + val + ") for marking encoded region.");
-       }
-
-       // Check length
-       if((pOffset + pEncodedData_Length) > last)
-       {
-               constexpr uint8_t val_size = 64;
-
-               char val[val_size];
-
-               ai_snprintf(val, val_size, "%llu, %llu", (long long)pOffset, (long long)pEncodedData_Length);
-               throw DeadlyImportError(std::string("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 BufferView::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.");
-}
+//
+// struct Accessor
+//
 
 inline void Accessor::Read(Value& obj, Asset& r)
 {
@@ -477,12 +484,12 @@ inline uint8_t* Accessor::GetPointer()
     size_t offset = byteOffset + bufferView->byteOffset;
 
        // Check if region is encoded.
-       if(bufferView->EncodedRegion_Current != nullptr)
+       if(bufferView->buffer->EncodedRegion_Current != nullptr)
        {
-               const size_t begin = bufferView->EncodedRegion_Current->Offset;
-               const size_t end = bufferView->EncodedRegion_Current->Offset + bufferView->EncodedRegion_Current->DecodedData_Length;
+               const size_t begin = bufferView->buffer->EncodedRegion_Current->Offset;
+               const size_t end = bufferView->buffer->EncodedRegion_Current->Offset + bufferView->buffer->EncodedRegion_Current->DecodedData_Length;
 
-               if((offset >= begin) && (offset < end)) return &bufferView->EncodedRegion_Current->DecodedData[offset - begin];
+               if((offset >= begin) && (offset < end)) return &bufferView->buffer->EncodedRegion_Current->DecodedData[offset - begin];
        }
 
        return basePtr + offset;
@@ -770,33 +777,7 @@ namespace {
 
 inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root)
 {
-       //
-       // Mesh extensions
-       //
-       // At first check for extensions. They can affect on interpretaion of mesh data.
-       Value* extensions = FindObject(pJSON_Object, "extensions");
-
-       if(extensions != nullptr)
-       {
-               // At first check if data of mesh is compressed. Because buffer data must be decoded before another get data from it.
-               // Only Open3DGC supported at now.
-               Value* o3dgc = FindObject(*extensions, "Open3DGC-compression");
-
-               if(o3dgc != nullptr)
-               {
-                       // Search compressed data
-                       Value* comp_data = FindObject(*o3dgc, "compressedData");
-
-                       if(comp_data == nullptr) throw DeadlyImportError("GLTF: \"Open3DGC-compression\" must has \"compressedData\".");
-
-                       Assimp::DefaultLogger::get()->info("GLTF: Decompressing Open3DGC data.");
-                       Decode_O3DGC(*comp_data, pAsset_Root);
-               }// if(o3dgc == nullptr)
-       }// if(extensions != nullptr)
-
-       //
-       // Mesh primitives.
-       //
+       /****************** Mesh primitives ******************/
        if (Value* primitives = FindArray(pJSON_Object, "primitives")) {
         this->primitives.resize(primitives->Size());
         for (unsigned int i = 0; i < primitives->Size(); ++i) {
@@ -831,158 +812,229 @@ inline void Mesh::Read(Value& pJSON_Object, Asset& pAsset_Root)
             }
         }
     }
-}
 
-inline void Mesh::Decode_O3DGC(Value& pJSON_Object_CompressedData, Asset& pAsset_Root)
-{
-// Compressed data contain description of part of "bufferView" which is encoded. This part must be decoded and
-// new data must replace old encoded part. In fact \"compressedData\" is similar to "accessor" structure.
-const char* bufview_id;
-uint32_t byte_offset, count, count_indices, count_vertices;
-const char* mode_str;
-const char* type_str;
-ComponentType component_type;
-std::list<o3dgc::Real*> float_attributes_indices;// See above about "floatAttributesIndexes".
-
-       /**********************************************************/
-       /************** Read data from JSON-document **************/
-       /**********************************************************/
-
-       #define MESH_READ_COMPRESSEDDATA_MEMBER(pFieldName, pOut) \
-               if(!ReadMember(pJSON_Object_CompressedData, pFieldName, pOut)) { throw DeadlyImportError(std::string("GLTF: \"compressedData\" must has \"") + pFieldName + "\"."); }
-
-       MESH_READ_COMPRESSEDDATA_MEMBER("bufferView", bufview_id);
-       MESH_READ_COMPRESSEDDATA_MEMBER("byteOffset", byte_offset);
-       MESH_READ_COMPRESSEDDATA_MEMBER("componentType", component_type);
-       MESH_READ_COMPRESSEDDATA_MEMBER("count", count);
-       MESH_READ_COMPRESSEDDATA_MEMBER("indicesCount", count_indices);
-       MESH_READ_COMPRESSEDDATA_MEMBER("verticesCount", count_vertices);
-       MESH_READ_COMPRESSEDDATA_MEMBER("mode", mode_str);
-       MESH_READ_COMPRESSEDDATA_MEMBER("type", type_str);
-
-       #undef MESH_READ_COMPRESSEDDATA_MEMBER
+       /****************** Mesh extensions ******************/
+       Value* json_extensions = FindObject(pJSON_Object, "extensions");
 
-       //
-       // Check for float attributes
-       //
-       // Object example:
-       // "floatAttributesIndexes": {
-       //              "accessor_1356": 0,
-       //              "accessor_1358": 1,
-       //              "accessor_1360": 2
-       //      },
-       //
-       // Find object "floatAttributesIndexes"
-       Value* float_attr_ind = FindObject(pJSON_Object_CompressedData, "floatAttributesIndexes");
+       if(json_extensions == nullptr) goto mr_skip_extensions;
 
-       if(float_attr_ind != nullptr)
+       for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); it_memb++)
        {
-               size_t attr_num = 0;
-
-               // Walk thru children and get accessors and numbers of attributes.
-               for(Value::MemberIterator memb_it = float_attr_ind->MemberBegin(); memb_it != float_attr_ind->MemberEnd(); ++memb_it)
+               if(it_memb->name.GetString() == std::string("Open3DGC-compression"))
                {
-                       if(!memb_it->name.IsString()) throw DeadlyImportError("GLTF: name of the member of \"floatAttributesIndexes\" must be a string.");
-                       if(!memb_it->value.IsUint()) throw DeadlyImportError(std::string("GLTF: value of the member (\"") + memb_it->name.GetString() + \
-                                                                                                                                       "\") in \"floatAttributesIndexes\" must be an unsigned integer.");
-                       /*if(attr_num != memb_it->value.GetUint()) throw DeadlyImportError(std::string("GLTF: invalid number of float attribute index. Member (\"") + \
-                                                                                                                                                               memb_it->name.GetString() + "\".");*/
+                       // Search for compressed data.
+                       // Compressed data contain description of part of "buffer" which is encoded. This part must be decoded and
+                       // new data will replace old encoded part by request. In fact \"compressedData\" is kind of "accessor" structure.
+                       Value* comp_data = FindObject(it_memb->value, "compressedData");
 
-                       attr_num++;
-                       // Checks passed, extract data.
-                       Ref<Accessor> attr_cur_acc = pAsset_Root.accessors.Get(memb_it->name.GetString());
+                       if(comp_data == nullptr) throw DeadlyImportError("GLTF: \"Open3DGC-compression\" must has \"compressedData\".");
 
-                       float_attributes_indices.push_back((float*)attr_cur_acc->GetPointer());
+                       Assimp::DefaultLogger::get()->info("GLTF: Decompressing Open3DGC data.");
+
+                       /************** Read data from JSON-document **************/
+                       #define MESH_READ_COMPRESSEDDATA_MEMBER(pFieldName, pOut) \
+                               if(!ReadMember(*comp_data, pFieldName, pOut)) \
+                               { \
+                                       throw DeadlyImportError(std::string("GLTF: \"compressedData\" must has \"") + pFieldName + "\"."); \
+                               }
+
+                       const char* mode_str;
+                       const char* type_str;
+                       ComponentType component_type;
+                       SCompression_Open3DGC* ext_o3dgc = new SCompression_Open3DGC;
+
+                       MESH_READ_COMPRESSEDDATA_MEMBER("buffer", ext_o3dgc->Buffer);
+                       MESH_READ_COMPRESSEDDATA_MEMBER("byteOffset", ext_o3dgc->Offset);
+                       MESH_READ_COMPRESSEDDATA_MEMBER("componentType", component_type);
+                       MESH_READ_COMPRESSEDDATA_MEMBER("type", type_str);
+                       MESH_READ_COMPRESSEDDATA_MEMBER("count", ext_o3dgc->Count);
+                       MESH_READ_COMPRESSEDDATA_MEMBER("mode", mode_str);
+                       MESH_READ_COMPRESSEDDATA_MEMBER("indicesCount", ext_o3dgc->IndicesCount);
+                       MESH_READ_COMPRESSEDDATA_MEMBER("verticesCount", ext_o3dgc->VerticesCount);
+
+                       #undef MESH_READ_COMPRESSEDDATA_MEMBER
+
+                       // Check some values
+                       if(strcmp(type_str, "SCALAR")) throw DeadlyImportError("GLTF: only \"SCALAR\" type is supported for compressed data.");
+                       if(component_type != ComponentType_UNSIGNED_BYTE) throw DeadlyImportError("GLTF: only \"UNSIGNED_BYTE\" component type is supported for compressed data.");
+
+                       // Set read/write data mode.
+                       if(strcmp(mode_str, "binary") == 0)
+                               ext_o3dgc->Binary = true;
+                       else if(strcmp(mode_str, "ascii") == 0)
+                               ext_o3dgc->Binary = false;
+                       else
+                               throw DeadlyImportError(std::string("GLTF: for compressed data supported modes is: \"ascii\", \"binary\". Not the: \"") + mode_str + "\".");
+
+                       /************************ Decoding ************************/
+                       Decode_O3DGC(*ext_o3dgc, pAsset_Root);
+                       Extension.push_back(ext_o3dgc);// store info in mesh extensions list.
+               }// if(it_memb->name.GetString() == "Open3DGC-compression")
+               else
+               {
+                       throw DeadlyImportError(std::string("GLTF: Unknown mesh extension: \"") + it_memb->name.GetString() + "\".");
                }
-       }// if(float_attr_ind != nullptr)
+       }// for(Value::MemberIterator it_memb = json_extensions->MemberBegin(); it_memb != json_extensions->MemberEnd(); json_extensions++)
 
-       // Check some values
-       if(strcmp(type_str, "SCALAR")) throw DeadlyImportError("GLTF: only \"SCALAR\" type is supported for compressed data.");
-       if(component_type != ComponentType_UNSIGNED_BYTE) throw DeadlyImportError("GLTF: only \"UNSIGNED_BYTE\" component type is supported for compressed data.");
-       if((strcmp(mode_str, "binary") != 0) && (strcmp(mode_str, "ascii") != 0))
-       {
-               throw DeadlyImportError(std::string("GLTF: for compressed data supported modes is: \"ascii\", \"binary\". Not the: \"") + mode_str + "\".");
-       }
+mr_skip_extensions:
 
-       /**********************************************************/
-       /********************* Decoding data **********************/
-       /**********************************************************/
+       return;// After label some operators must be present.
+}
 
-       // Search for "bufferView" by ID.
-       Ref<BufferView> bufview = pAsset_Root.bufferViews.Get(bufview_id);
+inline void Mesh::Decode_O3DGC(const SCompression_Open3DGC& pCompression_Open3DGC, Asset& pAsset_Root)
+{
+using IndicesType = unsigned short;///< \sa glTFExporter::ExportMeshes.
 
-       //
-       // Decode data. Adapted piece of code from COLLADA2GLTF converter.
-       //
-       //      void testDecode(shared_ptr <GLTFMesh> mesh, BinaryStream &bstream)
-       o3dgc::SC3DMCDecoder<uint16_t> decoder;
-       o3dgc::IndexedFaceSet<uint16_t> ifs;
-       uint8_t* output_data;
-       size_t size_vertex, size_normal, size_indices, output_data_size;
-       o3dgc::BinaryStream bstream;
-       float* tarrays[6];
+o3dgc::SC3DMCDecoder<IndicesType> decoder;
+o3dgc::IndexedFaceSet<IndicesType> ifs;
+o3dgc::BinaryStream bstream;
+uint8_t* decoded_data;
+size_t decoded_data_size = 0;
+Ref<Buffer> buf = pAsset_Root.buffers.Get(pCompression_Open3DGC.Buffer);
 
        // Read data from buffer and place it in BinaryStream for decoder.
-       bstream.LoadFromBuffer(&bufview->buffer->GetPointer()[bufview->byteOffset + byte_offset], count);
+       // Just "Count" because always is used type equivalent to uint8_t.
+       bstream.LoadFromBuffer(&buf->GetPointer()[pCompression_Open3DGC.Offset], pCompression_Open3DGC.Count);
 
-       // After decoding header we can get size of primitives
+       // After decoding header we can get size of primitives.
        if(decoder.DecodeHeader(ifs, bstream) != o3dgc::O3DGC_OK) throw DeadlyImportError("GLTF: can not decode Open3DGC header.");
 
-       size_indices = ifs.GetNCoordIndex() * 3 * sizeof(unsigned short);
-       size_vertex = ifs.GetNCoord() * 3 * sizeof(float);
-       size_normal = ifs.GetNNormal() * 3 * sizeof(float);
+       /****************** Get sizes of arrays and check sizes ******************/
+       // Note. See "Limitations for meshes when using Open3DGC-compression".
 
-       for(size_t idx_attr = 0, idx_attr_end = ifs.GetNumFloatAttributes(); idx_attr < idx_attr_end; idx_attr++)
-       {
-               size_t qty_attr = ifs.GetNFloatAttribute(idx_attr);
+       // Indices
+       size_t size_coordindex = ifs.GetNCoordIndex() * 3;// See float attributes note.
+
+       if(primitives[0].indices->count != size_coordindex)
+               throw DeadlyImportError("GLTF: Open3DGC. Compressed indices count (" + std::to_string(size_coordindex) +
+                                                               ") not equal to uncompressed (" + std::to_string(primitives[0].indices->count) + ").");
+
+       size_coordindex *= sizeof(IndicesType);
+       // Coordinates
+       size_t size_coord = ifs.GetNCoord();// See float attributes note.
+
+       if(primitives[0].attributes.position[0]->count != size_coord)
+               throw DeadlyImportError("GLTF: Open3DGC. Compressed positions count (" + std::to_string(size_coord) +
+                                                               ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.position[0]->count) + ").");
+
+       size_coord *= 3 * sizeof(float);
+       // Normals
+       size_t size_normal = ifs.GetNNormal();// See float attributes note.
 
-               if(qty_attr == 0) continue;
+       if(primitives[0].attributes.normal[0]->count != size_normal)
+               throw DeadlyImportError("GLTF: Open3DGC. Compressed normals count (" + std::to_string(size_normal) +
+                                                               ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.normal[0]->count) + ").");
 
-               tarrays[idx_attr] = new float[qty_attr * ifs.GetFloatAttributeDim(idx_attr)];
-               ifs.SetFloatAttribute(idx_attr, tarrays[idx_attr]);
-               /*
-               switch(ifs.GetFloatAttributeType(idx_attr))
+       size_normal *= 3 * sizeof(float);
+       // Additional attributes.
+       std::vector<size_t> size_floatattr;
+       std::vector<size_t> size_intattr;
+
+       size_floatattr.resize(ifs.GetNumFloatAttributes());
+       size_intattr.resize(ifs.GetNumIntAttributes());
+
+       decoded_data_size = size_coordindex + size_coord + size_normal;
+       for(size_t idx = 0, idx_end = size_floatattr.size(), idx_texcoord = 0; idx < idx_end; idx++)
+       {
+               // size = number_of_elements * components_per_element * size_of_component.
+               // Note. But as you can see above, at first we are use this variable in meaning "count". After checking count of objects...
+               size_t tval = ifs.GetNFloatAttribute(idx);
+
+               switch(ifs.GetFloatAttributeType(idx))
                {
-                       // Unknown for Open3DGC,
-                       // but not for COLLADA2GLTF - GLTF::Semantic::JOINT. What's mean "JOINT"? Good question, but lim(comments in COLLADA2GLTF) = 0.
-                       case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_UNKOWN:// 0
-                               break;
-                       case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_POSITION:// 1
-                               break;
-                       case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_NORMAL:// 2
-                               break;
-                       case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_COLOR:// 3
-                               break;
-                       case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD:// 4
-                               break;
-                       case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_WEIGHT:// 5
+                       case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD:
+                               // Check situation when encoded data contain texture coordinates but primitive not.
+                               if(idx_texcoord < primitives[0].attributes.texcoord.size())
+                               {
+                                       if(primitives[0].attributes.texcoord[idx]->count != tval)
+                                               throw DeadlyImportError("GLTF: Open3DGC. Compressed texture coordinates count (" + std::to_string(tval) +
+                                                                                               ") not equal to uncompressed (" + std::to_string(primitives[0].attributes.texcoord[idx]->count) + ").");
+
+                                       idx_texcoord++;
+                               }
+                               else
+                               {
+                                       ifs.SetNFloatAttribute(idx, 0);// Disable decoding this attribute.
+                               }
+
                                break;
                        default:
-                               throw DeadlyImportError("GLTF: Unknown type of float attribute (" + std::to_string(idx_attr) + ") for Open3DGC encoding.");
-               }*/
+                               throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + std::to_string(ifs.GetFloatAttributeType(idx)));
+               }
+
+               tval *=  ifs.GetFloatAttributeDim(idx) * sizeof(o3dgc::Real);// After checking count of objects we can get size of array.
+               size_floatattr[idx] = tval;
+               decoded_data_size += tval;
+       }
+
+       for(size_t idx = 0, idx_end = size_intattr.size(); idx < idx_end; idx++)
+       {
+               // size = number_of_elements * components_per_element * size_of_component. See float attributes note.
+               size_t tval = ifs.GetNIntAttribute(idx);
+
+               switch(ifs.GetIntAttributeType(idx))
+               {
+                       default:
+                               throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + std::to_string(ifs.GetIntAttributeType(idx)));
+               }
+
+               tval *= ifs.GetIntAttributeDim(idx) * sizeof(long);// See float attributes note.
+               size_intattr[idx] = tval;
+               decoded_data_size += tval;
        }
 
-       //size_texcoord = ifs.GetNFloatAttribute(0) * 2 * sizeof(float);
+       // Create array for decoded data.
+       decoded_data = new uint8_t[decoded_data_size];
 
-       output_data_size = size_vertex + size_normal + /*size_texcoord*/ + size_indices;
-       output_data = new uint8_t[output_data_size];
+       /****************** Set right array regions for decoder ******************/
 
-       float* uncompressed_vertices = (float* const)(output_data + size_indices);// size_indices => vertex offset
+       auto get_buf_offset = [](Ref<Accessor>& pAccessor) -> size_t { return pAccessor->byteOffset + pAccessor->bufferView->byteOffset; };
 
-       ifs.SetCoordIndex((uint16_t* const)output_data);
-       ifs.SetCoord((float* const)uncompressed_vertices);
+       // Indices
+       ifs.SetCoordIndex((IndicesType* const)(decoded_data + get_buf_offset(primitives[0].indices)));
+       // Coordinates
+       ifs.SetCoord((o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.position[0])));
+       // Normals
+       if(size_normal)
+       {
+               ifs.SetNormal((o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.normal[0])));
+       }
 
-       if(ifs.GetNNormal() > 0) ifs.SetNormal((float* const)(output_data + size_indices + size_vertex));
+       for(size_t idx = 0, idx_end = size_floatattr.size(), idx_texcoord = 0; idx < idx_end; idx++)
+       {
+               switch(ifs.GetFloatAttributeType(idx))
+               {
+                       case o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD:
+                               if(idx_texcoord < primitives[0].attributes.texcoord.size())
+                               {
+                                       // See above about absent attributes.
+                                       ifs.SetFloatAttribute(idx, (o3dgc::Real* const)(decoded_data + get_buf_offset(primitives[0].attributes.texcoord[idx])));
+                                       idx_texcoord++;
+                               }
 
-       //if(ifs.GetNFloatAttribute(0)) ifs.SetFloatAttribute(0, (float* const)(output_data + size_indices + size_vertex + size_normal));
+                               break;
+                       default:
+                               throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of float attribute: " + std::to_string(ifs.GetFloatAttributeType(idx)));
+               }
+       }
+
+       for(size_t idx = 0, idx_end = size_intattr.size(); idx < idx_end; idx++)
+       {
+               switch(ifs.GetIntAttributeType(idx))
+               {
+                       // ifs.SetIntAttribute(idx, (long* const)(decoded_data + get_buf_offset(primitives[0].attributes.joint)));
+                       default:
+                               throw DeadlyImportError("GLTF: Open3DGC. Unsupported type of int attribute: " + std::to_string(ifs.GetIntAttributeType(idx)));
+               }
+       }
 
+       //
        // Decode data
-       if(decoder.DecodePlayload(ifs, bstream) != o3dgc::O3DGC_OK) throw DeadlyImportError("GLTF: can not decode Open3DGC data.");
+       //
+       if(decoder.DecodePayload(ifs, bstream) != o3dgc::O3DGC_OK) throw DeadlyImportError("GLTF: can not decode Open3DGC data.");
 
-       // Set encoded region for bufferView.
-       bufview->EncodedRegion_Mark(byte_offset, count, output_data, output_data_size, name);
-       // Ans set is current
-       bufview->EncodedRegion_SetCurrent(name);
+       // Set encoded region for "buffer".
+       buf->EncodedRegion_Mark(pCompression_Open3DGC.Offset, pCompression_Open3DGC.Count, decoded_data, decoded_data_size, id);
        // No. Do not delete "output_data". After calling "EncodedRegion_Mark" bufferView is owner of "output_data".
        // "delete [] output_data;"
 }
index f30e8cf..d76bb5d 100644 (file)
@@ -224,6 +224,11 @@ namespace glTF {
                                                        json_comp_data.AddMember("componentType", 5121, w.mAl);
                                                        json_comp_data.AddMember("type", "SCALAR", w.mAl);
                                                        json_comp_data.AddMember("count", ptr_ext_comp->Count, w.mAl);
+                                                       if(ptr_ext_comp->Binary)
+                                                               json_comp_data.AddMember("mode", "binary", w.mAl);
+                                                       else
+                                                               json_comp_data.AddMember("mode", "ascii", w.mAl);
+
                                                        json_comp_data.AddMember("indicesCount", ptr_ext_comp->IndicesCount, w.mAl);
                                                        json_comp_data.AddMember("verticesCount", ptr_ext_comp->VerticesCount, w.mAl);
                                                        // filling object "Open3DGC-compression"
@@ -237,7 +242,7 @@ namespace glTF {
 
                                                break;
                                        default:
-                                               throw DeadlyImportError("GLTF: Unknown mesh extension, Only Open3DGC is supported.");
+                                               throw DeadlyImportError("GLTF: Can not write mesh: unknown mesh extension, only Open3DGC is supported.");
                                }// switch(ptr_ext->Type)
                        }// for(Mesh::SExtension* ptr_ext : m.Extension)
 
index 1dc35b5..48ab099 100644 (file)
@@ -261,7 +261,7 @@ void glTFExporter::ExportMeshes()
 //     using IndicesType = decltype(aiFace::mNumIndices);
 // But yes for
 //     using IndicesType = unsigned short;
-// because "ComponentType_UNSIGNED_SHORT" used for indices. And its maximal type according to glTF specification.
+// because "ComponentType_UNSIGNED_SHORT" used for indices. And it's a maximal type according to glTF specification.
 using IndicesType = unsigned short;
 
 // Variables needed for compression. BEGIN.
@@ -273,18 +273,26 @@ size_t idx_srcdata_ind;// Index of begin of coordinates indices array in buffer.
 bool comp_allow;// Point that data of current mesh can be compressed.
 // Variables needed for compression. END.
 
-    for (unsigned int i = 0; i < mScene->mNumMeshes; ++i) {
-        const aiMesh* aim = mScene->mMeshes[i];
+       for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) {
+               const aiMesh* aim = mScene->mMeshes[idx_mesh];
 
                // Check if compressing requested and mesh can be encoded.
-               if((aim->mPrimitiveTypes == aiPrimitiveType_TRIANGLE) && (aim->mNumVertices > 0))///TODO: export properties if(compression is needed)
+               comp_allow = mProperties->GetPropertyBool("extensions.Open3DGC.use", false);
+               if(comp_allow && (aim->mPrimitiveTypes == aiPrimitiveType_TRIANGLE) && (aim->mNumVertices > 0) && (aim->mNumFaces > 0))
                {
-                       comp_allow = true;
+                       idx_srcdata_tc.clear();
                        idx_srcdata_tc.reserve(AI_MAX_NUMBER_OF_TEXTURECOORDS);
                }
                else
                {
-                       comp_allow = false;
+                       std::string msg;
+
+                       if(aim->mPrimitiveTypes != aiPrimitiveType_TRIANGLE)
+                               msg = "all primitives of the mesh must be a triangles.";
+                       else
+                               msg = "mesh must has vertices and faces.";
+
+                       DefaultLogger::get()->warn("GLTF: can not use Open3DGC-compression: " + msg);
                }
 
         std::string meshId = mAsset->FindUniqueID(aim->mName.C_Str(), "mesh");
@@ -367,28 +375,30 @@ bool comp_allow;// Point that data of current mesh can be compressed.
                        //
                        // Fill data for encoder.
                        //
-                       unsigned qcoord = 12;///TODO: dbg
-                       unsigned qnormal = 10;///TODO: dbg
-                       unsigned qtexCoord = 10;///TODO: dbg
+                       // Quantization
+                       unsigned quant_coord = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.POSITION", 12);
+                       unsigned quant_normal = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.NORMAL", 10);
+                       unsigned quant_texcoord = mProperties->GetPropertyInteger("extensions.Open3DGC.quantization.TEXCOORD", 10);
 
-                       o3dgc::O3DGCSC3DMCPredictionMode positionPrediction = o3dgc::O3DGC_SC3DMC_PARALLELOGRAM_PREDICTION;///TODO: dbg
-                       o3dgc::O3DGCSC3DMCPredictionMode normalPrediction = o3dgc::O3DGC_SC3DMC_SURF_NORMALS_PREDICTION;///TODO: dbg
-                       o3dgc::O3DGCSC3DMCPredictionMode texcoordPrediction = o3dgc::O3DGC_SC3DMC_PARALLELOGRAM_PREDICTION;///TODO: dbg
+                       // Prediction
+                       o3dgc::O3DGCSC3DMCPredictionMode prediction_position = o3dgc::O3DGC_SC3DMC_PARALLELOGRAM_PREDICTION;
+                       o3dgc::O3DGCSC3DMCPredictionMode prediction_normal =  o3dgc::O3DGC_SC3DMC_SURF_NORMALS_PREDICTION;
+                       o3dgc::O3DGCSC3DMCPredictionMode prediction_texcoord = o3dgc::O3DGC_SC3DMC_PARALLELOGRAM_PREDICTION;
 
                        // IndexedFacesSet: "Crease angle", "solid", "convex" are set to default.
                        comp_o3dgc_ifs.SetCCW(true);
                        comp_o3dgc_ifs.SetIsTriangularMesh(true);
                        comp_o3dgc_ifs.SetNumFloatAttributes(0);
                        // Coordinates
-                       comp_o3dgc_params.SetCoordQuantBits(qcoord);///TODO: IME
-                       comp_o3dgc_params.SetCoordPredMode(positionPrediction);///TODO: IME
+                       comp_o3dgc_params.SetCoordQuantBits(quant_coord);
+                       comp_o3dgc_params.SetCoordPredMode(prediction_position);
                        comp_o3dgc_ifs.SetNCoord(aim->mNumVertices);
                        comp_o3dgc_ifs.SetCoord((o3dgc::Real* const)&b->GetPointer()[idx_srcdata_begin]);
                        // Normals
                        if(idx_srcdata_normal != SIZE_MAX)
                        {
-                               comp_o3dgc_params.SetNormalQuantBits(qnormal);///TODO: IME
-                               comp_o3dgc_params.SetNormalPredMode(normalPrediction);///TODO: IME
+                               comp_o3dgc_params.SetNormalQuantBits(quant_normal);
+                               comp_o3dgc_params.SetNormalPredMode(prediction_normal);
                                comp_o3dgc_ifs.SetNNormal(aim->mNumVertices);
                                comp_o3dgc_ifs.SetNormal((o3dgc::Real* const)&b->GetPointer()[idx_srcdata_normal]);
                        }
@@ -398,10 +408,10 @@ bool comp_allow;// Point that data of current mesh can be compressed.
                        {
                                size_t num = comp_o3dgc_ifs.GetNumFloatAttributes();
 
-                               comp_o3dgc_params.SetFloatAttributeQuantBits(num, qtexCoord);///TODO: IME
-                               comp_o3dgc_params.SetFloatAttributePredMode(num, texcoordPrediction);///TODO: IME
+                               comp_o3dgc_params.SetFloatAttributeQuantBits(num, quant_texcoord);
+                               comp_o3dgc_params.SetFloatAttributePredMode(num, prediction_texcoord);
                                comp_o3dgc_ifs.SetNFloatAttribute(num, aim->mNumVertices);// number of elements.
-                               comp_o3dgc_ifs.SetFloatAttributeDim(num, aim->mNumUVComponents[i]);// components per element: aiVector3D => x * float
+                               comp_o3dgc_ifs.SetFloatAttributeDim(num, aim->mNumUVComponents[num_tc]);// components per element: aiVector3D => x * float
                                comp_o3dgc_ifs.SetFloatAttributeType(num, o3dgc::O3DGC_IFS_FLOAT_ATTRIBUTE_TYPE_TEXCOORD);
                                comp_o3dgc_ifs.SetFloatAttribute(num, (o3dgc::Real* const)&b->GetPointer()[idx_srcdata_tc[num_tc]]);
                                comp_o3dgc_ifs.SetNumFloatAttributes(num + 1);
@@ -412,7 +422,12 @@ bool comp_allow;// Point that data of current mesh can be compressed.
                        comp_o3dgc_ifs.SetCoordIndex((IndicesType* const)&b->GetPointer()[idx_srcdata_ind]);
                        // Prepare to enconding
                        comp_o3dgc_params.SetNumFloatAttributes(comp_o3dgc_ifs.GetNumFloatAttributes());
-                       comp_o3dgc_params.SetStreamType(o3dgc::O3DGC_STREAM_TYPE_BINARY);///TODO: exporter params
+                       if(mProperties->GetPropertyBool("extensions.Open3DGC.binary", true))
+                               comp_o3dgc_params.SetStreamType(o3dgc::O3DGC_STREAM_TYPE_BINARY);
+                       else
+                               comp_o3dgc_params.SetStreamType(o3dgc::O3DGC_STREAM_TYPE_ASCII);
+
+                       comp_o3dgc_ifs.ComputeMinMax(o3dgc::O3DGC_SC3DMC_MAX_ALL_DIMS);
                        //
                        // Encoding
                        //
@@ -429,14 +444,13 @@ bool comp_allow;// Point that data of current mesh can be compressed.
                        ext->Buffer = b->id;
                        ext->Offset = idx_srcdata_begin;
                        ext->Count = b->byteLength - idx_srcdata_begin;
-                       ext->IndicesCount = comp_o3dgc_ifs.GetNCoordIndex();
+                       ext->Binary = mProperties->GetPropertyBool("extensions.Open3DGC.binary");
+                       ext->IndicesCount = comp_o3dgc_ifs.GetNCoordIndex() * 3;
                        ext->VerticesCount = comp_o3dgc_ifs.GetNCoord();
                        // And assign to mesh.
                        m->Extension.push_back(ext);
                }// if(comp_allow)
        }// for (unsigned int i = 0; i < mScene->mNumMeshes; ++i) {
-
-       ///TODO: export properties if(compression is used)
 }
 
 unsigned int glTFExporter::ExportNode(const aiNode* n)
index 5fdad12..5cdd856 100644 (file)
@@ -259,7 +259,36 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
     for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
         Mesh& mesh = r.meshes[m];
 
-        meshOffsets.push_back(k);
+               // Check if mesh extensions is used
+               if(mesh.Extension.size() > 0)
+               {
+                       for(Mesh::SExtension* cur_ext : mesh.Extension)
+                       {
+                               if(cur_ext->Type == Mesh::SExtension::EType::Compression_Open3DGC)
+                               {
+                                       // Limitations for meshes when using Open3DGC-compression.
+                                       // It's a current limitation of sp... Specification have not this part still - about mesh compression. Why only one primitive?
+                                       // Because glTF is very flexibly. But in fact it ugly flexible. Every primitive can has own set of accessors and accessors can
+                                       // point to a-a-a-a-any part of buffer (thru bufferview ofcourse) and even to another buffer. We know that "Open3DGC-compression"
+                                       // is applicable only to part of buffer. As we can't guaranty continuity of the data for decoder, we will limit quantity of primitives.
+                                       // Yes indices, coordinates etc. still can br stored in different buffers, but with current specification it's a exporter problem.
+                                       // Also primitive can has only one of "POSITION", "NORMAL" and less then "AI_MAX_NUMBER_OF_TEXTURECOORDS" of "TEXCOORD". All accessor
+                                       // of primitive must point to one continuous region of the buffer.
+                                       if(mesh.primitives.size() > 2) throw DeadlyImportError("GLTF: When using Open3DGC compression then only one primitive per mesh are allowed.");
+
+                                       Mesh::SCompression_Open3DGC* o3dgc_ext = (Mesh::SCompression_Open3DGC*)cur_ext;
+                                       Ref<Buffer> buf = r.buffers.Get(o3dgc_ext->Buffer);
+
+                                       buf->EncodedRegion_SetCurrent(mesh.id);
+                               }
+                               else
+                               {
+                                       throw DeadlyImportError("GLTF: Can not import mesh: unknown mesh extension, only Open3DGC is supported.");
+                               }
+                       }
+               }// if(mesh.Extension.size() > 0)
+
+               meshOffsets.push_back(k);
         k += unsigned(mesh.primitives.size());
 
         for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
@@ -295,25 +324,13 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
 
             Mesh::Primitive::Attributes& attr = prim.attributes;
 
-                       // if "bufferView" of current accessor is containing encoded data then set ID of region.
-                       if(attr.position[0]->bufferView->EncodedRegion_List.size() > 0) attr.position[0]->bufferView->EncodedRegion_SetCurrent(mesh.name);
-
                        if (attr.position.size() > 0 && attr.position[0]) {
                 aim->mNumVertices = attr.position[0]->count;
                 attr.position[0]->ExtractData(aim->mVertices);
                        }
 
-                       // if "bufferView" of current accessor is containing encoded data then set ID of region.
-                       if(attr.normal[0]->bufferView->EncodedRegion_List.size() > 0) attr.normal[0]->bufferView->EncodedRegion_SetCurrent(mesh.name);
-
                        if (attr.normal.size() > 0 && attr.normal[0]) attr.normal[0]->ExtractData(aim->mNormals);
 
-                       // if "bufferView" of current accessor is containing encoded data then set ID of region.
-                       if((attr.texcoord.size() > 0) && (attr.texcoord[0]->bufferView->EncodedRegion_List.size() > 0))
-                       {
-                               attr.texcoord[0]->bufferView->EncodedRegion_SetCurrent(mesh.name);
-                       }
-
                        for (size_t tc = 0; tc < attr.texcoord.size() && tc <= AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) {
                                attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]);
                                aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents();
@@ -326,9 +343,6 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
 
 
             if (prim.indices) {
-                               // if "bufferView" of current accessor is containing encoded data then set ID of region.
-                               if(prim.indices->bufferView->EncodedRegion_List.size() > 0) prim.indices->bufferView->EncodedRegion_SetCurrent(mesh.name);
-
                                aiFace* faces = 0;
                 unsigned int nFaces = 0;