From f6720271cb7f58084695e9cff2a9f3b3640b4525 Mon Sep 17 00:00:00 2001 From: Yingying Wang Date: Fri, 15 May 2020 12:20:31 -0700 Subject: [PATCH] sparce accessor exporter --- code/AssetLib/glTF2/glTF2Asset.h | 24 ++++ code/AssetLib/glTF2/glTF2Asset.inl | 130 ++++++++++++++++++- code/AssetLib/glTF2/glTF2AssetWriter.inl | 48 +++++-- code/AssetLib/glTF2/glTF2Exporter.cpp | 156 ++++++++++++++++++++++- 4 files changed, 346 insertions(+), 12 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 48dab24a7..a60ac98f3 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -367,6 +367,7 @@ struct Object { //! An accessor provides a typed view into a BufferView or a subset of a BufferView //! similar to how WebGL's vertexAttribPointer() defines an attribute in a buffer. struct Accessor : public Object { + struct Sparse; //wangyi 0506 Ref bufferView; //!< The ID of the bufferView. (required) size_t byteOffset; //!< The offset relative to the start of the bufferView in bytes. (required) ComponentType componentType; //!< The datatype of components in the attribute. (required) @@ -374,6 +375,7 @@ struct Accessor : public Object { AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required) std::vector max; //!< Maximum value of each component in this attribute. std::vector min; //!< Minimum value of each component in this attribute. + std::unique_ptr sparse; //wangyi 0506 unsigned int GetNumComponents(); unsigned int GetBytesPerComponent(); @@ -385,6 +387,10 @@ struct Accessor : public Object { void ExtractData(T *&outData); void WriteData(size_t count, const void *src_buffer, size_t src_stride); + //wangyi 0506 + void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride); + void WriteSparseIndices(size_t count, const void *src_idx, size_t src_idxStride); + //! Helper class to iterate the data class Indexer { @@ -423,6 +429,22 @@ struct Accessor : public Object { Accessor() {} void Read(Value &obj, Asset &r); + + //wangyi 0506 + //sparse + struct Sparse { + size_t count; + ComponentType indicesType; + Ref indices; + size_t indicesByteOffset; + Ref values; + size_t valuesByteOffset; + + std::vector data; //!< Actual data, which may be defaulted to an array of zeros or the original data, with the sparse buffer view applied on top of it. + + void PopulateData(size_t numBytes, uint8_t *bytes); + void PatchData(unsigned int elementSize); + }; }; //! A buffer points to binary geometry, animation, or skins. @@ -555,6 +577,8 @@ struct BufferView : public Object { BufferViewTarget target; //! The target that the WebGL buffer should be bound to. void Read(Value &obj, Asset &r); + //wangyi 0506 + uint8_t *GetPointer(size_t accOffset); }; struct Camera : public Object { diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 881fd4efc..09fd48761 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -550,10 +550,67 @@ inline void BufferView::Read(Value &obj, Asset &r) { byteStride = MemberOrDefault(obj, "byteStride", 0u); } +//wangyi 0506 +inline uint8_t *BufferView::GetPointer(size_t accOffset) { + if (!buffer) return 0; + uint8_t *basePtr = buffer->GetPointer(); + if (!basePtr) return 0; + + size_t offset = accOffset + byteOffset; + if (buffer->EncodedRegion_Current != nullptr) { + const size_t begin = buffer->EncodedRegion_Current->Offset; + const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length; + if ((offset >= begin) && (offset < end)) + return &buffer->EncodedRegion_Current->DecodedData[offset - begin]; + } + + return basePtr + offset; +} + // // struct Accessor // +//wangyi 0506 +inline void Accessor::Sparse::PopulateData(size_t numBytes, uint8_t *bytes) { + if (bytes) { + data.assign(bytes, bytes + numBytes); + } else { + data.resize(numBytes, 0x00); + } +} + +inline void Accessor::Sparse::PatchData(unsigned int elementSize) { + uint8_t *pIndices = indices->GetPointer(indicesByteOffset); + const unsigned int indexSize = int(ComponentTypeSize(indicesType)); + uint8_t *indicesEnd = pIndices + count * indexSize; + + uint8_t *pValues = values->GetPointer(valuesByteOffset); + while (pIndices != indicesEnd) { + size_t offset; + switch (indicesType) { + case ComponentType_UNSIGNED_BYTE: + offset = *pIndices; + break; + case ComponentType_UNSIGNED_SHORT: + offset = *reinterpret_cast(pIndices); + break; + case ComponentType_UNSIGNED_INT: + offset = *reinterpret_cast(pIndices); + break; + default: + // have fun with float and negative values from signed types as indices. + throw DeadlyImportError("Unsupported component type in index."); + } + + offset *= elementSize; + std::memcpy(data.data() + offset, pValues, elementSize); + + pValues += elementSize; + pIndices += indexSize; + } +} +// wangyi 0506 inline void Accessor::Read(Value &obj, Asset &r) { if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { @@ -566,6 +623,43 @@ inline void Accessor::Read(Value &obj, Asset &r) { const char *typestr; type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR; + + //wangyi 0506 + if (Value *sparseValue = FindObject(obj, "sparse")) { + sparse.reset(new Sparse); + // count + ReadMember(*sparseValue, "count", sparse->count); + + // indices + if (Value *indicesValue = FindObject(*sparseValue, "indices")) { + //indices bufferView + Value *indiceViewID = FindUInt(*indicesValue, "bufferView"); + sparse->indices = r.bufferViews.Retrieve(indiceViewID->GetUint()); + //indices byteOffset + sparse->indicesByteOffset = MemberOrDefault(*indicesValue, "byteOffset", size_t(0)); + //indices componentType + sparse->indicesType = MemberOrDefault(*indicesValue, "componentType", ComponentType_BYTE); + //sparse->indices->Read(*indicesValue, r); + } + + // value + if (Value *valuesValue = FindObject(*sparseValue, "values")) { + //value bufferView + Value *valueViewID = FindUInt(*valuesValue, "bufferView"); + sparse->values = r.bufferViews.Retrieve(valueViewID->GetUint()); + //value byteOffset + sparse->valuesByteOffset = MemberOrDefault(*valuesValue, "byteOffset", size_t(0)); + //sparse->values->Read(*valuesValue, r); + } + + // indicesType + sparse->indicesType = MemberOrDefault(*sparseValue, "componentType", ComponentType_UNSIGNED_SHORT); + + const unsigned int elementSize = GetElementSize(); + const size_t dataSize = count * elementSize; + sparse->PopulateData(dataSize, bufferView ? bufferView->GetPointer(byteOffset) : 0); + sparse->PatchData(elementSize); + } } inline unsigned int Accessor::GetNumComponents() { @@ -580,7 +674,11 @@ inline unsigned int Accessor::GetElementSize() { return GetNumComponents() * GetBytesPerComponent(); } +// wangyi 0506 inline uint8_t *Accessor::GetPointer() { + if (sparse) + return sparse->data.data(); + if (!bufferView || !bufferView->buffer) return 0; uint8_t *basePtr = bufferView->buffer->GetPointer(); if (!basePtr) return 0; @@ -635,7 +733,8 @@ void Accessor::ExtractData(T *&outData) const size_t targetElemSize = sizeof(T); ai_assert(elemSize <= targetElemSize); - ai_assert(count * stride <= bufferView->byteLength); + //wangyi 0506 + ai_assert(count * stride <= (bufferView ? bufferView->byteLength : sparse->data.size())); outData = new T[count]; if (stride == elemSize && targetElemSize == elemSize) { @@ -660,6 +759,35 @@ inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t sr CopyData(_count, src, src_stride, dst, dst_stride); } +//wangyi 0506 +inline void Accessor::WriteSparseValues(size_t _count, const void *src_data, size_t src_dataStride) { + if (!sparse) + return; + + // values + uint8_t *value_buffer_ptr = sparse->values->buffer->GetPointer(); + size_t value_offset = sparse->valuesByteOffset + sparse->values->byteOffset; + size_t value_dst_stride = GetNumComponents() * GetBytesPerComponent(); + const uint8_t *value_src = reinterpret_cast(src_data); + uint8_t *value_dst = reinterpret_cast(value_buffer_ptr + value_offset); + ai_assert(value_dst + _count * value_dst_stride <= value_buffer_ptr + sparse->values->buffer->byteLength); + CopyData(_count, value_src, src_dataStride, value_dst, value_dst_stride); +} + +//wangyi 0506 +inline void Accessor::WriteSparseIndices(size_t _count, const void *src_idx, size_t src_idxStride) { + if (!sparse) + return; + + // indices + uint8_t *indices_buffer_ptr = sparse->indices->buffer->GetPointer(); + size_t indices_offset = sparse->indicesByteOffset + sparse->indices->byteOffset; + size_t indices_dst_stride = 1 * sizeof(unsigned short); + const uint8_t *indices_src = reinterpret_cast(src_idx); + uint8_t *indices_dst = reinterpret_cast(indices_buffer_ptr + indices_offset); + ai_assert(indices_dst + _count * indices_dst_stride <= indices_buffer_ptr + sparse->indices->buffer->byteLength); + CopyData(_count, indices_src, src_idxStride, indices_dst, indices_dst_stride); +} inline Accessor::Indexer::Indexer(Accessor &acc) : accessor(acc), data(acc.GetPointer()), elemSize(acc.GetElementSize()), stride(acc.bufferView && acc.bufferView->byteStride ? acc.bufferView->byteStride : elemSize) { } diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index 798f38c1c..5e936384f 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -107,21 +107,49 @@ namespace glTF2 { inline void Write(Value& obj, Accessor& a, AssetWriter& w) { - obj.AddMember("bufferView", a.bufferView->index, w.mAl); - obj.AddMember("byteOffset", (unsigned int)a.byteOffset, w.mAl); + //wangyi 0506 + if (a.bufferView) { + obj.AddMember("bufferView", a.bufferView->index, w.mAl); + obj.AddMember("byteOffset", (unsigned int)a.byteOffset, w.mAl); + Value vTmpMax, vTmpMin; + if (a.componentType == ComponentType_FLOAT) { + obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl); + obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl); + } else { + obj.AddMember("max", MakeValueCast(vTmpMax, a.max, w.mAl), w.mAl); + obj.AddMember("min", MakeValueCast(vTmpMin, a.min, w.mAl), w.mAl); + } + } obj.AddMember("componentType", int(a.componentType), w.mAl); obj.AddMember("count", (unsigned int)a.count, w.mAl); obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl); - Value vTmpMax, vTmpMin; - if (a.componentType == ComponentType_FLOAT) { - obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl); - obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl); - } else { - obj.AddMember("max", MakeValueCast(vTmpMax, a.max, w.mAl), w.mAl); - obj.AddMember("min", MakeValueCast(vTmpMin, a.min, w.mAl), w.mAl); - } + // wangyi 0506 + if (a.sparse) { + Value sparseValue; + sparseValue.SetObject(); + + //count + sparseValue.AddMember("count", (unsigned int)a.sparse->count, w.mAl); + + //indices + Value indices; + indices.SetObject(); + indices.AddMember("bufferView", a.sparse->indices->index, w.mAl); + indices.AddMember("byteOffset", (unsigned int)a.sparse->indicesByteOffset, w.mAl); + indices.AddMember("componentType", int(a.sparse->indicesType), w.mAl); + sparseValue.AddMember("indices", indices, w.mAl); + + //values + Value values; + values.SetObject(); + values.AddMember("bufferView", a.sparse->values->index, w.mAl); + values.AddMember("byteOffset", (unsigned int)a.sparse->valuesByteOffset, w.mAl); + sparseValue.AddMember("values", values, w.mAl); + + obj.AddMember("sparse", sparseValue, w.mAl); + } } inline void Write(Value& obj, Animation& a, AssetWriter& w) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index da6d9ab2e..8d6471db2 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -207,6 +207,152 @@ inline void SetAccessorRange(ComponentType compType, Ref acc, void* da } } +// wangyi 0506 +// compute the (data-dataBase), store the non-zero data items +template +size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn, unsigned int numCompsOut, void *&outputNZDiff, void *&outputNZIdx) { + std::vector vNZDiff; + std::vector vNZIdx; + size_t totalComps = count * numCompsIn; + T *bufferData_ptr = static_cast(data); + T *bufferData_end = bufferData_ptr + totalComps; + T *bufferBase_ptr = static_cast(dataBase); + + // Search and set extreme values. + for (short idx = 0; bufferData_ptr < bufferData_end; idx += 1, bufferData_ptr += numCompsIn) { + bool bNonZero = false; + + //for the data, check any component Non Zero + for (unsigned int j = 0; j < numCompsOut; j++) { + double valueData = bufferData_ptr[j]; + double valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0; + if ((valueData - valueBase) != 0) { + bNonZero = true; + break; + } + } + + //all zeros, continue + if (!bNonZero) + continue; + + //non zero, store the data + for (unsigned int j = 0; j < numCompsOut; j++) { + T valueData = bufferData_ptr[j]; + T valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0; + vNZDiff.push_back(valueData - valueBase); + } + vNZIdx.push_back(idx); + } + + //process data + outputNZDiff = new T[vNZDiff.size()]; + memcpy(outputNZDiff, vNZDiff.data(), vNZDiff.size() * sizeof(T)); + + outputNZIdx = new unsigned short[vNZIdx.size()]; + memcpy(outputNZIdx, vNZIdx.data(), vNZIdx.size() * sizeof(unsigned short)); + return vNZIdx.size(); +} + +inline size_t NZDiff(ComponentType compType, void *data, void *dataBase, size_t count, unsigned int numCompsIn, unsigned int numCompsOut, void *&nzDiff, void *&nzIdx) { + switch (compType) { + case ComponentType_SHORT: + return NZDiff(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); + case ComponentType_UNSIGNED_SHORT: + return NZDiff(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); + case ComponentType_UNSIGNED_INT: + return NZDiff(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); + case ComponentType_FLOAT: + return NZDiff(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); + case ComponentType_BYTE: + return NZDiff(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); + case ComponentType_UNSIGNED_BYTE: + return NZDiff(data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); + } + return 0; +} +// wangyi 0506 +inline Ref ExportDataSparse(Asset &a, std::string &meshName, Ref &buffer, + size_t count, void *data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, BufferViewTarget target = BufferViewTarget_NONE, void *dataBase = 0) { + if (!count || !data) { + return Ref(); + } + + unsigned int numCompsIn = AttribType::GetNumComponents(typeIn); + unsigned int numCompsOut = AttribType::GetNumComponents(typeOut); + unsigned int bytesPerComp = ComponentTypeSize(compType); + + // accessor + Ref acc = a.accessors.Create(a.FindUniqueID(meshName, "accessor")); + + // if there is a basic data vector + if (dataBase) { + size_t base_offset = buffer->byteLength; + size_t base_padding = base_offset % bytesPerComp; + base_offset += base_padding; + size_t base_length = count * numCompsOut * bytesPerComp; + buffer->Grow(base_length + base_padding); + + Ref bv = a.bufferViews.Create(a.FindUniqueID(meshName, "view")); + bv->buffer = buffer; + bv->byteOffset = base_offset; + bv->byteLength = base_length; //! The target that the WebGL buffer should be bound to. + bv->byteStride = 0; + bv->target = target; + acc->bufferView = bv; + acc->WriteData(count, dataBase, numCompsIn * bytesPerComp); + } + acc->byteOffset = 0; + acc->componentType = compType; + acc->count = count; + acc->type = typeOut; + + if (data) { + void *nzDiff = 0, *nzIdx = 0; + size_t nzCount = NZDiff(compType, data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx); + acc->sparse.reset(new Accessor::Sparse); + acc->sparse->count = nzCount; + + //indices + unsigned int bytesPerIdx = sizeof(unsigned short); + size_t indices_offset = buffer->byteLength; + size_t indices_padding = indices_offset % bytesPerIdx; + indices_offset += indices_padding; + size_t indices_length = nzCount * 1 * bytesPerIdx; + buffer->Grow(indices_length + indices_padding); + + Ref indicesBV = a.bufferViews.Create(a.FindUniqueID(meshName, "view")); + indicesBV->buffer = buffer; + indicesBV->byteOffset = indices_offset; + indicesBV->byteLength = indices_length; + indicesBV->byteStride = 0; + acc->sparse->indices = indicesBV; + acc->sparse->indicesType = ComponentType_UNSIGNED_SHORT; + acc->sparse->indicesByteOffset = 0; + acc->WriteSparseIndices(nzCount, nzIdx, 1 * bytesPerIdx); + + //values + size_t values_offset = buffer->byteLength; + size_t values_padding = values_offset % bytesPerComp; + values_offset += values_padding; + size_t values_length = nzCount * numCompsOut * bytesPerComp; + buffer->Grow(values_length + values_padding); + + Ref valuesBV = a.bufferViews.Create(a.FindUniqueID(meshName, "view")); + valuesBV->buffer = buffer; + valuesBV->byteOffset = values_offset; + valuesBV->byteLength = values_length; + valuesBV->byteStride = 0; + acc->sparse->values = valuesBV; + acc->sparse->valuesByteOffset = 0; + acc->WriteSparseValues(nzCount, nzDiff, numCompsIn * bytesPerComp); + + //clear + delete[] nzDiff; + delete[] nzIdx; + } + return acc; +} inline Ref ExportData(Asset& a, std::string& meshName, Ref& buffer, size_t count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, BufferViewTarget target = BufferViewTarget_NONE) { @@ -828,7 +974,11 @@ void glTF2Exporter::ExportMeshes() for (unsigned int vt = 0; vt < pAnimMesh->mNumVertices; ++vt) { pPositionDiff[vt] = pAnimMesh->mVertices[vt] - aim->mVertices[vt]; } - Ref vec = ExportData(*mAsset, meshId, b, + /*Ref vec = ExportData(*mAsset, meshId, b, + pAnimMesh->mNumVertices, pPositionDiff, + AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);*/ + //wangyi 0506 + Ref vec = ExportDataSparse(*mAsset, meshId, b, pAnimMesh->mNumVertices, pPositionDiff, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT); if (vec) { @@ -843,6 +993,10 @@ void glTF2Exporter::ExportMeshes() for (unsigned int vt = 0; vt < pAnimMesh->mNumVertices; ++vt) { pNormalDiff[vt] = pAnimMesh->mNormals[vt] - aim->mNormals[vt]; } + /*Ref vec = ExportData(*mAsset, meshId, b, + pAnimMesh->mNumVertices, pNormalDiff, + AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);*/ + //wangyi 0506 Ref vec = ExportData(*mAsset, meshId, b, pAnimMesh->mNumVertices, pNormalDiff, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT);