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