ソースを参照

[F] Fixed problem with more then one mesh in scene. More detaily read at line 529 in glTFAsset.inl.

Alexandr Arutjunov 9 年 前
コミット
29e982e185
3 ファイル変更165 行追加55 行削除
  1. 69 10
      code/glTFAsset.h
  2. 73 38
      code/glTFAsset.inl
  3. 23 7
      code/glTFImporter.cpp

+ 69 - 10
code/glTFAsset.h

@@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #include <map>
 #include <map>
 #include <string>
 #include <string>
+#include <list>
 #include <vector>
 #include <vector>
 #include <algorithm>
 #include <algorithm>
 #include <stdexcept>
 #include <stdexcept>
@@ -477,15 +478,6 @@ namespace glTF
 
 
         bool LoadFromStream(IOStream& stream, size_t length = 0, size_t baseOffset = 0);
         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);
         size_t AppendData(uint8_t* data, size_t length);
         void Grow(size_t amount);
         void Grow(size_t amount);
 
 
@@ -501,6 +493,31 @@ namespace glTF
         static const char* TranslateId(Asset& r, const char* id);
         static const char* TranslateId(Asset& r, const char* id);
     };
     };
 
 
+	/// \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.
     //! A view into a buffer generally representing a subset of the buffer.
     struct BufferView : public Object
     struct BufferView : public Object
@@ -509,10 +526,52 @@ namespace glTF
         size_t byteOffset; //! The offset into the buffer in bytes. (required)
         size_t byteOffset; //! The offset into the buffer in bytes. (required)
         size_t byteLength; //! The length of the bufferView in bytes. (default: 0)
         size_t byteLength; //! The length of the bufferView in bytes. (default: 0)
 
 
+		/// \var EncodedRegion_Current
+		/// Pointer to currently active encoded region.
+		/// Why not decoding all regions at once and not to set one buffer with decoded data?
+		/// Yes, why not? Even "accessor" point to decoded data. I mean that fields "byteOffset", "byteStride" and "count" has values which describes decoded
+		/// data array. But only in range of mesh while is active parameters from "compressedData". For another mesh accessors point to decoded data too. But
+		/// offset is counted for another regions is encoded.
+		/// Example. You have two meshes. For every of it you have 4 bytes of data. That data compressed to 2 bytes. So, you have buffer with encoded data:
+		/// M1_E0, M1_E1, M2_E0, M2_E1.
+		/// After decoding you'll get:
+		/// M1_D0, M1_D1, M1_D2, M1_D3, M2_D0, M2_D1, M2_D2, M2_D3.
+		/// "accessors" must to use values that point to decoded data - obviously. So, you'll expect "accessors" like
+		/// "accessor_0" : { byteOffset: 0, byteLength: 4}, "accessor_1" : { byteOffset: 4, byteLength: 4}
+		/// but in real life you'll get:
+		/// "accessor_0" : { byteOffset: 0, byteLength: 4}, "accessor_1" : { byteOffset: 2, byteLength: 4}
+		/// Yes, accessor of next mesh has offset and length which mean: current mesh data is decoded, all other data is encoded.
+		/// And when before you start to read data of current mesh (with encoded data ofcourse) you must decode region of "bufferView", after read finished
+		/// delete encoding mark. And after that you can repeat process: decode data of mesh, read, delete decoded data.
+		SEncodedRegion* EncodedRegion_Current;
+
+		/// \var EncodedRegion_List
+		/// List of encoded regions.
+		std::list<SEncodedRegion*> EncodedRegion_List;
+
         BufferViewTarget target; //! The target that the WebGL buffer should be bound to.
         BufferViewTarget target; //! The target that the WebGL buffer should be bound to.
 
 
-        BufferView() {}
+		BufferView()
+			: EncodedRegion_Current(nullptr)
+		{}
+
+		~BufferView() { for(SEncodedRegion* reg : EncodedRegion_List) delete reg; }
+
         void Read(Value& obj, Asset& r);
         void Read(Value& obj, Asset& r);
+
+		/// \fn void EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string& pID)
+		/// Mark region of "bufferView" as encoded. When data is request from such region then "bufferView" use decoded data.
+		/// \param [in] pOffset - offset from begin of "bufferView" to encoded region, in bytes.
+		/// \param [in] pEncodedData_Length - size of encoded region, in bytes.
+		/// \param [in] pDecodedData - pointer to decoded data array.
+		/// \param [in] pDecodedData_Length - size of encoded region, in bytes.
+		/// \param [in] pID - ID of the region.
+		void EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string& pID);
+
+		/// \fn void EncodedRegion_SetCurrent(const std::string& pID)
+		/// Select current encoded region by ID. \sa EncodedRegion_Current.
+		/// \param [in] pID - ID of the region.
+		void EncodedRegion_SetCurrent(const std::string& pID);
     };
     };
 
 
 
 

