From 686ec713a1cec04adb2d996cedf028b26d8c09f3 Mon Sep 17 00:00:00 2001 From: Adam Bialogonski Date: Tue, 16 May 2023 16:53:24 +0100 Subject: [PATCH] Blendshapes fix (still missing tests) When using sparse accessors, the gltf loader loads data correctly but the blendshapes generator (texture and data) always assumes that morph targets contain all the vertices which isn't a case. Fix makes sure that for sparse access only indexed vertices are affected by the blendshape. It still generate full vertex data (for whole mesh) so it's compatible with current shader. Change-Id: I1f36c1d9d24dec0eb194d0aaf1b93488b61b1d58 Signed-off-by: Adam Bialogonski --- dali-scene3d/public-api/loader/mesh-definition.cpp | 185 ++++++++++++++++----- dali-scene3d/public-api/loader/mesh-definition.h | 6 +- 2 files changed, 145 insertions(+), 46 deletions(-) diff --git a/dali-scene3d/public-api/loader/mesh-definition.cpp b/dali-scene3d/public-api/loader/mesh-definition.cpp index 444eb37..5ccf70c 100644 --- a/dali-scene3d/public-api/loader/mesh-definition.cpp +++ b/dali-scene3d/public-api/loader/mesh-definition.cpp @@ -113,7 +113,7 @@ void ReadValues(const std::vector& valuesBuffer, const std::vector* sparseIndices) { bool success = false; @@ -152,31 +152,63 @@ bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source return false; } + // If non-null sparse indices vector, prepare it for output + if(sparseIndices) + { + sparseIndices->resize(accessor.mSparse->mCount); + } + switch(indices.mElementSizeHint) { case 1u: { ReadValues(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint); + if(sparseIndices) + { + // convert 8-bit indices into 32-bit + std::transform(indicesBuffer.begin(), indicesBuffer.end(), sparseIndices->begin(), [](const uint8_t& value) { return uint32_t(value); }); + } break; } case 2u: { ReadValues(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint); + if(sparseIndices) + { + // convert 16-bit indices into 32-bit + std::transform(reinterpret_cast(indicesBuffer.data()), + reinterpret_cast(indicesBuffer.data()) + accessor.mSparse->mCount, + sparseIndices->begin(), + [](const uint16_t& value) { + return uint32_t(value); + }); + } break; } case 4u: { ReadValues(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint); + if(sparseIndices) + { + std::copy(indicesBuffer.begin(), indicesBuffer.end(), reinterpret_cast(sparseIndices->data())); + } break; } default: + { DALI_ASSERT_DEBUG(!"Unsupported type for an index"); + } } } return success; } +bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target) +{ + return ReadAccessor(accessor, source, target, nullptr); +} + template void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath) { @@ -427,21 +459,41 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector= sizeof(Vector3)) && "Blend Shape position buffer length not a multiple of element size"); - const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize(); - std::vector buffer(bufferSize); - if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data())) + const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize(); + std::vector buffer(bufferSize); + std::vector sparseIndices{}; + + if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices)) { - blendShape.deltas.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data())); + blendShape.deltas.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data()), &sparseIndices); + // Calculate the difference with the original mesh. // Find the max distance to normalize the deltas. - const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); + const auto* const deltasBuffer = reinterpret_cast(buffer.data()); - for(uint32_t index = 0u; index < numberOfVertices; ++index) - { - Vector3& delta = geometryBufferV3[geometryBufferIndex++]; - delta = deltasBuffer[index]; + auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) { + Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex]; + delta = deltasBuffer[deltaIndex]; + return std::max(maxDistanceSquared, delta.LengthSquared()); + }; - maxDistanceSquared = std::max(maxDistanceSquared, delta.LengthSquared()); + if(sparseIndices.empty()) + { + for(uint32_t index = 0u; index < numberOfVertices; ++index) + { + maxDistanceSquared = ProcessVertex(geometryBufferIndex++, index); + } + } + else + { + // initialize blendshape texture + // TODO: there may be a case when sparse accessor uses a base buffer view for initial values. + std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3::ZERO); + for(auto index : sparseIndices) + { + maxDistanceSquared = ProcessVertex(geometryBufferIndex + index, index); + } + geometryBufferIndex += numberOfVertices; } } } @@ -452,20 +504,18 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector= sizeof(Vector3)) && "Blend Shape normals buffer length not a multiple of element size"); - const auto bufferSize = blendShape.normals.mBlob.GetBufferSize(); - std::vector buffer(bufferSize); - if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data())) + const auto bufferSize = blendShape.normals.mBlob.GetBufferSize(); + std::vector buffer(bufferSize); + std::vector sparseIndices; + + if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices)) { - blendShape.normals.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data())); + blendShape.normals.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data()), &sparseIndices); // Calculate the difference with the original mesh, and translate to make all values positive. - const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); - - for(uint32_t index = 0u; index < numberOfVertices; ++index) - { - Vector3& delta = geometryBufferV3[geometryBufferIndex++]; - delta = deltasBuffer[index]; - + const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); + auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) { + Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex]; delta.x *= 0.5f; delta.y *= 0.5f; delta.z *= 0.5f; @@ -473,6 +523,23 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector= sizeof(Vector3)) && "Blend Shape tangents buffer length not a multiple of element size"); - const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize(); - std::vector buffer(bufferSize); - if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data())) + const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize(); + std::vector buffer(bufferSize); + std::vector sparseIndices; + + if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data()), &sparseIndices) { - blendShape.tangents.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data())); + blendShape.tangents.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data()), &sparseIndices); // Calculate the difference with the original mesh, and translate to make all values positive. - const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); - - for(uint32_t index = 0u; index < numberOfVertices; ++index) - { - Vector3& delta = geometryBufferV3[geometryBufferIndex++]; - delta = deltasBuffer[index]; - + const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); + auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) { + Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex]; delta.x *= 0.5f; delta.y *= 0.5f; delta.z *= 0.5f; @@ -504,6 +569,23 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector& min, std::vector& min, const std::vector& max, uint32_t count, float* values) +void MeshDefinition::Blob::ApplyMinMax(const std::vector& min, const std::vector& max, uint32_t count, float* values, std::vector* sparseIndices) { DALI_ASSERT_DEBUG(max.size() == min.size() || max.size() * min.size() == 0); const auto numComponents = std::max(min.size(), max.size()); @@ -617,16 +699,31 @@ void MeshDefinition::Blob::ApplyMinMax(const std::vector& min, const std: return; } - auto end = values + count * numComponents; - while(values != end) + // If there are sparse indices then process only relevant data + if(sparseIndices && !sparseIndices->empty()) { - auto nextElement = values + numComponents; - uint32_t i = 0; - while(values != nextElement) + for(auto elementIndex : *sparseIndices) { - clampFn(min.data(), max.data(), i, *values); - ++values; - ++i; + auto value = values + (elementIndex * numComponents); + for(auto i = 0u; i < numComponents; ++i) + { + clampFn(min.data(), max.data(), i, *value); + } + } + } + else // if there's no sparse indices process all vertices + { + auto end = values + count * numComponents; + while(values != end) + { + auto nextElement = values + numComponents; + uint32_t i = 0; + while(values != nextElement) + { + clampFn(min.data(), max.data(), i, *values); + ++values; + ++i; + } } } } @@ -651,9 +748,9 @@ void MeshDefinition::Blob::ComputeMinMax(uint32_t numComponents, uint32_t count, ComputeMinMax(mMin, mMax, numComponents, count, values); } -void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values) const +void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values, std::vector* sparseIndices) const { - ApplyMinMax(mMin, mMax, count, values); + ApplyMinMax(mMin, mMax, count, values, sparseIndices); } void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const diff --git a/dali-scene3d/public-api/loader/mesh-definition.h b/dali-scene3d/public-api/loader/mesh-definition.h index 570537e..7bf1f7c 100644 --- a/dali-scene3d/public-api/loader/mesh-definition.h +++ b/dali-scene3d/public-api/loader/mesh-definition.h @@ -83,7 +83,7 @@ struct DALI_SCENE3D_API MeshDefinition static void ComputeMinMax(std::vector& min, std::vector& max, uint32_t numComponents, uint32_t count, const float* values); - static void ApplyMinMax(const std::vector& min, const std::vector& max, uint32_t count, float* values); + static void ApplyMinMax(const std::vector& min, const std::vector& max, uint32_t count, float* values, std::vector* sparseIndices = nullptr); Blob() = default; @@ -132,8 +132,10 @@ struct DALI_SCENE3D_API MeshDefinition * * @param[in] count The number of data. * @param[in] values Data for the mesh that min / max values will be applied. + * @param[in] sparseIndices Pointer to array of sparse indices (or nullptr if not provided) + * */ - void ApplyMinMax(uint32_t count, float* values) const; + void ApplyMinMax(uint32_t count, float* values, std::vector* sparseIndices = nullptr) const; }; /** -- 2.7.4