//! 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.
/// 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.
/// 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
{
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".
/// \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
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)
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
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;
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)
{
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;
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) {
}
}
}
-}
-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;"
}
// 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.
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");
//
// 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]);
}
{
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);
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
//
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)