[+] glTF. Support for import meshes with Open3DGC compression.
authorAlexandr Arutjunov <smal.root@gmail.com>
Wed, 3 Aug 2016 00:46:04 +0000 (03:46 +0300)
committerAlexandr Arutjunov <smal.root@gmail.com>
Wed, 3 Aug 2016 00:46:04 +0000 (03:46 +0300)
code/glTFAsset.h
code/glTFAsset.inl

index 6d5ac1a..c5c7d13 100644 (file)
@@ -1,4 +1,4 @@
-/*
+/*
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
@@ -477,6 +477,15 @@ namespace glTF
 
         bool LoadFromStream(IOStream& stream, size_t length = 0, size_t baseOffset = 0);
 
+               /// \fn void ReplaceData(uint8_t* pBufferData_Offset, size_t pBufferData_Count, uint8_t pPrepend_Data, size_t pPrepend_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);
+
         size_t AppendData(uint8_t* data, size_t length);
         void Grow(size_t amount);
 
@@ -634,7 +643,18 @@ namespace glTF
         std::vector<Primitive> primitives;
 
         Mesh() {}
-        void Read(Value& obj, Asset& r);
+
+               /// \fn void Read(Value& pJSON_Object, Asset& pAsset_Root)
+               /// 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 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.
+               /// \param [out] pAsset_Root - reference to root assed where data will be stored.
+               void Decode_O3DGC(Value& pJSON_Object_CompressedData, Asset& pAsset_Root);
     };
 
     struct Node : public Object
index d52c825..60af03e 100644 (file)
@@ -1,4 +1,4 @@
-/*
+/*
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 
@@ -40,6 +40,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "StringUtils.h"
 
+// Header files, Open3DGC.h,
+#include <Open3DGC/o3dgcSC3DMCDecoder.h>
+
 namespace glTF {
 
 namespace {
@@ -324,6 +327,26 @@ inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseO
     return true;
 }
 
+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;
+
+uint8_t* new_data;
+
+       if((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) return false;
+
+       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);
+       byteLength = new_data_size;
+}
+
 inline size_t Buffer::AppendData(uint8_t* data, size_t length)
 {
     size_t offset = this->byteLength;
@@ -676,9 +699,35 @@ namespace {
     }
 }
 
-inline void Mesh::Read(Value& obj, Asset& r)
-{  
-    if (Value* primitives = FindArray(obj, "primitives")) {
+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\".");
+
+                       Decode_O3DGC(*comp_data, pAsset_Root);
+               }// if(o3dgc == nullptr)
+       }// if(extensions != nullptr)
+
+       //
+       // Mesh primitives.
+       //
+       if (Value* primitives = FindArray(pJSON_Object, "primitives")) {
         this->primitives.resize(primitives->Size());
         for (unsigned int i = 0; i < primitives->Size(); ++i) {
             Value& primitive = (*primitives)[i];
@@ -698,22 +747,109 @@ inline void Mesh::Read(Value& obj, Asset& r)
                     if (GetAttribVector(prim, attr, vec, undPos)) {
                         size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
                         if ((*vec).size() <= idx) (*vec).resize(idx + 1);
-                        (*vec)[idx] = r.accessors.Get(it->value.GetString());
+                                               (*vec)[idx] = pAsset_Root.accessors.Get(it->value.GetString());
                     }
                 }
             }
 
             if (Value* indices = FindString(primitive, "indices")) {
-                prim.indices = r.accessors.Get(indices->GetString());
+                               prim.indices = pAsset_Root.accessors.Get(indices->GetString());
             }
 
             if (Value* material = FindString(primitive, "material")) {
-                prim.material = r.materials.Get(material->GetString());
+                               prim.material = pAsset_Root.materials.Get(material->GetString());
             }
         }
     }
 }
 
+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;
+
+       #define MESH_READ_COMPRESSEDDATA_MEMBER(pID, pOut) \
+               if(!ReadMember(pJSON_Object_CompressedData, pID, pOut)) { throw DeadlyImportError(std::string("GLTF: \"compressedData\" must has \"") + pID + "\"."); }
+
+       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
+
+       // 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 + "\".");
+       }
+
+       // Search for "bufferView" by ID.
+       Ref<BufferView> bufview = pAsset_Root.bufferViews.Get(bufview_id);
+
+       //
+       // Decode data. Adapted piece of code from COLLADA2GLTF converter.
+       //
+       //      void testDecode(shared_ptr <GLTFMesh> mesh, BinaryStream &bstream)
+       o3dgc::SC3DMCDecoder<uint16_t> decoder;
+       o3dgc::IndexedFaceSet <unsigned short> ifs;
+       uint8_t* output_data;
+       size_t size_vertex, size_normal, size_texcoord, size_indices, output_data_size;
+       o3dgc::BinaryStream bstream;
+
+       // Read data from buffer and place it in BinaryStream for decoder.
+       bstream.LoadFromBuffer(&bufview->buffer->GetPointer()[bufview->byteOffset + byte_offset], count);
+
+       // 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_vertex = ifs.GetNCoord() * 3 * sizeof(float);
+       size_normal = ifs.GetNNormal() * 3 * sizeof(float);
+       size_texcoord = ifs.GetNFloatAttribute(0) * 2 * sizeof(float);
+       size_indices = ifs.GetNCoordIndex() * 3 * sizeof(unsigned short);
+
+       output_data_size = size_vertex + size_normal + size_texcoord + size_indices;
+       output_data = new uint8_t[output_data_size];
+
+       float* uncompressed_vertices = (float* const)(output_data + size_indices);// size_indices => vertex offset
+
+       ifs.SetCoordIndex((uint16_t* const)output_data);
+       ifs.SetCoord((float* const)uncompressed_vertices);
+
+       if(ifs.GetNNormal() > 0) ifs.SetNormal((float* const)(output_data + size_indices + size_vertex));
+
+       if(ifs.GetNFloatAttribute(0)) ifs.SetFloatAttribute(0, (float* const)(output_data + size_indices + size_vertex + size_normal));
+
+       // Decode data
+       if(decoder.DecodePlayload(ifs, bstream) != o3dgc::O3DGC_OK) throw DeadlyImportError("GLTF: can not decode Open3DGC data.");
+
+       // Set/extend buffer
+       bufview->buffer->ReplaceData(byte_offset, count, output_data, output_data_size);
+       // Also correct size of current "bufferView" ...
+       bufview->byteLength = output_data_size;
+       // and offset for all other "bufferViews" which are placed after edited.
+       const size_t difference = output_data_size - count;
+
+       for(size_t idx_bv = 0; idx_bv < pAsset_Root.bufferViews.Size(); idx_bv++)
+       {
+               size_t off = pAsset_Root.bufferViews[idx_bv].byteOffset;
+
+               if(off > (byte_offset + count)) pAsset_Root.bufferViews[idx_bv].byteOffset += difference;
+       }
+
+       delete [] output_data;
+}
 
 inline void Camera::Read(Value& obj, Asset& r)
 {