diff --git a/code/glTFAsset.h b/code/glTFAsset.h index 6d5ac1a71..c5c7d1349 100644 --- a/code/glTFAsset.h +++ b/code/glTFAsset.h @@ -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 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 diff --git a/code/glTFAsset.inl b/code/glTFAsset.inl index d52c825c4..60af03ee3 100644 --- a/code/glTFAsset.inl +++ b/code/glTFAsset.inl @@ -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 + 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 bufview = pAsset_Root.bufferViews.Get(bufview_id); + + // + // Decode data. Adapted piece of code from COLLADA2GLTF converter. + // + // void testDecode(shared_ptr mesh, BinaryStream &bstream) + o3dgc::SC3DMCDecoder decoder; + o3dgc::IndexedFaceSet 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) {