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 <adam.b@samsung.com>
-bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target)
+bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target, std::vector<uint32_t>* sparseIndices)
+ // If non-null sparse indices vector, prepare it for output
+ if(sparseIndices)
+ {
+ sparseIndices->resize(accessor.mSparse->mCount);
+ }
+
switch(indices.mElementSizeHint)
{
case 1u:
{
ReadValues<uint8_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
switch(indices.mElementSizeHint)
{
case 1u:
{
ReadValues<uint8_t>(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<uint16_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
break;
}
case 2u:
{
ReadValues<uint16_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
+ if(sparseIndices)
+ {
+ // convert 16-bit indices into 32-bit
+ std::transform(reinterpret_cast<uint16_t*>(indicesBuffer.data()),
+ reinterpret_cast<uint16_t*>(indicesBuffer.data()) + accessor.mSparse->mCount,
+ sparseIndices->begin(),
+ [](const uint16_t& value) {
+ return uint32_t(value);
+ });
+ }
break;
}
case 4u:
{
ReadValues<uint32_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
break;
}
case 4u:
{
ReadValues<uint32_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
+ if(sparseIndices)
+ {
+ std::copy(indicesBuffer.begin(), indicesBuffer.end(), reinterpret_cast<uint8_t*>(sparseIndices->data()));
+ }
DALI_ASSERT_DEBUG(!"Unsupported type for an index");
DALI_ASSERT_DEBUG(!"Unsupported type for an index");
+bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target)
+{
+ return ReadAccessor(accessor, source, target, nullptr);
+}
+
template<typename T>
void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath)
{
template<typename T>
void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath)
{
blendShape.deltas.mBlob.mStride >= sizeof(Vector3)) &&
"Blend Shape position buffer length not a multiple of element size");
blendShape.deltas.mBlob.mStride >= sizeof(Vector3)) &&
"Blend Shape position buffer length not a multiple of element size");
- const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize();
- std::vector<uint8_t> buffer(bufferSize);
- if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data()))
+ const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize();
+ std::vector<uint8_t> buffer(bufferSize);
+ std::vector<uint32_t> sparseIndices{};
+
+ if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
- blendShape.deltas.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()));
+ blendShape.deltas.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()), &sparseIndices);
+
// Calculate the difference with the original mesh.
// Find the max distance to normalize the deltas.
// Calculate the difference with the original mesh.
// Find the max distance to normalize the deltas.
- const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
+ const auto* const deltasBuffer = reinterpret_cast<const Vector3* const>(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;
blendShape.normals.mBlob.mStride >= sizeof(Vector3)) &&
"Blend Shape normals buffer length not a multiple of element size");
blendShape.normals.mBlob.mStride >= sizeof(Vector3)) &&
"Blend Shape normals buffer length not a multiple of element size");
- const auto bufferSize = blendShape.normals.mBlob.GetBufferSize();
- std::vector<uint8_t> buffer(bufferSize);
- if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data()))
+ const auto bufferSize = blendShape.normals.mBlob.GetBufferSize();
+ std::vector<uint8_t> buffer(bufferSize);
+ std::vector<uint32_t> sparseIndices;
+
+ if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
- blendShape.normals.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()));
+ blendShape.normals.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()), &sparseIndices);
// Calculate the difference with the original mesh, and translate to make all values positive.
// Calculate the difference with the original mesh, and translate to make all values positive.
- const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
-
- for(uint32_t index = 0u; index < numberOfVertices; ++index)
- {
- Vector3& delta = geometryBufferV3[geometryBufferIndex++];
- delta = deltasBuffer[index];
-
+ const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(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;
delta.x *= 0.5f;
delta.y *= 0.5f;
delta.z *= 0.5f;
delta.x += 0.5f;
delta.y += 0.5f;
delta.z += 0.5f;
delta.x += 0.5f;
delta.y += 0.5f;
delta.z += 0.5f;
+ };
+
+ if(sparseIndices.empty())
+ {
+ for(uint32_t index = 0u; index < numberOfVertices; ++index)
+ {
+ ProcessVertex(geometryBufferIndex++, index);
+ }
+ }
+ else
+ {
+ std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5));
+ for(auto index : sparseIndices)
+ {
+ ProcessVertex(geometryBufferIndex + index, index);
+ }
+ geometryBufferIndex += numberOfVertices;
blendShape.tangents.mBlob.mStride >= sizeof(Vector3)) &&
"Blend Shape tangents buffer length not a multiple of element size");
blendShape.tangents.mBlob.mStride >= sizeof(Vector3)) &&
"Blend Shape tangents buffer length not a multiple of element size");
- const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize();
- std::vector<uint8_t> buffer(bufferSize);
- if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data()))
+ const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize();
+ std::vector<uint8_t> buffer(bufferSize);
+ std::vector<uint32_t> sparseIndices;
+
+ if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data()), &sparseIndices)
- blendShape.tangents.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()));
+ blendShape.tangents.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()), &sparseIndices);
// Calculate the difference with the original mesh, and translate to make all values positive.
// Calculate the difference with the original mesh, and translate to make all values positive.
- const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
-
- for(uint32_t index = 0u; index < numberOfVertices; ++index)
- {
- Vector3& delta = geometryBufferV3[geometryBufferIndex++];
- delta = deltasBuffer[index];
-
+ const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(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;
delta.x *= 0.5f;
delta.y *= 0.5f;
delta.z *= 0.5f;
delta.x += 0.5f;
delta.y += 0.5f;
delta.z += 0.5f;
delta.x += 0.5f;
delta.y += 0.5f;
delta.z += 0.5f;
+ };
+
+ if(sparseIndices.empty())
+ {
+ for(uint32_t index = 0u; index < numberOfVertices; ++index)
+ {
+ ProcessVertex(geometryBufferIndex++, index);
+ }
+ }
+ else
+ {
+ std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5));
+ for(auto index : sparseIndices)
+ {
+ ProcessVertex(geometryBufferIndex + index, index);
+ }
+ geometryBufferIndex += numberOfVertices;
-void MeshDefinition::Blob::ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max, uint32_t count, float* values)
+void MeshDefinition::Blob::ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max, uint32_t count, float* values, std::vector<uint32_t>* sparseIndices)
{
DALI_ASSERT_DEBUG(max.size() == min.size() || max.size() * min.size() == 0);
const auto numComponents = std::max(min.size(), max.size());
{
DALI_ASSERT_DEBUG(max.size() == min.size() || max.size() * min.size() == 0);
const auto numComponents = std::max(min.size(), max.size());
- 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;
+ }
ComputeMinMax(mMin, mMax, numComponents, count, values);
}
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<uint32_t>* sparseIndices) const
- ApplyMinMax(mMin, mMax, count, values);
+ ApplyMinMax(mMin, mMax, count, values, sparseIndices);
}
void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const
}
void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const
static void ComputeMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t numComponents, uint32_t count, const float* values);
static void ComputeMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t numComponents, uint32_t count, const float* values);
- static void ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max, uint32_t count, float* values);
+ static void ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max, uint32_t count, float* values, std::vector<uint32_t>* sparseIndices = nullptr);
*
* @param[in] count The number of data.
* @param[in] values Data for the mesh that min / max values will be applied.
*
* @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<uint32_t>* sparseIndices = nullptr) const;