From 87e2d3a54de9379675d0cf8899ebd1e2db750902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gy=C3=B6rgy=20Straub?= <> Date: Mon, 30 Mar 2020 14:30:33 +0100 Subject: [PATCH] Added support for glTF2 sparse accessors. Refactored Accessors, pulling out reusable bits between those and sparse accessors' indices / values. --- code/glTF2/glTF2Asset.h | 88 +++++++++++++++++++++++++++++--- code/glTF2/glTF2Asset.inl | 102 +++++++++++++++++++++++++++----------- 2 files changed, 154 insertions(+), 36 deletions(-) diff --git a/code/glTF2/glTF2Asset.h b/code/glTF2/glTF2Asset.h index c27522df3..1df87ace3 100644 --- a/code/glTF2/glTF2Asset.h +++ b/code/glTF2/glTF2Asset.h @@ -357,24 +357,46 @@ struct Object { // Classes for each glTF top-level object type // +//! Base class for types that access binary data from a BufferView, such as accessors +//! or sparse accessors' indices. +//! N.B. not a virtual class. All owned pointers to BufferViewClient instances should +//! be to their most derived types (which may of course be just BufferViewClient). +struct BufferViewClient : public Object { + Ref bufferView; //!< The ID of the bufferView. (required) + size_t byteOffset; //!< The offset relative to the start of the bufferView in bytes. (required) + + inline uint8_t *GetPointer(); + + void Read(Value &obj, Asset &r); +}; + +//! BufferViewClient with component type information. +//! N.B. not a virtual class. All owned pointers to ComponentTypedBufferViewClient +//! instances should be to their most derived types (which may of course be just +//! ComponentTypedBufferViewClient). +struct ComponentTypedBufferViewClient : public BufferViewClient { + ComponentType componentType; //!< The datatype of components in the attribute. (required) + + unsigned int GetBytesPerComponent(); + + void Read(Value &obj, Asset &r); +}; + //! A typed view into a BufferView. A BufferView contains raw binary data. //! 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 { - 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) +struct Accessor : public ComponentTypedBufferViewClient { + struct Sparse; + size_t count; //!< The number of attributes referenced by this accessor. (required) 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; //!< Sparse accessor information unsigned int GetNumComponents(); - unsigned int GetBytesPerComponent(); unsigned int GetElementSize(); - inline uint8_t *GetPointer(); - template bool ExtractData(T *&outData); @@ -405,11 +427,63 @@ struct Accessor : public Object { } }; + //! Sparse accessor information + struct Sparse { + size_t count; + ComponentTypedBufferViewClient indices; + BufferViewClient values; + + 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. + + inline void PopulateData(size_t numBytes, uint8_t *bytes) { + if (bytes) { + data.assign(bytes, bytes + numBytes); + } else { + data.resize(numBytes, 0x00); + } + } + + inline void PatchData(unsigned int elementSize) + { + uint8_t *pIndices = indices.GetPointer(); + const unsigned int indexSize = indices.GetBytesPerComponent(); + uint8_t *indicesEnd = pIndices + count * indexSize; + + uint8_t *pValues = values.GetPointer(); + while (pIndices != indicesEnd) { + size_t offset; + switch (indices.componentType) { + 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; + } + } + }; + inline Indexer GetIndexer() { return Indexer(*this); } Accessor() {} + + inline uint8_t *GetPointer(); + void Read(Value &obj, Asset &r); }; diff --git a/code/glTF2/glTF2Asset.inl b/code/glTF2/glTF2Asset.inl index f9c3aa805..79725bace 100644 --- a/code/glTF2/glTF2Asset.inl +++ b/code/glTF2/glTF2Asset.inl @@ -540,36 +540,10 @@ inline void BufferView::Read(Value &obj, Asset &r) { } // -// struct Accessor +// struct BufferViewClient // -inline void Accessor::Read(Value &obj, Asset &r) { - - if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { - bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); - } - - byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0)); - componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); - count = MemberOrDefault(obj, "count", size_t(0)); - - const char *typestr; - type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR; -} - -inline unsigned int Accessor::GetNumComponents() { - return AttribType::GetNumComponents(type); -} - -inline unsigned int Accessor::GetBytesPerComponent() { - return int(ComponentTypeSize(componentType)); -} - -inline unsigned int Accessor::GetElementSize() { - return GetNumComponents() * GetBytesPerComponent(); -} - -inline uint8_t *Accessor::GetPointer() { +inline uint8_t *BufferViewClient::GetPointer() { if (!bufferView || !bufferView->buffer) return 0; uint8_t *basePtr = bufferView->buffer->GetPointer(); if (!basePtr) return 0; @@ -588,6 +562,76 @@ inline uint8_t *Accessor::GetPointer() { return basePtr + offset; } +inline void BufferViewClient::Read(Value &obj, Asset &r) { + + if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { + bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); + } + + byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0)); +} + +// +// struct ComponentTypedBufferViewClient +// + +inline unsigned int ComponentTypedBufferViewClient::GetBytesPerComponent() { + return int(ComponentTypeSize(componentType)); +} + +inline void ComponentTypedBufferViewClient::Read(Value &obj, Asset &r) { + + BufferViewClient::Read(obj, r); + + componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); +} + +// +// struct Accessor +// + +inline uint8_t *Accessor::GetPointer() { + if (!sparse) return BufferViewClient::GetPointer(); + + return sparse->data.data(); +} + +inline void Accessor::Read(Value &obj, Asset &r) { + + ComponentTypedBufferViewClient::Read(obj, r); + + count = MemberOrDefault(obj, "count", size_t(0)); + + const char *typestr; + type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR; + + if (Value *sparseValue = FindObject(obj, "sparse")) { + sparse.reset(new Sparse); + ReadMember(*sparseValue, "count", sparse->count); + + if (Value *indicesValue = FindObject(*sparseValue, "indices")) { + sparse->indices.Read(*indicesValue, r); + } + + if (Value *valuesValue = FindObject(*sparseValue, "values")) { + sparse->values.Read(*valuesValue, r); + } + + const unsigned int elementSize = GetElementSize(); + const size_t dataSize = count * elementSize; + sparse->PopulateData(dataSize, BufferViewClient::GetPointer()); + sparse->PatchData(elementSize); + } +} + +inline unsigned int Accessor::GetNumComponents() { + return AttribType::GetNumComponents(type); +} + +inline unsigned int Accessor::GetElementSize() { + return GetNumComponents() * GetBytesPerComponent(); +} + namespace { inline void CopyData(size_t count, const uint8_t *src, size_t src_stride, @@ -621,7 +665,7 @@ bool Accessor::ExtractData(T *&outData) { const size_t targetElemSize = sizeof(T); ai_assert(elemSize <= targetElemSize); - ai_assert(count * stride <= bufferView->byteLength); + ai_assert(count * stride <= (bufferView ? bufferView->byteLength : sparse->data.size())); outData = new T[count]; if (stride == elemSize && targetElemSize == elemSize) {