Added support for glTF2 sparse accessors.
Refactored Accessors, pulling out reusable bits between those and sparse accessors' indices / values.pull/3111/head
parent
b6b3b39bb3
commit
87e2d3a54d
|
@ -357,24 +357,46 @@ struct Object {
|
||||||
// Classes for each glTF top-level object type
|
// 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> 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.
|
//! 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
|
//! 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.
|
//! similar to how WebGL's vertexAttribPointer() defines an attribute in a buffer.
|
||||||
struct Accessor : public Object {
|
struct Accessor : public ComponentTypedBufferViewClient {
|
||||||
Ref<BufferView> bufferView; //!< The ID of the bufferView. (required)
|
struct Sparse;
|
||||||
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)
|
|
||||||
size_t count; //!< The number of attributes referenced by this accessor. (required)
|
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)
|
AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required)
|
||||||
std::vector<double> max; //!< Maximum value of each component in this attribute.
|
std::vector<double> max; //!< Maximum value of each component in this attribute.
|
||||||
std::vector<double> min; //!< Minimum value of each component in this attribute.
|
std::vector<double> min; //!< Minimum value of each component in this attribute.
|
||||||
|
std::unique_ptr<Sparse> sparse; //!< Sparse accessor information
|
||||||
|
|
||||||
unsigned int GetNumComponents();
|
unsigned int GetNumComponents();
|
||||||
unsigned int GetBytesPerComponent();
|
|
||||||
unsigned int GetElementSize();
|
unsigned int GetElementSize();
|
||||||
|
|
||||||
inline uint8_t *GetPointer();
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
bool ExtractData(T *&outData);
|
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<uint8_t> 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<uint16_t *>(pIndices);
|
||||||
|
break;
|
||||||
|
case ComponentType_UNSIGNED_INT:
|
||||||
|
offset = *reinterpret_cast<uint32_t *>(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() {
|
inline Indexer GetIndexer() {
|
||||||
return Indexer(*this);
|
return Indexer(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Accessor() {}
|
Accessor() {}
|
||||||
|
|
||||||
|
inline uint8_t *GetPointer();
|
||||||
|
|
||||||
void Read(Value &obj, Asset &r);
|
void Read(Value &obj, Asset &r);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -540,36 +540,10 @@ inline void BufferView::Read(Value &obj, Asset &r) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// struct Accessor
|
// struct BufferViewClient
|
||||||
//
|
//
|
||||||
|
|
||||||
inline void Accessor::Read(Value &obj, Asset &r) {
|
inline uint8_t *BufferViewClient::GetPointer() {
|
||||||
|
|
||||||
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() {
|
|
||||||
if (!bufferView || !bufferView->buffer) return 0;
|
if (!bufferView || !bufferView->buffer) return 0;
|
||||||
uint8_t *basePtr = bufferView->buffer->GetPointer();
|
uint8_t *basePtr = bufferView->buffer->GetPointer();
|
||||||
if (!basePtr) return 0;
|
if (!basePtr) return 0;
|
||||||
|
@ -588,6 +562,76 @@ inline uint8_t *Accessor::GetPointer() {
|
||||||
return basePtr + offset;
|
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 {
|
namespace {
|
||||||
inline void CopyData(size_t count,
|
inline void CopyData(size_t count,
|
||||||
const uint8_t *src, size_t src_stride,
|
const uint8_t *src, size_t src_stride,
|
||||||
|
@ -621,7 +665,7 @@ bool Accessor::ExtractData(T *&outData) {
|
||||||
const size_t targetElemSize = sizeof(T);
|
const size_t targetElemSize = sizeof(T);
|
||||||
ai_assert(elemSize <= targetElemSize);
|
ai_assert(elemSize <= targetElemSize);
|
||||||
|
|
||||||
ai_assert(count * stride <= bufferView->byteLength);
|
ai_assert(count * stride <= (bufferView ? bufferView->byteLength : sparse->data.size()));
|
||||||
|
|
||||||
outData = new T[count];
|
outData = new T[count];
|
||||||
if (stride == elemSize && targetElemSize == elemSize) {
|
if (stride == elemSize && targetElemSize == elemSize) {
|
||||||
|
|
Loading…
Reference in New Issue