2 * Copyright (c) 2023 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-scene3d/public-api/loader/mesh-definition.h>
22 #include <dali/devel-api/adaptor-framework/file-stream.h>
23 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/math/compile-time-math.h>
29 #include <type_traits>
31 namespace Dali::Scene3D::Loader
35 template<bool use32BitIndices>
39 using IndexType = typename std::conditional_t<use32BitIndices, uint32_t, uint16_t>;
40 IndexProvider(const uint16_t* indices)
41 : mData(reinterpret_cast<uintptr_t>(indices)),
42 mFunc(indices ? IncrementPointer : Increment)
46 IndexType operator()()
52 static IndexType Increment(uintptr_t& data)
54 // mData was 'zero' at construct time. Just simply return counter start with 0.
55 return static_cast<IndexType>(data++);
58 static IndexType IncrementPointer(uintptr_t& data)
60 auto iPtr = reinterpret_cast<const IndexType*>(data);
62 data = reinterpret_cast<uintptr_t>(++iPtr);
67 IndexType (*mFunc)(uintptr_t&);
70 const char* QUAD("quad");
72 ///@brief Reads a blob from the given stream @a source into @a target, which must have
73 /// at least @a descriptor.length bytes.
74 bool ReadBlob(const MeshDefinition::Blob& descriptor, std::istream& source, uint8_t* target)
77 if(!source.seekg(descriptor.mOffset, std::istream::beg))
82 if(descriptor.IsConsecutive())
84 return !!source.read(reinterpret_cast<char*>(target), static_cast<std::streamsize>(static_cast<size_t>(descriptor.mLength)));
88 if(descriptor.mStride > descriptor.mElementSizeHint)
90 const uint32_t diff = descriptor.mStride - descriptor.mElementSizeHint;
91 uint32_t readSize = 0;
92 uint32_t totalSize = (descriptor.mLength / descriptor.mElementSizeHint) * descriptor.mStride;
93 while(readSize < totalSize &&
94 source.read(reinterpret_cast<char*>(target), descriptor.mElementSizeHint))
96 readSize += descriptor.mStride;
97 target += descriptor.mElementSizeHint;
98 source.seekg(diff, std::istream::cur);
100 return readSize == totalSize;
107 void ReadValues(const std::vector<uint8_t>& valuesBuffer, const std::vector<uint8_t>& indicesBuffer, uint8_t* target, uint32_t count, uint32_t elementSizeHint)
109 const T* const indicesPtr = reinterpret_cast<const T* const>(indicesBuffer.data());
110 for(uint32_t index = 0u; index < count; ++index)
112 uint32_t valuesIndex = indicesPtr[index] * elementSizeHint;
113 memcpy(target + valuesIndex, &valuesBuffer[index * elementSizeHint], elementSizeHint);
117 bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target, std::vector<uint32_t>* sparseIndices)
119 bool success = false;
121 if(accessor.mBlob.IsDefined())
123 success = ReadBlob(accessor.mBlob, source, target);
132 const MeshDefinition::Blob& indices = accessor.mSparse->mIndices;
133 const MeshDefinition::Blob& values = accessor.mSparse->mValues;
135 if(!indices.IsDefined() || !values.IsDefined())
140 const auto indicesBufferSize = indices.GetBufferSize();
141 std::vector<uint8_t> indicesBuffer(indicesBufferSize);
142 success = ReadBlob(indices, source, indicesBuffer.data());
148 const auto valuesBufferSize = values.GetBufferSize();
149 std::vector<uint8_t> valuesBuffer(valuesBufferSize);
150 success = ReadBlob(values, source, valuesBuffer.data());
156 // If non-null sparse indices vector, prepare it for output
159 sparseIndices->resize(accessor.mSparse->mCount);
162 switch(indices.mElementSizeHint)
166 ReadValues<uint8_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
169 // convert 8-bit indices into 32-bit
170 std::transform(indicesBuffer.begin(), indicesBuffer.end(), sparseIndices->begin(), [](const uint8_t& value) { return uint32_t(value); });
176 ReadValues<uint16_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
179 // convert 16-bit indices into 32-bit
180 std::transform(reinterpret_cast<uint16_t*>(indicesBuffer.data()),
181 reinterpret_cast<uint16_t*>(indicesBuffer.data()) + accessor.mSparse->mCount,
182 sparseIndices->begin(),
183 [](const uint16_t& value) {
184 return uint32_t(value);
191 ReadValues<uint32_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
194 std::copy(indicesBuffer.begin(), indicesBuffer.end(), reinterpret_cast<uint8_t*>(sparseIndices->data()));
200 DALI_ASSERT_DEBUG(!"Unsupported type for an index");
208 bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target)
210 return ReadAccessor(accessor, source, target, nullptr);
214 void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath, const std::string& name)
216 constexpr auto sizeofBlobUnit = sizeof(T) * 4;
218 DALI_ASSERT_ALWAYS(((accessor.mBlob.mLength % sizeofBlobUnit == 0) ||
219 accessor.mBlob.mStride >= sizeofBlobUnit) &&
220 "Joints buffer length not a multiple of element size");
221 const auto inBufferSize = accessor.mBlob.GetBufferSize();
222 const auto outBufferSize = (sizeof(Vector4) / sizeofBlobUnit) * inBufferSize;
224 std::vector<uint8_t> buffer(outBufferSize);
225 auto inBuffer = buffer.data() + outBufferSize - inBufferSize;
226 if(!ReadAccessor(accessor, source, inBuffer))
228 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read joints from '" << meshPath << "'.";
231 if constexpr(sizeofBlobUnit != sizeof(Vector4))
233 auto floats = reinterpret_cast<float*>(buffer.data());
234 const auto end = inBuffer + inBufferSize;
235 while(inBuffer != end)
237 const auto value = *reinterpret_cast<T*>(inBuffer);
238 *floats = static_cast<float>(value);
240 inBuffer += sizeof(T);
244 raw.mAttribs.push_back({name, Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
247 void ReadTypedJointAccessor(MeshDefinition::RawData& raw, uint32_t flags, MeshDefinition::Accessor& accessor, std::iostream& stream, std::string& path, const std::string& name)
249 if(MaskMatch(flags, MeshDefinition::U16_JOINT_IDS))
251 ReadJointAccessor<uint16_t>(raw, accessor, stream, path, name);
253 else if(MaskMatch(flags, MeshDefinition::U8_JOINT_IDS))
255 ReadJointAccessor<uint8_t>(raw, accessor, stream, path, name);
259 ReadJointAccessor<float>(raw, accessor, stream, path, name);
264 void ReadWeightAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath, const std::string& name)
266 constexpr auto sizeofBlobUnit = sizeof(T) * 4;
268 DALI_ASSERT_ALWAYS(((accessor.mBlob.mLength % sizeofBlobUnit == 0) ||
269 accessor.mBlob.mStride >= sizeofBlobUnit) &&
270 "weights buffer length not a multiple of element size");
271 const auto inBufferSize = accessor.mBlob.GetBufferSize();
272 const auto outBufferSize = (sizeof(Vector4) / sizeofBlobUnit) * inBufferSize;
274 std::vector<uint8_t> buffer(outBufferSize);
275 auto inBuffer = buffer.data() + outBufferSize - inBufferSize;
276 if(!ReadAccessor(accessor, source, inBuffer))
278 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read weights from '" << meshPath << "'.";
281 if constexpr(sizeofBlobUnit != sizeof(Vector4))
283 auto floats = reinterpret_cast<float*>(buffer.data());
284 const auto end = inBuffer + inBufferSize;
285 while(inBuffer != end)
287 const auto value = *reinterpret_cast<T*>(inBuffer);
288 // Normalize weight value. value /= 255 for uint8_t weight, and value /= 65535 for uint16_t weight.
289 *floats = static_cast<float>(value) / static_cast<float>((1 << (sizeof(T) * 8)) - 1);
291 inBuffer += sizeof(T);
295 raw.mAttribs.push_back({name, Property::VECTOR4, static_cast<uint32_t>(outBufferSize / sizeof(Vector4)), std::move(buffer)});
298 void ReadTypedWeightAccessor(MeshDefinition::RawData& raw, uint32_t flags, MeshDefinition::Accessor& accessor, std::iostream& stream, std::string& path, std::string name)
300 if(MaskMatch(flags, MeshDefinition::U16_WEIGHT))
302 ReadWeightAccessor<uint16_t>(raw, accessor, stream, path, name);
304 else if(MaskMatch(flags, MeshDefinition::U8_WEIGHT))
306 ReadWeightAccessor<uint8_t>(raw, accessor, stream, path, name);
310 ReadWeightAccessor<float>(raw, accessor, stream, path, name);
314 template<bool use32BitsIndices, typename IndexProviderType = IndexProvider<use32BitsIndices>>
315 bool GenerateNormals(MeshDefinition::RawData& raw)
317 using IndexType = typename IndexProviderType::IndexType;
319 // mIndicies size must be even if we use 32bit indices.
320 if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
325 auto& attribs = raw.mAttribs;
326 DALI_ASSERT_DEBUG(attribs.size() > 0); // positions
328 IndexProviderType getIndex(raw.mIndices.data());
330 const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
332 auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
334 std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(Vector3));
335 auto normals = reinterpret_cast<Vector3*>(buffer.data());
337 for(uint32_t i = 0; i < numIndices; i += 3)
339 IndexType indices[]{getIndex(), getIndex(), getIndex()};
340 Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
342 Vector3 a = pos[1] - pos[0];
343 Vector3 b = pos[2] - pos[0];
345 Vector3 normal(a.Cross(b));
346 normals[indices[0]] += normal;
347 normals[indices[1]] += normal;
348 normals[indices[2]] += normal;
351 auto iEnd = normals + attribs[0].mNumElements;
352 while(normals != iEnd)
354 normals->Normalize();
358 attribs.push_back({"aNormal", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer)});
363 template<bool use32BitsIndices, bool useVec3, bool hasUvs, typename T = std::conditional_t<useVec3, Vector3, Vector4>, typename = std::enable_if_t<(std::is_same<T, Vector3>::value || std::is_same<T, Vector4>::value)>, typename IndexProviderType = IndexProvider<use32BitsIndices>>
364 bool GenerateTangents(MeshDefinition::RawData& raw)
366 using IndexType = typename IndexProviderType::IndexType;
368 // mIndicies size must be even if we use 32bit indices.
369 if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0)))
374 auto& attribs = raw.mAttribs;
375 // Required positions, normals, uvs (if we have). If not, skip generation
376 if(DALI_UNLIKELY(attribs.size() < (2 + static_cast<size_t>(hasUvs))))
381 std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(T));
382 auto tangents = reinterpret_cast<T*>(buffer.data());
386 IndexProviderType getIndex(raw.mIndices.data());
388 const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast<uint32_t>(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t)));
390 auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
391 auto* uvs = reinterpret_cast<const Vector2*>(attribs[2].mData.data());
393 for(uint32_t i = 0; i < numIndices; i += 3)
395 IndexType indices[]{getIndex(), getIndex(), getIndex()};
396 Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]};
397 Vector2 uv[]{uvs[indices[0]], uvs[indices[1]], uvs[indices[2]]};
399 float x0 = pos[1].x - pos[0].x;
400 float y0 = pos[1].y - pos[0].y;
401 float z0 = pos[1].z - pos[0].z;
403 float x1 = pos[2].x - pos[0].x;
404 float y1 = pos[2].y - pos[0].y;
405 float z1 = pos[2].z - pos[0].z;
407 float s0 = uv[1].x - uv[0].x;
408 float t0 = uv[1].y - uv[0].y;
410 float s1 = uv[2].x - uv[0].x;
411 float t1 = uv[2].y - uv[0].y;
413 float det = (s0 * t1 - t0 * s1);
414 float r = 1.f / ((std::abs(det) < Dali::Epsilon<1000>::value) ? (Dali::Epsilon<1000>::value * (det > 0.0f ? 1.f : -1.f)) : det);
415 Vector3 tangent((x0 * t1 - t0 * x1) * r, (y0 * t1 - t0 * y1) * r, (z0 * t1 - t0 * z1) * r);
416 tangents[indices[0]] += T(tangent);
417 tangents[indices[1]] += T(tangent);
418 tangents[indices[2]] += T(tangent);
422 auto* normals = reinterpret_cast<const Vector3*>(attribs[1].mData.data());
423 auto iEnd = normals + attribs[1].mNumElements;
424 while(normals != iEnd)
429 // Calculated by indexs
430 tangentVec3 = Vector3((*tangents).x, (*tangents).y, (*tangents).z);
434 // Only choiced by normal vector. by indexs
435 Vector3 t[]{normals->Cross(Vector3::XAXIS), normals->Cross(Vector3::YAXIS)};
436 tangentVec3 = t[t[1].LengthSquared() > t[0].LengthSquared()];
439 tangentVec3 -= *normals * normals->Dot(tangentVec3);
440 tangentVec3.Normalize();
441 if constexpr(useVec3)
443 *tangents = tangentVec3;
447 *tangents = Vector4(tangentVec3.x, tangentVec3.y, tangentVec3.z, 1.0f);
453 attribs.push_back({"aTangent", useVec3 ? Property::VECTOR3 : Property::VECTOR4, attribs[0].mNumElements, std::move(buffer)});
458 void CalculateTextureSize(uint32_t totalTextureSize, uint32_t& textureWidth, uint32_t& textureHeight)
460 DALI_ASSERT_DEBUG(0u != totalTextureSize && "totalTextureSize is zero.")
462 // Calculate the dimensions of the texture.
463 // The total size of the texture is the length of the blend shapes blob.
468 if(0u == totalTextureSize)
474 const uint32_t pow2 = static_cast<uint32_t>(ceil(log2(totalTextureSize)));
475 const uint32_t powWidth = pow2 >> 1u;
476 const uint32_t powHeight = pow2 - powWidth;
478 textureWidth = 1u << powWidth;
479 textureHeight = 1u << powHeight;
483 float GetNormalizedScale()
485 return 1.0f / (std::numeric_limits<T>::max());
489 void DequantizeData(std::vector<uint8_t>& buffer, float* dequantizedValues, uint32_t numValues, bool normalized)
491 // see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_mesh_quantization#encoding-quantized-data
493 T* values = reinterpret_cast<T*>(buffer.data());
495 for(uint32_t i = 0; i < numValues; ++i)
497 *dequantizedValues = normalized ? std::max((*values) * GetNormalizedScale<T>(), -1.0f) : *values;
504 void GetDequantizedData(std::vector<uint8_t>& buffer, uint32_t numComponents, uint32_t count, uint32_t flags, bool normalized)
506 bool dequantized = false;
508 std::vector<uint8_t> dequantizedBuffer(count * numComponents * sizeof(float));
509 float* dequantizedValues = reinterpret_cast<float*>(dequantizedBuffer.data());
511 if(MaskMatch(flags, MeshDefinition::Flags::S8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S8_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S8_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S8_TEXCOORD))
513 DequantizeData<int8_t>(buffer, dequantizedValues, numComponents * count, normalized);
516 else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD))
518 DequantizeData<uint8_t>(buffer, dequantizedValues, numComponents * count, normalized);
521 else if(MaskMatch(flags, MeshDefinition::Flags::S16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S16_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S16_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S16_TEXCOORD))
523 DequantizeData<int16_t>(buffer, dequantizedValues, numComponents * count, normalized);
526 else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD))
528 DequantizeData<uint16_t>(buffer, dequantizedValues, numComponents * count, normalized);
534 buffer = std::move(dequantizedBuffer);
538 void GetDequantizedMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t flags)
542 if(MaskMatch(flags, MeshDefinition::Flags::S8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S8_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S8_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S8_TEXCOORD))
544 scale = GetNormalizedScale<int8_t>();
546 else if(MaskMatch(flags, MeshDefinition::Flags::U8_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U8_TEXCOORD))
548 scale = GetNormalizedScale<uint8_t>();
550 else if(MaskMatch(flags, MeshDefinition::Flags::S16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::S16_NORMAL) || MaskMatch(flags, MeshDefinition::Flags::S16_TANGENT) || MaskMatch(flags, MeshDefinition::Flags::S16_TEXCOORD))
552 scale = GetNormalizedScale<int16_t>();
554 else if(MaskMatch(flags, MeshDefinition::Flags::U16_POSITION) || MaskMatch(flags, MeshDefinition::Flags::U16_TEXCOORD))
556 scale = GetNormalizedScale<uint16_t>();
561 for(float& value : min)
563 value = std::max(value * scale, -1.0f);
566 for(float& value : max)
568 value = std::min(value * scale, 1.0f);
573 void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::vector<MeshDefinition::BlendShape>& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor, BufferDefinition::Vector& buffers)
575 uint32_t geometryBufferIndex = 0u;
576 float maxDistanceSquared = 0.f;
577 Vector3* geometryBufferV3 = reinterpret_cast<Vector3*>(geometryBuffer);
578 for(auto& blendShape : blendShapes)
580 if(blendShape.deltas.IsDefined())
582 const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize();
585 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_POSITION))
587 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % (sizeof(uint8_t) * 3) == 0) ||
588 blendShape.deltas.mBlob.mStride >= (sizeof(uint8_t) * 3)) &&
589 "Blend Shape position buffer length not a multiple of element size");
590 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 3));
592 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_POSITION))
594 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % (sizeof(uint16_t) * 3) == 0) ||
595 blendShape.deltas.mBlob.mStride >= (sizeof(uint16_t) * 3)) &&
596 "Blend Shape position buffer length not a multiple of element size");
597 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 3));
601 DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % sizeof(Vector3) == 0) ||
602 blendShape.deltas.mBlob.mStride >= sizeof(Vector3)) &&
603 "Blend Shape position buffer length not a multiple of element size");
604 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
607 std::vector<uint8_t> buffer(bufferSize);
608 std::vector<uint32_t> sparseIndices{};
610 if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
612 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::POSITIONS_MASK, blendShape.deltas.mNormalized);
614 if(blendShape.deltas.mNormalized)
616 GetDequantizedMinMax(blendShape.deltas.mBlob.mMin, blendShape.deltas.mBlob.mMax, blendShape.mFlags & MeshDefinition::POSITIONS_MASK);
619 blendShape.deltas.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
621 // Calculate the difference with the original mesh.
622 // Find the max distance to normalize the deltas.
623 const auto* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
625 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
626 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
627 delta = deltasBuffer[deltaIndex];
628 return std::max(maxDistanceSquared, delta.LengthSquared());
631 if(sparseIndices.empty())
633 for(uint32_t index = 0u; index < numberOfVertices; ++index)
635 maxDistanceSquared = ProcessVertex(geometryBufferIndex++, index);
640 // initialize blendshape texture
641 // TODO: there may be a case when sparse accessor uses a base buffer view for initial values.
642 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3::ZERO);
643 for(auto index : sparseIndices)
645 maxDistanceSquared = ProcessVertex(geometryBufferIndex + index, index);
647 geometryBufferIndex += numberOfVertices;
652 if(blendShape.normals.IsDefined())
654 const auto bufferSize = blendShape.normals.mBlob.GetBufferSize();
657 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_NORMAL))
659 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
660 blendShape.normals.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
661 "Blend Shape normals buffer length not a multiple of element size");
662 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
664 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_NORMAL))
666 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
667 blendShape.normals.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
668 "Blend Shape normals buffer length not a multiple of element size");
669 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
673 DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % sizeof(Vector3) == 0) ||
674 blendShape.normals.mBlob.mStride >= sizeof(Vector3)) &&
675 "Blend Shape normals buffer length not a multiple of element size");
676 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
679 std::vector<uint8_t> buffer(bufferSize);
680 std::vector<uint32_t> sparseIndices;
682 if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
684 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::NORMALS_MASK, blendShape.normals.mNormalized);
686 if(blendShape.normals.mNormalized)
688 GetDequantizedMinMax(blendShape.normals.mBlob.mMin, blendShape.normals.mBlob.mMax, blendShape.mFlags & MeshDefinition::NORMALS_MASK);
691 blendShape.normals.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
693 // Calculate the difference with the original mesh, and translate to make all values positive.
694 const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
695 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
696 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
706 if(sparseIndices.empty())
708 for(uint32_t index = 0u; index < numberOfVertices; ++index)
710 ProcessVertex(geometryBufferIndex++, index);
715 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5));
716 for(auto index : sparseIndices)
718 ProcessVertex(geometryBufferIndex + index, index);
720 geometryBufferIndex += numberOfVertices;
725 if(blendShape.tangents.IsDefined())
727 const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize();
731 if(MaskMatch(blendShape.mFlags, MeshDefinition::S8_TANGENT))
733 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
734 blendShape.tangents.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
735 "Blend Shape tangents buffer length not a multiple of element size");
736 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
738 else if(MaskMatch(blendShape.mFlags, MeshDefinition::S16_TANGENT))
740 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
741 blendShape.tangents.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
742 "Blend Shape tangents buffer length not a multiple of element size");
743 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
747 DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % sizeof(Vector3) == 0) ||
748 blendShape.tangents.mBlob.mStride >= sizeof(Vector3)) &&
749 "Blend Shape tangents buffer length not a multiple of element size");
750 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
753 std::vector<uint8_t> buffer(bufferSize);
754 std::vector<uint32_t> sparseIndices;
756 if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices))
758 GetDequantizedData(buffer, 3u, numVector3, blendShape.mFlags & MeshDefinition::TANGENTS_MASK, blendShape.tangents.mNormalized);
760 if(blendShape.tangents.mNormalized)
762 GetDequantizedMinMax(blendShape.tangents.mBlob.mMin, blendShape.tangents.mBlob.mMax, blendShape.mFlags & MeshDefinition::TANGENTS_MASK);
765 blendShape.tangents.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()), &sparseIndices);
767 // Calculate the difference with the original mesh, and translate to make all values positive.
768 const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
769 auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) {
770 Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex];
780 if(sparseIndices.empty())
782 for(uint32_t index = 0u; index < numberOfVertices; ++index)
784 ProcessVertex(geometryBufferIndex++, index);
789 std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5));
790 for(auto index : sparseIndices)
792 ProcessVertex(geometryBufferIndex + index, index);
794 geometryBufferIndex += numberOfVertices;
800 geometryBufferIndex = 0u;
802 const float maxDistance = sqrtf(maxDistanceSquared);
804 const float normalizeFactor = (maxDistanceSquared < Math::MACHINE_EPSILON_100) ? 1.f : (0.5f / maxDistance);
806 // Calculate and store the unnormalize factor.
807 blendShapeUnnormalizeFactor = maxDistance * 2.0f;
809 for(const auto& blendShape : blendShapes)
811 // Normalize all the deltas and translate to a possitive value.
812 // Deltas are going to be passed to the shader in a color texture
813 // whose values that are less than zero are clamped.
814 if(blendShape.deltas.IsDefined())
816 for(uint32_t index = 0u; index < numberOfVertices; ++index)
818 Vector3& delta = geometryBufferV3[geometryBufferIndex++];
819 delta.x = Clamp(((delta.x * normalizeFactor) + 0.5f), 0.f, 1.f);
820 delta.y = Clamp(((delta.y * normalizeFactor) + 0.5f), 0.f, 1.f);
821 delta.z = Clamp(((delta.z * normalizeFactor) + 0.5f), 0.f, 1.f);
825 if(blendShape.normals.IsDefined())
827 geometryBufferIndex += numberOfVertices;
830 if(blendShape.tangents.IsDefined())
832 geometryBufferIndex += numberOfVertices;
837 std::iostream& GetAvailableData(std::fstream& meshStream, const std::string& meshPath, BufferDefinition& buffer, std::string& availablePath)
839 auto& stream = (meshStream.is_open()) ? meshStream : buffer.GetBufferStream();
840 availablePath = (meshStream.is_open()) ? meshPath : buffer.GetUri();
846 MeshDefinition::SparseBlob::SparseBlob(const Blob& indices, const Blob& values, uint32_t count)
853 MeshDefinition::SparseBlob::SparseBlob(Blob&& indices, Blob&& values, uint32_t count)
854 : mIndices(std::move(indices)),
855 mValues(std::move(values)),
860 MeshDefinition::Accessor::Accessor(const MeshDefinition::Blob& blob,
861 const MeshDefinition::SparseBlob& sparse,
865 mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{sparse} : nullptr},
866 mBufferIdx(bufferIndex),
867 mNormalized(normalized)
871 MeshDefinition::Accessor::Accessor(MeshDefinition::Blob&& blob,
872 MeshDefinition::SparseBlob&& sparse,
875 : mBlob{std::move(blob)},
876 mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{std::move(sparse)} : nullptr},
877 mBufferIdx(bufferIndex),
878 mNormalized(normalized)
882 void MeshDefinition::Blob::ComputeMinMax(std::vector<float>& min, std::vector<float>& max, uint32_t numComponents, uint32_t count, const float* values)
884 min.assign(numComponents, MAXFLOAT);
885 max.assign(numComponents, -MAXFLOAT);
886 for(uint32_t i = 0; i < count; ++i)
888 for(uint32_t j = 0; j < numComponents; ++j)
890 min[j] = std::min(min[j], *values);
891 max[j] = std::max(max[j], *values);
897 void MeshDefinition::Blob::ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max, uint32_t count, float* values, std::vector<uint32_t>* sparseIndices)
899 DALI_ASSERT_DEBUG(max.size() == min.size() || max.size() * min.size() == 0);
900 const auto numComponents = std::max(min.size(), max.size());
902 using ClampFn = void (*)(const float*, const float*, uint32_t, float&);
903 ClampFn clampFn = min.empty() ? (max.empty() ? static_cast<ClampFn>(nullptr) : [](const float* min, const float* max, uint32_t i, float& value) { value = std::min(max[i], value); })
904 : (max.empty() ? [](const float* min, const float* max, uint32_t i, float& value) { value = std::max(min[i], value); }
905 : static_cast<ClampFn>([](const float* min, const float* max, uint32_t i, float& value) { value = std::min(std::max(min[i], value), max[i]); }));
912 auto end = values + count * numComponents;
915 auto nextElement = values + numComponents;
917 while(values != nextElement)
919 clampFn(min.data(), max.data(), i, *values);
926 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)
930 mElementSizeHint(elementSizeHint),
936 uint32_t MeshDefinition::Blob::GetBufferSize() const
941 void MeshDefinition::Blob::ComputeMinMax(uint32_t numComponents, uint32_t count, float* values)
943 ComputeMinMax(mMin, mMax, numComponents, count, values);
946 void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values, std::vector<uint32_t>* sparseIndices) const
948 ApplyMinMax(mMin, mMax, count, values, sparseIndices);
951 void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const
953 Property::Map attribMap;
954 attribMap[mName] = mType;
955 VertexBuffer attribBuffer = VertexBuffer::New(attribMap);
956 attribBuffer.SetData(mData.data(), mNumElements);
958 g.AddVertexBuffer(attribBuffer);
961 bool MeshDefinition::IsQuad() const
963 return CaseInsensitiveStringCompare(QUAD, mUri);
966 bool MeshDefinition::IsSkinned() const
968 return !mJoints.empty() && !mWeights.empty();
970 uint32_t MeshDefinition::GetNumberOfJointSets() const
972 return static_cast<uint32_t>(mJoints.size());
975 bool MeshDefinition::HasBlendShapes() const
977 return !mBlendShapes.empty();
980 void MeshDefinition::RequestNormals()
982 mNormals.mBlob.mLength = mPositions.mBlob.GetBufferSize();
985 void MeshDefinition::RequestTangents()
987 mTangents.mBlob.mLength = mNormals.mBlob.GetBufferSize();
990 MeshDefinition::RawData
991 MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& buffers)
999 std::string meshPath;
1000 meshPath = modelsPath + mUri;
1001 std::fstream fileStream;
1004 fileStream.open(meshPath, std::ios::in | std::ios::binary);
1005 if(!fileStream.is_open())
1007 DALI_LOG_ERROR("Fail to open buffer from %s.\n", meshPath.c_str());
1011 if(mIndices.IsDefined())
1013 if(MaskMatch(mFlags, U32_INDICES))
1015 DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(uint32_t) == 0) ||
1016 mIndices.mBlob.mStride >= sizeof(uint32_t)) &&
1017 "Index buffer length not a multiple of element size");
1018 const auto indexCount = mIndices.mBlob.GetBufferSize() / sizeof(uint32_t);
1019 raw.mIndices.resize(indexCount * 2); // NOTE: we need space for uint32_ts initially.
1022 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path);
1023 if(!ReadAccessor(mIndices, stream, reinterpret_cast<uint8_t*>(raw.mIndices.data())))
1025 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
1028 else if(MaskMatch(mFlags, U8_INDICES))
1030 DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(uint8_t) == 0) ||
1031 mIndices.mBlob.mStride >= sizeof(uint8_t)) &&
1032 "Index buffer length not a multiple of element size");
1033 const auto indexCount = mIndices.mBlob.GetBufferSize() / sizeof(uint8_t);
1034 raw.mIndices.resize(indexCount); // NOTE: we need space for uint16_ts initially.
1037 auto u8s = reinterpret_cast<uint8_t*>(raw.mIndices.data()) + indexCount;
1038 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path);
1039 if(!ReadAccessor(mIndices, stream, u8s))
1041 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
1044 auto u16s = raw.mIndices.data();
1045 auto end = u8s + indexCount;
1048 *u16s = static_cast<uint16_t>(*u8s);
1055 DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(unsigned short) == 0) ||
1056 mIndices.mBlob.mStride >= sizeof(unsigned short)) &&
1057 "Index buffer length not a multiple of element size");
1058 raw.mIndices.resize(mIndices.mBlob.mLength / sizeof(unsigned short));
1061 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path);
1062 if(!ReadAccessor(mIndices, stream, reinterpret_cast<uint8_t*>(raw.mIndices.data())))
1064 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'.";
1069 uint32_t numberOfVertices = 0u;
1071 std::vector<Vector3> positions;
1072 if(mPositions.IsDefined())
1074 const auto bufferSize = mPositions.mBlob.GetBufferSize();
1075 uint32_t numVector3;
1077 if(MaskMatch(mFlags, S8_POSITION) || MaskMatch(mFlags, U8_POSITION))
1079 DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % (sizeof(uint8_t) * 3) == 0) ||
1080 mPositions.mBlob.mStride >= (sizeof(uint8_t) * 3)) &&
1081 "Position buffer length not a multiple of element size");
1082 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 3));
1084 else if(MaskMatch(mFlags, S16_POSITION) || MaskMatch(mFlags, U16_POSITION))
1086 DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % (sizeof(uint16_t) * 3) == 0) ||
1087 mPositions.mBlob.mStride >= (sizeof(uint16_t) * 3)) &&
1088 "Position buffer length not a multiple of element size");
1089 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 3));
1093 DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % sizeof(Vector3) == 0) ||
1094 mPositions.mBlob.mStride >= sizeof(Vector3)) &&
1095 "Position buffer length not a multiple of element size");
1096 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
1099 numberOfVertices = numVector3;
1101 std::vector<uint8_t> buffer(bufferSize);
1104 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mPositions.mBufferIdx], path);
1105 if(!ReadAccessor(mPositions, stream, buffer.data()))
1107 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << path << "'.";
1110 GetDequantizedData(buffer, 3u, numVector3, mFlags & POSITIONS_MASK, mPositions.mNormalized);
1112 if(mPositions.mNormalized)
1114 GetDequantizedMinMax(mPositions.mBlob.mMin, mPositions.mBlob.mMax, mFlags & POSITIONS_MASK);
1117 if(mPositions.mBlob.mMin.size() != 3u || mPositions.mBlob.mMax.size() != 3u)
1119 mPositions.mBlob.ComputeMinMax(3u, numVector3, reinterpret_cast<float*>(buffer.data()));
1123 mPositions.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
1126 if(HasBlendShapes())
1128 positions.resize(numVector3);
1129 std::copy(buffer.data(), buffer.data() + buffer.size(), reinterpret_cast<uint8_t*>(positions.data()));
1132 raw.mAttribs.push_back({"aPosition", Property::VECTOR3, numVector3, std::move(buffer)});
1135 const auto isTriangles = mPrimitiveType == Geometry::TRIANGLES;
1136 auto hasNormals = mNormals.IsDefined();
1139 const auto bufferSize = mNormals.mBlob.GetBufferSize();
1140 uint32_t numVector3;
1142 if(MaskMatch(mFlags, S8_NORMAL))
1144 DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % (sizeof(int8_t) * 3) == 0) ||
1145 mNormals.mBlob.mStride >= (sizeof(int8_t) * 3)) &&
1146 "Normal buffer length not a multiple of element size");
1147 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * 3));
1149 else if(MaskMatch(mFlags, S16_NORMAL))
1151 DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % (sizeof(int16_t) * 3) == 0) ||
1152 mNormals.mBlob.mStride >= (sizeof(int16_t) * 3)) &&
1153 "Normal buffer length not a multiple of element size");
1154 numVector3 = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * 3));
1158 DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % sizeof(Vector3) == 0) ||
1159 mNormals.mBlob.mStride >= sizeof(Vector3)) &&
1160 "Normal buffer length not a multiple of element size");
1161 numVector3 = static_cast<uint32_t>(bufferSize / sizeof(Vector3));
1164 std::vector<uint8_t> buffer(bufferSize);
1167 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mNormals.mBufferIdx], path);
1168 if(!ReadAccessor(mNormals, stream, buffer.data()))
1170 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << path << "'.";
1173 GetDequantizedData(buffer, 3u, numVector3, mFlags & NORMALS_MASK, mNormals.mNormalized);
1175 if(mNormals.mNormalized)
1177 GetDequantizedMinMax(mNormals.mBlob.mMin, mNormals.mBlob.mMax, mFlags & NORMALS_MASK);
1180 mNormals.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
1182 raw.mAttribs.push_back({"aNormal", Property::VECTOR3, numVector3, std::move(buffer)});
1184 else if(mNormals.mBlob.mLength != 0 && isTriangles)
1186 DALI_ASSERT_DEBUG(mNormals.mBlob.mLength == mPositions.mBlob.GetBufferSize());
1187 static const std::function<bool(RawData&)> GenerateNormalsFunction[2] =
1189 GenerateNormals<false>,
1190 GenerateNormals<true>,
1192 const bool generateSuccessed = GenerateNormalsFunction[MaskMatch(mFlags, U32_INDICES)](raw);
1193 if(!generateSuccessed)
1195 DALI_LOG_ERROR("Failed to generate normal\n");
1203 if(!mTexCoords.empty() && mTexCoords[0].IsDefined())
1205 auto& texCoords = mTexCoords[0];
1206 const auto bufferSize = texCoords.mBlob.GetBufferSize();
1209 if(MaskMatch(mFlags, S8_TEXCOORD) || MaskMatch(mFlags, U8_TEXCOORD))
1211 DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % (sizeof(uint8_t) * 2) == 0) ||
1212 texCoords.mBlob.mStride >= (sizeof(uint8_t) * 2)) &&
1213 "TexCoords buffer length not a multiple of element size");
1214 uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint8_t) * 2));
1216 else if(MaskMatch(mFlags, S16_TEXCOORD) || MaskMatch(mFlags, U16_TEXCOORD))
1218 DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % (sizeof(uint16_t) * 2) == 0) ||
1219 texCoords.mBlob.mStride >= (sizeof(uint16_t) * 2)) &&
1220 "TexCoords buffer length not a multiple of element size");
1221 uvCount = static_cast<uint32_t>(bufferSize / (sizeof(uint16_t) * 2));
1225 DALI_ASSERT_ALWAYS(((texCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
1226 texCoords.mBlob.mStride >= sizeof(Vector2)) &&
1227 "TexCoords buffer length not a multiple of element size");
1228 uvCount = static_cast<uint32_t>(bufferSize / sizeof(Vector2));
1231 std::vector<uint8_t> buffer(bufferSize);
1234 auto& stream = GetAvailableData(fileStream, meshPath, buffers[texCoords.mBufferIdx], path);
1235 if(!ReadAccessor(texCoords, stream, buffer.data()))
1237 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << path << "'.";
1240 GetDequantizedData(buffer, 2u, uvCount, mFlags & TEXCOORDS_MASK, texCoords.mNormalized);
1242 if(MaskMatch(mFlags, FLIP_UVS_VERTICAL))
1244 auto uv = reinterpret_cast<Vector2*>(buffer.data());
1245 auto uvEnd = uv + uvCount;
1248 uv->y = 1.0f - uv->y;
1253 if(texCoords.mNormalized)
1255 GetDequantizedMinMax(texCoords.mBlob.mMin, texCoords.mBlob.mMax, mFlags & TEXCOORDS_MASK);
1258 texCoords.mBlob.ApplyMinMax(static_cast<uint32_t>(uvCount), reinterpret_cast<float*>(buffer.data()));
1259 raw.mAttribs.push_back({"aTexCoord", Property::VECTOR2, static_cast<uint32_t>(uvCount), std::move(buffer)});
1262 if(mTangents.IsDefined())
1264 const auto bufferSize = mTangents.mBlob.GetBufferSize();
1266 uint32_t propertySize = static_cast<uint32_t>((mTangentType == Property::VECTOR4) ? sizeof(Vector4) : sizeof(Vector3));
1267 uint32_t componentCount = static_cast<uint32_t>(propertySize / sizeof(float));
1269 uint32_t numTangents;
1271 if(MaskMatch(mFlags, S8_TANGENT))
1273 DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % (sizeof(int8_t) * componentCount) == 0) ||
1274 mTangents.mBlob.mStride >= (sizeof(int8_t) * componentCount)) &&
1275 "Tangents buffer length not a multiple of element size");
1276 numTangents = static_cast<uint32_t>(bufferSize / (sizeof(int8_t) * componentCount));
1278 else if(MaskMatch(mFlags, S16_TANGENT))
1280 DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % (sizeof(int16_t) * componentCount) == 0) ||
1281 mTangents.mBlob.mStride >= (sizeof(int16_t) * componentCount)) &&
1282 "Tangents buffer length not a multiple of element size");
1283 numTangents = static_cast<uint32_t>(bufferSize / (sizeof(int16_t) * componentCount));
1287 DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % propertySize == 0) ||
1288 mTangents.mBlob.mStride >= propertySize) &&
1289 "Tangents buffer length not a multiple of element size");
1290 numTangents = static_cast<uint32_t>(bufferSize / propertySize);
1293 std::vector<uint8_t> buffer(bufferSize);
1296 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mTangents.mBufferIdx], path);
1297 if(!ReadAccessor(mTangents, stream, buffer.data()))
1299 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << path << "'.";
1302 GetDequantizedData(buffer, componentCount, numTangents, mFlags & TANGENTS_MASK, mTangents.mNormalized);
1304 if(mTangents.mNormalized)
1306 GetDequantizedMinMax(mTangents.mBlob.mMin, mTangents.mBlob.mMax, mFlags & TANGENTS_MASK);
1309 mTangents.mBlob.ApplyMinMax(numTangents, reinterpret_cast<float*>(buffer.data()));
1311 raw.mAttribs.push_back({"aTangent", mTangentType, static_cast<uint32_t>(numTangents), std::move(buffer)});
1313 else if(mTangents.mBlob.mLength != 0 && hasNormals && isTriangles)
1315 DALI_ASSERT_DEBUG(mTangents.mBlob.mLength == mNormals.mBlob.GetBufferSize());
1316 static const std::function<bool(RawData&)> GenerateTangentsFunction[2][2][2] =
1320 GenerateTangents<false, false, false>,
1321 GenerateTangents<false, false, true>,
1324 GenerateTangents<false, true, false>,
1325 GenerateTangents<false, true, true>,
1330 GenerateTangents<true, false, false>,
1331 GenerateTangents<true, false, true>,
1334 GenerateTangents<true, true, false>,
1335 GenerateTangents<true, true, true>,
1338 const bool hasUvs = !mTexCoords.empty() && mTexCoords[0].IsDefined();
1339 const bool generateSuccessed = GenerateTangentsFunction[MaskMatch(mFlags, U32_INDICES)][mTangentType == Property::VECTOR3][hasUvs](raw);
1340 if(!generateSuccessed)
1342 DALI_LOG_ERROR("Failed to generate tangents\n");
1346 // Only support 1 vertex color
1347 if(!mColors.empty() && mColors[0].IsDefined())
1349 uint32_t propertySize = mColors[0].mBlob.mElementSizeHint;
1350 Property::Type propertyType = (propertySize == sizeof(Vector4)) ? Property::VECTOR4 : ((propertySize == sizeof(Vector3)) ? Property::VECTOR3 : Property::NONE);
1351 if(propertyType != Property::NONE)
1353 DALI_ASSERT_ALWAYS(((mColors[0].mBlob.mLength % propertySize == 0) ||
1354 mColors[0].mBlob.mStride >= propertySize) &&
1355 "Colors buffer length not a multiple of element size");
1356 const auto bufferSize = mColors[0].mBlob.GetBufferSize();
1357 std::vector<uint8_t> buffer(bufferSize);
1360 auto& stream = GetAvailableData(fileStream, meshPath, buffers[mColors[0].mBufferIdx], path);
1361 if(!ReadAccessor(mColors[0], stream, buffer.data()))
1363 ExceptionFlinger(ASSERT_LOCATION) << "Failed to read colors from '" << path << "'.";
1365 mColors[0].mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
1367 raw.mAttribs.push_back({"aVertexColor", propertyType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
1372 std::vector<uint8_t> buffer(raw.mAttribs[0].mNumElements * sizeof(Vector4));
1373 auto colors = reinterpret_cast<Vector4*>(buffer.data());
1375 for(uint32_t i = 0; i < raw.mAttribs[0].mNumElements; i++)
1377 colors[i] = Vector4::ONE;
1380 raw.mAttribs.push_back({"aVertexColor", Property::VECTOR4, raw.mAttribs[0].mNumElements, std::move(buffer)});
1386 for(auto& accessor : mJoints)
1388 std::string pathJoint;
1389 auto& streamJoint = GetAvailableData(fileStream, meshPath, buffers[accessor.mBufferIdx], pathJoint);
1390 std::ostringstream jointName;
1391 jointName << "aJoints" << setIndex;
1393 ReadTypedJointAccessor(raw, mFlags, accessor, streamJoint, pathJoint, jointName.str());
1396 for(auto& accessor : mWeights)
1398 std::string pathWeight;
1399 auto& streamWeight = GetAvailableData(fileStream, meshPath, buffers[accessor.mBufferIdx], pathWeight);
1400 std::ostringstream weightName;
1401 weightName << "aWeights" << setIndex;
1403 ReadTypedWeightAccessor(raw, mFlags, accessor, streamWeight, pathWeight, weightName.str());
1407 // Calculate the Blob for the blend shapes.
1408 Blob blendShapesBlob;
1409 blendShapesBlob.mOffset = std::numeric_limits<unsigned int>::max();
1410 blendShapesBlob.mLength = 0u;
1412 uint32_t totalTextureSize(0u);
1414 auto processAccessor = [&](const Accessor& accessor, uint32_t vector3Size) {
1415 if(accessor.IsDefined())
1417 blendShapesBlob.mOffset = std::min(blendShapesBlob.mOffset, accessor.mBlob.mOffset);
1418 blendShapesBlob.mLength += accessor.mBlob.mLength;
1420 totalTextureSize += accessor.mBlob.mLength / vector3Size;
1424 for(const auto& blendShape : mBlendShapes)
1426 const auto positionMask = blendShape.mFlags & POSITIONS_MASK;
1427 const auto normalMask = blendShape.mFlags & NORMALS_MASK;
1428 const auto tangentMask = blendShape.mFlags & TANGENTS_MASK;
1430 processAccessor(blendShape.deltas, MaskMatch(positionMask, S8_POSITION) ? sizeof(uint8_t) * 3 : (MaskMatch(positionMask, S16_POSITION) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1431 processAccessor(blendShape.normals, MaskMatch(normalMask, S8_NORMAL) ? sizeof(uint8_t) * 3 : (MaskMatch(normalMask, S16_NORMAL) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1432 processAccessor(blendShape.tangents, MaskMatch(tangentMask, S8_TANGENT) ? sizeof(uint8_t) * 3 : (MaskMatch(tangentMask, S16_TANGENT) ? sizeof(uint16_t) * 3 : sizeof(Vector3)));
1435 if(HasBlendShapes())
1437 // Calculate the size of one buffer inside the texture.
1438 raw.mBlendShapeBufferOffset = numberOfVertices;
1440 bool calculateGltf2BlendShapes = false;
1441 uint32_t textureWidth = 0u;
1442 uint32_t textureHeight = 0u;
1444 if(!mBlendShapeHeader.IsDefined())
1446 CalculateTextureSize(totalTextureSize, textureWidth, textureHeight);
1447 calculateGltf2BlendShapes = true;
1451 uint16_t header[2u];
1452 ReadBlob(mBlendShapeHeader, fileStream, reinterpret_cast<uint8_t*>(header));
1453 textureWidth = header[0u];
1454 textureHeight = header[1u];
1457 const uint32_t numberOfBlendShapes = mBlendShapes.size();
1458 raw.mBlendShapeUnnormalizeFactor.Resize(numberOfBlendShapes);
1460 Devel::PixelBuffer geometryPixelBuffer = Devel::PixelBuffer::New(textureWidth, textureHeight, Pixel::RGB32F);
1461 uint8_t* geometryBuffer = geometryPixelBuffer.GetBuffer();
1463 if(calculateGltf2BlendShapes)
1465 CalculateGltf2BlendShapes(geometryBuffer, mBlendShapes, numberOfVertices, raw.mBlendShapeUnnormalizeFactor[0u], buffers);
1469 Blob unnormalizeFactorBlob;
1470 unnormalizeFactorBlob.mLength = static_cast<uint32_t>(sizeof(float) * ((BlendShapes::Version::VERSION_2_0 == mBlendShapeVersion) ? 1u : numberOfBlendShapes));
1472 if(blendShapesBlob.IsDefined())
1474 if(ReadBlob(blendShapesBlob, fileStream, geometryBuffer))
1476 unnormalizeFactorBlob.mOffset = blendShapesBlob.mOffset + blendShapesBlob.mLength;
1480 // Read the unnormalize factors.
1481 if(unnormalizeFactorBlob.IsDefined())
1483 ReadBlob(unnormalizeFactorBlob, fileStream, reinterpret_cast<uint8_t*>(&raw.mBlendShapeUnnormalizeFactor[0u]));
1486 raw.mBlendShapeData = Devel::PixelBuffer::Convert(geometryPixelBuffer);
1492 MeshGeometry MeshDefinition::Load(RawData&& raw) const
1494 MeshGeometry meshGeometry;
1495 meshGeometry.geometry = Geometry::New();
1496 meshGeometry.geometry.SetType(mPrimitiveType);
1498 if(IsQuad()) // TODO: do this in raw data; provide MakeTexturedQuadGeometry() that only creates buffers.
1500 auto options = MaskMatch(mFlags, FLIP_UVS_VERTICAL) ? TexturedQuadOptions::FLIP_VERTICAL : 0;
1501 meshGeometry.geometry = MakeTexturedQuadGeometry(options);
1505 if(!raw.mIndices.empty())
1507 if(MaskMatch(mFlags, U32_INDICES))
1509 // TODO : We can only store indeces as uint16_type. Send Dali::Geometry that we use it as uint32_t actual.
1510 meshGeometry.geometry.SetIndexBuffer(reinterpret_cast<const uint32_t*>(raw.mIndices.data()), raw.mIndices.size() / 2);
1514 meshGeometry.geometry.SetIndexBuffer(raw.mIndices.data(), raw.mIndices.size());
1518 for(auto& a : raw.mAttribs)
1520 a.AttachBuffer(meshGeometry.geometry);
1523 if(HasBlendShapes())
1525 meshGeometry.blendShapeBufferOffset = raw.mBlendShapeBufferOffset;
1526 meshGeometry.blendShapeUnnormalizeFactor = std::move(raw.mBlendShapeUnnormalizeFactor);
1528 meshGeometry.blendShapeGeometry = Texture::New(TextureType::TEXTURE_2D,
1529 raw.mBlendShapeData.GetPixelFormat(),
1530 raw.mBlendShapeData.GetWidth(),
1531 raw.mBlendShapeData.GetHeight());
1532 meshGeometry.blendShapeGeometry.Upload(raw.mBlendShapeData);
1536 return meshGeometry;
1539 void MeshDefinition::RetrieveBlendShapeComponents(bool& hasPositions, bool& hasNormals, bool& hasTangents) const
1541 for(const auto& blendShape : mBlendShapes)
1543 hasPositions = hasPositions || blendShape.deltas.IsDefined();
1544 hasNormals = hasNormals || blendShape.normals.IsDefined();
1545 hasTangents = hasTangents || blendShape.tangents.IsDefined();
1549 } // namespace Dali::Scene3D::Loader