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
35 template<bool use32BitIndices>
39 using IndexType = typename std::conditional_t<use32BitIndices, uint32_t, uint16_t>;
40 IndexProvider(const uint16_t* indices)
41 : mData(reinterpret_cast<uintptr_t>(indices)),
42 mFunc(indices ? IncrementPointer : Increment)
46 IndexType operator()()
52 static IndexType Increment(uintptr_t& data)
54 // mData was 'zero' at construct time. Just simply return counter start with 0.
55 return static_cast<IndexType>(data++);
58 static IndexType IncrementPointer(uintptr_t& data)
60 auto iPtr = reinterpret_cast<const IndexType*>(data);
62 data = reinterpret_cast<uintptr_t>(++iPtr);
67 IndexType (*mFunc)(uintptr_t&);
70 const char* QUAD("quad");
72 ///@brief Reads a blob from the given stream @a source into @a target, which must have
73 /// at least @a descriptor.length bytes.
74 bool ReadBlob(const MeshDefinition::Blob& descriptor, std::istream& source, uint8_t* target)
77 if(!source.seekg(descriptor.mOffset, std::istream::beg))
82 if(descriptor.IsConsecutive())
84 return !!source.read(reinterpret_cast<char*>(target), static_cast<std::streamsize>(static_cast<size_t>(descriptor.mLength)));
88 if(descriptor.mStride > descriptor.mElementSizeHint)
90 const uint32_t diff = descriptor.mStride - descriptor.mElementSizeHint;
91 uint32_t readSize = 0;
92 uint32_t totalSize = (descriptor.mLength / descriptor.mElementSizeHint) * descriptor.mStride;
93 while(readSize < totalSize &&
94 source.read(reinterpret_cast<char*>(target), descriptor.mElementSizeHint))
96 readSize += descriptor.mStride;
97 target += descriptor.mElementSizeHint;
98 source.seekg(diff, std::istream::cur);
100 return readSize == totalSize;
107 void ReadValues(const std::vector<uint8_t>& valuesBuffer, const std::vector<uint8_t>& indicesBuffer, uint8_t* target, uint32_t count, uint32_t elementSizeHint)
109 const T* const indicesPtr = reinterpret_cast<const T* const>(indicesBuffer.data());
110 for(uint32_t index = 0u; index < count; ++index)
112 uint32_t valuesIndex = indicesPtr[index] * elementSizeHint;
113 memcpy(target + valuesIndex, &valuesBuffer[index * elementSizeHint], elementSizeHint);
117 bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target, std::vector<uint32_t>* sparseIndices)
119 bool success = false;
121 if(accessor.mBlob.IsDefined())
123 success = ReadBlob(accessor.mBlob, source, target);
132 const MeshDefinition::Blob& indices = accessor.mSparse->mIndices;
133 const MeshDefinition::Blob& values = accessor.mSparse->mValues;
135 if(!indices.IsDefined() || !values.IsDefined())
140 const auto indicesBufferSize = indices.GetBufferSize();
141 std::vector<uint8_t> indicesBuffer(indicesBufferSize);
142 success = ReadBlob(indices, source, indicesBuffer.data());
148 const auto valuesBufferSize = values.GetBufferSize();
149 std::vector<uint8_t> valuesBuffer(valuesBufferSize);
150 success = ReadBlob(values, source, valuesBuffer.data());
156 // If non-null sparse indices vector, prepare it for output
159 sparseIndices->resize(accessor.mSparse->mCount);
162 switch(indices.mElementSizeHint)
166 ReadValues<uint8_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
169 // convert 8-bit indices into 32-bit
170 std::transform(indicesBuffer.begin(), indicesBuffer.end(), sparseIndices->begin(), [](const uint8_t& value) { return uint32_t(value); });
176 ReadValues<uint16_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
179 // convert 16-bit indices into 32-bit
180 std::transform(reinterpret_cast<uint16_t*>(indicesBuffer.data()),
181 reinterpret_cast<uint16_t*>(indicesBuffer.data()) + accessor.mSparse->mCount,
182 sparseIndices->begin(),
183 [](const uint16_t& value) {
184 return uint32_t(value);
191 ReadValues<uint32_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
194 std::copy(indicesBuffer.begin(), indicesBuffer.end(), reinterpret_cast<uint8_t*>(sparseIndices->data()));
200 DALI_ASSERT_DEBUG(!"Unsupported type for an index");
208 bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target)
210 return ReadAccessor(accessor, source, target, nullptr);
214 void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath)
216 constexpr auto sizeofBlobUnit = sizeof(T) * 4;
218 DALI_ASSERT_ALWAYS(((accessor.mBlob.mLength % sizeofBlobUnit == 0) ||
219 accessor.mBlob.mStride >= sizeofBlobUnit) &&
220 "Joints buffer length not a multiple of element size");
221 const auto inBufferSize = accessor.mBlob.GetBufferSize();
222 const auto outBufferSize = (sizeof(Vector4) / sizeofBlobUnit) * inBufferSize;
224 std::vector<uint8_t> buffer(outBufferSize);
225 auto inBuffer = buffer.data() + outBufferSize - inBufferSize;
226 if(!ReadAccessor(accessor, source, inBuffer))
228 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read joints from '" << meshPath << "'.";
231 if constexpr(sizeofBlobUnit != sizeof(Vector4))
233 auto floats = reinterpret_cast<float*>(buffer.data());
234 const auto end = inBuffer + inBufferSize;
235 while(inBuffer != end)
237 const auto value = *reinterpret_cast<T*>(inBuffer);
238 *floats = static_cast<float>(value);
240 inBuffer += sizeof(T);
244 raw.mAttribs.push_back({"aJoints", Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
248 void ReadWeightAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath)
250 constexpr auto sizeofBlobUnit = sizeof(T) * 4;
252 DALI_ASSERT_ALWAYS(((accessor.mBlob.mLength % sizeofBlobUnit == 0) ||
253 accessor.mBlob.mStride >= sizeofBlobUnit) &&
254 "weights buffer length not a multiple of element size");
255 const auto inBufferSize = accessor.mBlob.GetBufferSize();
256 const auto outBufferSize = (sizeof(Vector4) / sizeofBlobUnit) * inBufferSize;
258 std::vector<uint8_t> buffer(outBufferSize);
259 auto inBuffer = buffer.data() + outBufferSize - inBufferSize;
260 if(!ReadAccessor(accessor, source, inBuffer))
262 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read weights from '" << meshPath << "'.";
265 if constexpr(sizeofBlobUnit != sizeof(Vector4))
267 auto floats = reinterpret_cast<float*>(buffer.data());
268 const auto end = inBuffer + inBufferSize;
269 while(inBuffer != end)
271 const auto value = *reinterpret_cast<T*>(inBuffer);
272 // Normalize weight value. value /= 255 for uint8_t weight, and value /= 65535 for uint16_t weight.
273 *floats = static_cast<float>(value) / static_cast<float>((1 << (sizeof(T) * 8)) - 1);
275 inBuffer += sizeof(T);
279 raw.mAttribs.push_back({"aWeights", Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
282 template<bool use32BitsIndices, typename IndexProviderType = IndexProvider<use32BitsIndices>>
283 bool GenerateNormals(MeshDefinition::RawData& raw)
285 using IndexType = typename IndexProviderType::IndexType;
287 // mIndicies size must be even if we use 32bit indices.
288 if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
293 auto& attribs = raw.mAttribs;
294 DALI_ASSERT_DEBUG(attribs.size() > 0); // positions
296 IndexProviderType getIndex(raw.mIndices.data());
298 const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
300 auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
302 std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(Vector3));
303 auto normals = reinterpret_cast<Vector3*>(buffer.data());
305 for(uint32_t i = 0; i < numIndices; i += 3)
307 IndexType indices[]{getIndex(), getIndex(), getIndex()};
308 Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
310 Vector3 a = pos[1] - pos[0];
311 Vector3 b = pos[2] - pos[0];
313 Vector3 normal(a.Cross(b));
314 normals[indices[0]] += normal;
315 normals[indices[1]] += normal;
316 normals[indices[2]] += normal;
319 auto iEnd = normals + attribs[0].mNumElements;
320 while(normals != iEnd)
322 normals->Normalize();
326 attribs.push_back({"aNormal", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer)});
331 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>>
332 bool GenerateTangents(MeshDefinition::RawData& raw)
334 using IndexType = typename IndexProviderType::IndexType;
336 // mIndicies size must be even if we use 32bit indices.
337 if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
342 auto& attribs = raw.mAttribs;
343 // Required positions, normals, uvs (if we have). If not, skip generation
344 if(DALI_UNLIKELY(attribs.size() < (2 + static_cast<size_t>(hasUvs))))
349 std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(T));
350 auto tangents = reinterpret_cast<T*>(buffer.data());
354 IndexProviderType getIndex(raw.mIndices.data());
356 const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
358 auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
359 auto* uvs = reinterpret_cast<const Vector2*>(attribs[2].mData.data());
361 for(uint32_t i = 0; i < numIndices; i += 3)
363 IndexType indices[]{getIndex(), getIndex(), getIndex()};
364 Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
365 Vector2 uv[]{uvs[indices[0]], uvs[indices[1]], uvs[indices[2]]};
367 float x0 = pos[1].x - pos[0].x;
368 float y0 = pos[1].y - pos[0].y;
369 float z0 = pos[1].z - pos[0].z;
371 float x1 = pos[2].x - pos[0].x;
372 float y1 = pos[2].y - pos[0].y;
373 float z1 = pos[2].z - pos[0].z;
375 float s0 = uv[1].x - uv[0].x;
376 float t0 = uv[1].y - uv[0].y;
378 float s1 = uv[2].x - uv[0].x;
379 float t1 = uv[2].y - uv[0].y;
381 float det = (s0 * t1 - t0 * s1);
382 float r = 1.f / ((std::abs(det) < Dali::Epsilon<1000>::value) ? (Dali::Epsilon<1000>::value * (det > 0.0f ? 1.f : -1.f)) : det);
383 Vector3 tangent((x0 * t1 - t0 * x1) * r, (y0 * t1 - t0 * y1) * r, (z0 * t1 - t0 * z1) * r);
384 tangents[indices[0]] += T(tangent);
385 tangents[indices[1]] += T(tangent);
386 tangents[indices[2]] += T(tangent);
390 auto* normals = reinterpret_cast<const Vector3*>(attribs[1].mData.data());
391 auto iEnd = normals + attribs[1].mNumElements;
392 while(normals != iEnd)
397 // Calculated by indexs
398 tangentVec3 = Vector3((*tangents).x, (*tangents).y, (*tangents).z);
402 // Only choiced by normal vector. by indexs
403 Vector3 t[]{normals->Cross(Vector3::XAXIS), normals->Cross(Vector3::YAXIS)};
404 tangentVec3 = t[t[1].LengthSquared() > t[0].LengthSquared()];
407 tangentVec3 -= *normals * normals->Dot(tangentVec3);
408 tangentVec3.Normalize();
409 if constexpr(useVec3)
411 *tangents = tangentVec3;
415 *tangents = Vector4(tangentVec3.x, tangentVec3.y, tangentVec3.z, 1.0f);
421 attribs.push_back({"aTangent", useVec3 ? Property::VECTOR3 : Property::VECTOR4, attribs[0].mNumElements, std::move(buffer)});
426 void CalculateTextureSize(uint32_t totalTextureSize, uint32_t& textureWidth, uint32_t& textureHeight)
428 DALI_ASSERT_DEBUG(0u != totalTextureSize && "totalTextureSize is zero.")
430 // Calculate the dimensions of the texture.
431 // The total size of the texture is the length of the blend shapes blob.
436 if(0u == totalTextureSize)
442 const uint32_t pow2 = static_cast<uint32_t>(ceil(log2(totalTextureSize)));
443 const uint32_t powWidth = pow2 >> 1u;
444 const uint32_t powHeight = pow2 - powWidth;
446 textureWidth = 1u << powWidth;
447 textureHeight = 1u << powHeight;
451 float GetNormalizedScale()
453 return 1.0f / (std::numeric_limits<T>::max());
457 void DequantizeData(std::vector<uint8_t>& buffer, float* dequantizedValues, uint32_t numValues, bool normalized)
459 // see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data
461 T* values = reinterpret_cast<T*>(buffer.data());
463 for(uint32_t i = 0; i < numValues; ++i)
465 *dequantizedValues = normalized ? std::max((*values) * GetNormalizedScale<T>(), -1.0f) : *values;
472 void GetDequantizedData(std::vector<uint8_t>& buffer, uint32_t numComponents, uint32_t count, uint32_t flags, bool normalized)
474 bool dequantized = false;
476 std::vector<uint8_t> dequantizedBuffer(count * numComponents * sizeof(float));
477 float* dequantizedValues = reinterpret_cast<float*>(dequantizedBuffer.data());
479 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))
481 DequantizeData<int8_t>(buffer, dequantizedValues, numComponents * count, normalized);
484 else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD))
486 DequantizeData<uint8_t>(buffer, dequantizedValues, numComponents * count, normalized);
489 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))
491 DequantizeData<int16_t>(buffer, dequantizedValues, numComponents * count, normalized);
494 else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD))
496 DequantizeData<uint16_t>(buffer, dequantizedValues, numComponents * count, normalized);
502 buffer = std::move(dequantizedBuffer);
506 void GetDequantizedMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t flags)
510 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))
512 scale = GetNormalizedScale<int8_t>();
514 else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD))
516 scale = GetNormalizedScale<uint8_t>();
518 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))
520 scale = GetNormalizedScale<int16_t>();
522 else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD))
524 scale = GetNormalizedScale<uint16_t>();
529 for(float& value : min)
531 value = std::max(value * scale, -1.0f);
534 for(float& value : max)
536 value = std::min(value * scale, 1.0f);
541 void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::vector<MeshDefinition::BlendShape>& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor, BufferDefinition::Vector& buffers)
543 uint32_t geometryBufferIndex = 0u;
544 float maxDistanceSquared = 0.f;
545 Vector3* geometryBufferV3 = reinterpret_cast<Vector3*>(geometryBuffer);
546 for(auto& blendShape : blendShapes)
548 if(blendShape.deltas.IsDefined())
550 const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize();
553 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_POSITION))
555 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % (sizeof(uint8_t) * 3) == 0) ||
556 blendShape.deltas.mBlob.mStride >= (sizeof(uint8_t) * 3)) &&
557 "Blend Shape position buffer length not a multiple of element size");
558 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 3));
560 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_POSITION))
562 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % (sizeof(uint16_t) * 3) == 0) ||
563 blendShape.deltas.mBlob.mStride >= (sizeof(uint16_t) * 3)) &&
564 "Blend Shape position buffer length not a multiple of element size");
565 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 3));
569 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % sizeof(Vector3) == 0) ||
570 blendShape.deltas.mBlob.mStride >= sizeof(Vector3)) &&
571 "Blend Shape position buffer length not a multiple of element size");
572 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
575 std::vector<uint8_t> buffer(bufferSize);
576 std::vector<uint32_t> sparseIndices{};
578 if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
580 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::POSITIONS_MASK, blendShape.deltas.mNormalized);
582 if(blendShape.deltas.mNormalized)
584 GetDequantizedMinMax(blendShape.deltas.mBlob.mMin, blendShape.deltas.mBlob.mMax, blendShape.mFlags & MeshDefinition::POSITIONS_MASK);
587 blendShape.deltas.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
589 // Calculate the difference with the original mesh.
590 // Find the max distance to normalize the deltas.
591 const auto* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
593 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
594 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
595 delta = deltasBuffer[deltaIndex];
596 return std::max(maxDistanceSquared, delta.LengthSquared());
599 if(sparseIndices.empty())
601 for(uint32_t index = 0u; index < numberOfVertices; ++index)
603 maxDistanceSquared = ProcessVertex(geometryBufferIndex++, index);
608 // initialize blendshape texture
609 // TODO: there may be a case when sparse accessor uses a base buffer view for initial values.
610 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3::ZERO);
611 for(auto index : sparseIndices)
613 maxDistanceSquared = ProcessVertex(geometryBufferIndex + index, index);
615 geometryBufferIndex += numberOfVertices;
620 if(blendShape.normals.IsDefined())
622 const auto bufferSize = blendShape.normals.mBlob.GetBufferSize();
625 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_NORMAL))
627 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
628 blendShape.normals.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
629 "Blend Shape normals buffer length not a multiple of element size");
630 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
632 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_NORMAL))
634 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
635 blendShape.normals.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
636 "Blend Shape normals buffer length not a multiple of element size");
637 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
641 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % sizeof(Vector3) == 0) ||
642 blendShape.normals.mBlob.mStride >= sizeof(Vector3)) &&
643 "Blend Shape normals buffer length not a multiple of element size");
644 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
647 std::vector<uint8_t> buffer(bufferSize);
648 std::vector<uint32_t> sparseIndices;
650 if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
652 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::NORMALS_MASK, blendShape.normals.mNormalized);
654 if(blendShape.normals.mNormalized)
656 GetDequantizedMinMax(blendShape.normals.mBlob.mMin, blendShape.normals.mBlob.mMax, blendShape.mFlags & MeshDefinition::NORMALS_MASK);
659 blendShape.normals.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
661 // Calculate the difference with the original mesh, and translate to make all values positive.
662 const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
663 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
664 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
674 if(sparseIndices.empty())
676 for(uint32_t index = 0u; index < numberOfVertices; ++index)
678 ProcessVertex(geometryBufferIndex++, index);
683 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5));
684 for(auto index : sparseIndices)
686 ProcessVertex(geometryBufferIndex + index, index);
688 geometryBufferIndex += numberOfVertices;
693 if(blendShape.tangents.IsDefined())
695 const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize();
699 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_TANGENT))
701 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
702 blendShape.tangents.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
703 "Blend Shape tangents buffer length not a multiple of element size");
704 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
706 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_TANGENT))
708 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
709 blendShape.tangents.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
710 "Blend Shape tangents buffer length not a multiple of element size");
711 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
715 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % sizeof(Vector3) == 0) ||
716 blendShape.tangents.mBlob.mStride >= sizeof(Vector3)) &&
717 "Blend Shape tangents buffer length not a multiple of element size");
718 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
721 std::vector<uint8_t> buffer(bufferSize);
722 std::vector<uint32_t> sparseIndices;
724 if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
726 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::TANGENTS_MASK, blendShape.tangents.mNormalized);
728 if(blendShape.tangents.mNormalized)
730 GetDequantizedMinMax(blendShape.tangents.mBlob.mMin, blendShape.tangents.mBlob.mMax, blendShape.mFlags & MeshDefinition::TANGENTS_MASK);
733 blendShape.tangents.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
735 // Calculate the difference with the original mesh, and translate to make all values positive.
736 const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
737 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
738 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
748 if(sparseIndices.empty())
750 for(uint32_t index = 0u; index < numberOfVertices; ++index)
752 ProcessVertex(geometryBufferIndex++, index);
757 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5));
758 for(auto index : sparseIndices)
760 ProcessVertex(geometryBufferIndex + index, index);
762 geometryBufferIndex += numberOfVertices;
768 geometryBufferIndex = 0u;
770 const float maxDistance = sqrtf(maxDistanceSquared);
772 const float normalizeFactor = (maxDistanceSquared < Math::MACHINE_EPSILON_100) ? 1.f : (0.5f / maxDistance);
774 // Calculate and store the unnormalize factor.
775 blendShapeUnnormalizeFactor = maxDistance * 2.0f;
777 for(const auto& blendShape : blendShapes)
779 // Normalize all the deltas and translate to a possitive value.
780 // Deltas are going to be passed to the shader in a color texture
781 // whose values that are less than zero are clamped.
782 if(blendShape.deltas.IsDefined())
784 for(uint32_t index = 0u; index < numberOfVertices; ++index)
786 Vector3& delta = geometryBufferV3[geometryBufferIndex++];
787 delta.x = Clamp(((delta.x * normalizeFactor) + 0.5f), 0.f, 1.f);
788 delta.y = Clamp(((delta.y * normalizeFactor) + 0.5f), 0.f, 1.f);
789 delta.z = Clamp(((delta.z * normalizeFactor) + 0.5f), 0.f, 1.f);
793 if(blendShape.normals.IsDefined())
795 geometryBufferIndex += numberOfVertices;
798 if(blendShape.tangents.IsDefined())
800 geometryBufferIndex += numberOfVertices;
805 std::iostream& GetAvailableData(std::fstream& meshStream, const std::string& meshPath, BufferDefinition& buffer, std::string& availablePath)
807 auto& stream = (meshStream.is_open()) ? meshStream : buffer.GetBufferStream();
808 availablePath = (meshStream.is_open()) ? meshPath : buffer.GetUri();
814 MeshDefinition::SparseBlob::SparseBlob(const Blob& indices, const Blob& values, uint32_t count)
821 MeshDefinition::SparseBlob::SparseBlob(Blob&& indices, Blob&& values, uint32_t count)
822 : mIndices(std::move(indices)),
823 mValues(std::move(values)),
828 MeshDefinition::Accessor::Accessor(const MeshDefinition::Blob& blob,
829 const MeshDefinition::SparseBlob& sparse,
832 mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{sparse} : nullptr},
833 mBufferIdx(bufferIndex)
837 MeshDefinition::Accessor::Accessor(MeshDefinition::Blob&& blob,
838 MeshDefinition::SparseBlob&& sparse,
840 : mBlob{std::move(blob)},
841 mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{std::move(sparse)} : nullptr},
842 mBufferIdx(bufferIndex)
846 void MeshDefinition::Blob::ComputeMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t numComponents, uint32_t count, const float* values)
848 min.assign(numComponents, MAXFLOAT);
849 max.assign(numComponents, -MAXFLOAT);
850 for(uint32_t i = 0; i < count; ++i)
852 for(uint32_t j = 0; j < numComponents; ++j)
854 min[j] = std::min(min[j], *values);
855 max[j] = std::max(max[j], *values);
861 void MeshDefinition::Blob::ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max, uint32_t count, float* values, std::vector<uint32_t>* sparseIndices)
863 DALI_ASSERT_DEBUG(max.size() == min.size() || max.size() * min.size() == 0);
864 const auto numComponents = std::max(min.size(), max.size());
866 using ClampFn = void (*)(const float*, const float*, uint32_t, float&);
867 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); })
868 : (max.empty() ? [](const float* min, const float* max, uint32_t i, float& value) { value = std::max(min[i], value); }
869 : static_cast<ClampFn>([](const float* min, const float* max, uint32_t i, float& value) { value = std::min(std::max(min[i], value), max[i]); }));
876 auto end = values + count * numComponents;
879 auto nextElement = values + numComponents;
881 while(values != nextElement)
883 clampFn(min.data(), max.data(), i, *values);
890 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)
894 mElementSizeHint(elementSizeHint),
900 uint32_t MeshDefinition::Blob::GetBufferSize() const
905 void MeshDefinition::Blob::ComputeMinMax(uint32_t numComponents, uint32_t count, float* values)
907 ComputeMinMax(mMin, mMax, numComponents, count, values);
910 void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values, std::vector<uint32_t>* sparseIndices) const
912 ApplyMinMax(mMin, mMax, count, values, sparseIndices);
915 void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const
917 Property::Map attribMap;
918 attribMap[mName] = mType;
919 VertexBuffer attribBuffer = VertexBuffer::New(attribMap);
920 attribBuffer.SetData(mData.data(), mNumElements);
922 g.AddVertexBuffer(attribBuffer);
925 bool MeshDefinition::IsQuad() const
927 return CaseInsensitiveStringCompare(QUAD, mUri);
930 bool MeshDefinition::IsSkinned() const
932 return mJoints0.IsDefined() && mWeights0.IsDefined();
935 bool MeshDefinition::HasBlendShapes() const
937 return !mBlendShapes.empty();
940 void MeshDefinition::RequestNormals()
942 mNormals.mBlob.mLength = mPositions.mBlob.GetBufferSize();
945 void MeshDefinition::RequestTangents()
947 mTangents.mBlob.mLength = mNormals.mBlob.GetBufferSize();
950 MeshDefinition::RawData
951 MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& buffers)
959 std::string meshPath;
960 meshPath = modelsPath + mUri;
961 std::fstream fileStream;
964 fileStream.open(meshPath, std::ios::in | std::ios::binary);
965 if(!fileStream.is_open())
967 DALI_LOG_ERROR("Fail to open buffer from %s.\n", meshPath.c_str());
971 if(mIndices.IsDefined())
973 if(MaskMatch(mFlags, U32_INDICES))
975 DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(uint32_t) == 0) ||
976 mIndices.mBlob.mStride >= sizeof(uint32_t)) &&
977 "Index buffer length not a multiple of element size");
978 const auto indexCount = mIndices.mBlob.GetBufferSize() / sizeof(uint32_t);
979 raw.mIndices.resize(indexCount * 2); // NOTE: we need space for uint32_ts initially.
982 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path);
983 if(!ReadAccessor(mIndices, stream, reinterpret_cast<uint8_t*>(raw.mIndices.data())))
985 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
988 else if(MaskMatch(mFlags, U8_INDICES))
990 DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(uint8_t) == 0) ||
991 mIndices.mBlob.mStride >= sizeof(uint8_t)) &&
992 "Index buffer length not a multiple of element size");
993 const auto indexCount = mIndices.mBlob.GetBufferSize() / sizeof(uint8_t);
994 raw.mIndices.resize(indexCount); // NOTE: we need space for uint16_ts initially.
997 auto u8s = reinterpret_cast<uint8_t*>(raw.mIndices.data()) + indexCount;
998 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path);
999 if(!ReadAccessor(mIndices, stream, u8s))
1001 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
1004 auto u16s = raw.mIndices.data();
1005 auto end = u8s + indexCount;
1008 *u16s = static_cast<uint16_t>(*u8s);
1015 DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(unsigned short) == 0) ||
1016 mIndices.mBlob.mStride >= sizeof(unsigned short)) &&
1017 "Index buffer length not a multiple of element size");
1018 raw.mIndices.resize(mIndices.mBlob.mLength / sizeof(unsigned short));
1021 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path);
1022 if(!ReadAccessor(mIndices, stream, reinterpret_cast<uint8_t*>(raw.mIndices.data())))
1024 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
1029 uint32_t numberOfVertices = 0u;
1031 std::vector<Vector3> positions;
1032 if(mPositions.IsDefined())
1034 const auto bufferSize = mPositions.mBlob.GetBufferSize();
1035 uint32_t numVector3;
1037 if(MaskMatch(mFlags, S8_POSITION) || MaskMatch(mFlags, U8_POSITION))
1039 DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % (sizeof(uint8_t) * 3) == 0) ||
1040 mPositions.mBlob.mStride >= (sizeof(uint8_t) * 3)) &&
1041 "Position buffer length not a multiple of element size");
1042 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 3));
1044 else if(MaskMatch(mFlags, S16_POSITION) || MaskMatch(mFlags, U16_POSITION))
1046 DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % (sizeof(uint16_t) * 3) == 0) ||
1047 mPositions.mBlob.mStride >= (sizeof(uint16_t) * 3)) &&
1048 "Position buffer length not a multiple of element size");
1049 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 3));
1053 DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % sizeof(Vector3) == 0) ||
1054 mPositions.mBlob.mStride >= sizeof(Vector3)) &&
1055 "Position buffer length not a multiple of element size");
1056 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
1059 numberOfVertices = numVector3;
1061 std::vector<uint8_t> buffer(bufferSize);
1064 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mPositions.mBufferIdx], path);
1065 if(!ReadAccessor(mPositions, stream, buffer.data()))
1067 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << path << "'.";
1070 GetDequantizedData(buffer, 3u, numVector3, mFlags & POSITIONS_MASK, mPositions.mNormalized);
1072 if(mPositions.mNormalized)
1074 GetDequantizedMinMax(mPositions.mBlob.mMin, mPositions.mBlob.mMax, mFlags & POSITIONS_MASK);
1077 if(mPositions.mBlob.mMin.size() != 3u || mPositions.mBlob.mMax.size() != 3u)
1079 mPositions.mBlob.ComputeMinMax(3u, numVector3, reinterpret_cast<float*>(buffer.data()));
1083 mPositions.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
1086 if(HasBlendShapes())
1088 positions.resize(numVector3);
1089 std::copy(buffer.data(), buffer.data() + buffer.size(), reinterpret_cast<uint8_t*>(positions.data()));
1092 raw.mAttribs.push_back({"aPosition", Property::VECTOR3, numVector3, std::move(buffer)});
1095 const auto isTriangles = mPrimitiveType == Geometry::TRIANGLES;
1096 auto hasNormals = mNormals.IsDefined();
1099 const auto bufferSize = mNormals.mBlob.GetBufferSize();
1100 uint32_t numVector3;
1102 if(MaskMatch(mFlags, S8_NORMAL))
1104 DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
1105 mNormals.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
1106 "Normal buffer length not a multiple of element size");
1107 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
1109 else if(MaskMatch(mFlags, S16_NORMAL))
1111 DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
1112 mNormals.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
1113 "Normal buffer length not a multiple of element size");
1114 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
1118 DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % sizeof(Vector3) == 0) ||
1119 mNormals.mBlob.mStride >= sizeof(Vector3)) &&
1120 "Normal buffer length not a multiple of element size");
1121 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
1124 std::vector<uint8_t> buffer(bufferSize);
1127 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mNormals.mBufferIdx], path);
1128 if(!ReadAccessor(mNormals, stream, buffer.data()))
1130 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << path << "'.";
1133 GetDequantizedData(buffer, 3u, numVector3, mFlags & NORMALS_MASK, mNormals.mNormalized);
1135 if(mNormals.mNormalized)
1137 GetDequantizedMinMax(mNormals.mBlob.mMin, mNormals.mBlob.mMax, mFlags & NORMALS_MASK);
1140 mNormals.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
1142 raw.mAttribs.push_back({"aNormal", Property::VECTOR3, numVector3, std::move(buffer)});
1144 else if(mNormals.mBlob.mLength != 0 && isTriangles)
1146 DALI_ASSERT_DEBUG(mNormals.mBlob.mLength == mPositions.mBlob.GetBufferSize());
1147 static const std::function<bool(RawData&)> GenerateNormalsFunction[2] =
1149 GenerateNormals<false>,
1150 GenerateNormals<true>,
1152 const bool generateSuccessed = GenerateNormalsFunction[MaskMatch(mFlags, U32_INDICES)](raw);
1153 if(!generateSuccessed)
1155 DALI_LOG_ERROR("Failed to generate normal\n");
1163 const auto hasUvs = mTexCoords.IsDefined();
1166 const auto bufferSize = mTexCoords.mBlob.GetBufferSize();
1170 if(MaskMatch(mFlags, S8_TEXCOORD) || MaskMatch(mFlags, U8_TEXCOORD))
1172 DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % (sizeof(uint8_t) * 2) == 0) ||
1173 mTexCoords.mBlob.mStride >= (sizeof(uint8_t) * 2)) &&
1174 "TexCoords buffer length not a multiple of element size");
1175 uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 2));
1177 else if(MaskMatch(mFlags, S16_TEXCOORD) || MaskMatch(mFlags, U16_TEXCOORD))
1179 DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % (sizeof(uint16_t) * 2) == 0) ||
1180 mTexCoords.mBlob.mStride >= (sizeof(uint16_t) * 2)) &&
1181 "TexCoords buffer length not a multiple of element size");
1182 uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 2));
1186 DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
1187 mTexCoords.mBlob.mStride >= sizeof(Vector2)) &&
1188 "TexCoords buffer length not a multiple of element size");
1189 uvCount = static_cast<uint32_t>(bufferSize / sizeof(Vector2));
1192 std::vector<uint8_t> buffer(bufferSize);
1195 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mTexCoords.mBufferIdx], path);
1196 if(!ReadAccessor(mTexCoords, stream, buffer.data()))
1198 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << path << "'.";
1201 GetDequantizedData(buffer, 2u, uvCount, mFlags & TEXCOORDS_MASK, mTexCoords.mNormalized);
1203 if(MaskMatch(mFlags, FLIP_UVS_VERTICAL))
1205 auto uv = reinterpret_cast<Vector2*>(buffer.data());
1206 auto uvEnd = uv + uvCount;
1209 uv->y = 1.0f - uv->y;
1214 if(mTexCoords.mNormalized)
1216 GetDequantizedMinMax(mTexCoords.mBlob.mMin, mTexCoords.mBlob.mMax, mFlags & TEXCOORDS_MASK);
1219 mTexCoords.mBlob.ApplyMinMax(static_cast<uint32_t>(uvCount), reinterpret_cast<float*>(buffer.data()));
1221 raw.mAttribs.push_back({"aTexCoord", Property::VECTOR2, static_cast<uint32_t>(uvCount), std::move(buffer)});
1224 if(mTangents.IsDefined())
1226 const auto bufferSize = mTangents.mBlob.GetBufferSize();
1228 uint32_t propertySize = static_cast<uint32_t>((mTangentType == Property::VECTOR4) ? sizeof(Vector4) : sizeof(Vector3));
1229 uint32_t componentCount = static_cast<uint32_t>(propertySize / sizeof(float));
1231 uint32_t numTangents;
1233 if(MaskMatch(mFlags, S8_TANGENT))
1235 DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % (sizeof(int8_t) * componentCount) == 0) ||
1236 mTangents.mBlob.mStride >= (sizeof(int8_t) * componentCount)) &&
1237 "Tangents buffer length not a multiple of element size");
1238 numTangents = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * componentCount));
1240 else if(MaskMatch(mFlags, S16_TANGENT))
1242 DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % (sizeof(int16_t) * componentCount) == 0) ||
1243 mTangents.mBlob.mStride >= (sizeof(int16_t) * componentCount)) &&
1244 "Tangents buffer length not a multiple of element size");
1245 numTangents = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * componentCount));
1249 DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % propertySize == 0) ||
1250 mTangents.mBlob.mStride >= propertySize) &&
1251 "Tangents buffer length not a multiple of element size");
1252 numTangents = static_cast<uint32_t>(bufferSize / propertySize);
1255 std::vector<uint8_t> buffer(bufferSize);
1258 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mTangents.mBufferIdx], path);
1259 if(!ReadAccessor(mTangents, stream, buffer.data()))
1261 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << path << "'.";
1264 GetDequantizedData(buffer, componentCount, numTangents, mFlags & TANGENTS_MASK, mTangents.mNormalized);
1266 if(mTangents.mNormalized)
1268 GetDequantizedMinMax(mTangents.mBlob.mMin, mTangents.mBlob.mMax, mFlags & TANGENTS_MASK);
1271 mTangents.mBlob.ApplyMinMax(numTangents, reinterpret_cast<float*>(buffer.data()));
1273 raw.mAttribs.push_back({"aTangent", mTangentType, static_cast<uint32_t>(numTangents), std::move(buffer)});
1275 else if(mTangents.mBlob.mLength != 0 && hasNormals && isTriangles)
1277 DALI_ASSERT_DEBUG(mTangents.mBlob.mLength == mNormals.mBlob.GetBufferSize());
1278 static const std::function<bool(RawData&)> GenerateTangentsFunction[2][2][2] =
1282 GenerateTangents<false, false, false>,
1283 GenerateTangents<false, false, true>,
1286 GenerateTangents<false, true, false>,
1287 GenerateTangents<false, true, true>,
1292 GenerateTangents<true, false, false>,
1293 GenerateTangents<true, false, true>,
1296 GenerateTangents<true, true, false>,
1297 GenerateTangents<true, true, true>,
1300 const bool generateSuccessed = GenerateTangentsFunction[MaskMatch(mFlags, U32_INDICES)][mTangentType == Property::VECTOR3][hasUvs](raw);
1301 if(!generateSuccessed)
1303 DALI_LOG_ERROR("Failed to generate tangents\n");
1307 if(mColors.IsDefined())
1309 uint32_t propertySize = mColors.mBlob.mElementSizeHint;
1310 Property::Type propertyType = (propertySize == sizeof(Vector4)) ? Property::VECTOR4 : ((propertySize == sizeof(Vector3)) ? Property::VECTOR3 : Property::NONE);
1311 if(propertyType != Property::NONE)
1313 DALI_ASSERT_ALWAYS(((mColors.mBlob.mLength % propertySize == 0) ||
1314 mColors.mBlob.mStride >= propertySize) &&
1315 "Colors buffer length not a multiple of element size");
1316 const auto bufferSize = mColors.mBlob.GetBufferSize();
1317 std::vector<uint8_t> buffer(bufferSize);
1320 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mColors.mBufferIdx], path);
1321 if(!ReadAccessor(mColors, stream, buffer.data()))
1323 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read colors from '" << path << "'.";
1325 mColors.mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
1327 raw.mAttribs.push_back({"aVertexColor", propertyType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
1332 std::vector<uint8_t> buffer(raw.mAttribs[0].mNumElements * sizeof(Vector4));
1333 auto colors = reinterpret_cast<Vector4*>(buffer.data());
1335 for(uint32_t i = 0; i < raw.mAttribs[0].mNumElements; i++)
1337 colors[i] = Vector4::ONE;
1340 raw.mAttribs.push_back({"aVertexColor", Property::VECTOR4, raw.mAttribs[0].mNumElements, std::move(buffer)});
1345 std::string pathJoint;
1346 auto& streamJoint = GetAvailableData(fileStream, meshPath, buffers[mJoints0.mBufferIdx], pathJoint);
1347 if(MaskMatch(mFlags, U16_JOINT_IDS))
1349 ReadJointAccessor<uint16_t>(raw, mJoints0, streamJoint, pathJoint);
1351 else if(MaskMatch(mFlags, U8_JOINT_IDS))
1353 ReadJointAccessor<uint8_t>(raw, mJoints0, streamJoint, pathJoint);
1357 ReadJointAccessor<float>(raw, mJoints0, streamJoint, pathJoint);
1360 std::string pathWeight;
1361 auto& streamWeight = GetAvailableData(fileStream, meshPath, buffers[mWeights0.mBufferIdx], pathWeight);
1362 if(MaskMatch(mFlags, U16_WEIGHT))
1364 ReadWeightAccessor<uint16_t>(raw, mWeights0, streamWeight, pathWeight);
1366 else if(MaskMatch(mFlags, U8_WEIGHT))
1368 ReadWeightAccessor<uint8_t>(raw, mWeights0, streamWeight, pathWeight);
1372 ReadWeightAccessor<float>(raw, mWeights0, streamWeight, pathWeight);
1376 // Calculate the Blob for the blend shapes.
1377 Blob blendShapesBlob;
1378 blendShapesBlob.mOffset = std::numeric_limits<unsigned int>::max();
1379 blendShapesBlob.mLength = 0u;
1381 uint32_t totalTextureSize(0u);
1383 auto processAccessor = [&](const Accessor& accessor, uint32_t vector3Size) {
1384 if(accessor.IsDefined())
1386 blendShapesBlob.mOffset = std::min(blendShapesBlob.mOffset, accessor.mBlob.mOffset);
1387 blendShapesBlob.mLength += accessor.mBlob.mLength;
1389 totalTextureSize += accessor.mBlob.mLength / vector3Size;
1393 for(const auto& blendShape : mBlendShapes)
1395 const auto positionMask = blendShape.mFlags & POSITIONS_MASK;
1396 const auto normalMask = blendShape.mFlags & NORMALS_MASK;
1397 const auto tangentMask = blendShape.mFlags & TANGENTS_MASK;
1399 processAccessor(blendShape.deltas, MaskMatch(positionMask, S8_POSITION) ? sizeof(uint8_t) * 3 : (MaskMatch(positionMask, S16_POSITION) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1400 processAccessor(blendShape.normals, MaskMatch(normalMask, S8_NORMAL) ? sizeof(uint8_t) * 3 : (MaskMatch(normalMask, S16_NORMAL) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1401 processAccessor(blendShape.tangents, MaskMatch(tangentMask, S8_TANGENT) ? sizeof(uint8_t) * 3 : (MaskMatch(tangentMask, S16_TANGENT) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1404 if(HasBlendShapes())
1406 // Calculate the size of one buffer inside the texture.
1407 raw.mBlendShapeBufferOffset = numberOfVertices;
1409 bool calculateGltf2BlendShapes = false;
1410 uint32_t textureWidth = 0u;
1411 uint32_t textureHeight = 0u;
1413 if(!mBlendShapeHeader.IsDefined())
1415 CalculateTextureSize(totalTextureSize, textureWidth, textureHeight);
1416 calculateGltf2BlendShapes = true;
1420 uint16_t header[2u];
1421 ReadBlob(mBlendShapeHeader, fileStream, reinterpret_cast<uint8_t*>(header));
1422 textureWidth = header[0u];
1423 textureHeight = header[1u];
1426 const uint32_t numberOfBlendShapes = mBlendShapes.size();
1427 raw.mBlendShapeUnnormalizeFactor.Resize(numberOfBlendShapes);
1429 Devel::PixelBuffer geometryPixelBuffer = Devel::PixelBuffer::New(textureWidth, textureHeight, Pixel::RGB32F);
1430 uint8_t* geometryBuffer = geometryPixelBuffer.GetBuffer();
1432 if(calculateGltf2BlendShapes)
1434 CalculateGltf2BlendShapes(geometryBuffer, mBlendShapes, numberOfVertices, raw.mBlendShapeUnnormalizeFactor[0u], buffers);
1438 Blob unnormalizeFactorBlob;
1439 unnormalizeFactorBlob.mLength = static_cast<uint32_t>(sizeof(float) * ((BlendShapes::Version::VERSION_2_0 == mBlendShapeVersion) ? 1u : numberOfBlendShapes));
1441 if(blendShapesBlob.IsDefined())
1443 if(ReadBlob(blendShapesBlob, fileStream, geometryBuffer))
1445 unnormalizeFactorBlob.mOffset = blendShapesBlob.mOffset + blendShapesBlob.mLength;
1449 // Read the unnormalize factors.
1450 if(unnormalizeFactorBlob.IsDefined())
1452 ReadBlob(unnormalizeFactorBlob, fileStream, reinterpret_cast<uint8_t*>(&raw.mBlendShapeUnnormalizeFactor[0u]));
1455 raw.mBlendShapeData = Devel::PixelBuffer::Convert(geometryPixelBuffer);
1461 MeshGeometry MeshDefinition::Load(RawData&& raw) const
1463 MeshGeometry meshGeometry;
1464 meshGeometry.geometry = Geometry::New();
1465 meshGeometry.geometry.SetType(mPrimitiveType);
1467 if(IsQuad()) // TODO: do this in raw data; provide MakeTexturedQuadGeometry() that only creates buffers.
1469 auto options = MaskMatch(mFlags, FLIP_UVS_VERTICAL) ? TexturedQuadOptions::FLIP_VERTICAL : 0;
1470 meshGeometry.geometry = MakeTexturedQuadGeometry(options);
1474 if(!raw.mIndices.empty())
1476 if(MaskMatch(mFlags, U32_INDICES))
1478 // TODO : We can only store indeces as uint16_type. Send Dali::Geometry that we use it as uint32_t actual.
1479 meshGeometry.geometry.SetIndexBuffer(reinterpret_cast<const uint32_t*>(raw.mIndices.data()), raw.mIndices.size() / 2);
1483 meshGeometry.geometry.SetIndexBuffer(raw.mIndices.data(), raw.mIndices.size());
1487 for(auto& a : raw.mAttribs)
1489 a.AttachBuffer(meshGeometry.geometry);
1492 if(HasBlendShapes())
1494 meshGeometry.blendShapeBufferOffset = raw.mBlendShapeBufferOffset;
1495 meshGeometry.blendShapeUnnormalizeFactor = std::move(raw.mBlendShapeUnnormalizeFactor);
1497 meshGeometry.blendShapeGeometry = Texture::New(TextureType::TEXTURE_2D,
1498 raw.mBlendShapeData.GetPixelFormat(),
1499 raw.mBlendShapeData.GetWidth(),
1500 raw.mBlendShapeData.GetHeight());
1501 meshGeometry.blendShapeGeometry.Upload(raw.mBlendShapeData);
1505 return meshGeometry;
1508 void MeshDefinition::RetrieveBlendShapeComponents(bool& hasPositions, bool& hasNormals, bool& hasTangents) const
1510 for(const auto& blendShape : mBlendShapes)
1512 hasPositions = hasPositions || blendShape.deltas.IsDefined();
1513 hasNormals = hasNormals || blendShape.normals.IsDefined();
1514 hasTangents = hasTangents || blendShape.tangents.IsDefined();
1518 } // namespace Dali::Scene3D::Loader