+ 73 - 38
code/glTFAsset.inl

@@ -330,26 +330,6 @@ inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseO
     return true;
     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)
 inline size_t Buffer::AppendData(uint8_t* data, size_t length)
 {
 {
     size_t offset = this->byteLength;
     size_t offset = this->byteLength;
@@ -367,6 +347,9 @@ inline void Buffer::Grow(size_t amount)
     byteLength += amount;
     byteLength += amount;
 }
 }
 
 
+//
+// struct BufferView
+//
 
 
 inline void BufferView::Read(Value& obj, Asset& r)
 inline void BufferView::Read(Value& obj, Asset& r)
 {
 {
@@ -379,7 +362,58 @@ inline void BufferView::Read(Value& obj, Asset& r)
     byteLength = MemberOrDefault(obj, "byteLength", 0u);
     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.");
+}
 
 
 inline void Accessor::Read(Value& obj, Asset& r)
 inline void Accessor::Read(Value& obj, Asset& r)
 {
 {
@@ -419,7 +453,17 @@ inline uint8_t* Accessor::GetPointer()
     if (!basePtr) return 0;
     if (!basePtr) return 0;
 
 
     size_t offset = byteOffset + bufferView->byteOffset;
     size_t offset = byteOffset + bufferView->byteOffset;
-    return basePtr + offset;
+
+	// Check if region is encoded.
+	if(bufferView->EncodedRegion_Current != nullptr)
+	{
+		const size_t begin = bufferView->EncodedRegion_Current->Offset;
+		const size_t end = bufferView->EncodedRegion_Current->Offset + bufferView->EncodedRegion_Current->DecodedData_Length;
+
+		if((offset >= begin) && (offset < end)) return &bufferView->EncodedRegion_Current->DecodedData[offset - begin];
+	}
+
+	return basePtr + offset;
 }
 }
 
 
 namespace {
 namespace {
@@ -777,8 +821,8 @@ const char* mode_str;
 const char* type_str;
 const char* type_str;
 ComponentType component_type;
 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 + "\"."); }
+	#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("bufferView", bufview_id);
 	MESH_READ_COMPRESSEDDATA_MEMBER("byteOffset", byte_offset);
 	MESH_READ_COMPRESSEDDATA_MEMBER("byteOffset", byte_offset);
@@ -838,21 +882,12 @@ ComponentType component_type;
 	// Decode data
 	// Decode data
 	if(decoder.DecodePlayload(ifs, bstream) != o3dgc::O3DGC_OK) throw DeadlyImportError("GLTF: can not decode Open3DGC 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;
+	// 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);
+	// No. Do not delete "output_data". After calling "EncodedRegion_Mark" bufferView is owner of "output_data".
+	// "delete [] output_data;"
 }
 }
 
 
 inline void Camera::Read(Value& obj, Asset& r)
 inline void Camera::Read(Value& obj, Asset& r)

+ 23 - 7
code/glTFImporter.cpp

@@ -1,4 +1,4 @@
-/*
+/*
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
@@ -294,17 +294,30 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
             }
             }
 
 
             Mesh::Primitive::Attributes& attr = prim.attributes;
             Mesh::Primitive::Attributes& attr = prim.attributes;
-            if (attr.position.size() > 0 && attr.position[0]) {
+
+			// 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;
                 aim->mNumVertices = attr.position[0]->count;
                 attr.position[0]->ExtractData(aim->mVertices);
                 attr.position[0]->ExtractData(aim->mVertices);
             }
             }
 
 
-            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.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);
             }
             }
 
 
-            for (size_t tc = 0; tc < attr.texcoord.size() && tc <= AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) {
-                attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]);
+			// 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();
                 aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents();
 
 
                 aiVector3D* values = aim->mTextureCoords[tc];
                 aiVector3D* values = aim->mTextureCoords[tc];
@@ -315,7 +328,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
 
 
 
 
             if (prim.indices) {
             if (prim.indices) {
-                aiFace* faces = 0;
+				// 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;
                 unsigned int nFaces = 0;
 
 
                 unsigned int count = prim.indices->count;
                 unsigned int count = prim.indices->count;