2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "dali-scene-loader/public-api/mesh-definition.h"
22 #include "dali/devel-api/adaptor-framework/pixel-buffer.h"
33 using Uint16Vector4 = uint16_t[4];
38 IndexProvider(const uint16_t* indices)
39 : mData(reinterpret_cast<uintptr_t>(indices)),
40 mFunc(indices ? IncrementPointer : Increment)
49 static uint16_t Increment(uintptr_t& data)
51 return static_cast<uint16_t>(data++);
54 static uint16_t IncrementPointer(uintptr_t& data)
56 auto iPtr = reinterpret_cast<const uint16_t*>(data);
58 data = reinterpret_cast<uintptr_t>(++iPtr);
63 uint16_t(*mFunc)(uintptr_t&);
67 const std::string QUAD("quad");
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)
73 if (!source.seekg(descriptor.mOffset, std::istream::beg))
78 if (descriptor.IsConsecutive())
80 return !!source.read(reinterpret_cast<char*>(target), descriptor.mLength);
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))
91 readSize += descriptor.mStride;
92 target += descriptor.mElementSizeHint;
94 return readSize == descriptor.mLength;
99 void ReadValues(const std::vector<uint8_t>& valuesBuffer, const std::vector<uint8_t>& indicesBuffer, uint8_t* target, uint32_t count, uint32_t elementSizeHint)
101 const T* const indicesPtr = reinterpret_cast<const T* const>(indicesBuffer.data());
102 for (uint32_t index = 0u; index < count; ++index)
104 uint32_t valuesIndex = indicesPtr[index] * elementSizeHint;
105 memcpy(target + valuesIndex, &valuesBuffer[index * elementSizeHint], elementSizeHint);
109 bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target)
111 bool success = false;
113 if (accessor.mBlob.IsDefined())
115 success = ReadBlob(accessor.mBlob, source, target);
122 if (accessor.mSparse)
124 const MeshDefinition::Blob& indices = accessor.mSparse->mIndices;
125 const MeshDefinition::Blob& values = accessor.mSparse->mValues;
127 if (!indices.IsDefined() || !values.IsDefined())
132 const auto indicesBufferSize = indices.GetBufferSize();
133 std::vector<uint8_t> indicesBuffer(indicesBufferSize);
134 success = ReadBlob(indices, source, indicesBuffer.data());
140 const auto valuesBufferSize = values.GetBufferSize();
141 std::vector<uint8_t> valuesBuffer(valuesBufferSize);
142 success = ReadBlob(values, source, valuesBuffer.data());
148 switch (indices.mElementSizeHint)
152 ReadValues<uint8_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
157 ReadValues<uint16_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
162 ReadValues<uint32_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
166 DALI_ASSERT_DEBUG(!"Unsupported type for an index");
173 void GenerateNormals(MeshDefinition::RawData& raw)
175 auto& attribs = raw.mAttribs;
176 DALI_ASSERT_DEBUG(attribs.size() > 0); // positions
177 IndexProvider getIndex(raw.mIndices.data());
179 const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : raw.mIndices.size();
181 auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
183 std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(Vector3));
184 auto normals = reinterpret_cast<Vector3*>(buffer.data());
186 for (uint32_t i = 0; i < numIndices; i += 3)
188 uint16_t indices[]{ getIndex(), getIndex(), getIndex() };
189 Vector3 pos[]{ positions[indices[0]], positions[indices[1]], positions[indices[2]] };
191 Vector3 a = pos[1] - pos[0];
192 Vector3 b = pos[2] - pos[0];
194 Vector3 normal(a.Cross(b));
195 normals[indices[0]] += normal;
196 normals[indices[1]] += normal;
197 normals[indices[2]] += normal;
200 auto iEnd = normals + attribs[0].mNumElements;
201 while (normals != iEnd)
203 normals->Normalize();
207 attribs.push_back({ "aNormal", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer) });
210 void GenerateTangentsWithUvs(MeshDefinition::RawData& raw)
212 auto& attribs = raw.mAttribs;
213 DALI_ASSERT_DEBUG(attribs.size() > 2); // positions, normals, uvs
214 IndexProvider getIndex(raw.mIndices.data());
216 const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : raw.mIndices.size();
218 auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
219 auto* uvs = reinterpret_cast<const Vector2*>(attribs[2].mData.data());
221 std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(Vector3));
222 auto tangents = reinterpret_cast<Vector3*>(buffer.data());
224 for (uint32_t i = 0; i < numIndices; i += 3)
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]] };
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;
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;
238 float s0 = uv[1].x - uv[0].x;
239 float t0 = uv[1].y - uv[0].y;
241 float s1 = uv[2].x - uv[0].x;
242 float t1 = uv[2].y - uv[0].y;
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;
251 auto* normals = reinterpret_cast<const Vector3*>(attribs[1].mData.data());
252 auto iEnd = normals + attribs[1].mNumElements;
253 while (normals != iEnd)
255 *tangents -= *normals * normals->Dot(*tangents);
256 tangents->Normalize();
261 attribs.push_back({ "aTangent", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer) });
264 void GenerateTangents(MeshDefinition::RawData& raw)
266 auto& attribs = raw.mAttribs;
267 DALI_ASSERT_DEBUG(attribs.size() > 1); // positions, normals
269 auto* normals = reinterpret_cast<const Vector3*>(attribs[1].mData.data());
271 std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(Vector3));
272 auto tangents = reinterpret_cast<Vector3*>(buffer.data());
274 auto iEnd = normals + attribs[1].mNumElements;
275 while (normals != iEnd)
277 Vector3 t[]{ normals->Cross(Vector3::XAXIS), normals->Cross(Vector3::YAXIS) };
279 *tangents = t[t[1].LengthSquared() > t[0].LengthSquared()];
280 *tangents -= *normals * normals->Dot(*tangents);
281 tangents->Normalize();
286 attribs.push_back({ "aTangent", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer) });
289 void CalculateTextureSize(uint32_t totalTextureSize, uint32_t& textureWidth, uint32_t& textureHeight)
291 DALI_ASSERT_DEBUG(0u != totalTextureSize && "totalTextureSize is zero.")
293 // Calculate the dimensions of the texture.
294 // The total size of the texture is the length of the blend shapes blob.
299 if (0u == totalTextureSize)
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;
309 textureWidth = 1u << powWidth;
310 textureHeight = 1u << powHeight;
313 void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::ifstream& binFile, const std::vector<MeshDefinition::BlendShape>& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor)
315 uint32_t geometryBufferIndex = 0u;
316 float maxDistance = 0.f;
317 Vector3* geometryBufferV3 = reinterpret_cast<Vector3*>(geometryBuffer);
318 for (const auto& blendShape : blendShapes)
320 if (blendShape.deltas.IsDefined())
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");
326 const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize();
327 std::vector<uint8_t> buffer(bufferSize);
328 if (ReadAccessor(blendShape.deltas, binFile, buffer.data()))
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());
335 for (uint32_t index = 0u; index < numberOfVertices; ++index)
337 Vector3& delta = geometryBufferV3[geometryBufferIndex++];
338 delta = deltasBuffer[index];
340 maxDistance = std::max(maxDistance, delta.LengthSquared());
345 if (blendShape.normals.IsDefined())
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");
351 const auto bufferSize = blendShape.normals.mBlob.GetBufferSize();
352 std::vector<uint8_t> buffer(bufferSize);
353 if (ReadAccessor(blendShape.normals, binFile, buffer.data()))
355 blendShape.normals.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
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());
360 for (uint32_t index = 0u; index < numberOfVertices; ++index)
362 Vector3& delta = geometryBufferV3[geometryBufferIndex++];
363 delta = deltasBuffer[index];
376 if (blendShape.tangents.IsDefined())
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");
382 const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize();
383 std::vector<uint8_t> buffer(bufferSize);
384 if (ReadAccessor(blendShape.tangents, binFile, buffer.data()))
386 blendShape.tangents.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
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());
391 for (uint32_t index = 0u; index < numberOfVertices; ++index)
393 Vector3& delta = geometryBufferV3[geometryBufferIndex++];
394 delta = deltasBuffer[index];
408 geometryBufferIndex = 0u;
409 for (const auto& blendShape : blendShapes)
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())
417 const float normalizeFactor = (fabsf(maxDistance) < Math::MACHINE_EPSILON_1000) ? 1.f : (0.5f / sqrtf(maxDistance));
419 for (uint32_t index = 0u; index < numberOfVertices; ++index)
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);
427 // Calculate and store the unnormalize factor.
428 blendShapeUnnormalizeFactor = 1.f / normalizeFactor;
431 if (blendShape.normals.IsDefined())
433 geometryBufferIndex += numberOfVertices;
436 if (blendShape.tangents.IsDefined())
438 geometryBufferIndex += numberOfVertices;
445 MeshDefinition::SparseBlob::SparseBlob(const Blob& indices, const Blob& values, uint32_t count)
451 MeshDefinition::Accessor::Accessor(const MeshDefinition::Blob& blob,
452 const MeshDefinition::SparseBlob& sparse)
454 mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{sparse} : nullptr}
457 void MeshDefinition::Blob::ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max,
458 uint32_t count, float* values)
460 DALI_ASSERT_DEBUG(max.size() == min.size() || max.size() * min.size() == 0);
461 const auto numComponents = std::max(min.size(), max.size());
463 using ClampFn = void(*)(const float*, const float*, uint32_t, float&);
464 ClampFn clampFn = min.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);
471 [](const float* min, const float* max, uint32_t i, float& value) {
472 value = std::max(min[i], value);
474 [](const float* min, const float* max, uint32_t i, float& value) {
475 value = std::min(std::max(min[i], value), max[i]);
478 auto end = values + count * numComponents;
479 while (values != end)
481 auto nextElement = values + numComponents;
483 while (values != nextElement)
485 clampFn(min.data(), max.data(), i, *values);
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)
496 mElementSizeHint(elementSizeHint),
501 uint32_t MeshDefinition::Blob::GetBufferSize() const
503 return IsConsecutive() ? mLength : (mLength * mElementSizeHint / mStride);
506 void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values) const
508 ApplyMinMax(mMin, mMax, count, values);
511 void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const
513 Property::Map attribMap;
514 attribMap[mName] = mType;
515 VertexBuffer attribBuffer = VertexBuffer::New(attribMap);
516 attribBuffer.SetData(mData.data(), mNumElements);
518 g.AddVertexBuffer(attribBuffer);
521 bool MeshDefinition::IsQuad() const
523 return CaseInsensitiveStringCompare(QUAD, mUri);
526 bool MeshDefinition::IsSkinned() const
528 return mJoints0.IsDefined() && mWeights0.IsDefined();
531 bool MeshDefinition::HasBlendShapes() const
533 return !mBlendShapes.empty();
536 void MeshDefinition::RequestNormals()
538 mNormals.mBlob.mLength = mPositions.mBlob.GetBufferSize();
541 void MeshDefinition::RequestTangents()
543 mTangents.mBlob.mLength = mNormals.mBlob.GetBufferSize();
546 MeshDefinition::RawData
547 MeshDefinition::LoadRaw(const std::string& modelsPath) const
555 const std::string meshPath = modelsPath + mUri;
556 std::ifstream binFile(meshPath, std::ios::binary);
559 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read geometry data from '" << meshPath << "'";
562 if (mIndices.IsDefined())
564 if (MaskMatch(mFlags, U32_INDICES))
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())))
573 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << meshPath << "'.";
576 auto u16s = raw.mIndices.data();
577 auto u32s = reinterpret_cast<uint32_t*>(raw.mIndices.data());
578 auto end = u32s + indexCount;
581 *u16s = static_cast<uint16_t>(*u32s);
586 raw.mIndices.resize(indexCount);
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())))
596 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << meshPath << "'.";
601 std::vector<Vector3> positions;
602 if (mPositions.IsDefined())
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()))
611 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << meshPath << "'.";
614 uint32_t numVector3 = bufferSize / sizeof(Vector3);
615 mPositions.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
617 if (HasBlendShapes())
619 positions.resize(numVector3);
620 std::copy(buffer.data(), buffer.data() + buffer.size(), reinterpret_cast<uint8_t*>(positions.data()));
623 raw.mAttribs.push_back({ "aPosition", Property::VECTOR3, numVector3, std::move(buffer) });
626 const auto isTriangles = mPrimitiveType == Geometry::TRIANGLES;
627 auto hasNormals = mNormals.IsDefined();
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()))
637 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << meshPath << "'.";
640 mNormals.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
642 raw.mAttribs.push_back({ "aNormal", Property::VECTOR3,
643 static_cast<uint32_t>(bufferSize / sizeof(Vector3)), std::move(buffer) });
645 else if (mNormals.mBlob.mLength != 0 && isTriangles)
647 DALI_ASSERT_DEBUG(mNormals.mBlob.mLength == mPositions.mBlob.GetBufferSize());
648 GenerateNormals(raw);
652 const auto hasUvs = mTexCoords.IsDefined();
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()))
662 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << meshPath << "'.";
665 const auto uvCount = bufferSize / sizeof(Vector2);
666 if (MaskMatch(mFlags, FLIP_UVS_VERTICAL))
668 auto uv = reinterpret_cast<Vector2*>(buffer.data());
669 auto uvEnd = uv + uvCount;
672 uv->y = 1.0f - uv->y;
677 mTexCoords.mBlob.ApplyMinMax(bufferSize / sizeof(Vector2), reinterpret_cast<float*>(buffer.data()));
679 raw.mAttribs.push_back({ "aTexCoord", Property::VECTOR2, static_cast<uint32_t>(uvCount),
680 std::move(buffer) });
683 if (mTangents.IsDefined())
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()))
692 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << meshPath << "'.";
695 mTangents.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
697 raw.mAttribs.push_back({ "aTangent", Property::VECTOR3,
698 static_cast<uint32_t>(bufferSize / sizeof(Vector3)), std::move(buffer) });
700 else if (mTangents.mBlob.mLength != 0 && hasNormals && isTriangles)
702 DALI_ASSERT_DEBUG(mTangents.mBlob.mLength == mNormals.mBlob.GetBufferSize());
703 hasUvs ? GenerateTangentsWithUvs(raw) : GenerateTangents(raw);
708 if (MaskMatch(mFlags, U16_JOINT_IDS))
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))
718 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read joints from '" << meshPath << "'.";
721 auto floats = reinterpret_cast<float*>(buffer.data());
722 auto end = u16s + inBufferSize;
725 auto value = *reinterpret_cast<uint16_t*>(u16s);
726 *floats = static_cast<float>(value);
728 u16s += sizeof(uint16_t);
731 raw.mAttribs.push_back({ "aJoints", Property::VECTOR4,
732 static_cast<uint32_t>(buffer.size() / sizeof(Vector4)), std::move(buffer) });
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()))
743 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read joints from '" << meshPath << "'.";
746 raw.mAttribs.push_back({ "aJoints", Property::VECTOR4,
747 static_cast<uint32_t>(bufferSize / sizeof(Vector4)), std::move(buffer) });
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()))
757 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read weights from '" << meshPath << "'.";
760 raw.mAttribs.push_back({ "aWeights", Property::VECTOR4,
761 static_cast<uint32_t>(bufferSize / sizeof(Vector4)), std::move(buffer) });
764 // Calculate the Blob for the blend shapes.
765 Blob blendShapesBlob;
766 blendShapesBlob.mOffset = std::numeric_limits<unsigned int>::max();
767 blendShapesBlob.mLength = 0u;
769 for (const auto& blendShape : mBlendShapes)
771 for (auto i : { &blendShape.deltas, &blendShape.normals, &blendShape.tangents })
775 blendShapesBlob.mOffset = std::min(blendShapesBlob.mOffset, i->mBlob.mOffset);
776 blendShapesBlob.mLength += i->mBlob.mLength;
781 if (HasBlendShapes())
783 const uint32_t numberOfVertices = mPositions.mBlob.mLength / sizeof(Vector3);
785 // Calculate the size of one buffer inside the texture.
786 raw.mBlendShapeBufferOffset = numberOfVertices;
788 bool calculateGltf2BlendShapes = false;
789 uint32_t textureWidth = 0u;
790 uint32_t textureHeight = 0u;
792 if (!mBlendShapeHeader.IsDefined())
794 CalculateTextureSize(blendShapesBlob.mLength / sizeof(Vector3), textureWidth, textureHeight);
795 calculateGltf2BlendShapes = true;
800 ReadBlob(mBlendShapeHeader, binFile, reinterpret_cast<uint8_t*>(header));
801 textureWidth = header[0u];
802 textureHeight = header[1u];
805 const uint32_t numberOfBlendShapes = mBlendShapes.size();
806 raw.mBlendShapeUnnormalizeFactor.Resize(numberOfBlendShapes);
808 Devel::PixelBuffer geometryPixelBuffer = Devel::PixelBuffer::New(textureWidth, textureHeight, Pixel::RGB32F);
809 uint8_t* geometryBuffer = geometryPixelBuffer.GetBuffer();
811 if (calculateGltf2BlendShapes)
813 CalculateGltf2BlendShapes(geometryBuffer, binFile, mBlendShapes, numberOfVertices, raw.mBlendShapeUnnormalizeFactor[0u]);
817 Blob unnormalizeFactorBlob;
818 unnormalizeFactorBlob.mLength = sizeof(float) * ((BlendShapes::Version::VERSION_2_0 == mBlendShapeVersion) ? 1u : numberOfBlendShapes);
820 if (blendShapesBlob.IsDefined())
822 if (ReadBlob(blendShapesBlob, binFile, geometryBuffer))
824 unnormalizeFactorBlob.mOffset = blendShapesBlob.mOffset + blendShapesBlob.mLength;
828 // Read the unnormalize factors.
829 if (unnormalizeFactorBlob.IsDefined())
831 ReadBlob(unnormalizeFactorBlob, binFile, reinterpret_cast<uint8_t*>(&raw.mBlendShapeUnnormalizeFactor[0u]));
834 raw.mBlendShapeData = Devel::PixelBuffer::Convert(geometryPixelBuffer);
840 MeshGeometry MeshDefinition::Load(RawData&& raw) const
842 MeshGeometry meshGeometry;
843 meshGeometry.geometry = Geometry::New();
844 meshGeometry.geometry.SetType(mPrimitiveType);
846 if (IsQuad()) // TODO: do this in raw data; provide MakeTexturedQuadGeometry() that only creates buffers.
848 auto options = MaskMatch(mFlags, FLIP_UVS_VERTICAL) ? TexturedQuadOptions::FLIP_VERTICAL : 0;
849 meshGeometry.geometry = MakeTexturedQuadGeometry(options);
853 if (!raw.mIndices.empty())
855 meshGeometry.geometry.SetIndexBuffer(raw.mIndices.data(), raw.mIndices.size());
858 for (auto& a : raw.mAttribs)
860 a.AttachBuffer(meshGeometry.geometry);
863 if (HasBlendShapes())
865 meshGeometry.blendShapeBufferOffset = raw.mBlendShapeBufferOffset;
866 meshGeometry.blendShapeUnnormalizeFactor = std::move(raw.mBlendShapeUnnormalizeFactor);
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);