From eda5342286dde7e14a5d6dcdc5892ebe039e1f41 Mon Sep 17 00:00:00 2001 From: Gordon Chapman Date: Sun, 10 Nov 2019 16:19:15 -0800 Subject: [PATCH 1/3] Added support for sparse accessors in blendshapes Testing still TBD --- src/FBX2glTF.cpp | 5 ++ src/FBX2glTF.h | 2 + src/gltf/GltfModel.hpp | 28 +++++- src/gltf/Raw2Gltf.cpp | 119 ++++++++++++++++++++----- src/gltf/properties/AccessorData.cpp | 36 +++++++- src/gltf/properties/AccessorData.hpp | 29 +++--- src/gltf/properties/BufferViewData.hpp | 16 ++++ 7 files changed, 191 insertions(+), 44 deletions(-) diff --git a/src/FBX2glTF.cpp b/src/FBX2glTF.cpp index 7c8948d..71a8163 100644 --- a/src/FBX2glTF.cpp +++ b/src/FBX2glTF.cpp @@ -142,6 +142,11 @@ int main(int argc, char* argv[]) { gltfOptions.enableUserProperties, "Transcribe FBX User Properties into glTF node and material 'extras'."); + app.add_flag( + "--blend-shape-sparse", + gltfOptions.enableSparseBlendShapes, + "Use sparse accessors to store blend shapes"); + app.add_flag( "--blend-shape-normals", gltfOptions.useBlendShapeNormals, diff --git a/src/FBX2glTF.h b/src/FBX2glTF.h index 56c7c2c..a054468 100644 --- a/src/FBX2glTF.h +++ b/src/FBX2glTF.h @@ -112,6 +112,8 @@ struct GltfOptions { /** Whether to include lights through the KHR_punctual_lights extension. */ bool useKHRLightsPunctual{true}; + /** Whether to use sparse accessors in blend shapes */ + bool enableSparseBlendShapes{false}; /** Whether to include blend shape normals, if present according to the SDK. */ bool useBlendShapeNormals{false}; /** Whether to include blend shape tangents, if present according to the SDK. */ diff --git a/src/gltf/GltfModel.hpp b/src/gltf/GltfModel.hpp index 9a15437..9b358cf 100644 --- a/src/gltf/GltfModel.hpp +++ b/src/gltf/GltfModel.hpp @@ -69,6 +69,14 @@ class GltfModel { BufferData& buffer, const std::string& filename); + template + void CopyToBufferView( + BufferViewData& bufferView, + const std::vector& source, + const GLType& type) { + bufferView.appendAsBinaryArray(source, *binary, type); + } + template std::shared_ptr AddAccessorWithView( BufferViewData& bufferView, @@ -76,8 +84,24 @@ class GltfModel { const std::vector& source, std::string name) { auto accessor = accessors.hold(new AccessorData(bufferView, type, name)); - accessor->appendAsBinaryArray(source, *binary); - bufferView.byteLength = accessor->byteLength(); + bufferView.appendAsBinaryArray(source, *binary, type); + accessor->count = bufferView.count; + return accessor; + } + + template + std::shared_ptr AddSparseAccessorWithView( + AccessorData& baseAccessor, + BufferViewData& indexBufferView, + const GLType& indexBufferViewType, + BufferViewData& bufferView, + const GLType& type, + const std::vector& 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; } diff --git a/src/gltf/Raw2Gltf.cpp b/src/gltf/Raw2Gltf.cpp index 0e2c592..768904a 100644 --- a/src/gltf/Raw2Gltf.cpp +++ b/src/gltf/Raw2Gltf.cpp @@ -470,6 +470,10 @@ ModelData* Raw2Gltf( // // surface vertices // + // Base Accessors needed for Sparse Accessors + std::shared_ptr pAccBase; + std::shared_ptr nAccBase; + std::shared_ptr tAccBase; { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) { const AttributeDefinition ATTR_POSITION( @@ -483,6 +487,8 @@ ModelData* Raw2Gltf( accessor->min = toStdVec(rawSurface.bounds.min); accessor->max = toStdVec(rawSurface.bounds.max); + + pAccBase = accessor; } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_NORMAL) != 0) { const AttributeDefinition ATTR_NORMAL( @@ -493,11 +499,13 @@ ModelData* Raw2Gltf( draco::DT_FLOAT32); const auto _ = gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_NORMAL); + nAccBase = _; } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0) { const AttributeDefinition ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F); const auto _ = gltf->AddAttributeToPrimitive( buffer, surfaceModel, *primitive, ATTR_TANGENT); + tAccBase = _; } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) { const AttributeDefinition ATTR_COLOR( @@ -559,43 +567,106 @@ ModelData* Raw2Gltf( std::vector positions, normals; std::vector tangents; + + std::vector sparseIndices; + for (int jj = 0; jj < surfaceModel.GetVertexCount(); jj++) { auto blendVertex = surfaceModel.GetVertex(jj).blends[channelIx]; shapeBounds.AddPoint(blendVertex.position); - positions.push_back(blendVertex.position); - if (options.useBlendShapeTangents && channel.hasNormals) { - normals.push_back(blendVertex.normal); + + bool isSparseVertex = !options.enableSparseBlendShapes; // If sparse is off, add all vertices + // Check to see whether position, normal or tangent deviates from base mesh and flag as sparse. + if (blendVertex.position.Length()>0.00){ + isSparseVertex = true; } - if (options.useBlendShapeTangents && channel.hasTangents) { - tangents.push_back(blendVertex.tangent); + if (options.useBlendShapeNormals && channel.hasNormals && 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 pAcc = gltf->AddAccessorWithView( - *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER), - GLT_VEC3F, - positions, +/* + std::shared_ptr pAccIx = gltf->AddAccessorWithView( + *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ELEMENT_ARRAY_BUFFER), + useLongIndices ? GLT_UINT : GLT_USHORT, + sparseIndices, channel.name); - pAcc->min = toStdVec(shapeBounds.min); - pAcc->max = toStdVec(shapeBounds.max); - + */ + std::shared_ptr pAcc; std::shared_ptr nAcc; - if (!normals.empty()) { - nAcc = gltf->AddAccessorWithView( + std::shared_ptr tAcc; + + if(options.enableSparseBlendShapes){ + fmt::printf("\rSparse Index Count: %d \n", sparseIndices.size()); + // Build Orphan Bufferview for Sparse Indices + std::shared_ptr 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), GLT_VEC3F, - normals, - channel.name); - } - - std::shared_ptr tAcc; - if (!tangents.empty()) { - nAcc = gltf->AddAccessorWithView( - *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER), - GLT_VEC4F, - tangents, + positions, 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()); } } diff --git a/src/gltf/properties/AccessorData.cpp b/src/gltf/properties/AccessorData.cpp index 3626c4e..10964d6 100644 --- a/src/gltf/properties/AccessorData.cpp +++ b/src/gltf/properties/AccessorData.cpp @@ -15,15 +15,34 @@ AccessorData::AccessorData(const BufferViewData& bufferView, GLType type, std::s type(std::move(type)), byteOffset(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) - : Holdable(), bufferView(-1), type(std::move(type)), byteOffset(0), count(0) {} + : Holdable(), bufferView(-1), type(std::move(type)), byteOffset(0), count(0), sparse(false){} json AccessorData::serialize() const { json result{ {"componentType", type.componentType.glType}, {"type", type.dataType}, {"count", count}}; - if (bufferView >= 0) { + if (bufferView >= 0 && !sparse) { result["bufferView"] = bufferView; result["byteOffset"] = byteOffset; } @@ -33,6 +52,17 @@ json AccessorData::serialize() const { if (!max.empty()) { 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) { result["name"] = name; } diff --git a/src/gltf/properties/AccessorData.hpp b/src/gltf/properties/AccessorData.hpp index 5a72e70..18fd468 100644 --- a/src/gltf/properties/AccessorData.hpp +++ b/src/gltf/properties/AccessorData.hpp @@ -11,25 +11,16 @@ #include "gltf/Raw2Gltf.hpp" 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); + AccessorData(const AccessorData& baseAccessor, + const BufferViewData& sparseIdxBufferView, + const BufferViewData& sparseDataBufferView, + GLType type, std::string name); json serialize() const override; - template - void appendAsBinaryArray(const std::vector& in, std::vector& out) { - 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 { return type.byteStride() * count; } @@ -42,4 +33,12 @@ struct AccessorData : Holdable { std::vector min; std::vector max; std::string name; + + const bool sparse; + int sparseIdxCount; + int sparseIdxBufferView; + int sparseIdxBufferViewOffset; + int sparseIdxBufferViewType; + int sparseDataBufferView; + int sparseDataBufferViewOffset; }; diff --git a/src/gltf/properties/BufferViewData.hpp b/src/gltf/properties/BufferViewData.hpp index 8e85cc5..5a837b6 100644 --- a/src/gltf/properties/BufferViewData.hpp +++ b/src/gltf/properties/BufferViewData.hpp @@ -21,9 +21,25 @@ struct BufferViewData : Holdable { json serialize() const override; + template + void appendAsBinaryArray(const std::vector& in, std::vector& 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 byteOffset; const GL_ArrayType target; + unsigned int count=0; unsigned int byteLength = 0; }; From 957615c6e224efe9fb1dec315de94474e374a3bf Mon Sep 17 00:00:00 2001 From: Gordon Chapman Date: Wed, 15 Apr 2020 16:19:33 -0700 Subject: [PATCH 2/3] Fixed duplicate entries in mesh.extras.targetnames --- src/gltf/properties/MeshData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gltf/properties/MeshData.cpp b/src/gltf/properties/MeshData.cpp index 36c06dc..bf17abf 100644 --- a/src/gltf/properties/MeshData.cpp +++ b/src/gltf/properties/MeshData.cpp @@ -17,7 +17,7 @@ json MeshData::serialize() const { json jsonTargetNamesArray = json::array(); for (const auto& primitive : primitives) { jsonPrimitivesArray.push_back(*primitive); - if (!primitive->targetNames.empty()) { + if (!primitive->targetNames.empty() && jsonTargetNamesArray.empty()) { for (auto targetName : primitive->targetNames) { jsonTargetNamesArray.push_back(targetName); } From 6ca6d06f0918d09a5ff13493d3c8dd847752d725 Mon Sep 17 00:00:00 2001 From: Gordon Chapman Date: Thu, 14 May 2020 19:49:07 -0700 Subject: [PATCH 3/3] Fixed bad sparse accessors for blendshapes on meshes with multiple primitives. --- src/gltf/GltfModel.hpp | 16 +++++++ src/gltf/Raw2Gltf.cpp | 104 +++++++++++++++++++++++++++-------------- 2 files changed, 84 insertions(+), 36 deletions(-) diff --git a/src/gltf/GltfModel.hpp b/src/gltf/GltfModel.hpp index 9b358cf..95d597d 100644 --- a/src/gltf/GltfModel.hpp +++ b/src/gltf/GltfModel.hpp @@ -105,6 +105,22 @@ class GltfModel { return accessor; } +// template + std::shared_ptr AddSparseAccessor( + AccessorData& baseAccessor, + BufferViewData& indexBufferView, + const GLType& indexBufferViewType, + BufferViewData& bufferView, + const GLType& type, +// const std::vector& 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 std::shared_ptr AddAccessorAndView(BufferData& buffer, const GLType& type, const std::vector& source) { diff --git a/src/gltf/Raw2Gltf.cpp b/src/gltf/Raw2Gltf.cpp index 768904a..999184c 100644 --- a/src/gltf/Raw2Gltf.cpp +++ b/src/gltf/Raw2Gltf.cpp @@ -418,6 +418,9 @@ ModelData* Raw2Gltf( surfaceModel.GetMaterial(surfaceModel.GetTriangle(0).materialIndex); const MaterialData& mData = require(materialsById, rawMaterial.id); + if (verboseOutput) + fmt::printf("\rMaterial Name: %s\n", mData.name); + MeshData* mesh = nullptr; auto meshIter = meshBySurfaceId.find(surfaceId); if (meshIter != meshBySurfaceId.end()) { @@ -474,6 +477,12 @@ ModelData* Raw2Gltf( std::shared_ptr pAccBase; std::shared_ptr nAccBase; std::shared_ptr 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 dummyIdxView; + std::shared_ptr dummyDataView; { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) { const AttributeDefinition ATTR_POSITION( @@ -597,51 +606,75 @@ ModelData* Raw2Gltf( } } } -/* - std::shared_ptr pAccIx = gltf->AddAccessorWithView( - *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ELEMENT_ARRAY_BUFFER), - useLongIndices ? GLT_UINT : GLT_USHORT, - sparseIndices, - channel.name); - */ + std::shared_ptr pAcc; std::shared_ptr nAcc; std::shared_ptr tAcc; if(options.enableSparseBlendShapes){ - fmt::printf("\rSparse Index Count: %d \n", sparseIndices.size()); - // Build Orphan Bufferview for Sparse Indices - std::shared_ptr indexBufferView = - gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE); - gltf->CopyToBufferView(*indexBufferView, sparseIndices, useLongIndices ? GLT_UINT : GLT_USHORT); + if (verboseOutput) + fmt::printf("\rChannel Name: %-50s Sparse Count: %d\n", channel.name,sparseIndices.size()); - 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, + if(sparseIndices.size()==0){ + // Initalize dummy bufferviews if needed + if(!dummyIdxView){ + std::vector 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 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 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, - 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, + 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( @@ -664,8 +697,7 @@ ModelData* Raw2Gltf( channel.name); } } - - pAcc->min = toStdVec(shapeBounds.min); + pAcc->min = toStdVec(shapeBounds.min); pAcc->max = toStdVec(shapeBounds.max); primitive->AddTarget(pAcc.get(), nAcc.get(), tAcc.get()); }