2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-scene3d/public-api/loader/mesh-definition.h>
22 #include <dali/devel-api/adaptor-framework/file-stream.h>
23 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/math/compile-time-math.h>
29 #include <type_traits>
31 namespace Dali::Scene3D::Loader
36 enum class LoadDataType
43 struct LoadAccessorInputs
45 MeshDefinition::RawData& rawData;
46 MeshDefinition::Accessor& accessor;
48 std::fstream& fileStream;
49 std::string& meshPath;
50 BufferDefinition::Vector& buffers;
53 struct LoadAccessorListInputs
55 MeshDefinition::RawData& rawData;
56 std::vector<MeshDefinition::Accessor>& accessors;
58 std::fstream& fileStream;
59 std::string& meshPath;
60 BufferDefinition::Vector& buffers;
63 template<bool use32BitIndices>
67 using IndexType = typename std::conditional_t<use32BitIndices, uint32_t, uint16_t>;
68 IndexProvider(const uint16_t* indices)
69 : mData(reinterpret_cast<uintptr_t>(indices)),
70 mFunc(indices ? IncrementPointer : Increment)
74 IndexType operator()()
80 static IndexType Increment(uintptr_t& data)
82 // mData was 'zero' at construct time. Just simply return counter start with 0.
83 return static_cast<IndexType>(data++);
86 static IndexType IncrementPointer(uintptr_t& data)
88 auto iPtr = reinterpret_cast<const IndexType*>(data);
90 data = reinterpret_cast<uintptr_t>(++iPtr);
95 IndexType (*mFunc)(uintptr_t&);
98 const char* QUAD("quad");
100 ///@brief Reads a blob from the given stream @a source into @a target, which must have
101 /// at least @a descriptor.length bytes.
102 bool ReadBlob(const MeshDefinition::Blob& descriptor, std::istream& source, uint8_t* target)
105 if(!source.seekg(descriptor.mOffset, std::istream::beg))
110 if(descriptor.IsConsecutive())
112 return !!source.read(reinterpret_cast<char*>(target), static_cast<std::streamsize>(static_cast<size_t>(descriptor.mLength)));
116 if(descriptor.mStride > descriptor.mElementSizeHint)
118 const uint32_t diff = descriptor.mStride - descriptor.mElementSizeHint;
119 uint32_t readSize = 0;
120 uint32_t totalSize = (descriptor.mLength / descriptor.mElementSizeHint) * descriptor.mStride;
121 while(readSize < totalSize &&
122 source.read(reinterpret_cast<char*>(target), descriptor.mElementSizeHint))
124 readSize += descriptor.mStride;
125 target += descriptor.mElementSizeHint;
126 source.seekg(diff, std::istream::cur);
128 return readSize == totalSize;
135 void ReadValues(const std::vector<uint8_t>& valuesBuffer, const std::vector<uint8_t>& indicesBuffer, uint8_t* target, uint32_t count, uint32_t elementSizeHint)
137 const T* const indicesPtr = reinterpret_cast<const T* const>(indicesBuffer.data());
138 for(uint32_t index = 0u; index < count; ++index)
140 uint32_t valuesIndex = indicesPtr[index] * elementSizeHint;
141 memcpy(target + valuesIndex, &valuesBuffer[index * elementSizeHint], elementSizeHint);
145 bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target, std::vector<uint32_t>* sparseIndices)
147 bool success = false;
149 if(accessor.mBlob.IsDefined())
151 success = ReadBlob(accessor.mBlob, source, target);
160 const MeshDefinition::Blob& indices = accessor.mSparse->mIndices;
161 const MeshDefinition::Blob& values = accessor.mSparse->mValues;
163 if(!indices.IsDefined() || !values.IsDefined())
168 const auto indicesBufferSize = indices.GetBufferSize();
169 std::vector<uint8_t> indicesBuffer(indicesBufferSize);
170 success = ReadBlob(indices, source, indicesBuffer.data());
176 const auto valuesBufferSize = values.GetBufferSize();
177 std::vector<uint8_t> valuesBuffer(valuesBufferSize);
178 success = ReadBlob(values, source, valuesBuffer.data());
184 // If non-null sparse indices vector, prepare it for output
187 sparseIndices->resize(accessor.mSparse->mCount);
190 switch(indices.mElementSizeHint)
194 ReadValues<uint8_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
197 // convert 8-bit indices into 32-bit
198 std::transform(indicesBuffer.begin(), indicesBuffer.end(), sparseIndices->begin(), [](const uint8_t& value) { return uint32_t(value); });
204 ReadValues<uint16_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
207 // convert 16-bit indices into 32-bit
208 std::transform(reinterpret_cast<uint16_t*>(indicesBuffer.data()),
209 reinterpret_cast<uint16_t*>(indicesBuffer.data()) + accessor.mSparse->mCount,
210 sparseIndices->begin(),
211 [](const uint16_t& value) {
212 return uint32_t(value);
219 ReadValues<uint32_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
222 std::copy(indicesBuffer.begin(), indicesBuffer.end(), reinterpret_cast<uint8_t*>(sparseIndices->data()));
228 DALI_ASSERT_DEBUG(!"Unsupported type for an index");
236 bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target)
238 return ReadAccessor(accessor, source, target, nullptr);
241 template<typename T, bool needsNormalize>
242 void ReadVectorAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, std::vector<uint8_t>& buffer)
244 constexpr auto sizeofBlobUnit = sizeof(T) * 4;
246 DALI_ASSERT_ALWAYS(((accessor.mBlob.mLength % sizeofBlobUnit == 0) ||
247 accessor.mBlob.mStride >= sizeofBlobUnit) &&
248 "Buffer length not a multiple of element size");
249 const auto inBufferSize = accessor.mBlob.GetBufferSize();
250 const auto outBufferSize = (sizeof(Vector4) / sizeofBlobUnit) * inBufferSize;
252 buffer.resize(outBufferSize);
253 auto inBuffer = buffer.data() + outBufferSize - inBufferSize;
254 if(!ReadAccessor(accessor, source, inBuffer))
256 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read vector data from Accessor.";
259 if(sizeofBlobUnit != sizeof(Vector4))
261 auto floats = reinterpret_cast<float*>(buffer.data());
262 const auto end = inBuffer + inBufferSize;
263 while(inBuffer != end)
265 const auto value = *reinterpret_cast<T*>(inBuffer);
266 *floats = (needsNormalize) ? static_cast<float>(value) / static_cast<float>((1 << (sizeof(T) * 8)) - 1) : static_cast<float>(value);
268 inBuffer += sizeof(T);
274 template<bool needsNormalize>
275 void ReadTypedVectorAccessor(LoadDataType loadDataType, MeshDefinition::Accessor& accessor, std::iostream& stream, std::vector<uint8_t>& buffer)
279 case LoadDataType::UNSIGNED_SHORT:
281 ReadVectorAccessor<uint16_t, needsNormalize>(accessor, stream, buffer);
284 case LoadDataType::UNSIGNED_BYTE:
286 ReadVectorAccessor<uint8_t, needsNormalize>(accessor, stream, buffer);
291 ReadVectorAccessor<float, needsNormalize>(accessor, stream, buffer);
297 template<bool use32BitsIndices, typename IndexProviderType = IndexProvider<use32BitsIndices>>
298 bool GenerateNormals(MeshDefinition::RawData& raw)
300 using IndexType = typename IndexProviderType::IndexType;
302 // mIndicies size must be even if we use 32bit indices.
303 if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
308 auto& attribs = raw.mAttribs;
309 DALI_ASSERT_DEBUG(attribs.size() > 0); // positions
311 IndexProviderType getIndex(raw.mIndices.data());
313 const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
315 auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
317 std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(Vector3));
318 auto normals = reinterpret_cast<Vector3*>(buffer.data());
320 for(uint32_t i = 0; i < numIndices; i += 3)
322 IndexType indices[]{getIndex(), getIndex(), getIndex()};
323 Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
325 Vector3 a = pos[1] - pos[0];
326 Vector3 b = pos[2] - pos[0];
328 Vector3 normal(a.Cross(b));
329 normals[indices[0]] += normal;
330 normals[indices[1]] += normal;
331 normals[indices[2]] += normal;
334 auto iEnd = normals + attribs[0].mNumElements;
335 while(normals != iEnd)
337 normals->Normalize();
341 attribs.push_back({"aNormal", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer)});
346 template<bool use32BitsIndices, bool useVec3, bool hasUvs, typename T = std::conditional_t<useVec3, Vector3, Vector4>, typename = std::enable_if_t<(std::is_same<T, Vector3>::value || std::is_same<T, Vector4>::value)>, typename IndexProviderType = IndexProvider<use32BitsIndices>>
347 bool GenerateTangents(MeshDefinition::RawData& raw)
349 using IndexType = typename IndexProviderType::IndexType;
351 // mIndicies size must be even if we use 32bit indices.
352 if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
357 auto& attribs = raw.mAttribs;
358 // Required positions, normals, uvs (if we have). If not, skip generation
359 if(DALI_UNLIKELY(attribs.size() < (2 + static_cast<size_t>(hasUvs))))
364 std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(T));
365 auto tangents = reinterpret_cast<T*>(buffer.data());
369 IndexProviderType getIndex(raw.mIndices.data());
371 const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
373 auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
374 auto* uvs = reinterpret_cast<const Vector2*>(attribs[2].mData.data());
376 for(uint32_t i = 0; i < numIndices; i += 3)
378 IndexType indices[]{getIndex(), getIndex(), getIndex()};
379 Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
380 Vector2 uv[]{uvs[indices[0]], uvs[indices[1]], uvs[indices[2]]};
382 float x0 = pos[1].x - pos[0].x;
383 float y0 = pos[1].y - pos[0].y;
384 float z0 = pos[1].z - pos[0].z;
386 float x1 = pos[2].x - pos[0].x;
387 float y1 = pos[2].y - pos[0].y;
388 float z1 = pos[2].z - pos[0].z;
390 float s0 = uv[1].x - uv[0].x;
391 float t0 = uv[1].y - uv[0].y;
393 float s1 = uv[2].x - uv[0].x;
394 float t1 = uv[2].y - uv[0].y;
396 float det = (s0 * t1 - t0 * s1);
397 float r = 1.f / ((std::abs(det) < Dali::Epsilon<1000>::value) ? (Dali::Epsilon<1000>::value * (det > 0.0f ? 1.f : -1.f)) : det);
398 Vector3 tangent((x0 * t1 - t0 * x1) * r, (y0 * t1 - t0 * y1) * r, (z0 * t1 - t0 * z1) * r);
399 tangents[indices[0]] += T(tangent);
400 tangents[indices[1]] += T(tangent);
401 tangents[indices[2]] += T(tangent);
405 auto* normals = reinterpret_cast<const Vector3*>(attribs[1].mData.data());
406 auto iEnd = normals + attribs[1].mNumElements;
407 while(normals != iEnd)
412 // Calculated by indexs
413 tangentVec3 = Vector3((*tangents).x, (*tangents).y, (*tangents).z);
417 // Only choiced by normal vector. by indexs
418 Vector3 t[]{normals->Cross(Vector3::XAXIS), normals->Cross(Vector3::YAXIS)};
419 tangentVec3 = t[t[1].LengthSquared() > t[0].LengthSquared()];
422 tangentVec3 -= *normals * normals->Dot(tangentVec3);
423 tangentVec3.Normalize();
424 if constexpr(useVec3)
426 *tangents = tangentVec3;
430 *tangents = Vector4(tangentVec3.x, tangentVec3.y, tangentVec3.z, 1.0f);
436 attribs.push_back({"aTangent", useVec3 ? Property::VECTOR3 : Property::VECTOR4, attribs[0].mNumElements, std::move(buffer)});
441 void CalculateTextureSize(uint32_t totalTextureSize, uint32_t& textureWidth, uint32_t& textureHeight)
443 DALI_ASSERT_DEBUG(0u != totalTextureSize && "totalTextureSize is zero.")
445 // Calculate the dimensions of the texture.
446 // The total size of the texture is the length of the blend shapes blob.
451 if(0u == totalTextureSize)
457 const uint32_t pow2 = static_cast<uint32_t>(ceil(log2(totalTextureSize)));
458 const uint32_t powWidth = pow2 >> 1u;
459 const uint32_t powHeight = pow2 - powWidth;
461 textureWidth = 1u << powWidth;
462 textureHeight = 1u << powHeight;
466 float GetNormalizedScale()
468 return 1.0f / (std::numeric_limits<T>::max());
472 void DequantizeData(std::vector<uint8_t>& buffer, float* dequantizedValues, uint32_t numValues, bool normalized)
474 // see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data
476 T* values = reinterpret_cast<T*>(buffer.data());
478 for(uint32_t i = 0; i < numValues; ++i)
480 *dequantizedValues = normalized ? std::max((*values) * GetNormalizedScale<T>(), -1.0f) : *values;
487 void GetDequantizedData(std::vector<uint8_t>& buffer, uint32_t numComponents, uint32_t count, uint32_t flags, bool normalized)
489 bool dequantized = false;
491 std::vector<uint8_t> dequantizedBuffer(count * numComponents * sizeof(float));
492 float* dequantizedValues = reinterpret_cast<float*>(dequantizedBuffer.data());
494 if(MaskMatch(flags, MeshDefinition::Flags::S8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S8_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S8_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S8_TEXCOORD))
496 DequantizeData<int8_t>(buffer, dequantizedValues, numComponents * count, normalized);
499 else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD))
501 DequantizeData<uint8_t>(buffer, dequantizedValues, numComponents * count, normalized);
504 else if(MaskMatch(flags, MeshDefinition::Flags::S16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S16_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S16_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S16_TEXCOORD))
506 DequantizeData<int16_t>(buffer, dequantizedValues, numComponents * count, normalized);
509 else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD))
511 DequantizeData<uint16_t>(buffer, dequantizedValues, numComponents * count, normalized);
517 buffer = std::move(dequantizedBuffer);
521 void GetDequantizedMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t flags)
525 if(MaskMatch(flags, MeshDefinition::Flags::S8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S8_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S8_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S8_TEXCOORD))
527 scale = GetNormalizedScale<int8_t>();
529 else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD))
531 scale = GetNormalizedScale<uint8_t>();
533 else if(MaskMatch(flags, MeshDefinition::Flags::S16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S16_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S16_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S16_TEXCOORD))
535 scale = GetNormalizedScale<int16_t>();
537 else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD))
539 scale = GetNormalizedScale<uint16_t>();
544 for(float& value : min)
546 value = std::max(value * scale, -1.0f);
549 for(float& value : max)
551 value = std::min(value * scale, 1.0f);
556 void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::vector<MeshDefinition::BlendShape>& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor, BufferDefinition::Vector& buffers)
558 uint32_t geometryBufferIndex = 0u;
559 float maxDistanceSquared = 0.f;
560 Vector3* geometryBufferV3 = reinterpret_cast<Vector3*>(geometryBuffer);
561 for(auto& blendShape : blendShapes)
563 if(blendShape.deltas.IsDefined())
565 const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize();
568 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_POSITION))
570 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % (sizeof(uint8_t) * 3) == 0) ||
571 blendShape.deltas.mBlob.mStride >= (sizeof(uint8_t) * 3)) &&
572 "Blend Shape position buffer length not a multiple of element size");
573 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 3));
575 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_POSITION))
577 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % (sizeof(uint16_t) * 3) == 0) ||
578 blendShape.deltas.mBlob.mStride >= (sizeof(uint16_t) * 3)) &&
579 "Blend Shape position buffer length not a multiple of element size");
580 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 3));
584 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % sizeof(Vector3) == 0) ||
585 blendShape.deltas.mBlob.mStride >= sizeof(Vector3)) &&
586 "Blend Shape position buffer length not a multiple of element size");
587 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
590 std::vector<uint8_t> buffer(bufferSize);
591 std::vector<uint32_t> sparseIndices{};
593 if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
595 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::POSITIONS_MASK, blendShape.deltas.mNormalized);
597 if(blendShape.deltas.mNormalized)
599 GetDequantizedMinMax(blendShape.deltas.mBlob.mMin, blendShape.deltas.mBlob.mMax, blendShape.mFlags & MeshDefinition::POSITIONS_MASK);
602 blendShape.deltas.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
604 // Calculate the difference with the original mesh.
605 // Find the max distance to normalize the deltas.
606 const auto* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
608 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
609 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
610 delta = deltasBuffer[deltaIndex];
611 return std::max(maxDistanceSquared, delta.LengthSquared());
614 if(sparseIndices.empty())
616 for(uint32_t index = 0u; index < numberOfVertices; ++index)
618 maxDistanceSquared = ProcessVertex(geometryBufferIndex++, index);
623 // initialize blendshape texture
624 // TODO: there may be a case when sparse accessor uses a base buffer view for initial values.
625 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3::ZERO);
626 for(auto index : sparseIndices)
628 maxDistanceSquared = ProcessVertex(geometryBufferIndex + index, index);
630 geometryBufferIndex += numberOfVertices;
635 if(blendShape.normals.IsDefined())
637 const auto bufferSize = blendShape.normals.mBlob.GetBufferSize();
640 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_NORMAL))
642 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
643 blendShape.normals.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
644 "Blend Shape normals buffer length not a multiple of element size");
645 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
647 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_NORMAL))
649 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
650 blendShape.normals.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
651 "Blend Shape normals buffer length not a multiple of element size");
652 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
656 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % sizeof(Vector3) == 0) ||
657 blendShape.normals.mBlob.mStride >= sizeof(Vector3)) &&
658 "Blend Shape normals buffer length not a multiple of element size");
659 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
662 std::vector<uint8_t> buffer(bufferSize);
663 std::vector<uint32_t> sparseIndices;
665 if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
667 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::NORMALS_MASK, blendShape.normals.mNormalized);
669 if(blendShape.normals.mNormalized)
671 GetDequantizedMinMax(blendShape.normals.mBlob.mMin, blendShape.normals.mBlob.mMax, blendShape.mFlags & MeshDefinition::NORMALS_MASK);
674 blendShape.normals.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
676 // Calculate the difference with the original mesh, and translate to make all values positive.
677 const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
678 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
679 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
689 if(sparseIndices.empty())
691 for(uint32_t index = 0u; index < numberOfVertices; ++index)
693 ProcessVertex(geometryBufferIndex++, index);
698 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5));
699 for(auto index : sparseIndices)
701 ProcessVertex(geometryBufferIndex + index, index);
703 geometryBufferIndex += numberOfVertices;
708 if(blendShape.tangents.IsDefined())
710 const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize();
714 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_TANGENT))
716 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
717 blendShape.tangents.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
718 "Blend Shape tangents buffer length not a multiple of element size");
719 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
721 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_TANGENT))
723 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
724 blendShape.tangents.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
725 "Blend Shape tangents buffer length not a multiple of element size");
726 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
730 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % sizeof(Vector3) == 0) ||
731 blendShape.tangents.mBlob.mStride >= sizeof(Vector3)) &&
732 "Blend Shape tangents buffer length not a multiple of element size");
733 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
736 std::vector<uint8_t> buffer(bufferSize);
737 std::vector<uint32_t> sparseIndices;
739 if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
741 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::TANGENTS_MASK, blendShape.tangents.mNormalized);
743 if(blendShape.tangents.mNormalized)
745 GetDequantizedMinMax(blendShape.tangents.mBlob.mMin, blendShape.tangents.mBlob.mMax, blendShape.mFlags & MeshDefinition::TANGENTS_MASK);
748 blendShape.tangents.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
750 // Calculate the difference with the original mesh, and translate to make all values positive.
751 const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
752 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
753 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
763 if(sparseIndices.empty())
765 for(uint32_t index = 0u; index < numberOfVertices; ++index)
767 ProcessVertex(geometryBufferIndex++, index);
772 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5));
773 for(auto index : sparseIndices)
775 ProcessVertex(geometryBufferIndex + index, index);
777 geometryBufferIndex += numberOfVertices;
783 geometryBufferIndex = 0u;
785 const float maxDistance = sqrtf(maxDistanceSquared);
787 const float normalizeFactor = (maxDistanceSquared < Math::MACHINE_EPSILON_100) ? 1.f : (0.5f / maxDistance);
789 // Calculate and store the unnormalize factor.
790 blendShapeUnnormalizeFactor = maxDistance * 2.0f;
792 for(const auto& blendShape : blendShapes)
794 // Normalize all the deltas and translate to a possitive value.
795 // Deltas are going to be passed to the shader in a color texture
796 // whose values that are less than zero are clamped.
797 if(blendShape.deltas.IsDefined())
799 for(uint32_t index = 0u; index < numberOfVertices; ++index)
801 Vector3& delta = geometryBufferV3[geometryBufferIndex++];
802 delta.x = Clamp(((delta.x * normalizeFactor) + 0.5f), 0.f, 1.f);
803 delta.y = Clamp(((delta.y * normalizeFactor) + 0.5f), 0.f, 1.f);
804 delta.z = Clamp(((delta.z * normalizeFactor) + 0.5f), 0.f, 1.f);
808 if(blendShape.normals.IsDefined())
810 geometryBufferIndex += numberOfVertices;
813 if(blendShape.tangents.IsDefined())
815 geometryBufferIndex += numberOfVertices;
820 std::iostream& GetAvailableData(std::fstream& meshStream, const std::string& meshPath, BufferDefinition& buffer, std::string& availablePath)
822 auto& stream = (meshStream.is_open()) ? meshStream : buffer.GetBufferStream();
823 availablePath = (meshStream.is_open()) ? meshPath : buffer.GetUri();
827 template<bool needsNormalize>
828 void ReadTypedVectorAccessors(LoadAccessorListInputs loadAccessorListInputs, LoadDataType loadDataType, std::string attributeName)
831 for(auto& accessor : loadAccessorListInputs.accessors)
833 std::string pathJoint;
834 auto& dataStream = GetAvailableData(loadAccessorListInputs.fileStream, loadAccessorListInputs.meshPath, loadAccessorListInputs.buffers[accessor.mBufferIdx], pathJoint);
835 std::ostringstream name;
836 name << attributeName << setIndex++;
837 std::vector<uint8_t> buffer;
838 ReadTypedVectorAccessor<needsNormalize>(loadDataType, accessor, dataStream, buffer);
839 loadAccessorListInputs.rawData.mAttribs.push_back({name.str(), Property::VECTOR4, static_cast<uint32_t>(buffer.size() / sizeof(Vector4)), std::move(buffer)});
843 void LoadIndices(LoadAccessorInputs indicesInput)
845 if(indicesInput.accessor.IsDefined())
847 if(MaskMatch(indicesInput.flags, MeshDefinition::Flags::U32_INDICES))
849 DALI_ASSERT_ALWAYS(((indicesInput.accessor.mBlob.mLength % sizeof(uint32_t) == 0) ||
850 indicesInput.accessor.mBlob.mStride >= sizeof(uint32_t)) &&
851 "Index buffer length not a multiple of element size");
852 const auto indexCount = indicesInput.accessor.mBlob.GetBufferSize() / sizeof(uint32_t);
853 indicesInput.rawData.mIndices.resize(indexCount * 2); // NOTE: we need space for uint32_ts initially.
856 auto& stream = GetAvailableData(indicesInput.fileStream, indicesInput.meshPath, indicesInput.buffers[indicesInput.accessor.mBufferIdx], path);
857 if(!ReadAccessor(indicesInput.accessor, stream, reinterpret_cast<uint8_t*>(indicesInput.rawData.mIndices.data())))
859 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
862 else if(MaskMatch(indicesInput.flags, MeshDefinition::Flags::U8_INDICES))
864 DALI_ASSERT_ALWAYS(((indicesInput.accessor.mBlob.mLength % sizeof(uint8_t) == 0) ||
865 indicesInput.accessor.mBlob.mStride >= sizeof(uint8_t)) &&
866 "Index buffer length not a multiple of element size");
867 const auto indexCount = indicesInput.accessor.mBlob.GetBufferSize() / sizeof(uint8_t);
868 indicesInput.rawData.mIndices.resize(indexCount); // NOTE: we need space for uint16_ts initially.
871 auto u8s = reinterpret_cast<uint8_t*>(indicesInput.rawData.mIndices.data()) + indexCount;
872 auto& stream = GetAvailableData(indicesInput.fileStream, indicesInput.meshPath, indicesInput.buffers[indicesInput.accessor.mBufferIdx], path);
873 if(!ReadAccessor(indicesInput.accessor, stream, u8s))
875 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
878 auto u16s = indicesInput.rawData.mIndices.data();
879 auto end = u8s + indexCount;
882 *u16s = static_cast<uint16_t>(*u8s);
889 DALI_ASSERT_ALWAYS(((indicesInput.accessor.mBlob.mLength % sizeof(unsigned short) == 0) ||
890 indicesInput.accessor.mBlob.mStride >= sizeof(unsigned short)) &&
891 "Index buffer length not a multiple of element size");
892 indicesInput.rawData.mIndices.resize(indicesInput.accessor.mBlob.mLength / sizeof(unsigned short));
895 auto& stream = GetAvailableData(indicesInput.fileStream, indicesInput.meshPath, indicesInput.buffers[indicesInput.accessor.mBufferIdx], path);
896 if(!ReadAccessor(indicesInput.accessor, stream, reinterpret_cast<uint8_t*>(indicesInput.rawData.mIndices.data())))
898 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indicesInput.accessor from '" << path << "'.";
904 uint32_t LoadPositions(LoadAccessorInputs positionsInput, bool hasBlendShape)
906 uint32_t numVector3 = 0u;
907 std::vector<Vector3> positions;
908 if(positionsInput.accessor.IsDefined())
910 const auto bufferSize = positionsInput.accessor.mBlob.GetBufferSize();
912 if(MaskMatch(positionsInput.flags, MeshDefinition::Flags::S8_POSITION) || MaskMatch(positionsInput.flags, MeshDefinition::Flags::U8_POSITION))
914 DALI_ASSERT_ALWAYS(((positionsInput.accessor.mBlob.mLength % (sizeof(uint8_t) * 3) == 0) ||
915 positionsInput.accessor.mBlob.mStride >= (sizeof(uint8_t) * 3)) &&
916 "Position buffer length not a multiple of element size");
917 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 3));
919 else if(MaskMatch(positionsInput.flags, MeshDefinition::Flags::S16_POSITION) || MaskMatch(positionsInput.flags, MeshDefinition::Flags::U16_POSITION))
921 DALI_ASSERT_ALWAYS(((positionsInput.accessor.mBlob.mLength % (sizeof(uint16_t) * 3) == 0) ||
922 positionsInput.accessor.mBlob.mStride >= (sizeof(uint16_t) * 3)) &&
923 "Position buffer length not a multiple of element size");
924 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 3));
928 DALI_ASSERT_ALWAYS(((positionsInput.accessor.mBlob.mLength % sizeof(Vector3) == 0) ||
929 positionsInput.accessor.mBlob.mStride >= sizeof(Vector3)) &&
930 "Position buffer length not a multiple of element size");
931 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
934 std::vector<uint8_t> buffer(bufferSize);
937 auto& stream = GetAvailableData(positionsInput.fileStream, positionsInput.meshPath, positionsInput.buffers[positionsInput.accessor.mBufferIdx], path);
938 if(!ReadAccessor(positionsInput.accessor, stream, buffer.data()))
940 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << path << "'.";
943 GetDequantizedData(buffer, 3u, numVector3, positionsInput.flags & MeshDefinition::FlagMasks::POSITIONS_MASK, positionsInput.accessor.mNormalized);
945 if(positionsInput.accessor.mNormalized)
947 GetDequantizedMinMax(positionsInput.accessor.mBlob.mMin, positionsInput.accessor.mBlob.mMax, positionsInput.flags & MeshDefinition::FlagMasks::POSITIONS_MASK);
950 if(positionsInput.accessor.mBlob.mMin.size() != 3u || positionsInput.accessor.mBlob.mMax.size() != 3u)
952 positionsInput.accessor.mBlob.ComputeMinMax(3u, numVector3, reinterpret_cast<float*>(buffer.data()));
956 positionsInput.accessor.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
961 positions.resize(numVector3);
962 std::copy(buffer.data(), buffer.data() + buffer.size(), reinterpret_cast<uint8_t*>(positions.data()));
965 positionsInput.rawData.mAttribs.push_back({"aPosition", Property::VECTOR3, numVector3, std::move(buffer)});
970 bool LoadNormals(LoadAccessorInputs normalsInput, bool isTriangles, uint32_t positionBufferSize)
972 auto hasNormals = normalsInput.accessor.IsDefined();
975 const auto bufferSize = normalsInput.accessor.mBlob.GetBufferSize();
978 if(MaskMatch(normalsInput.flags, MeshDefinition::Flags::S8_NORMAL))
980 DALI_ASSERT_ALWAYS(((normalsInput.accessor.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
981 normalsInput.accessor.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
982 "Normal buffer length not a multiple of element size");
983 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
985 else if(MaskMatch(normalsInput.flags, MeshDefinition::Flags::S16_NORMAL))
987 DALI_ASSERT_ALWAYS(((normalsInput.accessor.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
988 normalsInput.accessor.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
989 "Normal buffer length not a multiple of element size");
990 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
994 DALI_ASSERT_ALWAYS(((normalsInput.accessor.mBlob.mLength % sizeof(Vector3) == 0) ||
995 normalsInput.accessor.mBlob.mStride >= sizeof(Vector3)) &&
996 "Normal buffer length not a multiple of element size");
997 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
1000 std::vector<uint8_t> buffer(bufferSize);
1003 auto& stream = GetAvailableData(normalsInput.fileStream, normalsInput.meshPath, normalsInput.buffers[normalsInput.accessor.mBufferIdx], path);
1004 if(!ReadAccessor(normalsInput.accessor, stream, buffer.data()))
1006 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << path << "'.";
1009 GetDequantizedData(buffer, 3u, numVector3, normalsInput.flags & MeshDefinition::FlagMasks::NORMALS_MASK, normalsInput.accessor.mNormalized);
1011 if(normalsInput.accessor.mNormalized)
1013 GetDequantizedMinMax(normalsInput.accessor.mBlob.mMin, normalsInput.accessor.mBlob.mMax, normalsInput.flags & MeshDefinition::FlagMasks::NORMALS_MASK);
1016 normalsInput.accessor.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
1018 normalsInput.rawData.mAttribs.push_back({"aNormal", Property::VECTOR3, numVector3, std::move(buffer)});
1020 else if(normalsInput.accessor.mBlob.mLength != 0 && isTriangles)
1022 DALI_ASSERT_DEBUG(normalsInput.accessor.mBlob.mLength == positionBufferSize);
1023 static const std::function<bool(MeshDefinition::RawData&)> GenerateNormalsFunction[2] =
1025 GenerateNormals<false>,
1026 GenerateNormals<true>,
1028 const bool generateSuccessed = GenerateNormalsFunction[MaskMatch(normalsInput.flags, MeshDefinition::Flags::U32_INDICES)](normalsInput.rawData);
1029 if(!generateSuccessed)
1031 DALI_LOG_ERROR("Failed to generate normal\n");
1041 void LoadTextureCoordinates(LoadAccessorListInputs textureCoordinatesInput)
1043 if(!textureCoordinatesInput.accessors.empty() && textureCoordinatesInput.accessors[0].IsDefined())
1045 auto& texCoords = textureCoordinatesInput.accessors[0];
1046 const auto bufferSize = texCoords.mBlob.GetBufferSize();
1049 if(MaskMatch(textureCoordinatesInput.flags, MeshDefinition::Flags::S8_TEXCOORD) || MaskMatch(textureCoordinatesInput.flags, MeshDefinition::Flags::U8_TEXCOORD))
1051 DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % (sizeof(uint8_t) * 2) == 0) ||
1052 texCoords.mBlob.mStride >= (sizeof(uint8_t) * 2)) &&
1053 "TexCoords buffer length not a multiple of element size");
1054 uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 2));
1056 else if(MaskMatch(textureCoordinatesInput.flags, MeshDefinition::Flags::S16_TEXCOORD) || MaskMatch(textureCoordinatesInput.flags, MeshDefinition::Flags::U16_TEXCOORD))
1058 DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % (sizeof(uint16_t) * 2) == 0) ||
1059 texCoords.mBlob.mStride >= (sizeof(uint16_t) * 2)) &&
1060 "TexCoords buffer length not a multiple of element size");
1061 uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 2));
1065 DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
1066 texCoords.mBlob.mStride >= sizeof(Vector2)) &&
1067 "TexCoords buffer length not a multiple of element size");
1068 uvCount = static_cast<uint32_t>(bufferSize / sizeof(Vector2));
1071 std::vector<uint8_t> buffer(bufferSize);
1074 auto& stream = GetAvailableData(textureCoordinatesInput.fileStream, textureCoordinatesInput.meshPath, textureCoordinatesInput.buffers[texCoords.mBufferIdx], path);
1075 if(!ReadAccessor(texCoords, stream, buffer.data()))
1077 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << path << "'.";
1080 GetDequantizedData(buffer, 2u, uvCount, textureCoordinatesInput.flags & MeshDefinition::FlagMasks::TEXCOORDS_MASK, texCoords.mNormalized);
1082 if(MaskMatch(textureCoordinatesInput.flags, MeshDefinition::Flags::FLIP_UVS_VERTICAL))
1084 auto uv = reinterpret_cast<Vector2*>(buffer.data());
1085 auto uvEnd = uv + uvCount;
1088 uv->y = 1.0f - uv->y;
1093 if(texCoords.mNormalized)
1095 GetDequantizedMinMax(texCoords.mBlob.mMin, texCoords.mBlob.mMax, textureCoordinatesInput.flags & MeshDefinition::FlagMasks::TEXCOORDS_MASK);
1098 texCoords.mBlob.ApplyMinMax(static_cast<uint32_t>(uvCount), reinterpret_cast<float*>(buffer.data()));
1099 textureCoordinatesInput.rawData.mAttribs.push_back({"aTexCoord", Property::VECTOR2, static_cast<uint32_t>(uvCount), std::move(buffer)});
1103 void LoadTangents(LoadAccessorInputs tangentsInput, bool hasNormals, bool hasUvs, bool isTriangles, Property::Type tangentType, uint32_t normalBufferSize)
1105 if(tangentsInput.accessor.IsDefined())
1107 const auto bufferSize = tangentsInput.accessor.mBlob.GetBufferSize();
1109 uint32_t propertySize = static_cast<uint32_t>((tangentType == Property::VECTOR4) ? sizeof(Vector4) : sizeof(Vector3));
1110 uint32_t componentCount = static_cast<uint32_t>(propertySize / sizeof(float));
1112 uint32_t numTangents;
1114 if(MaskMatch(tangentsInput.flags, MeshDefinition::Flags::S8_TANGENT))
1116 DALI_ASSERT_ALWAYS(((tangentsInput.accessor.mBlob.mLength % (sizeof(int8_t) * componentCount) == 0) ||
1117 tangentsInput.accessor.mBlob.mStride >= (sizeof(int8_t) * componentCount)) &&
1118 "Tangents buffer length not a multiple of element size");
1119 numTangents = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * componentCount));
1121 else if(MaskMatch(tangentsInput.flags, MeshDefinition::Flags::S16_TANGENT))
1123 DALI_ASSERT_ALWAYS(((tangentsInput.accessor.mBlob.mLength % (sizeof(int16_t) * componentCount) == 0) ||
1124 tangentsInput.accessor.mBlob.mStride >= (sizeof(int16_t) * componentCount)) &&
1125 "Tangents buffer length not a multiple of element size");
1126 numTangents = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * componentCount));
1130 DALI_ASSERT_ALWAYS(((tangentsInput.accessor.mBlob.mLength % propertySize == 0) ||
1131 tangentsInput.accessor.mBlob.mStride >= propertySize) &&
1132 "Tangents buffer length not a multiple of element size");
1133 numTangents = static_cast<uint32_t>(bufferSize / propertySize);
1136 std::vector<uint8_t> buffer(bufferSize);
1139 auto& stream = GetAvailableData(tangentsInput.fileStream, tangentsInput.meshPath, tangentsInput.buffers[tangentsInput.accessor.mBufferIdx], path);
1140 if(!ReadAccessor(tangentsInput.accessor, stream, buffer.data()))
1142 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << path << "'.";
1145 GetDequantizedData(buffer, componentCount, numTangents, tangentsInput.flags & MeshDefinition::FlagMasks::TANGENTS_MASK, tangentsInput.accessor.mNormalized);
1147 if(tangentsInput.accessor.mNormalized)
1149 GetDequantizedMinMax(tangentsInput.accessor.mBlob.mMin, tangentsInput.accessor.mBlob.mMax, tangentsInput.flags & MeshDefinition::FlagMasks::TANGENTS_MASK);
1152 tangentsInput.accessor.mBlob.ApplyMinMax(numTangents, reinterpret_cast<float*>(buffer.data()));
1154 tangentsInput.rawData.mAttribs.push_back({"aTangent", tangentType, static_cast<uint32_t>(numTangents), std::move(buffer)});
1156 else if(tangentsInput.accessor.mBlob.mLength != 0 && hasNormals && isTriangles)
1158 DALI_ASSERT_DEBUG(tangentsInput.accessor.mBlob.mLength == normalBufferSize);
1159 static const std::function<bool(MeshDefinition::RawData&)> GenerateTangentsFunction[2][2][2] =
1163 GenerateTangents<false, false, false>,
1164 GenerateTangents<false, false, true>,
1167 GenerateTangents<false, true, false>,
1168 GenerateTangents<false, true, true>,
1173 GenerateTangents<true, false, false>,
1174 GenerateTangents<true, false, true>,
1177 GenerateTangents<true, true, false>,
1178 GenerateTangents<true, true, true>,
1181 const bool generateSuccessed = GenerateTangentsFunction[MaskMatch(tangentsInput.flags, MeshDefinition::Flags::U32_INDICES)][tangentType == Property::VECTOR3][hasUvs](tangentsInput.rawData);
1182 if(!generateSuccessed)
1184 DALI_LOG_ERROR("Failed to generate tangents\n");
1189 void LoadColors(LoadAccessorListInputs colorsInput)
1191 // Only support 1 vertex color
1192 if(!colorsInput.accessors.empty() && colorsInput.accessors[0].IsDefined())
1194 uint32_t propertySize = colorsInput.accessors[0].mBlob.mElementSizeHint;
1195 Property::Type propertyType = (propertySize == sizeof(Vector4)) ? Property::VECTOR4 : ((propertySize == sizeof(Vector3)) ? Property::VECTOR3 : Property::NONE);
1196 if(propertyType != Property::NONE)
1198 DALI_ASSERT_ALWAYS(((colorsInput.accessors[0].mBlob.mLength % propertySize == 0) ||
1199 colorsInput.accessors[0].mBlob.mStride >= propertySize) &&
1200 "Colors buffer length not a multiple of element size");
1201 const auto bufferSize = colorsInput.accessors[0].mBlob.GetBufferSize();
1202 std::vector<uint8_t> buffer(bufferSize);
1205 auto& stream = GetAvailableData(colorsInput.fileStream, colorsInput.meshPath, colorsInput.buffers[colorsInput.accessors[0].mBufferIdx], path);
1206 if(!ReadAccessor(colorsInput.accessors[0], stream, buffer.data()))
1208 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read colors from '" << path << "'.";
1210 colorsInput.accessors[0].mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
1212 colorsInput.rawData.mAttribs.push_back({"aVertexColor", propertyType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
1217 std::vector<uint8_t> buffer(colorsInput.rawData.mAttribs[0].mNumElements * sizeof(Vector4));
1218 auto colors = reinterpret_cast<Vector4*>(buffer.data());
1220 for(uint32_t i = 0; i < colorsInput.rawData.mAttribs[0].mNumElements; i++)
1222 colors[i] = Vector4::ONE;
1225 colorsInput.rawData.mAttribs.push_back({"aVertexColor", Property::VECTOR4, colorsInput.rawData.mAttribs[0].mNumElements, std::move(buffer)});
1229 void LoadBlendShapes(MeshDefinition::RawData& rawData, std::vector<MeshDefinition::BlendShape>& blendShapes, MeshDefinition::Blob& blendShapeHeader, BlendShapes::Version blendShapeVersion, uint32_t numberOfVertices, std::fstream& fileStream, BufferDefinition::Vector& buffers)
1231 // Calculate the Blob for the blend shapes.
1232 MeshDefinition::Blob blendShapesBlob;
1233 blendShapesBlob.mOffset = std::numeric_limits<unsigned int>::max();
1234 blendShapesBlob.mLength = 0u;
1236 uint32_t totalTextureSize(0u);
1238 auto processAccessor = [&](const MeshDefinition::Accessor& accessor, uint32_t vector3Size) {
1239 if(accessor.IsDefined())
1241 blendShapesBlob.mOffset = std::min(blendShapesBlob.mOffset, accessor.mBlob.mOffset);
1242 blendShapesBlob.mLength += accessor.mBlob.mLength;
1244 totalTextureSize += accessor.mBlob.mLength / vector3Size;
1248 for(const auto& blendShape : blendShapes)
1250 const auto positionMask = blendShape.mFlags & MeshDefinition::FlagMasks::POSITIONS_MASK;
1251 const auto normalMask = blendShape.mFlags & MeshDefinition::FlagMasks::NORMALS_MASK;
1252 const auto tangentMask = blendShape.mFlags & MeshDefinition::FlagMasks::TANGENTS_MASK;
1254 processAccessor(blendShape.deltas, MaskMatch(positionMask, MeshDefinition::S8_POSITION) ? sizeof(uint8_t) * 3 : (MaskMatch(positionMask, MeshDefinition::S16_POSITION) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1255 processAccessor(blendShape.normals, MaskMatch(normalMask, MeshDefinition::S8_NORMAL) ? sizeof(uint8_t) * 3 : (MaskMatch(normalMask, MeshDefinition::S16_NORMAL) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1256 processAccessor(blendShape.tangents, MaskMatch(tangentMask, MeshDefinition::S8_TANGENT) ? sizeof(uint8_t) * 3 : (MaskMatch(tangentMask, MeshDefinition::S16_TANGENT) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1259 if(!blendShapes.empty())
1261 // Calculate the size of one buffer inside the texture.
1262 rawData.mBlendShapeBufferOffset = numberOfVertices;
1264 bool calculateGltf2BlendShapes = false;
1265 uint32_t textureWidth = 0u;
1266 uint32_t textureHeight = 0u;
1268 if(!blendShapeHeader.IsDefined())
1270 CalculateTextureSize(totalTextureSize, textureWidth, textureHeight);
1271 calculateGltf2BlendShapes = true;
1275 uint16_t header[2u];
1276 ReadBlob(blendShapeHeader, fileStream, reinterpret_cast<uint8_t*>(header));
1277 textureWidth = header[0u];
1278 textureHeight = header[1u];
1281 const uint32_t numberOfBlendShapes = blendShapes.size();
1282 rawData.mBlendShapeUnnormalizeFactor.Resize(numberOfBlendShapes);
1284 Devel::PixelBuffer geometryPixelBuffer = Devel::PixelBuffer::New(textureWidth, textureHeight, Pixel::RGB32F);
1285 uint8_t* geometryBuffer = geometryPixelBuffer.GetBuffer();
1287 if(calculateGltf2BlendShapes)
1289 CalculateGltf2BlendShapes(geometryBuffer, blendShapes, numberOfVertices, rawData.mBlendShapeUnnormalizeFactor[0u], buffers);
1293 MeshDefinition::Blob unnormalizeFactorBlob;
1294 unnormalizeFactorBlob.mLength = static_cast<uint32_t>(sizeof(float) * ((BlendShapes::Version::VERSION_2_0 == blendShapeVersion) ? 1u : numberOfBlendShapes));
1296 if(blendShapesBlob.IsDefined())
1298 if(ReadBlob(blendShapesBlob, fileStream, geometryBuffer))
1300 unnormalizeFactorBlob.mOffset = blendShapesBlob.mOffset + blendShapesBlob.mLength;
1304 // Read the unnormalize factors.
1305 if(unnormalizeFactorBlob.IsDefined())
1307 ReadBlob(unnormalizeFactorBlob, fileStream, reinterpret_cast<uint8_t*>(&rawData.mBlendShapeUnnormalizeFactor[0u]));
1310 rawData.mBlendShapeData = Devel::PixelBuffer::Convert(geometryPixelBuffer);
1316 MeshDefinition::SparseBlob::SparseBlob(const Blob& indices, const Blob& values, uint32_t count)
1317 : mIndices{indices},
1323 MeshDefinition::SparseBlob::SparseBlob(Blob&& indices, Blob&& values, uint32_t count)
1324 : mIndices(std::move(indices)),
1325 mValues(std::move(values)),
1330 MeshDefinition::Accessor::Accessor(const MeshDefinition::Blob& blob,
1331 const MeshDefinition::SparseBlob& sparse,
1335 mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{sparse} : nullptr},
1336 mBufferIdx(bufferIndex),
1337 mNormalized(normalized)
1341 MeshDefinition::Accessor::Accessor(MeshDefinition::Blob&& blob,
1342 MeshDefinition::SparseBlob&& sparse,
1345 : mBlob{std::move(blob)},
1346 mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{std::move(sparse)} : nullptr},
1347 mBufferIdx(bufferIndex),
1348 mNormalized(normalized)
1352 void MeshDefinition::Blob::ComputeMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t numComponents, uint32_t count, const float* values)
1354 min.assign(numComponents, MAXFLOAT);
1355 max.assign(numComponents, -MAXFLOAT);
1356 for(uint32_t i = 0; i < count; ++i)
1358 for(uint32_t j = 0; j < numComponents; ++j)
1360 min[j] = std::min(min[j], *values);
1361 max[j] = std::max(max[j], *values);
1367 void MeshDefinition::Blob::ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max, uint32_t count, float* values, std::vector<uint32_t>* sparseIndices)
1369 DALI_ASSERT_DEBUG(max.size() == min.size() || max.size() * min.size() == 0);
1370 const auto numComponents = std::max(min.size(), max.size());
1372 using ClampFn = void (*)(const float*, const float*, uint32_t, float&);
1373 ClampFn clampFn = min.empty() ? (max.empty() ? static_cast<ClampFn>(nullptr) : [](const float* min, const float* max, uint32_t i, float& value) { value = std::min(max[i], value); })
1374 : (max.empty() ? [](const float* min, const float* max, uint32_t i, float& value) { value = std::max(min[i], value); }
1375 : static_cast<ClampFn>([](const float* min, const float* max, uint32_t i, float& value) { value = std::min(std::max(min[i], value), max[i]); }));
1382 auto end = values + count * numComponents;
1383 while(values != end)
1385 auto nextElement = values + numComponents;
1387 while(values != nextElement)
1389 clampFn(min.data(), max.data(), i, *values);
1396 MeshDefinition::Blob::Blob(uint32_t offset, uint32_t length, uint16_t stride, uint16_t elementSizeHint, const std::vector<float>& min, const std::vector<float>& max)
1400 mElementSizeHint(elementSizeHint),
1406 uint32_t MeshDefinition::Blob::GetBufferSize() const
1411 void MeshDefinition::Blob::ComputeMinMax(uint32_t numComponents, uint32_t count, float* values)
1413 ComputeMinMax(mMin, mMax, numComponents, count, values);
1416 void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values, std::vector<uint32_t>* sparseIndices) const
1418 ApplyMinMax(mMin, mMax, count, values, sparseIndices);
1421 void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const
1423 Property::Map attribMap;
1424 attribMap[mName] = mType;
1425 VertexBuffer attribBuffer = VertexBuffer::New(attribMap);
1426 attribBuffer.SetData(mData.data(), mNumElements);
1428 g.AddVertexBuffer(attribBuffer);
1431 bool MeshDefinition::IsQuad() const
1433 return CaseInsensitiveStringCompare(QUAD, mUri);
1436 bool MeshDefinition::IsSkinned() const
1438 return !mJoints.empty() && !mWeights.empty();
1441 bool MeshDefinition::HasVertexColor() const
1443 return !mColors.empty();
1446 uint32_t MeshDefinition::GetNumberOfJointSets() const
1448 uint32_t number = static_cast<uint32_t>(mJoints.size());
1449 if(number > MeshDefinition::MAX_NUMBER_OF_JOINT_SETS)
1451 number = MeshDefinition::MAX_NUMBER_OF_JOINT_SETS;
1456 bool MeshDefinition::HasBlendShapes() const
1458 return !mBlendShapes.empty();
1461 void MeshDefinition::RequestNormals()
1463 mNormals.mBlob.mLength = mPositions.mBlob.GetBufferSize();
1466 void MeshDefinition::RequestTangents()
1468 mTangents.mBlob.mLength = mNormals.mBlob.GetBufferSize();
1471 MeshDefinition::RawData
1472 MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& buffers)
1480 std::string meshPath;
1481 meshPath = modelsPath + mUri;
1482 std::fstream fileStream;
1485 fileStream.open(meshPath, std::ios::in | std::ios::binary);
1486 if(!fileStream.is_open())
1488 DALI_LOG_ERROR("Fail to open buffer from %s.\n", meshPath.c_str());
1492 LoadAccessorInputs indicesInput = {raw, mIndices, mFlags, fileStream, meshPath, buffers};
1493 LoadIndices(indicesInput);
1495 LoadAccessorInputs positionsInput = {raw, mPositions, mFlags, fileStream, meshPath, buffers};
1496 uint32_t numberOfVertices = LoadPositions(positionsInput, HasBlendShapes());
1498 const auto isTriangles = mPrimitiveType == Geometry::TRIANGLES;
1499 LoadAccessorInputs normalsInput = {raw, mNormals, mFlags, fileStream, meshPath, buffers};
1500 auto hasNormals = LoadNormals(normalsInput, isTriangles, mPositions.mBlob.GetBufferSize());
1502 LoadAccessorListInputs textureCoordinatesInput = {raw, mTexCoords, mFlags, fileStream, meshPath, buffers};
1503 LoadTextureCoordinates(textureCoordinatesInput);
1505 const bool hasUvs = !mTexCoords.empty() && mTexCoords[0].IsDefined();
1506 LoadAccessorInputs tangentsInput = {raw, mTangents, mFlags, fileStream, meshPath, buffers};
1507 LoadTangents(tangentsInput, hasNormals, hasUvs, isTriangles, mTangentType, mNormals.mBlob.GetBufferSize());
1509 LoadAccessorListInputs colorsInput = {raw, mColors, mFlags, fileStream, meshPath, buffers};
1510 LoadColors(colorsInput);
1514 LoadDataType loadDataType = (MaskMatch(mFlags, MeshDefinition::U16_JOINT_IDS)) ? LoadDataType::UNSIGNED_SHORT : (MaskMatch(mFlags, MeshDefinition::U8_JOINT_IDS) ? LoadDataType::UNSIGNED_BYTE : LoadDataType::FLOAT);
1515 LoadAccessorListInputs jointsInput = {raw, mJoints, mFlags, fileStream, meshPath, buffers};
1516 ReadTypedVectorAccessors<false>(jointsInput, loadDataType, "aJoints");
1518 loadDataType = (MaskMatch(mFlags, MeshDefinition::U16_WEIGHT)) ? LoadDataType::UNSIGNED_SHORT : (MaskMatch(mFlags, MeshDefinition::U8_WEIGHT) ? LoadDataType::UNSIGNED_BYTE : LoadDataType::FLOAT);
1519 LoadAccessorListInputs weightsInput = {raw, mWeights, mFlags, fileStream, meshPath, buffers};
1520 ReadTypedVectorAccessors<true>(weightsInput, loadDataType, "aWeights");
1523 LoadBlendShapes(raw, mBlendShapes, mBlendShapeHeader, mBlendShapeVersion, numberOfVertices, fileStream, buffers);
1528 MeshGeometry MeshDefinition::Load(RawData&& raw) const
1530 MeshGeometry meshGeometry;
1531 meshGeometry.geometry = Geometry::New();
1532 meshGeometry.geometry.SetType(mPrimitiveType);
1534 if(IsQuad()) // TODO: do this in raw data; provide MakeTexturedQuadGeometry() that only creates buffers.
1536 auto options = MaskMatch(mFlags, FLIP_UVS_VERTICAL) ? TexturedQuadOptions::FLIP_VERTICAL : 0;
1537 meshGeometry.geometry = MakeTexturedQuadGeometry(options);
1541 if(!raw.mIndices.empty())
1543 if(MaskMatch(mFlags, U32_INDICES))
1545 // TODO : We can only store indeces as uint16_type. Send Dali::Geometry that we use it as uint32_t actual.
1546 meshGeometry.geometry.SetIndexBuffer(reinterpret_cast<const uint32_t*>(raw.mIndices.data()), raw.mIndices.size() / 2);
1550 meshGeometry.geometry.SetIndexBuffer(raw.mIndices.data(), raw.mIndices.size());
1554 for(auto& a : raw.mAttribs)
1556 a.AttachBuffer(meshGeometry.geometry);
1559 if(HasBlendShapes())
1561 meshGeometry.blendShapeBufferOffset = raw.mBlendShapeBufferOffset;
1562 meshGeometry.blendShapeUnnormalizeFactor = std::move(raw.mBlendShapeUnnormalizeFactor);
1564 meshGeometry.blendShapeGeometry = Texture::New(TextureType::TEXTURE_2D,
1565 raw.mBlendShapeData.GetPixelFormat(),
1566 raw.mBlendShapeData.GetWidth(),
1567 raw.mBlendShapeData.GetHeight());
1568 meshGeometry.blendShapeGeometry.Upload(raw.mBlendShapeData);
1572 return meshGeometry;
1575 void MeshDefinition::RetrieveBlendShapeComponents(bool& hasPositions, bool& hasNormals, bool& hasTangents) const
1577 for(const auto& blendShape : mBlendShapes)
1579 hasPositions = hasPositions || blendShape.deltas.IsDefined();
1580 hasNormals = hasNormals || blendShape.normals.IsDefined();
1581 hasTangents = hasTangents || blendShape.tangents.IsDefined();
1585 } // namespace Dali::Scene3D::Loader