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