Add Sparse Accessor Support

This commit is contained in:
Mayank Nagpal 2021-07-05 13:57:56 +05:30 committed by K. S. Ernest (iFire) Lee
parent 1bfd930402
commit 2fe07994d1
7 changed files with 275 additions and 82 deletions

View File

@ -142,6 +142,11 @@ int main(int argc, char* argv[]) {
gltfOptions.enableUserProperties, gltfOptions.enableUserProperties,
"Transcribe FBX User Properties into glTF node and material 'extras'."); "Transcribe FBX User Properties into glTF node and material 'extras'.");
app.add_flag(
"--blend-shape-no-sparse",
gltfOptions.disableSparseBlendShapes,
"Don't use sparse accessors to store blend shapes");
app.add_flag( app.add_flag(
"--blend-shape-normals", "--blend-shape-normals",
gltfOptions.useBlendShapeNormals, gltfOptions.useBlendShapeNormals,
@ -159,17 +164,17 @@ int main(int argc, char* argv[]) {
true); true);
app.add_option( app.add_option(
"--skinning-weights", "--skinning-weights",
gltfOptions.maxSkinningWeights, gltfOptions.maxSkinningWeights,
"The number of joint influences per vertex.", "The number of joint influences per vertex.",
true) true)
->check(CLI::Range(0, 512)); ->check(CLI::Range(0, 512));
app.add_option( app.add_option(
"-k,--keep-attribute", "-k,--keep-attribute",
[&](std::vector<std::string> attributes) -> bool { [&](std::vector<std::string> attributes) -> bool {
gltfOptions.keepAttribs = gltfOptions.keepAttribs =
RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS;
for (std::string attribute : attributes) { for (std::string attribute : attributes) {
if (attribute == "position") { if (attribute == "position") {
gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_POSITION; gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_POSITION;
@ -250,7 +255,9 @@ int main(int argc, char* argv[]) {
->check(CLI::Range(1, 32)) ->check(CLI::Range(1, 32))
->group("Draco"); ->group("Draco");
app.add_option("--fbx-temp-dir", gltfOptions.fbxTempDir, "Temporary directory to be used by FBX SDK.")->check(CLI::ExistingDirectory); app.add_option(
"--fbx-temp-dir", gltfOptions.fbxTempDir, "Temporary directory to be used by FBX SDK.")
->check(CLI::ExistingDirectory);
CLI11_PARSE(app, argc, argv); CLI11_PARSE(app, argc, argv);

View File

@ -112,21 +112,23 @@ struct GltfOptions {
/** Whether to include lights through the KHR_punctual_lights extension. */ /** Whether to include lights through the KHR_punctual_lights extension. */
bool useKHRLightsPunctual{true}; bool useKHRLightsPunctual{true};
/** Whether to include blend shape normals, if present according to the SDK. */ /** Whether to not use sparse accessors in blend shapes */
bool useBlendShapeNormals { false }; bool disableSparseBlendShapes{false};
/** Whether to include blend shape tangents, if present according to the SDK. */ /** Whether to include blend shape normals, if present according to the SDK. */
bool useBlendShapeTangents { false }; bool useBlendShapeNormals{false};
/** Whether to normalized skinning weights. */ /** Whether to include blend shape tangents, if present according to the SDK. */
bool normalizeSkinningWeights { true }; bool useBlendShapeTangents{false};
/** Maximum number of bone influences per vertex. */ /** Whether to normalized skinning weights. */
int maxSkinningWeights { 8 }; bool normalizeSkinningWeights{true};
/** When to compute vertex normals from geometry. */ /** Maximum number of bone influences per vertex. */
ComputeNormalsOption computeNormals = ComputeNormalsOption::BROKEN; int maxSkinningWeights{8};
/** When to use 32-bit indices. */ /** When to compute vertex normals from geometry. */
UseLongIndicesOptions useLongIndices = UseLongIndicesOptions::AUTO; ComputeNormalsOption computeNormals = ComputeNormalsOption::BROKEN;
/** Select baked animation framerate. */ /** When to use 32-bit indices. */
AnimationFramerateOptions animationFramerate = AnimationFramerateOptions::BAKE30; UseLongIndicesOptions useLongIndices = UseLongIndicesOptions::AUTO;
/** Select baked animation framerate. */
/** Temporary directory used by FBX SDK. */ AnimationFramerateOptions animationFramerate = AnimationFramerateOptions::BAKE30;
std::string fbxTempDir;
/** Temporary directory used by FBX SDK. */
std::string fbxTempDir;
}; };

View File

@ -69,6 +69,12 @@ class GltfModel {
BufferData& buffer, BufferData& buffer,
const std::string& filename); const std::string& filename);
template <class T>
void
CopyToBufferView(BufferViewData& bufferView, const std::vector<T>& source, const GLType& type) {
bufferView.appendAsBinaryArray(source, *binary, type);
}
template <class T> template <class T>
std::shared_ptr<AccessorData> AddAccessorWithView( std::shared_ptr<AccessorData> AddAccessorWithView(
BufferViewData& bufferView, BufferViewData& bufferView,
@ -76,8 +82,42 @@ class GltfModel {
const std::vector<T>& source, const std::vector<T>& source,
std::string name) { std::string name) {
auto accessor = accessors.hold(new AccessorData(bufferView, type, name)); auto accessor = accessors.hold(new AccessorData(bufferView, type, name));
accessor->appendAsBinaryArray(source, *binary); bufferView.appendAsBinaryArray(source, *binary, type);
bufferView.byteLength = accessor->byteLength(); accessor->count = bufferView.count;
return accessor;
}
template <class T>
std::shared_ptr<AccessorData> AddSparseAccessorWithView(
AccessorData& baseAccessor,
BufferViewData& indexBufferView,
const GLType& indexBufferViewType,
BufferViewData& bufferView,
const GLType& type,
const std::vector<T>& source,
std::string name) {
auto accessor =
accessors.hold(new AccessorData(baseAccessor, indexBufferView, bufferView, type, name));
bufferView.appendAsBinaryArray(source, *binary, type);
accessor->count = baseAccessor.count;
accessor->sparseIdxBufferViewType = indexBufferViewType.componentType.glType;
return accessor;
}
// template <class T>
std::shared_ptr<AccessorData> AddSparseAccessor(
AccessorData& baseAccessor,
BufferViewData& indexBufferView,
const GLType& indexBufferViewType,
BufferViewData& bufferView,
const GLType& type,
// const std::vector<T>& source,
std::string name) {
auto accessor =
accessors.hold(new AccessorData(baseAccessor, indexBufferView, bufferView, type, name));
// bufferView.appendAsBinaryArray(source, *binary, type);
accessor->count = baseAccessor.count;
accessor->sparseIdxBufferViewType = indexBufferViewType.componentType.glType;
return accessor; return accessor;
} }
@ -124,10 +164,10 @@ class GltfModel {
template <class T> template <class T>
std::shared_ptr<AccessorData> AddAttributeArrayToPrimitive( std::shared_ptr<AccessorData> AddAttributeArrayToPrimitive(
BufferData& buffer, BufferData& buffer,
const RawModel& surfaceModel, const RawModel& surfaceModel,
PrimitiveData& primitive, PrimitiveData& primitive,
const AttributeArrayDefinition<T>& attrDef) { const AttributeArrayDefinition<T>& attrDef) {
// copy attribute data into vector // copy attribute data into vector
std::vector<T> attribArr; std::vector<T> attribArr;
surfaceModel.GetArrayAttributeArray<T>(attribArr, attrDef.rawAttributeIx, attrDef.arrayOffset); surfaceModel.GetArrayAttributeArray<T>(attribArr, attrDef.rawAttributeIx, attrDef.arrayOffset);
@ -138,8 +178,7 @@ class GltfModel {
accessor = accessors.hold(new AccessorData(attrDef.glType)); accessor = accessors.hold(new AccessorData(attrDef.glType));
accessor->count = attribArr.size(); accessor->count = attribArr.size();
} } else {
else {
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER); auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER);
accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string("")); accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string(""));
} }

View File

@ -470,6 +470,9 @@ ModelData* Raw2Gltf(
surfaceModel.GetMaterial(surfaceModel.GetTriangle(0).materialIndex); surfaceModel.GetMaterial(surfaceModel.GetTriangle(0).materialIndex);
const MaterialData& mData = require(materialsById, rawMaterial.id); const MaterialData& mData = require(materialsById, rawMaterial.id);
if (verboseOutput)
fmt::printf("\rMaterial Name: %s\n", mData.name);
MeshData* mesh = nullptr; MeshData* mesh = nullptr;
auto meshIter = meshBySurfaceId.find(surfaceId); auto meshIter = meshBySurfaceId.find(surfaceId);
if (meshIter != meshBySurfaceId.end()) { if (meshIter != meshBySurfaceId.end()) {
@ -522,6 +525,16 @@ ModelData* Raw2Gltf(
// //
// surface vertices // surface vertices
// //
// Base Accessors needed for Sparse Accessors
std::shared_ptr<AccessorData> pAccBase;
std::shared_ptr<AccessorData> nAccBase;
std::shared_ptr<AccessorData> tAccBase;
// Sparse accessors cannot be zero length, but morph targets can easily have
// no modified vertices in multiprim meshes. In order to utilise sparse accessors
// in this case, we need a couple of single element dummy buffer views to reference.
std::shared_ptr<BufferViewData> dummyIdxView;
std::shared_ptr<BufferViewData> dummyDataView;
{ {
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) {
const AttributeDefinition<Vec3f> ATTR_POSITION( const AttributeDefinition<Vec3f> ATTR_POSITION(
@ -535,6 +548,8 @@ ModelData* Raw2Gltf(
accessor->min = toStdVec(rawSurface.bounds.min); accessor->min = toStdVec(rawSurface.bounds.min);
accessor->max = toStdVec(rawSurface.bounds.max); accessor->max = toStdVec(rawSurface.bounds.max);
pAccBase = accessor;
} }
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_NORMAL) != 0) { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_NORMAL) != 0) {
const AttributeDefinition<Vec3f> ATTR_NORMAL( const AttributeDefinition<Vec3f> ATTR_NORMAL(
@ -545,11 +560,13 @@ ModelData* Raw2Gltf(
draco::DT_FLOAT32); draco::DT_FLOAT32);
const auto _ = const auto _ =
gltf->AddAttributeToPrimitive<Vec3f>(buffer, surfaceModel, *primitive, ATTR_NORMAL); gltf->AddAttributeToPrimitive<Vec3f>(buffer, surfaceModel, *primitive, ATTR_NORMAL);
nAccBase = _;
} }
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0) { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0) {
const AttributeDefinition<Vec4f> ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F); const AttributeDefinition<Vec4f> ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F);
const auto _ = const auto _ =
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_TANGENT); gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_TANGENT);
tAccBase = _;
} }
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) {
const AttributeDefinition<Vec4f> ATTR_COLOR( const AttributeDefinition<Vec4f> ATTR_COLOR(
@ -617,43 +634,133 @@ ModelData* Raw2Gltf(
std::vector<Vec3f> positions, normals; std::vector<Vec3f> positions, normals;
std::vector<Vec4f> tangents; std::vector<Vec4f> tangents;
std::vector<TriangleIndex> sparseIndices;
for (int jj = 0; jj < surfaceModel.GetVertexCount(); jj++) { for (int jj = 0; jj < surfaceModel.GetVertexCount(); jj++) {
auto blendVertex = surfaceModel.GetVertex(jj).blends[channelIx]; auto blendVertex = surfaceModel.GetVertex(jj).blends[channelIx];
shapeBounds.AddPoint(blendVertex.position); shapeBounds.AddPoint(blendVertex.position);
positions.push_back(blendVertex.position); bool isSparseVertex = options.disableSparseBlendShapes; // If sparse is off, add all vertices
if (options.useBlendShapeTangents && channel.hasNormals) { // Check to see whether position, normal or tangent deviates from base mesh and flag as
normals.push_back(blendVertex.normal); // sparse.
if (blendVertex.position.Length() > 0.00) {
isSparseVertex = true;
} }
if (options.useBlendShapeTangents && channel.hasTangents) { // if (options.useBlendShapeNormals && channel.hasNormals &&
tangents.push_back(blendVertex.tangent); // blendVertex.normal.Length() > 0.00) {
// isSparseVertex = true;
// }
// if (options.useBlendShapeTangents && channel.hasTangents &&
// blendVertex.tangent.Length() > 0.00) {
// isSparseVertex = true;
// }
if (isSparseVertex == true) {
sparseIndices.push_back(jj);
positions.push_back(blendVertex.position);
if (options.useBlendShapeNormals && channel.hasNormals) {
normals.push_back(blendVertex.normal);
}
if (options.useBlendShapeTangents && channel.hasTangents) {
tangents.push_back(blendVertex.tangent);
}
} }
} }
std::shared_ptr<AccessorData> pAcc = gltf->AddAccessorWithView(
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER),
GLT_VEC3F,
positions,
channel.name);
pAcc->min = toStdVec(shapeBounds.min);
pAcc->max = toStdVec(shapeBounds.max);
std::shared_ptr<AccessorData> pAcc;
std::shared_ptr<AccessorData> nAcc; std::shared_ptr<AccessorData> nAcc;
if (!normals.empty()) { std::shared_ptr<AccessorData> tAcc;
nAcc = gltf->AddAccessorWithView( if (!options.disableSparseBlendShapes) {
if (verboseOutput)
fmt::printf(
"\rChannel Name: %-50s Sparse Count: %d\n", channel.name, sparseIndices.size());
if (sparseIndices.size() == 0) {
// Initalize dummy bufferviews if needed
if (!dummyIdxView) {
std::vector<TriangleIndex> dummyIndices;
dummyIndices.push_back(int(0));
dummyIdxView = gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE);
gltf->CopyToBufferView(
*dummyIdxView, dummyIndices, useLongIndices ? GLT_UINT : GLT_USHORT);
}
if (!dummyDataView) {
std::vector<Vec3f> dummyData;
dummyData.push_back(Vec3f(0.0));
dummyDataView = gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE);
dummyDataView->appendAsBinaryArray(dummyData, *gltf->binary, GLT_VEC3F);
}
// Set up sparse accessor with dummy buffer views
pAcc = gltf->AddSparseAccessor(
*pAccBase,
*dummyIdxView,
useLongIndices ? GLT_UINT : GLT_USHORT,
*dummyDataView,
GLT_VEC3F,
channel.name);
} else {
// Build Orphan Bufferview for Sparse Indices
std::shared_ptr<BufferViewData> indexBufferView =
gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE);
gltf->CopyToBufferView(
*indexBufferView, sparseIndices, useLongIndices ? GLT_UINT : GLT_USHORT);
pAcc = gltf->AddSparseAccessorWithView(
*pAccBase,
*indexBufferView,
useLongIndices ? GLT_UINT : GLT_USHORT,
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE),
GLT_VEC3F,
positions,
channel.name);
if (!normals.empty()) {
nAcc = gltf->AddSparseAccessorWithView(
*nAccBase,
*indexBufferView,
useLongIndices ? GLT_UINT : GLT_USHORT,
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE),
GLT_VEC3F,
normals,
channel.name);
}
if (!tangents.empty()) {
tAcc = gltf->AddSparseAccessorWithView(
*nAccBase,
*indexBufferView,
useLongIndices ? GLT_UINT : GLT_USHORT,
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE),
GLT_VEC4F,
tangents,
channel.name);
}
}
} else {
pAcc = gltf->AddAccessorWithView(
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER), *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER),
GLT_VEC3F, GLT_VEC3F,
normals, positions,
channel.name);
}
std::shared_ptr<AccessorData> tAcc;
if (!tangents.empty()) {
nAcc = gltf->AddAccessorWithView(
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER),
GLT_VEC4F,
tangents,
channel.name); channel.name);
if (!normals.empty()) {
nAcc = gltf->AddAccessorWithView(
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER),
GLT_VEC3F,
normals,
channel.name);
}
if (!tangents.empty()) {
nAcc = gltf->AddAccessorWithView(
*gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER),
GLT_VEC4F,
tangents,
channel.name);
}
} }
pAcc->min = toStdVec(shapeBounds.min);
pAcc->max = toStdVec(shapeBounds.max);
primitive->AddTarget(pAcc.get(), nAcc.get(), tAcc.get()); primitive->AddTarget(pAcc.get(), nAcc.get(), tAcc.get());
} }
} }

