19d4544a53695e7d65e9087c1152798a53fad95b
[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_STRING("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.empty() || max.size() == min.size());
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 uint32_t MeshDefinition::Blob::GetBufferSize() const
493 {
494   return IsConsecutive() ? mLength : (mLength * mElementSizeHint / mStride);
495 }
496
497 void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values) const
498 {
499   ApplyMinMax(mMin, mMax, count, values);
500 }
501
502 void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const
503 {
504   Property::Map attribMap;
505   attribMap[mName] = mType;
506   VertexBuffer attribBuffer = VertexBuffer::New(attribMap);
507   attribBuffer.SetData(mData.data(), mNumElements);
508
509   g.AddVertexBuffer(attribBuffer);
510 }
511
512 bool MeshDefinition::IsQuad() const
513 {
514   return CaseInsensitiveStringCompare("quad", mUri);
515 }
516
517 bool MeshDefinition::IsSkinned() const
518 {
519   return mJoints0.IsDefined() && mWeights0.IsDefined();
520 }
521
522 bool MeshDefinition::HasBlendShapes() const
523 {
524   return !mBlendShapes.empty();
525 }
526
527 void MeshDefinition::RequestNormals()
528 {
529   mNormals.mBlob.mLength = mPositions.mBlob.GetBufferSize();
530 }
531
532 void MeshDefinition::RequestTangents()
533 {
534   mTangents.mBlob.mLength = mNormals.mBlob.GetBufferSize();
535 }
536
537 MeshDefinition::RawData
538   MeshDefinition::LoadRaw(const std::string& modelsPath) const
539 {
540   RawData raw;
541   if (IsQuad())
542   {
543     return raw;
544   }
545
546   const std::string meshPath = modelsPath + mUri;
547   std::ifstream binFile(meshPath, std::ios::binary);
548   if (!binFile)
549   {
550     ExceptionFlinger(ASSERT_LOCATION) << "Failed to read geometry data from '" << meshPath << "'";
551   }
552
553   if (mIndices.IsDefined())
554   {
555     if (MaskMatch(mFlags, U32_INDICES))
556     {
557       DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(uint32_t) == 0) ||
558         mIndices.mBlob.mStride >= sizeof(uint32_t)) &&
559         "Index buffer length not a multiple of element size");
560       const auto indexCount = mIndices.mBlob.GetBufferSize() / sizeof(uint32_t);
561       raw.mIndices.resize(indexCount * 2);  // NOTE: we need space for uint32_ts initially.
562       if (!ReadAccessor(mIndices, binFile, reinterpret_cast<uint8_t*>(raw.mIndices.data())))
563       {
564         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << meshPath << "'.";
565       }
566
567       auto u16s = raw.mIndices.data();
568       auto u32s = reinterpret_cast<uint32_t*>(raw.mIndices.data());
569       auto end = u32s + indexCount;
570       while (u32s != end)
571       {
572         *u16s = static_cast<uint16_t>(*u32s);
573         ++u16s;
574         ++u32s;
575       }
576
577       raw.mIndices.resize(indexCount);
578     }
579     else
580     {
581       DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(unsigned short) == 0) ||
582         mIndices.mBlob.mStride >= sizeof(unsigned short)) &&
583         "Index buffer length not a multiple of element size");
584       raw.mIndices.resize(mIndices.mBlob.mLength / sizeof(unsigned short));
585       if (!ReadAccessor(mIndices, binFile, reinterpret_cast<uint8_t*>(raw.mIndices.data())))
586       {
587         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << meshPath << "'.";
588       }
589     }
590   }
591
592   std::vector<Vector3> positions;
593   if (mPositions.IsDefined())
594   {
595     DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % sizeof(Vector3) == 0) ||
596       mPositions.mBlob.mStride >= sizeof(Vector3)) &&
597       "Position buffer length not a multiple of element size");
598     const auto bufferSize = mPositions.mBlob.GetBufferSize();
599     std::vector<uint8_t> buffer(bufferSize);
600     if (!ReadAccessor(mPositions, binFile, buffer.data()))
601     {
602       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << meshPath << "'.";
603     }
604
605     uint32_t numVector3 = bufferSize / sizeof(Vector3);
606     mPositions.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
607
608     if (HasBlendShapes())
609     {
610       positions.resize(numVector3);
611       std::copy(buffer.data(), buffer.data() + buffer.size(), reinterpret_cast<uint8_t*>(positions.data()));
612     }
613
614     raw.mAttribs.push_back({ "aPosition", Property::VECTOR3, numVector3, std::move(buffer) });
615   }
616
617   const auto isTriangles = mPrimitiveType == Geometry::TRIANGLES;
618   auto hasNormals = mNormals.IsDefined();
619   if (hasNormals)
620   {
621     DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % sizeof(Vector3) == 0) ||
622       mNormals.mBlob.mStride >= sizeof(Vector3)) &&
623       "Normal buffer length not a multiple of element size");
624     const auto bufferSize = mNormals.mBlob.GetBufferSize();
625     std::vector<uint8_t> buffer(bufferSize);
626     if (!ReadAccessor(mNormals, binFile, buffer.data()))
627     {
628       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << meshPath << "'.";
629     }
630
631     mNormals.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
632
633     raw.mAttribs.push_back({ "aNormal", Property::VECTOR3,
634       static_cast<uint32_t>(bufferSize / sizeof(Vector3)), std::move(buffer) });
635   }
636   else if (mNormals.mBlob.mLength != 0 && isTriangles)
637   {
638     DALI_ASSERT_DEBUG(mNormals.mBlob.mLength == mPositions.mBlob.GetBufferSize());
639     GenerateNormals(raw);
640     hasNormals = true;
641   }
642
643   const auto hasUvs = mTexCoords.IsDefined();
644   if (hasUvs)
645   {
646     DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
647       mTexCoords.mBlob.mStride >= sizeof(Vector2)) &&
648       "Normal buffer length not a multiple of element size");
649     const auto bufferSize = mTexCoords.mBlob.GetBufferSize();
650     std::vector<uint8_t> buffer(bufferSize);
651     if (!ReadAccessor(mTexCoords, binFile, buffer.data()))
652     {
653       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << meshPath << "'.";
654     }
655
656     const auto uvCount = bufferSize / sizeof(Vector2);
657     if (MaskMatch(mFlags, FLIP_UVS_VERTICAL))
658     {
659       auto uv = reinterpret_cast<Vector2*>(buffer.data());
660       auto uvEnd = uv + uvCount;
661       while (uv != uvEnd)
662       {
663         uv->y = 1.0f - uv->y;
664         ++uv;
665       }
666     }
667
668     mTexCoords.mBlob.ApplyMinMax(bufferSize / sizeof(Vector2), reinterpret_cast<float*>(buffer.data()));
669
670     raw.mAttribs.push_back({ "aTexCoord", Property::VECTOR2, static_cast<uint32_t>(uvCount),
671       std::move(buffer) });
672   }
673
674   if (mTangents.IsDefined())
675   {
676     DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % sizeof(Vector3) == 0) ||
677       mTangents.mBlob.mStride >= sizeof(Vector3)) &&
678       "Tangents buffer length not a multiple of element size");
679     const auto bufferSize = mTangents.mBlob.GetBufferSize();
680     std::vector<uint8_t> buffer(bufferSize);
681     if (!ReadAccessor(mTangents, binFile, buffer.data()))
682     {
683       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << meshPath << "'.";
684     }
685
686     mTangents.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
687
688     raw.mAttribs.push_back({ "aTangent", Property::VECTOR3,
689       static_cast<uint32_t>(bufferSize / sizeof(Vector3)), std::move(buffer) });
690   }
691   else if (mTangents.mBlob.mLength != 0 && hasNormals && isTriangles)
692   {
693     DALI_ASSERT_DEBUG(mTangents.mBlob.mLength == mNormals.mBlob.GetBufferSize());
694     hasUvs ? GenerateTangentsWithUvs(raw) : GenerateTangents(raw);
695   }
696
697   if (IsSkinned())
698   {
699     if (MaskMatch(mFlags, U16_JOINT_IDS))
700     {
701       DALI_ASSERT_ALWAYS(((mJoints0.mBlob.mLength % sizeof(Uint16Vector4) == 0) ||
702         mJoints0.mBlob.mStride >= sizeof(Uint16Vector4)) &&
703         "Joints buffer length not a multiple of element size");
704       const auto inBufferSize = mJoints0.mBlob.GetBufferSize();
705       std::vector<uint8_t> buffer(inBufferSize * 2);
706       auto u16s = buffer.data() + inBufferSize;
707       if (!ReadAccessor(mJoints0, binFile, u16s))
708       {
709         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read joints from '" << meshPath << "'.";
710       }
711
712       auto floats = reinterpret_cast<float*>(buffer.data());
713       auto end = u16s + inBufferSize;
714       while (u16s != end)
715       {
716         auto value = *reinterpret_cast<uint16_t*>(u16s);
717         *floats = static_cast<float>(value);
718
719         u16s += sizeof(uint16_t);
720         ++floats;
721       }
722       raw.mAttribs.push_back({ "aJoints", Property::VECTOR4,
723         static_cast<uint32_t>(buffer.size() / sizeof(Vector4)), std::move(buffer) });
724     }
725     else
726     {
727       DALI_ASSERT_ALWAYS(((mJoints0.mBlob.mLength % sizeof(Vector4) == 0) ||
728         mJoints0.mBlob.mStride >= sizeof(Vector4)) &&
729         "Joints buffer length not a multiple of element size");
730       const auto bufferSize = mJoints0.mBlob.GetBufferSize();
731       std::vector<uint8_t> buffer(bufferSize);
732       if (!ReadAccessor(mJoints0, binFile, buffer.data()))
733       {
734         ExceptionFlinger(ASSERT_LOCATION) << "Failed to read joints from '" << meshPath << "'.";
735       }
736
737       raw.mAttribs.push_back({ "aJoints", Property::VECTOR4,
738         static_cast<uint32_t>(bufferSize / sizeof(Vector4)), std::move(buffer) });
739     }
740
741     DALI_ASSERT_ALWAYS(((mWeights0.mBlob.mLength % sizeof(Vector4) == 0) ||
742       mWeights0.mBlob.mStride >= sizeof(Vector4)) &&
743       "Weights buffer length not a multiple of element size");
744     const auto bufferSize = mWeights0.mBlob.GetBufferSize();
745     std::vector<uint8_t> buffer(bufferSize);
746     if (!ReadAccessor(mWeights0, binFile, buffer.data()))
747     {
748       ExceptionFlinger(ASSERT_LOCATION) << "Failed to read weights from '" << meshPath << "'.";
749     }
750
751     raw.mAttribs.push_back({ "aWeights", Property::VECTOR4,
752       static_cast<uint32_t>(bufferSize / sizeof(Vector4)), std::move(buffer) });
753   }
754
755   // Calculate the Blob for the blend shapes.
756   Blob blendShapesBlob;
757   blendShapesBlob.mOffset = std::numeric_limits<unsigned int>::max();
758   blendShapesBlob.mLength = 0u;
759
760   for (const auto& blendShape : mBlendShapes)
761   {
762     for (auto i : { &blendShape.deltas, &blendShape.normals, &blendShape.tangents })
763     {
764       if (i->IsDefined())
765       {
766         blendShapesBlob.mOffset = std::min(blendShapesBlob.mOffset, i->mBlob.mOffset);
767         blendShapesBlob.mLength += i->mBlob.mLength;
768       }
769     }
770   }
771
772   if (HasBlendShapes())
773   {
774     const uint32_t numberOfVertices = mPositions.mBlob.mLength / sizeof(Vector3);
775
776     // Calculate the size of one buffer inside the texture.
777     raw.mBlendShapeBufferOffset = numberOfVertices;
778
779     bool calculateGltf2BlendShapes = false;
780     uint32_t textureWidth = 0u;
781     uint32_t textureHeight = 0u;
782
783     if (!mBlendShapeHeader.IsDefined())
784     {
785       CalculateTextureSize(blendShapesBlob.mLength / sizeof(Vector3), textureWidth, textureHeight);
786       calculateGltf2BlendShapes = true;
787     }
788     else
789     {
790       uint16_t header[2u];
791       ReadBlob(mBlendShapeHeader, binFile, reinterpret_cast<uint8_t*>(header));
792       textureWidth = header[0u];
793       textureHeight = header[1u];
794     }
795
796     const uint32_t numberOfBlendShapes = mBlendShapes.size();
797     raw.mBlendShapeUnnormalizeFactor.Resize(numberOfBlendShapes);
798
799     Devel::PixelBuffer geometryPixelBuffer = Devel::PixelBuffer::New(textureWidth, textureHeight, Pixel::RGB32F);
800     uint8_t* geometryBuffer = geometryPixelBuffer.GetBuffer();
801
802     if (calculateGltf2BlendShapes)
803     {
804       CalculateGltf2BlendShapes(geometryBuffer, binFile, mBlendShapes, numberOfVertices, raw.mBlendShapeUnnormalizeFactor[0u]);
805     }
806     else
807     {
808       Blob unnormalizeFactorBlob;
809       unnormalizeFactorBlob.mLength = sizeof(float) * ((BlendShapes::Version::VERSION_2_0 == mBlendShapeVersion) ? 1u : numberOfBlendShapes);
810
811       if (blendShapesBlob.IsDefined())
812       {
813         if (ReadBlob(blendShapesBlob, binFile, geometryBuffer))
814         {
815           unnormalizeFactorBlob.mOffset = blendShapesBlob.mOffset + blendShapesBlob.mLength;
816         }
817       }
818
819       // Read the unnormalize factors.
820       if (unnormalizeFactorBlob.IsDefined())
821       {
822         ReadBlob(unnormalizeFactorBlob, binFile, reinterpret_cast<uint8_t*>(&raw.mBlendShapeUnnormalizeFactor[0u]));
823       }
824     }
825     raw.mBlendShapeData = Devel::PixelBuffer::Convert(geometryPixelBuffer);
826   }
827
828   return raw;
829 }
830
831 MeshGeometry MeshDefinition::Load(RawData&& raw) const
832 {
833   MeshGeometry meshGeometry;
834   meshGeometry.geometry = Geometry::New();
835   meshGeometry.geometry.SetType(mPrimitiveType);
836
837   if (IsQuad())  // TODO: do this in raw data; provide MakeTexturedQuadGeometry() that only creates buffers.
838   {
839     auto options = MaskMatch(mFlags, FLIP_UVS_VERTICAL) ? TexturedQuadOptions::FLIP_VERTICAL : 0;
840     meshGeometry.geometry = MakeTexturedQuadGeometry(options);
841   }
842   else
843   {
844     if (!raw.mIndices.empty())
845     {
846       meshGeometry.geometry.SetIndexBuffer(raw.mIndices.data(), raw.mIndices.size());
847     }
848
849     for (auto& a : raw.mAttribs)
850     {
851       a.AttachBuffer(meshGeometry.geometry);
852     }
853
854     if (HasBlendShapes())
855     {
856       meshGeometry.blendShapeBufferOffset = raw.mBlendShapeBufferOffset;
857       meshGeometry.blendShapeUnnormalizeFactor = std::move(raw.mBlendShapeUnnormalizeFactor);
858
859       meshGeometry.blendShapeGeometry = Texture::New(  TextureType::TEXTURE_2D,
860                               raw.mBlendShapeData.GetPixelFormat(),
861                               raw.mBlendShapeData.GetWidth(),
862                               raw.mBlendShapeData.GetHeight());
863       meshGeometry.blendShapeGeometry.Upload(raw.mBlendShapeData);
864     }
865   }
866
867   return meshGeometry;
868 }
869
870 }
871 }