Implement draco decoding
parent
3a7d0f5656
commit
dde7605158
|
@ -376,87 +376,6 @@ struct Object {
|
||||||
// Classes for each glTF top-level object type
|
// Classes for each glTF top-level object type
|
||||||
//
|
//
|
||||||
|
|
||||||
//! 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 {
|
|
||||||
struct Sparse;
|
|
||||||
|
|
||||||
Ref<BufferView> 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)
|
|
||||||
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<double> max; //!< Maximum value of each component in this attribute.
|
|
||||||
std::vector<double> min; //!< Minimum value of each component in this attribute.
|
|
||||||
std::unique_ptr<Sparse> sparse;
|
|
||||||
|
|
||||||
unsigned int GetNumComponents();
|
|
||||||
unsigned int GetBytesPerComponent();
|
|
||||||
unsigned int GetElementSize();
|
|
||||||
|
|
||||||
inline uint8_t *GetPointer();
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
void ExtractData(T *&outData);
|
|
||||||
|
|
||||||
void WriteData(size_t count, const void *src_buffer, size_t src_stride);
|
|
||||||
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 {
|
|
||||||
friend struct Accessor;
|
|
||||||
|
|
||||||
// This field is reported as not used, making it protectd is the easiest way to work around it without going to the bottom of what the problem is:
|
|
||||||
// ../code/glTF2/glTF2Asset.h:392:19: error: private field 'accessor' is not used [-Werror,-Wunused-private-field]
|
|
||||||
protected:
|
|
||||||
Accessor &accessor;
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint8_t *data;
|
|
||||||
size_t elemSize, stride;
|
|
||||||
|
|
||||||
Indexer(Accessor &acc);
|
|
||||||
|
|
||||||
public:
|
|
||||||
//! Accesses the i-th value as defined by the accessor
|
|
||||||
template <class T>
|
|
||||||
T GetValue(int i);
|
|
||||||
|
|
||||||
//! Accesses the i-th value as defined by the accessor
|
|
||||||
inline unsigned int GetUInt(int i) {
|
|
||||||
return GetValue<unsigned int>(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool IsValid() const {
|
|
||||||
return data != 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline Indexer GetIndexer() {
|
|
||||||
return Indexer(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Accessor() {}
|
|
||||||
void Read(Value &obj, Asset &r);
|
|
||||||
|
|
||||||
//sparse
|
|
||||||
struct Sparse {
|
|
||||||
size_t count;
|
|
||||||
ComponentType indicesType;
|
|
||||||
Ref<BufferView> indices;
|
|
||||||
size_t indicesByteOffset;
|
|
||||||
Ref<BufferView> values;
|
|
||||||
size_t valuesByteOffset;
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
void PopulateData(size_t numBytes, uint8_t *bytes);
|
|
||||||
void PatchData(unsigned int elementSize);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
//! A buffer points to binary geometry, animation, or skins.
|
//! A buffer points to binary geometry, animation, or skins.
|
||||||
struct Buffer : public Object {
|
struct Buffer : public Object {
|
||||||
/********************* Types *********************/
|
/********************* Types *********************/
|
||||||
|
@ -594,6 +513,91 @@ struct BufferView : public Object {
|
||||||
uint8_t *GetPointer(size_t accOffset);
|
uint8_t *GetPointer(size_t accOffset);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//! 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 {
|
||||||
|
struct Sparse;
|
||||||
|
|
||||||
|
Ref<BufferView> 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)
|
||||||
|
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<double> max; //!< Maximum value of each component in this attribute.
|
||||||
|
std::vector<double> min; //!< Minimum value of each component in this attribute.
|
||||||
|
std::unique_ptr<Sparse> sparse;
|
||||||
|
std::unique_ptr<Buffer> decodedBuffer; // Packed decoded data, returned instead of original bufferView if present
|
||||||
|
|
||||||
|
unsigned int GetNumComponents();
|
||||||
|
unsigned int GetBytesPerComponent();
|
||||||
|
unsigned int GetElementSize();
|
||||||
|
|
||||||
|
inline uint8_t *GetPointer();
|
||||||
|
inline size_t GetStride();
|
||||||
|
inline size_t GetMaxByteSize();
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void ExtractData(T *&outData);
|
||||||
|
|
||||||
|
void WriteData(size_t count, const void *src_buffer, size_t src_stride);
|
||||||
|
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 {
|
||||||
|
friend struct Accessor;
|
||||||
|
|
||||||
|
// This field is reported as not used, making it protectd is the easiest way to work around it without going to the bottom of what the problem is:
|
||||||
|
// ../code/glTF2/glTF2Asset.h:392:19: error: private field 'accessor' is not used [-Werror,-Wunused-private-field]
|
||||||
|
protected:
|
||||||
|
Accessor &accessor;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t *data;
|
||||||
|
size_t elemSize, stride;
|
||||||
|
|
||||||
|
Indexer(Accessor &acc);
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Accesses the i-th value as defined by the accessor
|
||||||
|
template <class T>
|
||||||
|
T GetValue(int i);
|
||||||
|
|
||||||
|
//! Accesses the i-th value as defined by the accessor
|
||||||
|
inline unsigned int GetUInt(int i) {
|
||||||
|
return GetValue<unsigned int>(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsValid() const {
|
||||||
|
return data != 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Indexer GetIndexer() {
|
||||||
|
return Indexer(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Accessor() {}
|
||||||
|
void Read(Value &obj, Asset &r);
|
||||||
|
|
||||||
|
//sparse
|
||||||
|
struct Sparse {
|
||||||
|
size_t count;
|
||||||
|
ComponentType indicesType;
|
||||||
|
Ref<BufferView> indices;
|
||||||
|
size_t indicesByteOffset;
|
||||||
|
Ref<BufferView> values;
|
||||||
|
size_t valuesByteOffset;
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
void PopulateData(size_t numBytes, uint8_t *bytes);
|
||||||
|
void PatchData(unsigned int elementSize);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
struct Camera : public Object {
|
struct Camera : public Object {
|
||||||
enum Type {
|
enum Type {
|
||||||
Perspective,
|
Perspective,
|
||||||
|
@ -1092,6 +1096,7 @@ public:
|
||||||
bool KHR_materials_sheen;
|
bool KHR_materials_sheen;
|
||||||
bool KHR_materials_clearcoat;
|
bool KHR_materials_clearcoat;
|
||||||
bool KHR_materials_transmission;
|
bool KHR_materials_transmission;
|
||||||
|
bool KHR_draco_mesh_compression;
|
||||||
} extensionsUsed;
|
} extensionsUsed;
|
||||||
|
|
||||||
//! Keeps info about the required extensions
|
//! Keeps info about the required extensions
|
||||||
|
|
|
@ -46,6 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include <assimp/DefaultLogger.hpp>
|
#include <assimp/DefaultLogger.hpp>
|
||||||
#include <assimp/MemoryIOWrapper.h>
|
#include <assimp/MemoryIOWrapper.h>
|
||||||
|
|
||||||
|
#ifdef ASSIMP_ENABLE_DRACO
|
||||||
|
#include "draco/compression/decode.h"
|
||||||
|
#include "draco/core/decoder_buffer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace Assimp;
|
using namespace Assimp;
|
||||||
|
|
||||||
namespace glTF2 {
|
namespace glTF2 {
|
||||||
|
@ -175,6 +180,102 @@ inline Value *FindObject(Value &val, const char *id) {
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
#ifdef ASSIMP_ENABLE_DRACO
|
||||||
|
// Google draco library headers spew many warnings. Bad Google, no cookie
|
||||||
|
#if _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4018) // Signed/unsigned mismatch
|
||||||
|
#pragma warning(disable: 4804) // Unsafe use of type 'bool'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline void CopyFaceIndex_Draco(Buffer &decodedIndexBuffer, const draco::Mesh &draco_mesh) {
|
||||||
|
const size_t faceStride = sizeof(T) * 3;
|
||||||
|
for (draco::FaceIndex f(0); f < draco_mesh.num_faces(); ++f) {
|
||||||
|
const draco::Mesh::Face &face = draco_mesh.face(f);
|
||||||
|
T indices[3] = { static_cast<T>(face[0].value()), static_cast<T>(face[1].value()), static_cast<T>(face[2].value()) };
|
||||||
|
memcpy(decodedIndexBuffer.GetPointer() + (f.value() * faceStride), &indices[0], faceStride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetDecodedIndexBuffer_Draco(const draco::Mesh &dracoMesh, Mesh::Primitive &prim) {
|
||||||
|
if (!prim.indices || dracoMesh.num_faces() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Create a decoded Index buffer (if there is one)
|
||||||
|
size_t componentBytes = prim.indices->GetBytesPerComponent();
|
||||||
|
|
||||||
|
auto decodedIndexBuffer = std::make_unique<Buffer>();
|
||||||
|
decodedIndexBuffer->Grow(dracoMesh.num_faces() * 3 * componentBytes);
|
||||||
|
|
||||||
|
// If accessor uses the same size as draco implementation, copy the draco buffer directly
|
||||||
|
|
||||||
|
// Usually uint32_t but shouldn't assume
|
||||||
|
if (sizeof(dracoMesh.face(draco::FaceIndex(0))[0]) == componentBytes) {
|
||||||
|
memcpy(decodedIndexBuffer->GetPointer(), &dracoMesh.face(draco::FaceIndex(0))[0], decodedIndexBuffer->byteLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not same size, convert
|
||||||
|
switch (componentBytes) {
|
||||||
|
case sizeof(uint32_t): CopyFaceIndex_Draco<uint32_t>(*decodedIndexBuffer, dracoMesh); break;
|
||||||
|
case sizeof(uint16_t): CopyFaceIndex_Draco<uint16_t>(*decodedIndexBuffer, dracoMesh); break;
|
||||||
|
case sizeof(uint8_t): CopyFaceIndex_Draco<uint8_t>(*decodedIndexBuffer, dracoMesh); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign this alternate data buffer to the accessor
|
||||||
|
prim.indices->decodedBuffer.swap(decodedIndexBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static bool GetAttributeForAllPoints(const draco::Mesh &dracoMesh,
|
||||||
|
const draco::PointAttribute &dracoAttribute,
|
||||||
|
Buffer &outBuffer) {
|
||||||
|
size_t byteOffset = 0;
|
||||||
|
T values[4] = {0, 0, 0, 0};
|
||||||
|
for (draco::PointIndex i(0); i < dracoMesh.num_points(); ++i) {
|
||||||
|
const draco::AttributeValueIndex val_index = dracoAttribute.mapped_index(i);
|
||||||
|
if (!dracoAttribute.ConvertValue<T>(val_index, dracoAttribute.num_components(), values))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memcpy(outBuffer.GetPointer() + byteOffset, &values[0], sizeof(T) * dracoAttribute.num_components());
|
||||||
|
byteOffset += sizeof(T) * dracoAttribute.num_components();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void SetDecodedAttributeBuffer_Draco(const draco::Mesh &dracoMesh, uint32_t dracoAttribId, Accessor &accessor) {
|
||||||
|
// Create decoded buffer
|
||||||
|
const draco::PointAttribute *pDracoAttribute = dracoMesh.GetAttributeByUniqueId(dracoAttribId);
|
||||||
|
if (pDracoAttribute == nullptr)
|
||||||
|
throw DeadlyImportError("GLTF: Invalid draco attribute id: ", dracoAttribId);
|
||||||
|
|
||||||
|
size_t componentBytes = accessor.GetBytesPerComponent();
|
||||||
|
|
||||||
|
auto decodedAttribBuffer = std::make_unique<Buffer>();
|
||||||
|
decodedAttribBuffer->Grow(dracoMesh.num_points() * pDracoAttribute->num_components() * componentBytes);
|
||||||
|
|
||||||
|
switch(accessor.componentType)
|
||||||
|
{
|
||||||
|
case ComponentType_BYTE: GetAttributeForAllPoints<int8_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break;
|
||||||
|
case ComponentType_UNSIGNED_BYTE: GetAttributeForAllPoints<uint8_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break;
|
||||||
|
case ComponentType_SHORT : GetAttributeForAllPoints<int16_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break;
|
||||||
|
case ComponentType_UNSIGNED_SHORT: GetAttributeForAllPoints<uint16_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break;
|
||||||
|
case ComponentType_UNSIGNED_INT: GetAttributeForAllPoints<uint32_t>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break;
|
||||||
|
case ComponentType_FLOAT : GetAttributeForAllPoints<float>(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign this alternate data buffer to the accessor
|
||||||
|
accessor.decodedBuffer.swap(decodedAttribBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// LazyDict methods
|
// LazyDict methods
|
||||||
//
|
//
|
||||||
|
@ -709,6 +810,9 @@ inline unsigned int Accessor::GetElementSize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint8_t *Accessor::GetPointer() {
|
inline uint8_t *Accessor::GetPointer() {
|
||||||
|
if (decodedBuffer)
|
||||||
|
return decodedBuffer->GetPointer();
|
||||||
|
|
||||||
if (sparse)
|
if (sparse)
|
||||||
return sparse->data.data();
|
return sparse->data.data();
|
||||||
|
|
||||||
|
@ -730,6 +834,24 @@ inline uint8_t *Accessor::GetPointer() {
|
||||||
return basePtr + offset;
|
return basePtr + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline size_t Accessor::GetStride()
|
||||||
|
{
|
||||||
|
// Decoded buffer is always packed
|
||||||
|
if (decodedBuffer)
|
||||||
|
return GetElementSize();
|
||||||
|
|
||||||
|
// Sparse and normal bufferView
|
||||||
|
return (bufferView && bufferView->byteStride ? bufferView->byteStride : GetElementSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t Accessor::GetMaxByteSize()
|
||||||
|
{
|
||||||
|
if (decodedBuffer)
|
||||||
|
return decodedBuffer->byteLength;
|
||||||
|
|
||||||
|
return (bufferView ? bufferView->byteLength : sparse->data.size());
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
@ -761,7 +883,7 @@ void Accessor::ExtractData(T *&outData) {
|
||||||
const size_t elemSize = GetElementSize();
|
const size_t elemSize = GetElementSize();
|
||||||
const size_t totalSize = elemSize * count;
|
const size_t totalSize = elemSize * count;
|
||||||
|
|
||||||
const size_t stride = bufferView && bufferView->byteStride ? bufferView->byteStride : elemSize;
|
const size_t stride = GetStride();
|
||||||
|
|
||||||
const size_t targetElemSize = sizeof(T);
|
const size_t targetElemSize = sizeof(T);
|
||||||
|
|
||||||
|
@ -769,7 +891,7 @@ void Accessor::ExtractData(T *&outData) {
|
||||||
throw DeadlyImportError("GLTF: elemSize ", elemSize, " > targetElemSize ", targetElemSize, " in ", getContextForErrorMessages(id, name));
|
throw DeadlyImportError("GLTF: elemSize ", elemSize, " > targetElemSize ", targetElemSize, " in ", getContextForErrorMessages(id, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t maxSize = (bufferView ? bufferView->byteLength : sparse->data.size());
|
const size_t maxSize = GetMaxByteSize();
|
||||||
if (count*stride > maxSize) {
|
if (count*stride > maxSize) {
|
||||||
throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
|
throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
|
||||||
}
|
}
|
||||||
|
@ -828,14 +950,14 @@ inline Accessor::Indexer::Indexer(Accessor &acc) :
|
||||||
accessor(acc),
|
accessor(acc),
|
||||||
data(acc.GetPointer()),
|
data(acc.GetPointer()),
|
||||||
elemSize(acc.GetElementSize()),
|
elemSize(acc.GetElementSize()),
|
||||||
stride(acc.bufferView && acc.bufferView->byteStride ? acc.bufferView->byteStride : elemSize) {
|
stride(acc.GetStride()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Accesses the i-th value as defined by the accessor
|
//! Accesses the i-th value as defined by the accessor
|
||||||
template <class T>
|
template <class T>
|
||||||
T Accessor::Indexer::GetValue(int i) {
|
T Accessor::Indexer::GetValue(int i) {
|
||||||
ai_assert(data);
|
ai_assert(data);
|
||||||
ai_assert(i * stride < accessor.bufferView->byteLength);
|
ai_assert(i * stride < accessor.GetMaxByteSize());
|
||||||
// Ensure that the memcpy doesn't overwrite the local.
|
// Ensure that the memcpy doesn't overwrite the local.
|
||||||
const size_t sizeToCopy = std::min(elemSize, sizeof(T));
|
const size_t sizeToCopy = std::min(elemSize, sizeof(T));
|
||||||
T value = T();
|
T value = T();
|
||||||
|
@ -1192,6 +1314,14 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
|
||||||
Primitive &prim = this->primitives[i];
|
Primitive &prim = this->primitives[i];
|
||||||
prim.mode = MemberOrDefault(primitive, "mode", PrimitiveMode_TRIANGLES);
|
prim.mode = MemberOrDefault(primitive, "mode", PrimitiveMode_TRIANGLES);
|
||||||
|
|
||||||
|
if (Value *indices = FindUInt(primitive, "indices")) {
|
||||||
|
prim.indices = pAsset_Root.accessors.Retrieve(indices->GetUint());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Value *material = FindUInt(primitive, "material")) {
|
||||||
|
prim.material = pAsset_Root.materials.Retrieve(material->GetUint());
|
||||||
|
}
|
||||||
|
|
||||||
if (Value *attrs = FindObject(primitive, "attributes")) {
|
if (Value *attrs = FindObject(primitive, "attributes")) {
|
||||||
for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) {
|
for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) {
|
||||||
if (!it->value.IsUint()) continue;
|
if (!it->value.IsUint()) continue;
|
||||||
|
@ -1200,11 +1330,11 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
|
||||||
// and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc.
|
// and WEIGHT.Attribute semantics can be of the form[semantic]_[set_index], e.g., TEXCOORD_0, TEXCOORD_1, etc.
|
||||||
|
|
||||||
int undPos = 0;
|
int undPos = 0;
|
||||||
Mesh::AccessorList *vec = 0;
|
Mesh::AccessorList *vec = nullptr;
|
||||||
if (GetAttribVector(prim, attr, vec, undPos)) {
|
if (GetAttribVector(prim, attr, vec, undPos)) {
|
||||||
size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
|
size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
|
||||||
if ((*vec).size() != idx) {
|
if ((*vec).size() != idx) {
|
||||||
throw DeadlyImportError("GLTF: Invalid attribute: ", attr, ". All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc.");
|
throw DeadlyImportError("GLTF: Invalid attribute in mesh: ", name, " primitive: ", i,"attrib: ", attr, ". All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc.");
|
||||||
}
|
}
|
||||||
(*vec).resize(idx + 1);
|
(*vec).resize(idx + 1);
|
||||||
(*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint());
|
(*vec)[idx] = pAsset_Root.accessors.Retrieve(it->value.GetUint());
|
||||||
|
@ -1212,6 +1342,66 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ASSIMP_ENABLE_DRACO
|
||||||
|
// KHR_draco_mesh_compression spec: Draco can only be used for glTF Triangles or Triangle Strips
|
||||||
|
if (pAsset_Root.extensionsUsed.KHR_draco_mesh_compression && (prim.mode == PrimitiveMode_TRIANGLES || prim.mode == PrimitiveMode_TRIANGLE_STRIP)) {
|
||||||
|
// Look for draco mesh compression extension and bufferView
|
||||||
|
// Skip if any missing
|
||||||
|
if (Value *exts = FindObject(primitive, "extensions")) {
|
||||||
|
if (Value *dracoExt = FindObject(*exts, "KHR_draco_mesh_compression")) {
|
||||||
|
if (Value *bufView = FindUInt(*dracoExt, "bufferView")) {
|
||||||
|
// Attempt to load indices and attributes using draco compression
|
||||||
|
auto bufferView = pAsset_Root.bufferViews.Retrieve(bufView->GetUint());
|
||||||
|
// Attempt to perform the draco decode on the buffer data
|
||||||
|
const char *bufferViewData = reinterpret_cast<const char *>(bufferView->buffer->GetPointer() + bufferView->byteOffset);
|
||||||
|
draco::DecoderBuffer decoderBuffer;
|
||||||
|
decoderBuffer.Init(bufferViewData, bufferView->byteLength);
|
||||||
|
draco::Decoder decoder;
|
||||||
|
auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
|
||||||
|
if (!decodeResult.ok()) {
|
||||||
|
// A corrupt Draco isn't actually fatal if the primitive data is also provided in a standard buffer, but does anyone do that?
|
||||||
|
throw DeadlyImportError("GLTF: Invalid Draco mesh compression in mesh ", name, " primitive ", i, ": ", decodeResult.status().error_msg_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we have a draco mesh
|
||||||
|
const std::unique_ptr<draco::Mesh> &pDracoMesh = decodeResult.value();
|
||||||
|
|
||||||
|
// Redirect the accessors to the decoded data
|
||||||
|
|
||||||
|
// Indices
|
||||||
|
SetDecodedIndexBuffer_Draco(*pDracoMesh, prim);
|
||||||
|
|
||||||
|
// Vertex attributes
|
||||||
|
if (Value *attrs = FindObject(*dracoExt, "attributes")) {
|
||||||
|
for (Value::MemberIterator it = attrs->MemberBegin(); it != attrs->MemberEnd(); ++it) {
|
||||||
|
if (!it->value.IsUint()) continue;
|
||||||
|
const char *attr = it->name.GetString();
|
||||||
|
|
||||||
|
int undPos = 0;
|
||||||
|
Mesh::AccessorList *vec = nullptr;
|
||||||
|
if (GetAttribVector(prim, attr, vec, undPos)) {
|
||||||
|
size_t idx = (attr[undPos] == '_') ? atoi(attr + undPos + 1) : 0;
|
||||||
|
if (idx >= (*vec).size()) {
|
||||||
|
throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr, ". "
|
||||||
|
"All indices for indexed attribute semantics must start with 0 and be continuous positive integers: TEXCOORD_0, TEXCOORD_1, etc.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Accessor &attribAccessor = *(*vec)[idx];
|
||||||
|
if (attribAccessor.count == 0)
|
||||||
|
throw DeadlyImportError("GLTF: Invalid draco attribute in mesh: ", name, " primitive: ", i, " attrib: ", attr);
|
||||||
|
|
||||||
|
// Redirect this accessor to the appropriate Draco vertex attribute data
|
||||||
|
const uint32_t dracoAttribId = it->value.GetUint();
|
||||||
|
SetDecodedAttributeBuffer_Draco(*pDracoMesh, dracoAttribId, attribAccessor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Value *targetsArray = FindArray(primitive, "targets");
|
Value *targetsArray = FindArray(primitive, "targets");
|
||||||
if (nullptr != targetsArray) {
|
if (nullptr != targetsArray) {
|
||||||
prim.targets.resize(targetsArray->Size());
|
prim.targets.resize(targetsArray->Size());
|
||||||
|
@ -1238,14 +1428,6 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Value *indices = FindUInt(primitive, "indices")) {
|
|
||||||
prim.indices = pAsset_Root.accessors.Retrieve(indices->GetUint());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Value *material = FindUInt(primitive, "material")) {
|
|
||||||
prim.material = pAsset_Root.materials.Retrieve(material->GetUint());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1693,10 +1875,12 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) {
|
||||||
ReadExtensionsUsed(doc);
|
ReadExtensionsUsed(doc);
|
||||||
ReadExtensionsRequired(doc);
|
ReadExtensionsRequired(doc);
|
||||||
|
|
||||||
// Currently Draco is not supported
|
#ifndef ASSIMP_ENABLE_DRACO
|
||||||
|
// Is Draco supported?
|
||||||
if (extensionsRequired.KHR_draco_mesh_compression) {
|
if (extensionsRequired.KHR_draco_mesh_compression) {
|
||||||
throw DeadlyImportError("GLTF: Draco mesh compression not currently supported.");
|
throw DeadlyImportError("GLTF: Draco mesh compression not supported.");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Prepare the dictionaries
|
// Prepare the dictionaries
|
||||||
for (size_t i = 0; i < mDicts.size(); ++i) {
|
for (size_t i = 0; i < mDicts.size(); ++i) {
|
||||||
|
@ -1784,6 +1968,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) {
|
||||||
CHECK_EXT(KHR_materials_sheen);
|
CHECK_EXT(KHR_materials_sheen);
|
||||||
CHECK_EXT(KHR_materials_clearcoat);
|
CHECK_EXT(KHR_materials_clearcoat);
|
||||||
CHECK_EXT(KHR_materials_transmission);
|
CHECK_EXT(KHR_materials_transmission);
|
||||||
|
CHECK_EXT(KHR_draco_mesh_compression);
|
||||||
|
|
||||||
#undef CHECK_EXT
|
#undef CHECK_EXT
|
||||||
}
|
}
|
||||||
|
|
|
@ -574,3 +574,35 @@ TEST_F(utglTF2ImportExport, allIndicesOutOfRange) {
|
||||||
std::string error = importer.GetErrorString();
|
std::string error = importer.GetErrorString();
|
||||||
ASSERT_NE(error.find("Mesh \"Mesh\" has no faces"), std::string::npos);
|
ASSERT_NE(error.find("Mesh \"Mesh\" has no faces"), std::string::npos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
// Draco decoding
|
||||||
|
#ifdef ASSIMP_ENABLE_DRACO
|
||||||
|
|
||||||
|
TEST_F(utglTF2ImportExport, import_dracoEncoded) {
|
||||||
|
Assimp::Importer importer;
|
||||||
|
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/draco/2CylinderEngine.gltf",
|
||||||
|
aiProcess_ValidateDataStructure);
|
||||||
|
ASSERT_NE(scene, nullptr);
|
||||||
|
ASSERT_NE(scene->mMetaData, nullptr);
|
||||||
|
{
|
||||||
|
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT));
|
||||||
|
aiString format;
|
||||||
|
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, format));
|
||||||
|
ASSERT_EQ(strcmp(format.C_Str(), "glTF2 Importer"), 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT_VERSION));
|
||||||
|
aiString version;
|
||||||
|
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, version));
|
||||||
|
ASSERT_EQ(strcmp(version.C_Str(), "2.0"), 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ASSERT_TRUE(scene->mMetaData->HasKey(AI_METADATA_SOURCE_GENERATOR));
|
||||||
|
aiString generator;
|
||||||
|
ASSERT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_GENERATOR, generator));
|
||||||
|
ASSERT_EQ(strcmp(generator.C_Str(), "COLLADA2GLTF"), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue