2 * Copyright (c) 2024 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
35 enum class LoadDataType
42 struct LoadAccessorInputs
44 MeshDefinition::RawData& rawData;
45 MeshDefinition::Accessor& accessor;
47 std::fstream& fileStream;
48 std::string& meshPath;
49 BufferDefinition::Vector& buffers;
52 struct LoadAccessorListInputs
54 MeshDefinition::RawData& rawData;
55 std::vector<MeshDefinition::Accessor>& accessors;
57 std::fstream& fileStream;
58 std::string& meshPath;
59 BufferDefinition::Vector& buffers;
62 template<bool use32BitIndices>
66 using IndexType = typename std::conditional_t<use32BitIndices, uint32_t, uint16_t>;
67 IndexProvider(const uint16_t* indices)
68 : mData(reinterpret_cast<uintptr_t>(indices)),
69 mFunc(indices ? IncrementPointer : Increment)
73 IndexType operator()()
79 static IndexType Increment(uintptr_t& data)
81 // mData was 'zero' at construct time. Just simply return counter start with 0.
82 return static_cast<IndexType>(data++);
85 static IndexType IncrementPointer(uintptr_t& data)
87 auto iPtr = reinterpret_cast<const IndexType*>(data);
89 data = reinterpret_cast<uintptr_t>(++iPtr);
94 IndexType (*mFunc)(uintptr_t&);
97 const char* QUAD("quad");
99 ///@brief Reads a blob from the given stream @a source into @a target, which must have
100 /// at least @a descriptor.length bytes.
101 bool ReadBlob(const MeshDefinition::Blob& descriptor, std::istream& source, uint8_t* target)
104 if(!source.seekg(static_cast<std::streamoff>(static_cast<std::size_t>(descriptor.mOffset)), std::istream::beg))
109 if(descriptor.IsConsecutive())
111 return !!source.read(reinterpret_cast<char*>(target), static_cast<std::streamsize>(static_cast<size_t>(descriptor.mLength)));
115 if(descriptor.mStride > descriptor.mElementSizeHint)
117 const uint32_t diff = descriptor.mStride - descriptor.mElementSizeHint;
118 uint32_t readSize = 0;
119 uint32_t totalSize = (descriptor.mLength / descriptor.mElementSizeHint) * descriptor.mStride;
120 while(readSize < totalSize &&
121 source.read(reinterpret_cast<char*>(target), descriptor.mElementSizeHint))
123 readSize += descriptor.mStride;
124 target += descriptor.mElementSizeHint;
125 source.seekg(static_cast<std::streamoff>(static_cast<std::size_t>(diff)), std::istream::cur);
127 return readSize == totalSize;
134 void ReadValues(const std::vector<uint8_t>& valuesBuffer, const std::vector<uint8_t>& indicesBuffer, uint8_t* target, uint32_t count, uint32_t elementSizeHint)
136 const T* const indicesPtr = reinterpret_cast<const T* const>(indicesBuffer.data());
137 for(uint32_t index = 0u; index < count; ++index)
139 uint32_t valuesIndex = indicesPtr[index] * elementSizeHint;
140 memcpy(target + valuesIndex, &valuesBuffer[index * elementSizeHint], elementSizeHint);
144 bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target, std::vector<uint32_t>* sparseIndices)
146 bool success = false;
148 if(accessor.mBlob.IsDefined())
150 success = ReadBlob(accessor.mBlob, source, target);
159 const MeshDefinition::Blob& indices = accessor.mSparse->mIndices;
160 const MeshDefinition::Blob& values = accessor.mSparse->mValues;
162 if(!indices.IsDefined() || !values.IsDefined())
167 const auto indicesBufferSize = indices.GetBufferSize();
168 std::vector<uint8_t> indicesBuffer(indicesBufferSize);
169 success = ReadBlob(indices, source, indicesBuffer.data());
175 const auto valuesBufferSize = values.GetBufferSize();
176 std::vector<uint8_t> valuesBuffer(valuesBufferSize);
177 success = ReadBlob(values, source, valuesBuffer.data());
183 // If non-null sparse indices vector, prepare it for output
186 sparseIndices->resize(accessor.mSparse->mCount);
189 switch(indices.mElementSizeHint)
193 ReadValues<uint8_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
196 // convert 8-bit indices into 32-bit
197 std::transform(indicesBuffer.begin(), indicesBuffer.end(), sparseIndices->begin(), [](const uint8_t& value) { return uint32_t(value); });
203 ReadValues<uint16_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
206 // convert 16-bit indices into 32-bit
207 std::transform(reinterpret_cast<uint16_t*>(indicesBuffer.data()),
208 reinterpret_cast<uint16_t*>(indicesBuffer.data()) + accessor.mSparse->mCount,
209 sparseIndices->begin(),
210 [](const uint16_t& value) {
211 return uint32_t(value);
218 ReadValues<uint32_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
221 std::copy(indicesBuffer.begin(), indicesBuffer.end(), reinterpret_cast<uint8_t*>(sparseIndices->data()));
227 DALI_ASSERT_DEBUG(!"Unsupported type for an index");
235 bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target)
237 return ReadAccessor(accessor, source, target, nullptr);
240 template<typename T, bool needsNormalize>
241 void ReadVectorAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, std::vector<uint8_t>& buffer)
243 constexpr auto sizeofBlobUnit = sizeof(T) * 4;
245 DALI_ASSERT_ALWAYS(((accessor.mBlob.mLength % sizeofBlobUnit == 0) ||
246 accessor.mBlob.mStride >= sizeofBlobUnit) &&
247 "Buffer length not a multiple of element size");
248 const auto inBufferSize = accessor.mBlob.GetBufferSize();
249 const auto outBufferSize = (sizeof(Vector4) / sizeofBlobUnit) * inBufferSize;
251 buffer.resize(outBufferSize);
252 auto inBuffer = buffer.data() + outBufferSize - inBufferSize;
253 if(!ReadAccessor(accessor, source, inBuffer))
255 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read vector data from Accessor.";
258 if(sizeofBlobUnit != sizeof(Vector4))
260 auto floats = reinterpret_cast<float*>(buffer.data());
261 const auto end = inBuffer + inBufferSize;
262 while(inBuffer != end)
264 const auto value = *reinterpret_cast<T*>(inBuffer);
265 *floats = (needsNormalize) ? static_cast<float>(value) / static_cast<float>((1 << (sizeof(T) * 8)) - 1) : static_cast<float>(value);
267 inBuffer += sizeof(T);
273 template<bool needsNormalize>
274 void ReadTypedVectorAccessor(LoadDataType loadDataType, MeshDefinition::Accessor& accessor, std::iostream& stream, std::vector<uint8_t>& buffer)
278 case LoadDataType::UNSIGNED_SHORT:
280 ReadVectorAccessor<uint16_t, needsNormalize>(accessor, stream, buffer);
283 case LoadDataType::UNSIGNED_BYTE:
285 ReadVectorAccessor<uint8_t, needsNormalize>(accessor, stream, buffer);
290 ReadVectorAccessor<float, needsNormalize>(accessor, stream, buffer);
296 template<bool use32BitsIndices, typename IndexProviderType = IndexProvider<use32BitsIndices>>
297 bool GenerateNormals(MeshDefinition::RawData& raw)
299 using IndexType = typename IndexProviderType::IndexType;
301 // mIndicies size must be even if we use 32bit indices.
302 if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
307 auto& attribs = raw.mAttribs;
308 DALI_ASSERT_DEBUG(attribs.size() > 0); // positions
310 IndexProviderType getIndex(raw.mIndices.data());
312 const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
314 auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
316 std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(Vector3));
317 auto normals = reinterpret_cast<Vector3*>(buffer.data());
319 for(uint32_t i = 0; i < numIndices; i += 3)
321 IndexType indices[]{getIndex(), getIndex(), getIndex()};
322 Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
324 Vector3 a = pos[1] - pos[0];
325 Vector3 b = pos[2] - pos[0];
327 Vector3 normal(a.Cross(b));
328 normals[indices[0]] += normal;
329 normals[indices[1]] += normal;
330 normals[indices[2]] += normal;
333 auto iEnd = normals + attribs[0].mNumElements;
334 while(normals != iEnd)
336 normals->Normalize();
340 attribs.push_back({"aNormal", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer)});
345 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>>
346 bool GenerateTangents(MeshDefinition::RawData& raw)
348 using IndexType = typename IndexProviderType::IndexType;
350 // mIndicies size must be even if we use 32bit indices.
351 if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
356 auto& attribs = raw.mAttribs;
357 // Required positions, normals, uvs (if we have). If not, skip generation
358 if(DALI_UNLIKELY(attribs.size() < (2 + static_cast<size_t>(hasUvs))))
363 std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(T));
364 auto tangents = reinterpret_cast<T*>(buffer.data());
368 IndexProviderType getIndex(raw.mIndices.data());
370 const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
372 auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
373 auto* uvs = reinterpret_cast<const Vector2*>(attribs[2].mData.data());
375 for(uint32_t i = 0; i < numIndices; i += 3)
377 IndexType indices[]{getIndex(), getIndex(), getIndex()};
378 Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
379 Vector2 uv[]{uvs[indices[0]], uvs[indices[1]], uvs[indices[2]]};
381 float x0 = pos[1].x - pos[0].x;
382 float y0 = pos[1].y - pos[0].y;
383 float z0 = pos[1].z - pos[0].z;
385 float x1 = pos[2].x - pos[0].x;
386 float y1 = pos[2].y - pos[0].y;
387 float z1 = pos[2].z - pos[0].z;
389 float s0 = uv[1].x - uv[0].x;
390 float t0 = uv[1].y - uv[0].y;
392 float s1 = uv[2].x - uv[0].x;
393 float t1 = uv[2].y - uv[0].y;
395 float det = (s0 * t1 - t0 * s1);
396 float r = 1.f / ((std::abs(det) < Dali::Epsilon<1000>::value) ? (Dali::Epsilon<1000>::value * (det > 0.0f ? 1.f : -1.f)) : det);
397 Vector3 tangent((x0 * t1 - t0 * x1) * r, (y0 * t1 - t0 * y1) * r, (z0 * t1 - t0 * z1) * r);
398 tangents[indices[0]] += T(tangent);
399 tangents[indices[1]] += T(tangent);
400 tangents[indices[2]] += T(tangent);
404 auto* normals = reinterpret_cast<const Vector3*>(attribs[1].mData.data());
405 auto iEnd = normals + attribs[1].mNumElements;
406 while(normals != iEnd)
411 // Calculated by indexs
412 tangentVec3 = Vector3((*tangents).x, (*tangents).y, (*tangents).z);
416 // Only choiced by normal vector. by indexs
417 Vector3 t[]{normals->Cross(Vector3::XAXIS), normals->Cross(Vector3::YAXIS)};
418 tangentVec3 = t[t[1].LengthSquared() > t[0].LengthSquared()];
421 tangentVec3 -= *normals * normals->Dot(tangentVec3);
422 tangentVec3.Normalize();
423 if constexpr(useVec3)
425 *tangents = tangentVec3;
429 *tangents = Vector4(tangentVec3.x, tangentVec3.y, tangentVec3.z, 1.0f);
435 attribs.push_back({"aTangent", useVec3 ? Property::VECTOR3 : Property::VECTOR4, attribs[0].mNumElements, std::move(buffer)});
440 void CalculateTextureSize(uint32_t totalTextureSize, uint32_t& textureWidth, uint32_t& textureHeight)
442 DALI_ASSERT_DEBUG(0u != totalTextureSize && "totalTextureSize is zero.")
444 // Calculate the dimensions of the texture.
445 // The total size of the texture is the length of the blend shapes blob.
450 if(0u == totalTextureSize)
456 const uint32_t pow2 = static_cast<uint32_t>(ceil(log2(totalTextureSize)));
457 const uint32_t powWidth = pow2 >> 1u;
458 const uint32_t powHeight = pow2 - powWidth;
460 textureWidth = 1u << powWidth;
461 textureHeight = 1u << powHeight;
465 float GetNormalizedScale()
467 return 1.0f / (std::numeric_limits<T>::max());
471 void DequantizeData(std::vector<uint8_t>& buffer, float* dequantizedValues, uint32_t numValues, bool normalized)
473 // see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data
475 T* values = reinterpret_cast<T*>(buffer.data());
477 for(uint32_t i = 0; i < numValues; ++i)
479 *dequantizedValues = normalized ? std::max((*values) * GetNormalizedScale<T>(), -1.0f) : *values;
486 void GetDequantizedData(std::vector<uint8_t>& buffer, uint32_t numComponents, uint32_t count, uint32_t flags, bool normalized)
488 bool dequantized = false;
490 std::vector<uint8_t> dequantizedBuffer(count * numComponents * sizeof(float));
491 float* dequantizedValues = reinterpret_cast<float*>(dequantizedBuffer.data());
493 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))
495 DequantizeData<int8_t>(buffer, dequantizedValues, numComponents * count, normalized);
498 else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD))
500 DequantizeData<uint8_t>(buffer, dequantizedValues, numComponents * count, normalized);
503 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))
505 DequantizeData<int16_t>(buffer, dequantizedValues, numComponents * count, normalized);
508 else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD))
510 DequantizeData<uint16_t>(buffer, dequantizedValues, numComponents * count, normalized);
516 buffer = std::move(dequantizedBuffer);
520 void GetDequantizedMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t flags)
524 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))
526 scale = GetNormalizedScale<int8_t>();
528 else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD))
530 scale = GetNormalizedScale<uint8_t>();
532 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))
534 scale = GetNormalizedScale<int16_t>();
536 else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD))
538 scale = GetNormalizedScale<uint16_t>();
543 for(float& value : min)
545 value = std::max(value * scale, -1.0f);
548 for(float& value : max)
550 value = std::min(value * scale, 1.0f);
555 void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::vector<MeshDefinition::BlendShape>& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor, BufferDefinition::Vector& buffers)
557 uint32_t geometryBufferIndex = 0u;
558 float maxDistanceSquared = 0.f;
559 Vector3* geometryBufferV3 = reinterpret_cast<Vector3*>(geometryBuffer);
560 for(auto& blendShape : blendShapes)
562 if(blendShape.deltas.IsDefined())
564 const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize();
567 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_POSITION))
569 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % (sizeof(uint8_t) * 3) == 0) ||
570 blendShape.deltas.mBlob.mStride >= (sizeof(uint8_t) * 3)) &&
571 "Blend Shape position buffer length not a multiple of element size");
572 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 3));
574 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_POSITION))
576 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % (sizeof(uint16_t) * 3) == 0) ||
577 blendShape.deltas.mBlob.mStride >= (sizeof(uint16_t) * 3)) &&
578 "Blend Shape position buffer length not a multiple of element size");
579 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 3));
583 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % sizeof(Vector3) == 0) ||
584 blendShape.deltas.mBlob.mStride >= sizeof(Vector3)) &&
585 "Blend Shape position buffer length not a multiple of element size");
586 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
589 std::vector<uint8_t> buffer(bufferSize);
590 std::vector<uint32_t> sparseIndices{};
592 if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
594 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::POSITIONS_MASK, blendShape.deltas.mNormalized);
596 if(blendShape.deltas.mNormalized)
598 GetDequantizedMinMax(blendShape.deltas.mBlob.mMin, blendShape.deltas.mBlob.mMax, blendShape.mFlags & MeshDefinition::POSITIONS_MASK);
601 blendShape.deltas.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
603 // Calculate the difference with the original mesh.
604 // Find the max distance to normalize the deltas.
605 const auto* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
607 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
608 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
609 delta = deltasBuffer[deltaIndex];
610 return std::max(maxDistanceSquared, delta.LengthSquared());
613 if(sparseIndices.empty())
615 for(uint32_t index = 0u; index < numberOfVertices; ++index)
617 maxDistanceSquared = ProcessVertex(geometryBufferIndex++, index);
622 // initialize blendshape texture
623 // TODO: there may be a case when sparse accessor uses a base buffer view for initial values.
624 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3::ZERO);
625 for(auto index : sparseIndices)
627 maxDistanceSquared = ProcessVertex(geometryBufferIndex + index, index);
629 geometryBufferIndex += numberOfVertices;
634 if(blendShape.normals.IsDefined())
636 const auto bufferSize = blendShape.normals.mBlob.GetBufferSize();
639 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_NORMAL))
641 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
642 blendShape.normals.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
643 "Blend Shape normals buffer length not a multiple of element size");
644 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
646 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_NORMAL))
648 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
649 blendShape.normals.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
650 "Blend Shape normals buffer length not a multiple of element size");
651 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
655 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % sizeof(Vector3) == 0) ||
656 blendShape.normals.mBlob.mStride >= sizeof(Vector3)) &&
657 "Blend Shape normals buffer length not a multiple of element size");
658 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
661 std::vector<uint8_t> buffer(bufferSize);
662 std::vector<uint32_t> sparseIndices;
664 if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
666 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::NORMALS_MASK, blendShape.normals.mNormalized);
668 if(blendShape.normals.mNormalized)
670 GetDequantizedMinMax(blendShape.normals.mBlob.mMin, blendShape.normals.mBlob.mMax, blendShape.mFlags & MeshDefinition::NORMALS_MASK);
673 blendShape.normals.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
675 // Calculate the difference with the original mesh, and translate to make all values positive.
676 const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
677 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
678 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
688 if(sparseIndices.empty())
690 for(uint32_t index = 0u; index < numberOfVertices; ++index)
692 ProcessVertex(geometryBufferIndex++, index);
697 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5));
698 for(auto index : sparseIndices)
700 ProcessVertex(geometryBufferIndex + index, index);
702 geometryBufferIndex += numberOfVertices;
707 if(blendShape.tangents.IsDefined())
709 const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize();
713 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_TANGENT))
715 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
716 blendShape.tangents.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
717 "Blend Shape tangents buffer length not a multiple of element size");
718 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
720 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_TANGENT))
722 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
723 blendShape.tangents.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
724 "Blend Shape tangents buffer length not a multiple of element size");
725 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
729 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % sizeof(Vector3) == 0) ||
730 blendShape.tangents.mBlob.mStride >= sizeof(Vector3)) &&
731 "Blend Shape tangents buffer length not a multiple of element size");
732 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
735 std::vector<uint8_t> buffer(bufferSize);
736 std::vector<uint32_t> sparseIndices;
738 if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
740 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::TANGENTS_MASK, blendShape.tangents.mNormalized);
742 if(blendShape.tangents.mNormalized)
744 GetDequantizedMinMax(blendShape.tangents.mBlob.mMin, blendShape.tangents.mBlob.mMax, blendShape.mFlags & MeshDefinition::TANGENTS_MASK);
747 blendShape.tangents.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
749 // Calculate the difference with the original mesh, and translate to make all values positive.
750 const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
751 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
752 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
762 if(sparseIndices.empty())
764 for(uint32_t index = 0u; index < numberOfVertices; ++index)
766 ProcessVertex(geometryBufferIndex++, index);
771 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5));
772 for(auto index : sparseIndices)
774 ProcessVertex(geometryBufferIndex + index, index);
776 geometryBufferIndex += numberOfVertices;
782 geometryBufferIndex = 0u;
784 const float maxDistance = sqrtf(maxDistanceSquared);
786 const float normalizeFactor = (maxDistanceSquared < Math::MACHINE_EPSILON_100) ? 1.f : (0.5f / maxDistance);
788 // Calculate and store the unnormalize factor.
789 blendShapeUnnormalizeFactor = maxDistance * 2.0f;
791 for(const auto& blendShape : blendShapes)
793 // Normalize all the deltas and translate to a possitive value.
794 // Deltas are going to be passed to the shader in a color texture
795 // whose values that are less than zero are clamped.
796 if(blendShape.deltas.IsDefined())
798 for(uint32_t index = 0u; index < numberOfVertices; ++index)
800 Vector3& delta = geometryBufferV3[geometryBufferIndex++];
801 delta.x = Clamp(((delta.x * normalizeFactor) + 0.5f), 0.f, 1.f);
802 delta.y = Clamp(((delta.y * normalizeFactor) + 0.5f), 0.f, 1.f);
803 delta.z = Clamp(((delta.z * normalizeFactor) + 0.5f), 0.f, 1.f);
807 if(blendShape.normals.IsDefined())
809 geometryBufferIndex += numberOfVertices;
812 if(blendShape.tangents.IsDefined())
814 geometryBufferIndex += numberOfVertices;
819 std::iostream& GetAvailableData(std::fstream& meshStream, const std::string& meshPath, BufferDefinition& buffer, std::string& availablePath)
821 auto& stream = (meshStream.is_open()) ? meshStream : buffer.GetBufferStream();
822 availablePath = (meshStream.is_open()) ? meshPath : buffer.GetUri();
826 template<bool needsNormalize>
827 void ReadTypedVectorAccessors(LoadAccessorListInputs loadAccessorListInputs, LoadDataType loadDataType, std::string attributeName)
830 for(auto& accessor : loadAccessorListInputs.accessors)
832 std::string pathJoint;
833 auto& dataStream = GetAvailableData(loadAccessorListInputs.fileStream, loadAccessorListInputs.meshPath, loadAccessorListInputs.buffers[accessor.mBufferIdx], pathJoint);
834 std::ostringstream name;
835 name << attributeName << setIndex++;
836 std::vector<uint8_t> buffer;
837 ReadTypedVectorAccessor<needsNormalize>(loadDataType, accessor, dataStream, buffer);
838 loadAccessorListInputs.rawData.mAttribs.push_back({name.str(), Property::VECTOR4, static_cast<uint32_t>(buffer.size() / sizeof(Vector4)), std::move(buffer)});
842 void LoadIndices(LoadAccessorInputs indicesInput)
844 if(indicesInput.accessor.IsDefined())
846 if(MaskMatch(indicesInput.flags, MeshDefinition::Flags::U32_INDICES))
848 DALI_ASSERT_ALWAYS(((indicesInput.accessor.mBlob.mLength % sizeof(uint32_t) == 0) ||
849 indicesInput.accessor.mBlob.mStride >= sizeof(uint32_t)) &&
850 "Index buffer length not a multiple of element size");
851 const auto indexCount = indicesInput.accessor.mBlob.GetBufferSize() / sizeof(uint32_t);
852 indicesInput.rawData.mIndices.resize(indexCount * 2); // NOTE: we need space for uint32_ts initially.
855 auto& stream = GetAvailableData(indicesInput.fileStream, indicesInput.meshPath, indicesInput.buffers[indicesInput.accessor.mBufferIdx], path);
856 if(!ReadAccessor(indicesInput.accessor, stream, reinterpret_cast<uint8_t*>(indicesInput.rawData.mIndices.data())))
858 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
861 else if(MaskMatch(indicesInput.flags, MeshDefinition::Flags::U8_INDICES))
863 DALI_ASSERT_ALWAYS(((indicesInput.accessor.mBlob.mLength % sizeof(uint8_t) == 0) ||
864 indicesInput.accessor.mBlob.mStride >= sizeof(uint8_t)) &&
865 "Index buffer length not a multiple of element size");
866 const auto indexCount = indicesInput.accessor.mBlob.GetBufferSize() / sizeof(uint8_t);
867 indicesInput.rawData.mIndices.resize(indexCount); // NOTE: we need space for uint16_ts initially.
870 auto u8s = reinterpret_cast<uint8_t*>(indicesInput.rawData.mIndices.data()) + indexCount;
871 auto& stream = GetAvailableData(indicesInput.fileStream, indicesInput.meshPath, indicesInput.buffers[indicesInput.accessor.mBufferIdx], path);
872 if(!ReadAccessor(indicesInput.accessor, stream, u8s))
874 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
877 auto u16s = indicesInput.rawData.mIndices.data();
878 auto end = u8s + indexCount;
881 *u16s = static_cast<uint16_t>(*u8s);
888 DALI_ASSERT_ALWAYS(((indicesInput.accessor.mBlob.mLength % sizeof(unsigned short) == 0) ||
889 indicesInput.accessor.mBlob.mStride >= sizeof(unsigned short)) &&
890 "Index buffer length not a multiple of element size");
891 indicesInput.rawData.mIndices.resize(indicesInput.accessor.mBlob.mLength / sizeof(unsigned short));
894 auto& stream = GetAvailableData(indicesInput.fileStream, indicesInput.meshPath, indicesInput.buffers[indicesInput.accessor.mBufferIdx], path);
895 if(!ReadAccessor(indicesInput.accessor, stream, reinterpret_cast<uint8_t*>(indicesInput.rawData.mIndices.data())))
897 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indicesInput.accessor from '" << path << "'.";
903 uint32_t LoadPositions(LoadAccessorInputs positionsInput, bool hasBlendShape)
905 uint32_t numVector3 = 0u;
906 std::vector<Vector3> positions;
907 if(positionsInput.accessor.IsDefined())
909 const auto bufferSize = positionsInput.accessor.mBlob.GetBufferSize();
911 if(MaskMatch(positionsInput.flags, MeshDefinition::Flags::S8_POSITION) || MaskMatch(positionsInput.flags, MeshDefinition::Flags::U8_POSITION))
913 DALI_ASSERT_ALWAYS(((positionsInput.accessor.mBlob.mLength % (sizeof(uint8_t) * 3) == 0) ||
914 positionsInput.accessor.mBlob.mStride >= (sizeof(uint8_t) * 3)) &&
915 "Position buffer length not a multiple of element size");
916 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 3));
918 else if(MaskMatch(positionsInput.flags, MeshDefinition::Flags::S16_POSITION) || MaskMatch(positionsInput.flags, MeshDefinition::Flags::U16_POSITION))
920 DALI_ASSERT_ALWAYS(((positionsInput.accessor.mBlob.mLength % (sizeof(uint16_t) * 3) == 0) ||
921 positionsInput.accessor.mBlob.mStride >= (sizeof(uint16_t) * 3)) &&
922 "Position buffer length not a multiple of element size");
923 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 3));
927 DALI_ASSERT_ALWAYS(((positionsInput.accessor.mBlob.mLength % sizeof(Vector3) == 0) ||
928 positionsInput.accessor.mBlob.mStride >= sizeof(Vector3)) &&
929 "Position buffer length not a multiple of element size");
930 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
933 std::vector<uint8_t> buffer(bufferSize);
936 auto& stream = GetAvailableData(positionsInput.fileStream, positionsInput.meshPath, positionsInput.buffers[positionsInput.accessor.mBufferIdx], path);
937 if(!ReadAccessor(positionsInput.accessor, stream, buffer.data()))
939 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << path << "'.";
942 GetDequantizedData(buffer, 3u, numVector3, positionsInput.flags & MeshDefinition::FlagMasks::POSITIONS_MASK, positionsInput.accessor.mNormalized);
944 if(positionsInput.accessor.mNormalized)
946 GetDequantizedMinMax(positionsInput.accessor.mBlob.mMin, positionsInput.accessor.mBlob.mMax, positionsInput.flags & MeshDefinition::FlagMasks::POSITIONS_MASK);
949 if(positionsInput.accessor.mBlob.mMin.size() != 3u || positionsInput.accessor.mBlob.mMax.size() != 3u)
951 positionsInput.accessor.mBlob.ComputeMinMax(3u, numVector3, reinterpret_cast<float*>(buffer.data()));
955 positionsInput.accessor.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
960 positions.resize(numVector3);
961 std::copy(buffer.data(), buffer.data() + buffer.size(), reinterpret_cast<uint8_t*>(positions.data()));
964 positionsInput.rawData.mAttribs.push_back({"aPosition", Property::VECTOR3, numVector3, std::move(buffer)});
969 bool LoadNormals(LoadAccessorInputs normalsInput, bool isTriangles, uint32_t positionBufferSize)
971 auto hasNormals = normalsInput.accessor.IsDefined();
974 const auto bufferSize = normalsInput.accessor.mBlob.GetBufferSize();
977 if(MaskMatch(normalsInput.flags, MeshDefinition::Flags::S8_NORMAL))
979 DALI_ASSERT_ALWAYS(((normalsInput.accessor.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
980 normalsInput.accessor.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
981 "Normal buffer length not a multiple of element size");
982 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
984 else if(MaskMatch(normalsInput.flags, MeshDefinition::Flags::S16_NORMAL))
986 DALI_ASSERT_ALWAYS(((normalsInput.accessor.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
987 normalsInput.accessor.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
988 "Normal buffer length not a multiple of element size");
989 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
993 DALI_ASSERT_ALWAYS(((normalsInput.accessor.mBlob.mLength % sizeof(Vector3) == 0) ||
994 normalsInput.accessor.mBlob.mStride >= sizeof(Vector3)) &&
995 "Normal buffer length not a multiple of element size");
996 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
999 std::vector<uint8_t> buffer(bufferSize);
1002 auto& stream = GetAvailableData(normalsInput.fileStream, normalsInput.meshPath, normalsInput.buffers[normalsInput.accessor.mBufferIdx], path);
1003 if(!ReadAccessor(normalsInput.accessor, stream, buffer.data()))
1005 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << path << "'.";
1008 GetDequantizedData(buffer, 3u, numVector3, normalsInput.flags & MeshDefinition::FlagMasks::NORMALS_MASK, normalsInput.accessor.mNormalized);
1010 if(normalsInput.accessor.mNormalized)
1012 GetDequantizedMinMax(normalsInput.accessor.mBlob.mMin, normalsInput.accessor.mBlob.mMax, normalsInput.flags & MeshDefinition::FlagMasks::NORMALS_MASK);
1015 normalsInput.accessor.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
1017 normalsInput.rawData.mAttribs.push_back({"aNormal", Property::VECTOR3, numVector3, std::move(buffer)});
1019 else if(normalsInput.accessor.mBlob.mLength != 0 && isTriangles)
1021 DALI_ASSERT_DEBUG(normalsInput.accessor.mBlob.mLength == positionBufferSize);
1022 static const std::function<bool(MeshDefinition::RawData&)> GenerateNormalsFunction[2] =
1024 GenerateNormals<false>,
1025 GenerateNormals<true>,
1027 const bool generateSuccessed = GenerateNormalsFunction[MaskMatch(normalsInput.flags, MeshDefinition::Flags::U32_INDICES)](normalsInput.rawData);
1028 if(!generateSuccessed)
1030 DALI_LOG_ERROR("Failed to generate normal\n");
1040 void LoadTextureCoordinates(LoadAccessorListInputs textureCoordinatesInput)
1042 if(!textureCoordinatesInput.accessors.empty() && textureCoordinatesInput.accessors[0].IsDefined())
1044 auto& texCoords = textureCoordinatesInput.accessors[0];
1045 const auto bufferSize = texCoords.mBlob.GetBufferSize();
1048 if(MaskMatch(textureCoordinatesInput.flags, MeshDefinition::Flags::S8_TEXCOORD) || MaskMatch(textureCoordinatesInput.flags, MeshDefinition::Flags::U8_TEXCOORD))
1050 DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % (sizeof(uint8_t) * 2) == 0) ||
1051 texCoords.mBlob.mStride >= (sizeof(uint8_t) * 2)) &&
1052 "TexCoords buffer length not a multiple of element size");
1053 uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 2));
1055 else if(MaskMatch(textureCoordinatesInput.flags, MeshDefinition::Flags::S16_TEXCOORD) || MaskMatch(textureCoordinatesInput.flags, MeshDefinition::Flags::U16_TEXCOORD))
1057 DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % (sizeof(uint16_t) * 2) == 0) ||
1058 texCoords.mBlob.mStride >= (sizeof(uint16_t) * 2)) &&
1059 "TexCoords buffer length not a multiple of element size");
1060 uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 2));
1064 DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
1065 texCoords.mBlob.mStride >= sizeof(Vector2)) &&
1066 "TexCoords buffer length not a multiple of element size");
1067 uvCount = static_cast<uint32_t>(bufferSize / sizeof(Vector2));
1070 std::vector<uint8_t> buffer(bufferSize);
1073 auto& stream = GetAvailableData(textureCoordinatesInput.fileStream, textureCoordinatesInput.meshPath, textureCoordinatesInput.buffers[texCoords.mBufferIdx], path);
1074 if(!ReadAccessor(texCoords, stream, buffer.data()))
1076 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << path << "'.";
1079 GetDequantizedData(buffer, 2u, uvCount, textureCoordinatesInput.flags & MeshDefinition::FlagMasks::TEXCOORDS_MASK, texCoords.mNormalized);
1081 if(MaskMatch(textureCoordinatesInput.flags, MeshDefinition::Flags::FLIP_UVS_VERTICAL))
1083 auto uv = reinterpret_cast<Vector2*>(buffer.data());
1084 auto uvEnd = uv + uvCount;
1087 uv->y = 1.0f - uv->y;
1092 if(texCoords.mNormalized)
1094 GetDequantizedMinMax(texCoords.mBlob.mMin, texCoords.mBlob.mMax, textureCoordinatesInput.flags & MeshDefinition::FlagMasks::TEXCOORDS_MASK);
1097 texCoords.mBlob.ApplyMinMax(static_cast<uint32_t>(uvCount), reinterpret_cast<float*>(buffer.data()));
1098 textureCoordinatesInput.rawData.mAttribs.push_back({"aTexCoord", Property::VECTOR2, static_cast<uint32_t>(uvCount), std::move(buffer)});
1102 void LoadTangents(LoadAccessorInputs tangentsInput, bool hasNormals, bool hasUvs, bool isTriangles, Property::Type tangentType, uint32_t normalBufferSize)
1104 if(tangentsInput.accessor.IsDefined())
1106 const auto bufferSize = tangentsInput.accessor.mBlob.GetBufferSize();
1108 uint32_t propertySize = static_cast<uint32_t>((tangentType == Property::VECTOR4) ? sizeof(Vector4) : sizeof(Vector3));
1109 uint32_t componentCount = static_cast<uint32_t>(propertySize / sizeof(float));
1111 uint32_t numTangents;
1113 if(MaskMatch(tangentsInput.flags, MeshDefinition::Flags::S8_TANGENT))
1115 DALI_ASSERT_ALWAYS(((tangentsInput.accessor.mBlob.mLength % (sizeof(int8_t) * componentCount) == 0) ||
1116 tangentsInput.accessor.mBlob.mStride >= (sizeof(int8_t) * componentCount)) &&
1117 "Tangents buffer length not a multiple of element size");
1118 numTangents = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * componentCount));
1120 else if(MaskMatch(tangentsInput.flags, MeshDefinition::Flags::S16_TANGENT))
1122 DALI_ASSERT_ALWAYS(((tangentsInput.accessor.mBlob.mLength % (sizeof(int16_t) * componentCount) == 0) ||
1123 tangentsInput.accessor.mBlob.mStride >= (sizeof(int16_t) * componentCount)) &&
1124 "Tangents buffer length not a multiple of element size");
1125 numTangents = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * componentCount));
1129 DALI_ASSERT_ALWAYS(((tangentsInput.accessor.mBlob.mLength % propertySize == 0) ||
1130 tangentsInput.accessor.mBlob.mStride >= propertySize) &&
1131 "Tangents buffer length not a multiple of element size");
1132 numTangents = static_cast<uint32_t>(bufferSize / propertySize);
1135 std::vector<uint8_t> buffer(bufferSize);
1138 auto& stream = GetAvailableData(tangentsInput.fileStream, tangentsInput.meshPath, tangentsInput.buffers[tangentsInput.accessor.mBufferIdx], path);
1139 if(!ReadAccessor(tangentsInput.accessor, stream, buffer.data()))
1141 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << path << "'.";
1144 GetDequantizedData(buffer, componentCount, numTangents, tangentsInput.flags & MeshDefinition::FlagMasks::TANGENTS_MASK, tangentsInput.accessor.mNormalized);
1146 if(tangentsInput.accessor.mNormalized)
1148 GetDequantizedMinMax(tangentsInput.accessor.mBlob.mMin, tangentsInput.accessor.mBlob.mMax, tangentsInput.flags & MeshDefinition::FlagMasks::TANGENTS_MASK);
1151 tangentsInput.accessor.mBlob.ApplyMinMax(numTangents, reinterpret_cast<float*>(buffer.data()));
1153 tangentsInput.rawData.mAttribs.push_back({"aTangent", tangentType, static_cast<uint32_t>(numTangents), std::move(buffer)});
1155 else if(tangentsInput.accessor.mBlob.mLength != 0 && hasNormals && isTriangles)
1157 DALI_ASSERT_DEBUG(tangentsInput.accessor.mBlob.mLength == normalBufferSize);
1158 static const std::function<bool(MeshDefinition::RawData&)> GenerateTangentsFunction[2][2][2] =
1162 GenerateTangents<false, false, false>,
1163 GenerateTangents<false, false, true>,
1166 GenerateTangents<false, true, false>,
1167 GenerateTangents<false, true, true>,
1172 GenerateTangents<true, false, false>,
1173 GenerateTangents<true, false, true>,
1176 GenerateTangents<true, true, false>,
1177 GenerateTangents<true, true, true>,
1180 const bool generateSuccessed = GenerateTangentsFunction[MaskMatch(tangentsInput.flags, MeshDefinition::Flags::U32_INDICES)][tangentType == Property::VECTOR3][hasUvs](tangentsInput.rawData);
1181 if(!generateSuccessed)
1183 DALI_LOG_ERROR("Failed to generate tangents\n");
1188 void LoadColors(LoadAccessorListInputs colorsInput)
1190 // Only support 1 vertex color
1191 if(!colorsInput.accessors.empty() && colorsInput.accessors[0].IsDefined())
1193 uint32_t propertySize = colorsInput.accessors[0].mBlob.mElementSizeHint;
1194 Property::Type propertyType = (propertySize == sizeof(Vector4)) ? Property::VECTOR4 : ((propertySize == sizeof(Vector3)) ? Property::VECTOR3 : Property::NONE);
1195 if(propertyType != Property::NONE)
1197 DALI_ASSERT_ALWAYS(((colorsInput.accessors[0].mBlob.mLength % propertySize == 0) ||
1198 colorsInput.accessors[0].mBlob.mStride >= propertySize) &&
1199 "Colors buffer length not a multiple of element size");
1200 const auto bufferSize = colorsInput.accessors[0].mBlob.GetBufferSize();
1201 std::vector<uint8_t> buffer(bufferSize);
1204 auto& stream = GetAvailableData(colorsInput.fileStream, colorsInput.meshPath, colorsInput.buffers[colorsInput.accessors[0].mBufferIdx], path);
1205 if(!ReadAccessor(colorsInput.accessors[0], stream, buffer.data()))
1207 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read colors from '" << path << "'.";
1209 colorsInput.accessors[0].mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
1211 colorsInput.rawData.mAttribs.push_back({"aVertexColor", propertyType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
1216 std::vector<uint8_t> buffer(colorsInput.rawData.mAttribs[0].mNumElements * sizeof(Vector4));
1217 auto colors = reinterpret_cast<Vector4*>(buffer.data());
1219 for(uint32_t i = 0; i < colorsInput.rawData.mAttribs[0].mNumElements; i++)
1221 colors[i] = Vector4::ONE;
1224 colorsInput.rawData.mAttribs.push_back({"aVertexColor", Property::VECTOR4, colorsInput.rawData.mAttribs[0].mNumElements, std::move(buffer)});
1228 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)
1230 // Calculate the Blob for the blend shapes.
1231 MeshDefinition::Blob blendShapesBlob;
1232 blendShapesBlob.mOffset = std::numeric_limits<unsigned int>::max();
1233 blendShapesBlob.mLength = 0u;
1235 uint32_t totalTextureSize(0u);
1237 auto processAccessor = [&](const MeshDefinition::Accessor& accessor, uint32_t vector3Size) {
1238 if(accessor.IsDefined())
1240 blendShapesBlob.mOffset = std::min(blendShapesBlob.mOffset, accessor.mBlob.mOffset);
1241 blendShapesBlob.mLength += accessor.mBlob.mLength;
1243 totalTextureSize += accessor.mBlob.mLength / vector3Size;
1247 for(const auto& blendShape : blendShapes)
1249 const auto positionMask = blendShape.mFlags & MeshDefinition::FlagMasks::POSITIONS_MASK;
1250 const auto normalMask = blendShape.mFlags & MeshDefinition::FlagMasks::NORMALS_MASK;
1251 const auto tangentMask = blendShape.mFlags & MeshDefinition::FlagMasks::TANGENTS_MASK;
1253 processAccessor(blendShape.deltas, MaskMatch(positionMask, MeshDefinition::S8_POSITION) ? sizeof(uint8_t) * 3 : (MaskMatch(positionMask, MeshDefinition::S16_POSITION) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1254 processAccessor(blendShape.normals, MaskMatch(normalMask, MeshDefinition::S8_NORMAL) ? sizeof(uint8_t) * 3 : (MaskMatch(normalMask, MeshDefinition::S16_NORMAL) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1255 processAccessor(blendShape.tangents, MaskMatch(tangentMask, MeshDefinition::S8_TANGENT) ? sizeof(uint8_t) * 3 : (MaskMatch(tangentMask, MeshDefinition::S16_TANGENT) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1258 if(!blendShapes.empty())
1260 // Calculate the size of one buffer inside the texture.
1261 rawData.mBlendShapeBufferOffset = numberOfVertices;
1263 bool calculateGltf2BlendShapes = false;
1264 uint32_t textureWidth = 0u;
1265 uint32_t textureHeight = 0u;
1267 if(!blendShapeHeader.IsDefined())
1269 CalculateTextureSize(totalTextureSize, textureWidth, textureHeight);
1270 calculateGltf2BlendShapes = true;
1274 uint16_t header[2u];
1275 ReadBlob(blendShapeHeader, fileStream, reinterpret_cast<uint8_t*>(header));
1276 textureWidth = header[0u];
1277 textureHeight = header[1u];
1280 const uint32_t numberOfBlendShapes = blendShapes.size();
1281 rawData.mBlendShapeUnnormalizeFactor.Resize(numberOfBlendShapes);
1283 Devel::PixelBuffer geometryPixelBuffer = Devel::PixelBuffer::New(textureWidth, textureHeight, Pixel::RGB32F);
1284 uint8_t* geometryBuffer = geometryPixelBuffer.GetBuffer();
1286 if(calculateGltf2BlendShapes)
1288 CalculateGltf2BlendShapes(geometryBuffer, blendShapes, numberOfVertices, rawData.mBlendShapeUnnormalizeFactor[0u], buffers);
1292 MeshDefinition::Blob unnormalizeFactorBlob;
1293 unnormalizeFactorBlob.mLength = static_cast<uint32_t>(sizeof(float) * ((BlendShapes::Version::VERSION_2_0 == blendShapeVersion) ? 1u : numberOfBlendShapes));
1295 if(blendShapesBlob.IsDefined())
1297 if(ReadBlob(blendShapesBlob, fileStream, geometryBuffer))
1299 unnormalizeFactorBlob.mOffset = blendShapesBlob.mOffset + blendShapesBlob.mLength;
1303 // Read the unnormalize factors.
1304 if(unnormalizeFactorBlob.IsDefined())
1306 ReadBlob(unnormalizeFactorBlob, fileStream, reinterpret_cast<uint8_t*>(&rawData.mBlendShapeUnnormalizeFactor[0u]));
1309 rawData.mBlendShapeData = Devel::PixelBuffer::Convert(geometryPixelBuffer);
1313 constexpr uint32_t MINIMUM_SHADER_VERSION_SUPPORT_VERTEX_ID = 300;
1317 MeshDefinition::SparseBlob::SparseBlob(const Blob& indices, const Blob& values, uint32_t count)
1318 : mIndices{indices},
1324 MeshDefinition::SparseBlob::SparseBlob(Blob&& indices, Blob&& values, uint32_t count)
1325 : mIndices(std::move(indices)),
1326 mValues(std::move(values)),
1331 MeshDefinition::Accessor::Accessor(const MeshDefinition::Blob& blob,
1332 const MeshDefinition::SparseBlob& sparse,
1336 mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{sparse} : nullptr},
1337 mBufferIdx(bufferIndex),
1338 mNormalized(normalized)
1342 MeshDefinition::Accessor::Accessor(MeshDefinition::Blob&& blob,
1343 MeshDefinition::SparseBlob&& sparse,
1346 : mBlob{std::move(blob)},
1347 mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{std::move(sparse)} : nullptr},
1348 mBufferIdx(bufferIndex),
1349 mNormalized(normalized)
1353 void MeshDefinition::Blob::ComputeMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t numComponents, uint32_t count, const float* values)
1355 min.assign(numComponents, MAXFLOAT);
1356 max.assign(numComponents, -MAXFLOAT);
1357 for(uint32_t i = 0; i < count; ++i)
1359 for(uint32_t j = 0; j < numComponents; ++j)
1361 min[j] = std::min(min[j], *values);
1362 max[j] = std::max(max[j], *values);
1368 void MeshDefinition::Blob::ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max, uint32_t count, float* values, std::vector<uint32_t>* sparseIndices)
1370 DALI_ASSERT_DEBUG(max.size() == min.size() || max.size() * min.size() == 0);
1371 const auto numComponents = std::max(min.size(), max.size());
1373 using ClampFn = void (*)(const float*, const float*, uint32_t, float&);
1374 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); })
1375 : (max.empty() ? [](const float* min, const float* max, uint32_t i, float& value) { value = std::max(min[i], value); }
1376 : static_cast<ClampFn>([](const float* min, const float* max, uint32_t i, float& value) { value = std::min(std::max(min[i], value), max[i]); }));
1383 auto end = values + count * numComponents;
1384 while(values != end)
1386 auto nextElement = values + numComponents;
1388 while(values != nextElement)
1390 clampFn(min.data(), max.data(), i, *values);
1397 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)
1401 mElementSizeHint(elementSizeHint),
1407 uint32_t MeshDefinition::Blob::GetBufferSize() const
1412 void MeshDefinition::Blob::ComputeMinMax(uint32_t numComponents, uint32_t count, float* values)
1414 ComputeMinMax(mMin, mMax, numComponents, count, values);
1417 void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values, std::vector<uint32_t>* sparseIndices) const
1419 ApplyMinMax(mMin, mMax, count, values, sparseIndices);
1422 void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const
1424 Property::Map attribMap;
1425 attribMap[mName] = mType;
1426 VertexBuffer attribBuffer = VertexBuffer::New(attribMap);
1427 attribBuffer.SetData(mData.data(), mNumElements);
1429 g.AddVertexBuffer(attribBuffer);
1432 bool MeshDefinition::IsQuad() const
1434 return CaseInsensitiveStringCompare(QUAD, mUri);
1437 bool MeshDefinition::IsSkinned() const
1439 return !mJoints.empty() && !mWeights.empty();
1442 bool MeshDefinition::HasVertexColor() const
1444 return !mColors.empty();
1447 uint32_t MeshDefinition::GetNumberOfJointSets() const
1449 uint32_t number = static_cast<uint32_t>(mJoints.size());
1450 if(number > MeshDefinition::MAX_NUMBER_OF_JOINT_SETS)
1452 number = MeshDefinition::MAX_NUMBER_OF_JOINT_SETS;
1457 bool MeshDefinition::HasBlendShapes() const
1459 return !mBlendShapes.empty();
1462 void MeshDefinition::RequestNormals()
1464 mNormals.mBlob.mLength = mPositions.mBlob.GetBufferSize();
1467 void MeshDefinition::RequestTangents()
1469 mTangents.mBlob.mLength = mNormals.mBlob.GetBufferSize();
1472 MeshDefinition::RawData
1473 MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& buffers)
1481 std::string meshPath;
1482 meshPath = modelsPath + mUri;
1483 std::fstream fileStream;
1486 fileStream.open(meshPath, std::ios::in | std::ios::binary);
1487 if(!fileStream.is_open())
1489 DALI_LOG_ERROR("Fail to open buffer from %s.\n", meshPath.c_str());
1493 LoadAccessorInputs indicesInput = {raw, mIndices, mFlags, fileStream, meshPath, buffers};
1494 LoadIndices(indicesInput);
1496 LoadAccessorInputs positionsInput = {raw, mPositions, mFlags, fileStream, meshPath, buffers};
1497 uint32_t numberOfVertices = LoadPositions(positionsInput, HasBlendShapes());
1499 const auto isTriangles = mPrimitiveType == Geometry::TRIANGLES;
1500 LoadAccessorInputs normalsInput = {raw, mNormals, mFlags, fileStream, meshPath, buffers};
1501 auto hasNormals = LoadNormals(normalsInput, isTriangles, mPositions.mBlob.GetBufferSize());
1503 LoadAccessorListInputs textureCoordinatesInput = {raw, mTexCoords, mFlags, fileStream, meshPath, buffers};
1504 LoadTextureCoordinates(textureCoordinatesInput);
1506 const bool hasUvs = !mTexCoords.empty() && mTexCoords[0].IsDefined();
1507 LoadAccessorInputs tangentsInput = {raw, mTangents, mFlags, fileStream, meshPath, buffers};
1508 LoadTangents(tangentsInput, hasNormals, hasUvs, isTriangles, mTangentType, mNormals.mBlob.GetBufferSize());
1510 LoadAccessorListInputs colorsInput = {raw, mColors, mFlags, fileStream, meshPath, buffers};
1511 LoadColors(colorsInput);
1515 LoadDataType loadDataType = (MaskMatch(mFlags, MeshDefinition::U16_JOINT_IDS)) ? LoadDataType::UNSIGNED_SHORT : (MaskMatch(mFlags, MeshDefinition::U8_JOINT_IDS) ? LoadDataType::UNSIGNED_BYTE : LoadDataType::FLOAT);
1516 LoadAccessorListInputs jointsInput = {raw, mJoints, mFlags, fileStream, meshPath, buffers};
1517 ReadTypedVectorAccessors<false>(jointsInput, loadDataType, "aJoints");
1519 loadDataType = (MaskMatch(mFlags, MeshDefinition::U16_WEIGHT)) ? LoadDataType::UNSIGNED_SHORT : (MaskMatch(mFlags, MeshDefinition::U8_WEIGHT) ? LoadDataType::UNSIGNED_BYTE : LoadDataType::FLOAT);
1520 LoadAccessorListInputs weightsInput = {raw, mWeights, mFlags, fileStream, meshPath, buffers};
1521 ReadTypedVectorAccessors<true>(weightsInput, loadDataType, "aWeights");
1524 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 if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_VERTEX_ID && !raw.mAttribs.empty()))
1556 auto numElements = raw.mAttribs[0].mNumElements;
1558 // gl_VertexID not support. We should add buffer hard.
1559 Property::Map attribMap;
1560 attribMap["aVertexID"] = Property::FLOAT;
1562 VertexBuffer attribBuffer = VertexBuffer::New(attribMap);
1564 std::vector<uint8_t> buffer(numElements * sizeof(float));
1565 auto ids = reinterpret_cast<float*>(buffer.data());
1567 for(uint32_t i = 0; i < numElements; i++)
1569 ids[i] = static_cast<float>(i);
1572 attribBuffer.SetData(buffer.data(), numElements);
1574 meshGeometry.geometry.AddVertexBuffer(attribBuffer);
1577 for(auto& a : raw.mAttribs)
1579 a.AttachBuffer(meshGeometry.geometry);
1582 if(HasBlendShapes())
1584 meshGeometry.blendShapeBufferOffset = raw.mBlendShapeBufferOffset;
1585 meshGeometry.blendShapeUnnormalizeFactor = std::move(raw.mBlendShapeUnnormalizeFactor);
1587 meshGeometry.blendShapeGeometry = Texture::New(TextureType::TEXTURE_2D,
1588 raw.mBlendShapeData.GetPixelFormat(),
1589 raw.mBlendShapeData.GetWidth(),
1590 raw.mBlendShapeData.GetHeight());
1591 meshGeometry.blendShapeGeometry.Upload(raw.mBlendShapeData);
1595 return meshGeometry;
1598 void MeshDefinition::RetrieveBlendShapeComponents(bool& hasPositions, bool& hasNormals, bool& hasTangents) const
1600 for(const auto& blendShape : mBlendShapes)
1602 hasPositions = hasPositions || blendShape.deltas.IsDefined();
1603 hasNormals = hasNormals || blendShape.normals.IsDefined();
1604 hasTangents = hasTangents || blendShape.tangents.IsDefined();
1608 } // namespace Dali::Scene3D::Loader