Allow to load uint32_t as indices
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / mesh-definition.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // INTERNAL INCLUDES
19 #include "dali-scene3d/public-api/loader/mesh-definition.h"
20
21 // EXTERNAL INCLUDES
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>
26 #include <cstring>
27 #include <fstream>
28 #include <type_traits>
29
30 namespace Dali
31 {
32 namespace Scene3D
33 {
34 namespace Loader
35 {
36 namespace
37 {
38 template<bool use32BitIndices>
39 class IndexProvider
40 {
41 public:
42   using IndexType = typename std::conditional_t<use32BitIndices, uint32_t, uint16_t>;
43   IndexProvider(const uint16_t* indices)
44   : mData(reinterpret_cast<uintptr_t>(indices)),
45     mFunc(indices ? IncrementPointer : Increment)
46   {
47   }
48
49   IndexType operator()()
50   {
51     return mFunc(mData);
52   }
53
54 private:
55   static IndexType Increment(uintptr_t& data)
56   {
57     // mData was 'zero' at construct time. Just simply return counter start with 0.
58     return static_cast<IndexType>(data++);
59   }
60
61   static IndexType IncrementPointer(uintptr_t& data)
62   {
63     auto iPtr   = reinterpret_cast<const IndexType*>(data);
64     auto result = *iPtr;
65     data        = reinterpret_cast<uintptr_t>(++iPtr);
66     return result;
67   }
68
69   uintptr_t mData;
70   IndexType (*mFunc)(uintptr_t&);
71 };
72
73 const char* QUAD("quad");
74
75 ///@brief Reads a blob from the given stream @a source into @a target, which must have
76 /// at least @a descriptor.length bytes.
77 bool ReadBlob(const MeshDefinition::Blob& descriptor, std::istream& source, uint8_t* target)
78 {
79   source.clear();
80   if(!source.seekg(descriptor.mOffset, std::istream::beg))
81   {
82     return false;
83   }
84
85   if(descriptor.IsConsecutive())
86   {
87     return !!source.read(reinterpret_cast<char*>(target), static_cast<std::streamsize>(static_cast<size_t>(descriptor.mLength)));
88   }
89   else
90   {
91     if(descriptor.mStride > descriptor.mElementSizeHint)
92     {
93       const uint32_t diff      = descriptor.mStride - descriptor.mElementSizeHint;
94       uint32_t       readSize  = 0;
95       uint32_t       totalSize = (descriptor.mLength / descriptor.mElementSizeHint) * descriptor.mStride;
96       while(readSize < totalSize &&
97             source.read(reinterpret_cast<char*>(target), descriptor.mElementSizeHint) &&
98             source.seekg(diff, std::istream::cur))
99       {
100         readSize += descriptor.mStride;
101         target += descriptor.mElementSizeHint;
102       }
103       return readSize == totalSize;
104     }
105   }
106   return false;
107 }
108
109 template<typename T>
110 void ReadValues(const std::vector<uint8_t>& valuesBuffer, const std::vector<uint8_t>& indicesBuffer, uint8_t* target, uint32_t count, uint32_t elementSizeHint)
111 {
112   const T* const indicesPtr = reinterpret_cast<const T* const>(indicesBuffer.data());
113   for(uint32_t index = 0u; index < count; ++index)
114   {
115     uint32_t valuesIndex = indicesPtr[index] * elementSizeHint;
116     memcpy(target + valuesIndex, &valuesBuffer[index * elementSizeHint], elementSizeHint);
117   }
118 }
119
120 bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target)
121 {
122   bool success = false;
123
124   if(accessor.mBlob.IsDefined())
125   {
126     success = ReadBlob(accessor.mBlob, source, target);
127     if(!success)
128     {
129       return false;
130     }
131   }
132
133   if(accessor.mSparse)
134   {
135     const MeshDefinition::Blob& indices = accessor.mSparse->mIndices;
136     const MeshDefinition::Blob& values  = accessor.mSparse->mValues;
137
138     if(!indices.IsDefined() || !values.IsDefined())
139     {
140       return false;
141     }
142
143     const auto           indicesBufferSize = indices.GetBufferSize();
144     std::vector<uint8_t> indicesBuffer(indicesBufferSize);
145     success = ReadBlob(indices, source, indicesBuffer.data());
146     if(!success)
147     {
148       return false;
149     }
150
151     const auto           valuesBufferSize = values.GetBufferSize();
152     std::vector<uint8_t> valuesBuffer(valuesBufferSize);
153     success = ReadBlob(values, source, valuesBuffer.data());
154     if(!success)
155     {
156       return false;
157     }
158
159     switch(indices.mElementSizeHint)
160     {
161       case 1u:
162       {
163         ReadValues<uint8_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
164         break;
165       }
166       case 2u:
167       {
168         ReadValues<uint16_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
169         break;
170       }
171       case 4u:
172       {
173         ReadValues<uint32_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
174         break;
175       }
176       default:
177         DALI_ASSERT_DEBUG(!"Unsupported type for an index");
178     }
179   }
180
181   return success;
182 }
183
184 template<typename T>
185 void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath)
186 {
187   constexpr auto sizeofBlobUnit = sizeof(T) * 4;
188
189   DALI_ASSERT_ALWAYS(((accessor.mBlob.mLength % sizeofBlobUnit == 0) ||
190                       accessor.mBlob.mStride >= sizeofBlobUnit) &&
191                      "Joints buffer length not a multiple of element size");
192   const auto inBufferSize  = accessor.mBlob.GetBufferSize();
193   const auto outBufferSize = (sizeof(Vector4) / sizeofBlobUnit) * inBufferSize;
194
195   std::vector<uint8_t> buffer(outBufferSize);
196   auto                 inBuffer = buffer.data() + outBufferSize - inBufferSize;
197   if(!ReadAccessor(accessor, source, inBuffer))
198   {
199     ExceptionFlinger(ASSERT_LOCATION) << "Failed to read joints from '" << meshPath << "'.";
200   }
201
202   if constexpr(sizeofBlobUnit != sizeof(Vector4))
203   {
204     auto       floats = reinterpret_cast<float*>(buffer.data());
205     const auto end    = inBuffer + inBufferSize;
206     while(inBuffer != end)
207     {
208       const auto value = *reinterpret_cast<T*>(inBuffer);
209       *floats          = static_cast<float>(value);
210
211       inBuffer += sizeof(T);
212       ++floats;
213     }
214   }
215   raw.mAttribs.push_back({"aJoints", Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
216 }
217
218 template<bool use32BitsIndices, typename IndexProviderType = IndexProvider<use32BitsIndices>>
219 bool GenerateNormals(MeshDefinition::RawData& raw)
220 {
221   using IndexType = typename IndexProviderType::IndexType;
222
223   // mIndicies size must be even if we use 32bit indices.
224   if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
225   {
226     return false;
227   }
228
229   auto& attribs = raw.mAttribs;
230   DALI_ASSERT_DEBUG(attribs.size() > 0); // positions
231
232   IndexProviderType getIndex(raw.mIndices.data());
233
234   const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
235
236   auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
237
238   std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(Vector3));
239   auto                 normals = reinterpret_cast<Vector3*>(buffer.data());
240
241   for(uint32_t i = 0; i < numIndices; i += 3)
242   {
243     IndexType indices[]{getIndex(), getIndex(), getIndex()};
244     Vector3   pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
245
246     Vector3 a = pos[1] - pos[0];
247     Vector3 b = pos[2] - pos[0];
248
249     Vector3 normal(a.Cross(b));
250     normals[indices[0]] += normal;
251     normals[indices[1]] += normal;
252     normals[indices[2]] += normal;
253   }
254
255   auto iEnd = normals + attribs[0].mNumElements;
256   while(normals != iEnd)
257   {
258     normals->Normalize();
259     ++normals;
260   }
261
262   attribs.push_back({"aNormal", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer)});
263
264   return true;
265 }
266
267 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>>
268 bool GenerateTangents(MeshDefinition::RawData& raw)
269 {
270   using IndexType = typename IndexProviderType::IndexType;
271
272   // mIndicies size must be even if we use 32bit indices.
273   if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
274   {
275     return false;
276   }
277
278   auto& attribs = raw.mAttribs;
279   // Required positions, normals, uvs (if we have). If not, skip generation
280   if(DALI_UNLIKELY(attribs.size() < (2 + static_cast<size_t>(hasUvs))))
281   {
282     return false;
283   }
284
285   std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(T));
286   auto                 tangents = reinterpret_cast<T*>(buffer.data());
287
288   if constexpr(hasUvs)
289   {
290     IndexProviderType getIndex(raw.mIndices.data());
291
292     const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
293
294     auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
295     auto* uvs       = reinterpret_cast<const Vector2*>(attribs[2].mData.data());
296
297     for(uint32_t i = 0; i < numIndices; i += 3)
298     {
299       IndexType indices[]{getIndex(), getIndex(), getIndex()};
300       Vector3   pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
301       Vector2   uv[]{uvs[indices[0]], uvs[indices[1]], uvs[indices[2]]};
302
303       float x0 = pos[1].x - pos[0].x;
304       float y0 = pos[1].y - pos[0].y;
305       float z0 = pos[1].z - pos[0].z;
306
307       float x1 = pos[2].x - pos[0].x;
308       float y1 = pos[2].y - pos[0].y;
309       float z1 = pos[2].z - pos[0].z;
310
311       float s0 = uv[1].x - uv[0].x;
312       float t0 = uv[1].y - uv[0].y;
313
314       float s1 = uv[2].x - uv[0].x;
315       float t1 = uv[2].y - uv[0].y;
316
317       float   det = (s0 * t1 - t0 * s1);
318       float   r   = 1.f / ((std::abs(det) < Dali::Epsilon<1000>::value) ? (Dali::Epsilon<1000>::value * (det > 0.0f ? 1.f : -1.f)) : det);
319       Vector3 tangent((x0 * t1 - t0 * x1) * r, (y0 * t1 - t0 * y1) * r, (z0 * t1 - t0 * z1) * r);
320       tangents[indices[0]] += T(tangent);
321       tangents[indices[1]] += T(tangent);
322       tangents[indices[2]] += T(tangent);
323     }
324   }
325
326   auto* normals = reinterpret_cast<const Vector3*>(attribs[1].mData.data());
327   auto  iEnd    = normals + attribs[1].mNumElements;
328   while(normals != iEnd)
329   {
330     Vector3 tangentVec3;
331     if constexpr(hasUvs)
332     {
333       // Calculated by indexs
334       tangentVec3 = Vector3((*tangents).x, (*tangents).y, (*tangents).z);
335     }
336     else
337     {
338       // Only choiced by normal vector. by indexs
339       Vector3 t[]{normals->Cross(Vector3::XAXIS), normals->Cross(Vector3::YAXIS)};
340       tangentVec3 = t[t[1].LengthSquared() > t[0].LengthSquared()];
341     }
342
343     tangentVec3 -= *normals * normals->Dot(tangentVec3);
344     tangentVec3.Normalize();
345     if constexpr(useVec3)
346     {
347       *tangents = tangentVec3;
348     }
349     else
350     {
351       *tangents = Vector4(tangentVec3.x, tangentVec3.y, tangentVec3.z, 1.0f);
352     }
353
354     ++tangents;
355     ++normals;
356   }
357   attribs.push_back({"aTangent", useVec3 ? Property::VECTOR3 : Property::VECTOR4, attribs[0].mNumElements, std::move(buffer)});
358
359   return true;
360 }
361
362 void CalculateTextureSize(uint32_t totalTextureSize, uint32_t& textureWidth, uint32_t& textureHeight)
363 {
364   DALI_ASSERT_DEBUG(0u != totalTextureSize && "totalTextureSize is zero.")
365
366   // Calculate the dimensions of the texture.
367   // The total size of the texture is the length of the blend shapes blob.
368
369   textureWidth  = 0u;
370   textureHeight = 0u;
371
372   if(0u == totalTextureSize)
373   {
374     // nothing to do.
375     return;
376   }
377
378   const uint32_t pow2      = static_cast<uint32_t>(ceil(log2(totalTextureSize)));
379   const uint32_t powWidth  = pow2 >> 1u;
380   const uint32_t powHeight = pow2 - powWidth;
381
382   textureWidth  = 1u << powWidth;
383   textureHeight = 1u << powHeight;
384 }
385
386 void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector<MeshDefinition::BlendShape>& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor, BufferDefinition::Vector& buffers)
387 {
388   uint32_t geometryBufferIndex = 0u;
389   float    maxDistance         = 0.f;
390   Vector3* geometryBufferV3    = reinterpret_cast<Vector3*>(geometryBuffer);
391   for(const auto& blendShape : blendShapes)
392   {
393     if(blendShape.deltas.IsDefined())
394     {
395       DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % sizeof(Vector3) == 0u) ||
396                           blendShape.deltas.mBlob.mStride >= sizeof(Vector3)) &&
397                          "Blend Shape position buffer length not a multiple of element size");
398
399       const auto           bufferSize = blendShape.deltas.mBlob.GetBufferSize();
400       std::vector<uint8_t> buffer(bufferSize);
401       if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data()))
402       {
403         blendShape.deltas.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()));
404         // Calculate the difference with the original mesh.
405         // Find the max distance to normalize the deltas.
406         const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
407
408         for(uint32_t index = 0u; index < numberOfVertices; ++index)
409         {
410           Vector3& delta = geometryBufferV3[geometryBufferIndex++];
411           delta          = deltasBuffer[index];
412
413           maxDistance = std::max(maxDistance, delta.LengthSquared());
414         }
415       }
416     }
417
418     if(blendShape.normals.IsDefined())
419     {
420       DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % sizeof(Vector3) == 0u) ||
421                           blendShape.normals.mBlob.mStride >= sizeof(Vector3)) &&
422                          "Blend Shape normals buffer length not a multiple of element size");
423
424       const auto           bufferSize = blendShape.normals.mBlob.GetBufferSize();
425       std::vector<uint8_t> buffer(bufferSize);
426       if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data()))
427       {
428         blendShape.normals.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()));
429
430         // Calculate the difference with the original mesh, and translate to make all values positive.
431         const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
432
433         for(uint32_t index = 0u; index < numberOfVertices; ++index)
434         {
435           Vector3& delta = geometryBufferV3[geometryBufferIndex++];
436           delta          = deltasBuffer[index];
437
438           delta.x *= 0.5f;
439           delta.y *= 0.5f;
440           delta.z *= 0.5f;
441
442           delta.x += 0.5f;
443           delta.y += 0.5f;
444           delta.z += 0.5f;
445         }
446       }
447     }
448
449     if(blendShape.tangents.IsDefined())
450     {
451       DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % sizeof(Vector3) == 0u) ||
452                           blendShape.tangents.mBlob.mStride >= sizeof(Vector3)) &&
453                          "Blend Shape tangents buffer length not a multiple of element size");
454
455       const auto           bufferSize = blendShape.tangents.mBlob.GetBufferSize();
456       std::vector<uint8_t> buffer(bufferSize);
457       if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data()))
458       {
459         blendShape.tangents.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()));
460
461         // Calculate the difference with the original mesh, and translate to make all values positive.
462         const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
463
464         for(uint32_t index = 0u; index < numberOfVertices; ++index)
465         {
466           Vector3& delta = geometryBufferV3[geometryBufferIndex++];
467           delta          = deltasBuffer[index];
468
469           delta.x *= 0.5f;
470           delta.y *= 0.5f;
471           delta.z *= 0.5f;
472
473           delta.x += 0.5f;
474           delta.y += 0.5f;
475           delta.z += 0.5f;
476         }
477       }
478     }
479   }
480
481   geometryBufferIndex = 0u;
482   for(const auto& blendShape : blendShapes)
483   {
484     // Normalize all the deltas and translate to a possitive value.
485     // Deltas are going to be passed to the shader in a color texture
486     // whose values that are less than zero are clamped.
487     if(blendShape.deltas.IsDefined())
488     {
489       const float normalizeFactor = (fabsf(maxDistance) < Math::MACHINE_EPSILON_1000) ? 1.f : (0.5f / sqrtf(maxDistance));
490
491       for(uint32_t index = 0u; index < numberOfVertices; ++index)
492       {
493         Vector3& delta = geometryBufferV3[geometryBufferIndex++];
494         delta.x        = Clamp(((delta.x * normalizeFactor) + 0.5f), 0.f, 1.f);
495         delta.y        = Clamp(((delta.y * normalizeFactor) + 0.5f), 0.f, 1.f);
496         delta.z        = Clamp(((delta.z * normalizeFactor) + 0.5f), 0.f, 1.f);
497       }
498
499       // Calculate and store the unnormalize factor.
500       blendShapeUnnormalizeFactor = 1.f / normalizeFactor;
501     }
502
503     if(blendShape.normals.IsDefined())
504     {
505       geometryBufferIndex += numberOfVertices;
506     }
507
508     if(blendShape.tangents.IsDefined())
509     {
510       geometryBufferIndex += numberOfVertices;
511     }
512   }
513 }
514
515 std::iostream& GetAvailableData(std::fstream& meshStream, const std::string& meshPath, BufferDefinition& buffer, std::string& availablePath)
516 {
517   auto& stream  = (meshStream.is_open()) ? meshStream : buffer.GetBufferStream();
518   availablePath = (meshStream.is_open()) ? meshPath : buffer.GetUri();
519   return stream;
520 }
521
522 } // namespace
523
524 MeshDefinition::SparseBlob::SparseBlob(const Blob& indices, const Blob& values, uint32_t count)
525 : mIndices{indices},
526   mValues{values},
527   mCount{count}
528 {
529 }
530
531 MeshDefinition::SparseBlob::SparseBlob(Blob&& indices, Blob&& values, uint32_t count)
532 : mIndices(std::move(indices)),
533   mValues(std::move(values)),
534   mCount{count}
535 {
536 }
537
538 MeshDefinition::Accessor::Accessor(const MeshDefinition::Blob&       blob,
539                                    const MeshDefinition::SparseBlob& sparse,
540                                    Index                             bufferIndex)
541 : mBlob{blob},
542   mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{sparse} : nullptr},
543   mBufferIdx(bufferIndex)
544 {
545 }
546
547 MeshDefinition::Accessor::Accessor(MeshDefinition::Blob&&       blob,
548                                    MeshDefinition::SparseBlob&& sparse,
549                                    Index                        bufferIndex)
550 : mBlob{std::move(blob)},
551   mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{std::move(sparse)} : nullptr},
552   mBufferIdx(bufferIndex)
553 {
554 }
555
556 void MeshDefinition::Blob::ComputeMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t numComponents, uint32_t count, const float* values)
557 {
558   min.assign(numComponents, MAXFLOAT);
559   max.assign(numComponents, -MAXFLOAT);
560   for(uint32_t i = 0; i < count; ++i)
561   {
562     for(uint32_t j = 0; j < numComponents; ++j)
563     {
564       min[j] = std::min(min[j], *values);
565       max[j] = std::max(max[j], *values);
566       values++;
567     }
568   }
569 }
570
571 void MeshDefinition::Blob::ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max, uint32_t count, float* values)
572 {
573   DALI_ASSERT_DEBUG(max.size() == min.size() || max.size() * min.size() == 0);
574   const auto numComponents = std::max(min.size(), max.size());
575
576   using ClampFn   = void (*)(const float*, const float*, uint32_t, float&);
577   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); })
578                                 : (max.empty() ? [](const float* min, const float* max, uint32_t i, float& value) { value = std::max(min[i], value); }
579                                                : static_cast<ClampFn>([](const float* min, const float* max, uint32_t i, float& value) { value = std::min(std::max(min[i], value), max[i]); }));
580
581   if(!clampFn)
582   {
583     return;
584   }
585
586   auto end = values + count * numComponents;
587   while(values != end)
588   {
589     auto     nextElement = values + numComponents;
590     uint32_t i           = 0;
591     while(values != nextElement)
592     {
593       clampFn(min.data(), max.data(), i, *values);
594       ++values;
595       ++i;
596     }
597   }
598 }
599
600 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)
601 : mOffset(offset),
602   mLength(length),
603   mStride(stride),
604   mElementSizeHint(elementSizeHint),
605   mMin(min),
606   mMax(max)
607 {
608 }
609
610 uint32_t MeshDefinition::Blob::GetBufferSize() const
611 {
612   return mLength;
613 }
614
615 void MeshDefinition::Blob::ComputeMinMax(uint32_t numComponents, uint32_t count, float* values)
616 {
617   ComputeMinMax(mMin, mMax, numComponents, count, values);
618 }
619
620 void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values) const
621 {
622   ApplyMinMax(mMin, mMax, count, values);
623 }
624
625 void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const
626 {
627   Property::Map attribMap;
628   attribMap[mName]          = mType;
629   VertexBuffer attribBuffer = VertexBuffer::New(attribMap);
630   attribBuffer.SetData(mData.data(), mNumElements);
631
632   g.AddVertexBuffer(attribBuffer);
633 }
634
635 bool MeshDefinition::IsQuad() const
636 {
637   return CaseInsensitiveStringCompare(QUAD, mUri);
638 }
639
640 bool MeshDefinition::IsSkinned() const
641 {
642   return mJoints0.IsDefined() && mWeights0.IsDefined();
643 }
644
645 bool MeshDefinition::HasBlendShapes() const
646 {
647   return !mBlendShapes.empty();
648 }
649
650 void MeshDefinition::RequestNormals()
651 {
652   mNormals.mBlob.mLength = mPositions.mBlob.GetBufferSize();
653 }
654
655 void MeshDefinition::RequestTangents()
656 {
657   mTangents.mBlob.mLength = mNormals.mBlob.GetBufferSize();
658 }
659
660 MeshDefinition::RawData
661 MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& buffers)
662 {
663   RawData raw;
664   if(IsQuad())
665   {
666     return raw;
667   }
668
669   std::string meshPath;
670   meshPath = modelsPath + mUri;
671   std::fstream fileStream;
672   if(!mUri.empty())
673   {
674     fileStream.open(meshPath, std::ios::in | std::ios::binary);
675     if(!fileStream.is_open())
676     {
677       DALI_LOG_ERROR("Fail to open buffer from %s.\n", meshPath.c_str());
678     }
679   }
680
681   if(mIndices.IsDefined())
682   {
683     if(MaskMatch(mFlags, U32_INDICES))
684     {
685       DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(uint32_t) == 0) ||
686                           mIndices.mBlob.mStride >= sizeof(uint32_t)) &&
687                          "Index buffer length not a multiple of element size");
688       const auto indexCount = mIndices.mBlob.GetBufferSize() / sizeof(uint32_t);
689       raw.mIndices.resize(indexCount * 2); // NOTE: we need space for uint32_ts initially.
690
691       std::string path;
692       auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path);
693       if(!ReadAccessor(mIndices, stream, reinterpret_cast<uint8_t*>(raw.mIndices.data())))
694       {
695         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
696       }
697     }
698     else if(MaskMatch(mFlags, U8_INDICES))
699     {
700       DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(uint8_t) == 0) ||
701                           mIndices.mBlob.mStride >= sizeof(uint8_t)) &&
702                          "Index buffer length not a multiple of element size");
703       const auto indexCount = mIndices.mBlob.GetBufferSize() / sizeof(uint8_t);
704       raw.mIndices.resize(indexCount); // NOTE: we need space for uint16_ts initially.
705
706       std::string path;
707       auto        u8s    = reinterpret_cast<uint8_t*>(raw.mIndices.data()) + indexCount;
708       auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path);
709       if(!ReadAccessor(mIndices, stream, u8s))
710       {
711         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
712       }
713
714       auto u16s = raw.mIndices.data();
715       auto end  = u8s + indexCount;
716       while(u8s != end)
717       {
718         *u16s = static_cast<uint16_t>(*u8s);
719         ++u16s;
720         ++u8s;
721       }
722     }
723     else
724     {
725       DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(unsigned short) == 0) ||
726                           mIndices.mBlob.mStride >= sizeof(unsigned short)) &&
727                          "Index buffer length not a multiple of element size");
728       raw.mIndices.resize(mIndices.mBlob.mLength / sizeof(unsigned short));
729
730       std::string path;
731       auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path);
732       if(!ReadAccessor(mIndices, stream, reinterpret_cast<uint8_t*>(raw.mIndices.data())))
733       {
734         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
735       }
736     }
737   }
738
739   std::vector<Vector3> positions;
740   if(mPositions.IsDefined())
741   {
742     DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % sizeof(Vector3) == 0) ||
743                         mPositions.mBlob.mStride >= sizeof(Vector3)) &&
744                        "Position buffer length not a multiple of element size");
745     const auto           bufferSize = mPositions.mBlob.GetBufferSize();
746     std::vector<uint8_t> buffer(bufferSize);
747
748     std::string path;
749     auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mPositions.mBufferIdx], path);
750     if(!ReadAccessor(mPositions, stream, buffer.data()))
751     {
752       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << path << "'.";
753     }
754
755     uint32_t numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
756     if(mPositions.mBlob.mMin.size() != 3u || mPositions.mBlob.mMax.size() != 3u)
757     {
758       mPositions.mBlob.ComputeMinMax(3u, numVector3, reinterpret_cast<float*>(buffer.data()));
759     }
760     else
761     {
762       mPositions.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
763     }
764
765     if(HasBlendShapes())
766     {
767       positions.resize(numVector3);
768       std::copy(buffer.data(), buffer.data() + buffer.size(), reinterpret_cast<uint8_t*>(positions.data()));
769     }
770
771     raw.mAttribs.push_back({"aPosition", Property::VECTOR3, numVector3, std::move(buffer)});
772   }
773
774   const auto isTriangles = mPrimitiveType == Geometry::TRIANGLES;
775   auto       hasNormals  = mNormals.IsDefined();
776   if(hasNormals)
777   {
778     DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % sizeof(Vector3) == 0) ||
779                         mNormals.mBlob.mStride >= sizeof(Vector3)) &&
780                        "Normal buffer length not a multiple of element size");
781     const auto           bufferSize = mNormals.mBlob.GetBufferSize();
782     std::vector<uint8_t> buffer(bufferSize);
783
784     std::string path;
785     auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mNormals.mBufferIdx], path);
786     if(!ReadAccessor(mNormals, stream, buffer.data()))
787     {
788       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << path << "'.";
789     }
790
791     mNormals.mBlob.ApplyMinMax(static_cast<uint32_t>(bufferSize / sizeof(Vector3)), reinterpret_cast<float*>(buffer.data()));
792
793     raw.mAttribs.push_back({"aNormal", Property::VECTOR3, static_cast<uint32_t>(bufferSize / sizeof(Vector3)), std::move(buffer)});
794   }
795   else if(mNormals.mBlob.mLength != 0 && isTriangles)
796   {
797     DALI_ASSERT_DEBUG(mNormals.mBlob.mLength == mPositions.mBlob.GetBufferSize());
798     static const std::function<bool(RawData&)> GenerateNormalsFunction[2] =
799       {
800         GenerateNormals<false>,
801         GenerateNormals<true>,
802       };
803     const bool generateSuccessed = GenerateNormalsFunction[MaskMatch(mFlags, U32_INDICES)](raw);
804     if(!generateSuccessed)
805     {
806       DALI_LOG_ERROR("Failed to generate normal\n");
807     }
808     else
809     {
810       hasNormals = true;
811     }
812   }
813
814   const auto hasUvs = mTexCoords.IsDefined();
815   if(hasUvs)
816   {
817     DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
818                         mTexCoords.mBlob.mStride >= sizeof(Vector2)) &&
819                        "Normal buffer length not a multiple of element size");
820     const auto           bufferSize = mTexCoords.mBlob.GetBufferSize();
821     std::vector<uint8_t> buffer(bufferSize);
822
823     std::string path;
824     auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mTexCoords.mBufferIdx], path);
825     if(!ReadAccessor(mTexCoords, stream, buffer.data()))
826     {
827       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << path << "'.";
828     }
829
830     const auto uvCount = bufferSize / sizeof(Vector2);
831     if(MaskMatch(mFlags, FLIP_UVS_VERTICAL))
832     {
833       auto uv    = reinterpret_cast<Vector2*>(buffer.data());
834       auto uvEnd = uv + uvCount;
835       while(uv != uvEnd)
836       {
837         uv->y = 1.0f - uv->y;
838         ++uv;
839       }
840     }
841
842     mTexCoords.mBlob.ApplyMinMax(static_cast<uint32_t>(uvCount), reinterpret_cast<float*>(buffer.data()));
843
844     raw.mAttribs.push_back({"aTexCoord", Property::VECTOR2, static_cast<uint32_t>(uvCount), std::move(buffer)});
845   }
846
847   if(mTangents.IsDefined())
848   {
849     uint32_t propertySize = static_cast<uint32_t>((mTangentType == Property::VECTOR4) ? sizeof(Vector4) : sizeof(Vector3));
850     DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % propertySize == 0) ||
851                         mTangents.mBlob.mStride >= propertySize) &&
852                        "Tangents buffer length not a multiple of element size");
853     const auto           bufferSize = mTangents.mBlob.GetBufferSize();
854     std::vector<uint8_t> buffer(bufferSize);
855
856     std::string path;
857     auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mTangents.mBufferIdx], path);
858     if(!ReadAccessor(mTangents, stream, buffer.data()))
859     {
860       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << path << "'.";
861     }
862     mTangents.mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
863
864     raw.mAttribs.push_back({"aTangent", mTangentType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
865   }
866   else if(mTangents.mBlob.mLength != 0 && hasNormals && isTriangles)
867   {
868     DALI_ASSERT_DEBUG(mTangents.mBlob.mLength == mNormals.mBlob.GetBufferSize());
869     static const std::function<bool(RawData&)> GenerateTangentsFunction[2][2][2] =
870       {
871         {
872           {
873             GenerateTangents<false, false, false>,
874             GenerateTangents<false, false, true>,
875           },
876           {
877             GenerateTangents<false, true, false>,
878             GenerateTangents<false, true, true>,
879           },
880         },
881         {
882           {
883             GenerateTangents<true, false, false>,
884             GenerateTangents<true, false, true>,
885           },
886           {
887             GenerateTangents<true, true, false>,
888             GenerateTangents<true, true, true>,
889           },
890         }};
891     const bool generateSuccessed = GenerateTangentsFunction[MaskMatch(mFlags, U32_INDICES)][mTangentType == Property::VECTOR3][hasUvs](raw);
892     if(!generateSuccessed)
893     {
894       DALI_LOG_ERROR("Failed to generate tangents\n");
895     }
896   }
897
898   if(mColors.IsDefined())
899   {
900     uint32_t       propertySize = mColors.mBlob.mElementSizeHint;
901     Property::Type propertyType = (propertySize == sizeof(Vector4)) ? Property::VECTOR4 : ((propertySize == sizeof(Vector3)) ? Property::VECTOR3 : Property::NONE);
902     if(propertyType != Property::NONE)
903     {
904       DALI_ASSERT_ALWAYS(((mColors.mBlob.mLength % propertySize == 0) ||
905                           mColors.mBlob.mStride >= propertySize) &&
906                          "Colors buffer length not a multiple of element size");
907       const auto           bufferSize = mColors.mBlob.GetBufferSize();
908       std::vector<uint8_t> buffer(bufferSize);
909
910       std::string path;
911       auto&       stream = GetAvailableData(fileStream, meshPath, buffers[mColors.mBufferIdx], path);
912       if(!ReadAccessor(mColors, stream, buffer.data()))
913       {
914         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read colors from '" << path << "'.";
915       }
916       mColors.mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
917
918       raw.mAttribs.push_back({"aVertexColor", propertyType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
919     }
920   }
921
922   if(IsSkinned())
923   {
924     std::string pathJoint;
925     auto&       streamJoint = GetAvailableData(fileStream, meshPath, buffers[mJoints0.mBufferIdx], pathJoint);
926     if(MaskMatch(mFlags, U16_JOINT_IDS))
927     {
928       ReadJointAccessor<uint16_t>(raw, mJoints0, streamJoint, pathJoint);
929     }
930     else if(MaskMatch(mFlags, U8_JOINT_IDS))
931     {
932       ReadJointAccessor<uint8_t>(raw, mJoints0, streamJoint, pathJoint);
933     }
934     else
935     {
936       ReadJointAccessor<float>(raw, mJoints0, streamJoint, pathJoint);
937     }
938
939     DALI_ASSERT_ALWAYS(((mWeights0.mBlob.mLength % sizeof(Vector4) == 0) ||
940                         mWeights0.mBlob.mStride >= sizeof(Vector4)) &&
941                        "Weights buffer length not a multiple of element size");
942     const auto           bufferSize = mWeights0.mBlob.GetBufferSize();
943     std::vector<uint8_t> buffer(bufferSize);
944
945     std::string pathWeight;
946     auto&       streamWeight = GetAvailableData(fileStream, meshPath, buffers[mWeights0.mBufferIdx], pathWeight);
947     if(!ReadAccessor(mWeights0, streamWeight, buffer.data()))
948     {
949       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read weights from '" << pathWeight << "'.";
950     }
951
952     raw.mAttribs.push_back({"aWeights", Property::VECTOR4, static_cast<uint32_t>(bufferSize / sizeof(Vector4)), std::move(buffer)});
953   }
954
955   // Calculate the Blob for the blend shapes.
956   Blob blendShapesBlob;
957   blendShapesBlob.mOffset = std::numeric_limits<unsigned int>::max();
958   blendShapesBlob.mLength = 0u;
959
960   for(const auto& blendShape : mBlendShapes)
961   {
962     for(auto i : {&blendShape.deltas, &blendShape.normals, &blendShape.tangents})
963     {
964       if(i->IsDefined())
965       {
966         blendShapesBlob.mOffset = std::min(blendShapesBlob.mOffset, i->mBlob.mOffset);
967         blendShapesBlob.mLength += i->mBlob.mLength;
968       }
969     }
970   }
971
972   if(HasBlendShapes())
973   {
974     const uint32_t numberOfVertices = static_cast<uint32_t>(mPositions.mBlob.mLength / sizeof(Vector3));
975
976     // Calculate the size of one buffer inside the texture.
977     raw.mBlendShapeBufferOffset = numberOfVertices;
978
979     bool     calculateGltf2BlendShapes = false;
980     uint32_t textureWidth              = 0u;
981     uint32_t textureHeight             = 0u;
982
983     if(!mBlendShapeHeader.IsDefined())
984     {
985       CalculateTextureSize(static_cast<uint32_t>(blendShapesBlob.mLength / sizeof(Vector3)), textureWidth, textureHeight);
986       calculateGltf2BlendShapes = true;
987     }
988     else
989     {
990       uint16_t header[2u];
991       ReadBlob(mBlendShapeHeader, fileStream, reinterpret_cast<uint8_t*>(header));
992       textureWidth  = header[0u];
993       textureHeight = header[1u];
994     }
995
996     const uint32_t numberOfBlendShapes = mBlendShapes.size();
997     raw.mBlendShapeUnnormalizeFactor.Resize(numberOfBlendShapes);
998
999     Devel::PixelBuffer geometryPixelBuffer = Devel::PixelBuffer::New(textureWidth, textureHeight, Pixel::RGB32F);
1000     uint8_t*           geometryBuffer      = geometryPixelBuffer.GetBuffer();
1001
1002     if(calculateGltf2BlendShapes)
1003     {
1004       CalculateGltf2BlendShapes(geometryBuffer, mBlendShapes, numberOfVertices, raw.mBlendShapeUnnormalizeFactor[0u], buffers);
1005     }
1006     else
1007     {
1008       Blob unnormalizeFactorBlob;
1009       unnormalizeFactorBlob.mLength = static_cast<uint32_t>(sizeof(float) * ((BlendShapes::Version::VERSION_2_0 == mBlendShapeVersion) ? 1u : numberOfBlendShapes));
1010
1011       if(blendShapesBlob.IsDefined())
1012       {
1013         if(ReadBlob(blendShapesBlob, fileStream, geometryBuffer))
1014         {
1015           unnormalizeFactorBlob.mOffset = blendShapesBlob.mOffset + blendShapesBlob.mLength;
1016         }
1017       }
1018
1019       // Read the unnormalize factors.
1020       if(unnormalizeFactorBlob.IsDefined())
1021       {
1022         ReadBlob(unnormalizeFactorBlob, fileStream, reinterpret_cast<uint8_t*>(&raw.mBlendShapeUnnormalizeFactor[0u]));
1023       }
1024     }
1025     raw.mBlendShapeData = Devel::PixelBuffer::Convert(geometryPixelBuffer);
1026   }
1027
1028   return raw;
1029 }
1030
1031 MeshGeometry MeshDefinition::Load(RawData&& raw) const
1032 {
1033   MeshGeometry meshGeometry;
1034   meshGeometry.geometry = Geometry::New();
1035   meshGeometry.geometry.SetType(mPrimitiveType);
1036
1037   if(IsQuad()) // TODO: do this in raw data; provide MakeTexturedQuadGeometry() that only creates buffers.
1038   {
1039     auto options          = MaskMatch(mFlags, FLIP_UVS_VERTICAL) ? TexturedQuadOptions::FLIP_VERTICAL : 0;
1040     meshGeometry.geometry = MakeTexturedQuadGeometry(options);
1041   }
1042   else
1043   {
1044     if(!raw.mIndices.empty())
1045     {
1046       if(MaskMatch(mFlags, U32_INDICES))
1047       {
1048         // TODO : We can only store indeces as uint16_type. Send Dali::Geometry that we use it as uint32_t actual.
1049         meshGeometry.geometry.SetIndexBuffer(reinterpret_cast<const uint32_t*>(raw.mIndices.data()), raw.mIndices.size() / 2);
1050       }
1051       else
1052       {
1053         meshGeometry.geometry.SetIndexBuffer(raw.mIndices.data(), raw.mIndices.size());
1054       }
1055     }
1056
1057     for(auto& a : raw.mAttribs)
1058     {
1059       a.AttachBuffer(meshGeometry.geometry);
1060     }
1061
1062     if(HasBlendShapes())
1063     {
1064       meshGeometry.blendShapeBufferOffset      = raw.mBlendShapeBufferOffset;
1065       meshGeometry.blendShapeUnnormalizeFactor = std::move(raw.mBlendShapeUnnormalizeFactor);
1066
1067       meshGeometry.blendShapeGeometry = Texture::New(TextureType::TEXTURE_2D,
1068                                                      raw.mBlendShapeData.GetPixelFormat(),
1069                                                      raw.mBlendShapeData.GetWidth(),
1070                                                      raw.mBlendShapeData.GetHeight());
1071       meshGeometry.blendShapeGeometry.Upload(raw.mBlendShapeData);
1072     }
1073   }
1074
1075   return meshGeometry;
1076 }
1077
1078 } // namespace Loader
1079 } // namespace Scene3D
1080 } // namespace Dali