View File

@ -15,7 +15,23 @@ AccessorData::AccessorData(const BufferViewData& bufferView, GLType type, std::s
type(std::move(type)), type(std::move(type)),
byteOffset(0), byteOffset(0),
count(0), count(0),
name(name) {} name(name),
sparse(false) {}
AccessorData::AccessorData(const AccessorData& baseAccessor, const BufferViewData& sparseIdxBufferView, const BufferViewData& sparseDataBufferView, GLType type, std::string name)
: Holdable(),
bufferView(baseAccessor.bufferView),
type(std::move(type)),
byteOffset(baseAccessor.byteOffset),
count(baseAccessor.count),
name(name),
sparse(true),
sparseIdxCount(sparseIdxBufferView.count),
sparseIdxBufferView(sparseIdxBufferView.ix),
sparseIdxBufferViewOffset(0),
sparseIdxBufferViewType(0),
sparseDataBufferView(sparseDataBufferView.ix),
sparseDataBufferViewOffset(0) {}
AccessorData::AccessorData(GLType type) AccessorData::AccessorData(GLType type)
: Holdable(), bufferView(-1), type(std::move(type)), byteOffset(0), count(0) {} : Holdable(), bufferView(-1), type(std::move(type)), byteOffset(0), count(0) {}
@ -23,7 +39,7 @@ AccessorData::AccessorData(GLType type)
json AccessorData::serialize() const { json AccessorData::serialize() const {
json result{ json result{
{"componentType", type.componentType.glType}, {"type", type.dataType}, {"count", count}}; {"componentType", type.componentType.glType}, {"type", type.dataType}, {"count", count}};
if (bufferView >= 0) { if (bufferView >= 0 && !sparse) {
result["bufferView"] = bufferView; result["bufferView"] = bufferView;
result["byteOffset"] = byteOffset; result["byteOffset"] = byteOffset;
} }
@ -33,6 +49,17 @@ json AccessorData::serialize() const {
if (!max.empty()) { if (!max.empty()) {
result["max"] = max; result["max"] = max;
} }
if (sparse) {
json sparseData = {{"count", sparseIdxCount}};
sparseData["indices"] = { {"bufferView", sparseIdxBufferView},
{"byteOffset", sparseIdxBufferViewOffset},
{"componentType", sparseIdxBufferViewType}};
sparseData["values"] = { {"bufferView", sparseDataBufferView},
{"byteOffset", sparseDataBufferViewOffset}};
result["sparse"] = sparseData;
}
if (name.length() > 0) { if (name.length() > 0) {
result["name"] = name; result["name"] = name;
} }

View File

@ -11,35 +11,30 @@
#include "gltf/Raw2Gltf.hpp" #include "gltf/Raw2Gltf.hpp"
struct AccessorData : Holdable { struct AccessorData : Holdable {
AccessorData(const BufferViewData& bufferView, GLType type, std::string name); AccessorData(const BufferViewData& bufferView, GLType type, std::string name);
explicit AccessorData(GLType type); explicit AccessorData(GLType type);
AccessorData(const AccessorData& baseAccessor, const BufferViewData& sparseIdxBufferView, const BufferViewData& sparseDataBufferView, GLType type, std::string name);
json serialize() const override; json serialize() const override;
template <class T> unsigned int byteLength() const {
void appendAsBinaryArray(const std::vector<T>& in, std::vector<uint8_t>& out) { return type.byteStride() * count;
const unsigned int stride = type.byteStride();
const size_t offset = out.size();
const size_t count = in.size();
this->count = (unsigned int)count;
out.resize(offset + count * stride);
for (int ii = 0; ii < count; ii++) {
type.write(&out[offset + ii * stride], in[ii]);
} }
}
unsigned int byteLength() const { const int bufferView;
return type.byteStride() * count; const GLType type;
}
const int bufferView; unsigned int byteOffset;
const GLType type; unsigned int count;
std::vector<float> min;
std::vector<float> max;
std::string name;
unsigned int byteOffset; bool sparse;
unsigned int count; int sparseIdxCount;
std::vector<float> min; int sparseIdxBufferView;
std::vector<float> max; int sparseIdxBufferViewOffset;
std::string name; int sparseIdxBufferViewType;
int sparseDataBufferView;
int sparseDataBufferViewOffset;
}; };

View File

@ -21,9 +21,25 @@ struct BufferViewData : Holdable {
json serialize() const override; json serialize() const override;
template <class T>
void appendAsBinaryArray(const std::vector<T>& in, std::vector<uint8_t>& out, GLType type) {
const unsigned int stride = type.byteStride();
const size_t offset = out.size();
const size_t count = in.size();
this->byteLength = stride * count;
this->count = count;
out.resize(offset + count * stride);
for (int ii = 0; ii < count; ii++) {
type.write(&out[offset + ii * stride], in[ii]);
}
}
const unsigned int buffer; const unsigned int buffer;
const unsigned int byteOffset; const unsigned int byteOffset;
const GL_ArrayType target; const GL_ArrayType target;
unsigned int count = 0;
unsigned int byteLength = 0; unsigned int byteLength = 0;
}; };