X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-scene3d%2Fpublic-api%2Floader%2Fmesh-definition.cpp;h=76a6fa19ee9a6f7d9d499cd6a1fa255b0f4fdd45;hb=f8c5751dd0c375ecbfafa72b7c1ac36fe3d060fd;hp=35835b9fb3d7b502c7dfe7db1170b8d1a210b12e;hpb=75ee09089382a103cddba236e2a22c118bfd4b13;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git diff --git a/dali-scene3d/public-api/loader/mesh-definition.cpp b/dali-scene3d/public-api/loader/mesh-definition.cpp index 35835b9..76a6fa1 100644 --- a/dali-scene3d/public-api/loader/mesh-definition.cpp +++ b/dali-scene3d/public-api/loader/mesh-definition.cpp @@ -15,60 +15,65 @@ * */ -// INTERNAL INCLUDES -#include "dali-scene3d/public-api/loader/mesh-definition.h" +// CLASS HEADER +#include // EXTERNAL INCLUDES +#include +#include +#include +#include #include #include -#include "dali/devel-api/adaptor-framework/pixel-buffer.h" +#include +#include -namespace Dali -{ -namespace Scene3D -{ -namespace Loader +namespace Dali::Scene3D::Loader { namespace { +template class IndexProvider { public: + using IndexType = typename std::conditional_t; IndexProvider(const uint16_t* indices) : mData(reinterpret_cast(indices)), mFunc(indices ? IncrementPointer : Increment) { } - uint16_t operator()() + IndexType operator()() { return mFunc(mData); } private: - static uint16_t Increment(uintptr_t& data) + static IndexType Increment(uintptr_t& data) { - return static_cast(data++); + // mData was 'zero' at construct time. Just simply return counter start with 0. + return static_cast(data++); } - static uint16_t IncrementPointer(uintptr_t& data) + static IndexType IncrementPointer(uintptr_t& data) { - auto iPtr = reinterpret_cast(data); + auto iPtr = reinterpret_cast(data); auto result = *iPtr; data = reinterpret_cast(++iPtr); return result; } uintptr_t mData; - uint16_t (*mFunc)(uintptr_t&); + IndexType (*mFunc)(uintptr_t&); }; -const std::string QUAD("quad"); +const char* QUAD("quad"); ///@brief Reads a blob from the given stream @a source into @a target, which must have /// at least @a descriptor.length bytes. bool ReadBlob(const MeshDefinition::Blob& descriptor, std::istream& source, uint8_t* target) { + source.clear(); if(!source.seekg(descriptor.mOffset, std::istream::beg)) { return false; @@ -76,7 +81,7 @@ bool ReadBlob(const MeshDefinition::Blob& descriptor, std::istream& source, uint if(descriptor.IsConsecutive()) { - return !!source.read(reinterpret_cast(target), descriptor.mLength); + return !!source.read(reinterpret_cast(target), static_cast(static_cast(descriptor.mLength))); } else { @@ -86,11 +91,11 @@ bool ReadBlob(const MeshDefinition::Blob& descriptor, std::istream& source, uint uint32_t readSize = 0; uint32_t totalSize = (descriptor.mLength / descriptor.mElementSizeHint) * descriptor.mStride; while(readSize < totalSize && - source.read(reinterpret_cast(target), descriptor.mElementSizeHint) && - source.seekg(diff, std::istream::cur)) + source.read(reinterpret_cast(target), descriptor.mElementSizeHint)) { readSize += descriptor.mStride; target += descriptor.mElementSizeHint; + source.seekg(diff, std::istream::cur); } return readSize == totalSize; } @@ -109,7 +114,7 @@ void ReadValues(const std::vector& valuesBuffer, const std::vector* sparseIndices) { bool success = false; @@ -148,33 +153,65 @@ bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source return false; } + // If non-null sparse indices vector, prepare it for output + if(sparseIndices) + { + sparseIndices->resize(accessor.mSparse->mCount); + } + switch(indices.mElementSizeHint) { case 1u: { ReadValues(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint); + if(sparseIndices) + { + // convert 8-bit indices into 32-bit + std::transform(indicesBuffer.begin(), indicesBuffer.end(), sparseIndices->begin(), [](const uint8_t& value) { return uint32_t(value); }); + } break; } case 2u: { ReadValues(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint); + if(sparseIndices) + { + // convert 16-bit indices into 32-bit + std::transform(reinterpret_cast(indicesBuffer.data()), + reinterpret_cast(indicesBuffer.data()) + accessor.mSparse->mCount, + sparseIndices->begin(), + [](const uint16_t& value) { + return uint32_t(value); + }); + } break; } case 4u: { ReadValues(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint); + if(sparseIndices) + { + std::copy(indicesBuffer.begin(), indicesBuffer.end(), reinterpret_cast(sparseIndices->data())); + } break; } default: + { DALI_ASSERT_DEBUG(!"Unsupported type for an index"); + } } } return success; } +bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target) +{ + return ReadAccessor(accessor, source, target, nullptr); +} + template -void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::ifstream& binFile, const std::string& meshPath) +void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath) { constexpr auto sizeofBlobUnit = sizeof(T) * 4; @@ -186,7 +223,7 @@ void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Acces std::vector buffer(outBufferSize); auto inBuffer = buffer.data() + outBufferSize - inBufferSize; - if(!ReadAccessor(accessor, binFile, inBuffer)) + if(!ReadAccessor(accessor, source, inBuffer)) { ExceptionFlinger(ASSERT_LOCATION) << "Failed to read joints from '" << meshPath << "'."; } @@ -207,13 +244,58 @@ void ReadJointAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Acces raw.mAttribs.push_back({"aJoints", Property::VECTOR4, static_cast(outBufferSize / sizeof(Vector4)), std::move(buffer)}); } -void GenerateNormals(MeshDefinition::RawData& raw) +template +void ReadWeightAccessor(MeshDefinition::RawData& raw, const MeshDefinition::Accessor& accessor, std::istream& source, const std::string& meshPath) +{ + constexpr auto sizeofBlobUnit = sizeof(T) * 4; + + DALI_ASSERT_ALWAYS(((accessor.mBlob.mLength % sizeofBlobUnit == 0) || + accessor.mBlob.mStride >= sizeofBlobUnit) && + "weights buffer length not a multiple of element size"); + const auto inBufferSize = accessor.mBlob.GetBufferSize(); + const auto outBufferSize = (sizeof(Vector4) / sizeofBlobUnit) * inBufferSize; + + std::vector buffer(outBufferSize); + auto inBuffer = buffer.data() + outBufferSize - inBufferSize; + if(!ReadAccessor(accessor, source, inBuffer)) + { + ExceptionFlinger(ASSERT_LOCATION) << "Failed to read weights from '" << meshPath << "'."; + } + + if constexpr(sizeofBlobUnit != sizeof(Vector4)) + { + auto floats = reinterpret_cast(buffer.data()); + const auto end = inBuffer + inBufferSize; + while(inBuffer != end) + { + const auto value = *reinterpret_cast(inBuffer); + // Normalize weight value. value /= 255 for uint8_t weight, and value /= 65535 for uint16_t weight. + *floats = static_cast(value) / static_cast((1 << (sizeof(T) * 8)) - 1); + + inBuffer += sizeof(T); + ++floats; + } + } + raw.mAttribs.push_back({"aWeights", Property::VECTOR4, static_cast(outBufferSize / sizeof(Vector4)), std::move(buffer)}); +} + +template> +bool GenerateNormals(MeshDefinition::RawData& raw) { + using IndexType = typename IndexProviderType::IndexType; + + // mIndicies size must be even if we use 32bit indices. + if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0))) + { + return false; + } + auto& attribs = raw.mAttribs; DALI_ASSERT_DEBUG(attribs.size() > 0); // positions - IndexProvider getIndex(raw.mIndices.data()); - const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast(raw.mIndices.size()); + IndexProviderType getIndex(raw.mIndices.data()); + + const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t))); auto* positions = reinterpret_cast(attribs[0].mData.data()); @@ -222,8 +304,8 @@ void GenerateNormals(MeshDefinition::RawData& raw) for(uint32_t i = 0; i < numIndices; i += 3) { - uint16_t indices[]{getIndex(), getIndex(), getIndex()}; - Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]}; + IndexType indices[]{getIndex(), getIndex(), getIndex()}; + Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]}; Vector3 a = pos[1] - pos[0]; Vector3 b = pos[2] - pos[0]; @@ -242,85 +324,103 @@ void GenerateNormals(MeshDefinition::RawData& raw) } attribs.push_back({"aNormal", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer)}); + + return true; } -void GenerateTangentsWithUvs(MeshDefinition::RawData& raw) +template, typename = std::enable_if_t<(std::is_same::value || std::is_same::value)>, typename IndexProviderType = IndexProvider> +bool GenerateTangents(MeshDefinition::RawData& raw) { - auto& attribs = raw.mAttribs; - DALI_ASSERT_DEBUG(attribs.size() > 2); // positions, normals, uvs - IndexProvider getIndex(raw.mIndices.data()); - - const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast(raw.mIndices.size()); - - auto* positions = reinterpret_cast(attribs[0].mData.data()); - auto* uvs = reinterpret_cast(attribs[2].mData.data()); + using IndexType = typename IndexProviderType::IndexType; - std::vector buffer(attribs[0].mNumElements * sizeof(Vector3)); - auto tangents = reinterpret_cast(buffer.data()); + // mIndicies size must be even if we use 32bit indices. + if(DALI_UNLIKELY(use32BitsIndices && !raw.mIndices.empty() && !(raw.mIndices.size() % (sizeof(IndexType) / sizeof(uint16_t)) == 0))) + { + return false; + } - for(uint32_t i = 0; i < numIndices; i += 3) + auto& attribs = raw.mAttribs; + // Required positions, normals, uvs (if we have). If not, skip generation + if(DALI_UNLIKELY(attribs.size() < (2 + static_cast(hasUvs)))) { - uint16_t indices[]{getIndex(), getIndex(), getIndex()}; - Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]}; - Vector2 uv[]{uvs[indices[0]], uvs[indices[1]], uvs[indices[2]]}; + return false; + } - float x0 = pos[1].x - pos[0].x; - float y0 = pos[1].y - pos[0].y; - float z0 = pos[1].z - pos[0].z; + std::vector buffer(attribs[0].mNumElements * sizeof(T)); + auto tangents = reinterpret_cast(buffer.data()); - float x1 = pos[2].x - pos[0].x; - float y1 = pos[2].y - pos[0].y; - float z1 = pos[2].z - pos[0].z; + if constexpr(hasUvs) + { + IndexProviderType getIndex(raw.mIndices.data()); - float s0 = uv[1].x - uv[0].x; - float t0 = uv[1].y - uv[0].y; + const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : static_cast(raw.mIndices.size() / (sizeof(IndexType) / sizeof(uint16_t))); - float s1 = uv[2].x - uv[0].x; - float t1 = uv[2].y - uv[0].y; + auto* positions = reinterpret_cast(attribs[0].mData.data()); + auto* uvs = reinterpret_cast(attribs[2].mData.data()); - float r = 1.f / (s0 * t1 - t0 * s1); - Vector3 tangent((x0 * t1 - t0 * x1) * r, (y0 * t1 - t0 * y1) * r, (z0 * t1 - t0 * z1) * r); - tangents[indices[0]] += tangent; - tangents[indices[1]] += tangent; - tangents[indices[2]] += tangent; + for(uint32_t i = 0; i < numIndices; i += 3) + { + IndexType indices[]{getIndex(), getIndex(), getIndex()}; + Vector3 pos[]{positions[indices[0]], positions[indices[1]], positions[indices[2]]}; + Vector2 uv[]{uvs[indices[0]], uvs[indices[1]], uvs[indices[2]]}; + + float x0 = pos[1].x - pos[0].x; + float y0 = pos[1].y - pos[0].y; + float z0 = pos[1].z - pos[0].z; + + float x1 = pos[2].x - pos[0].x; + float y1 = pos[2].y - pos[0].y; + float z1 = pos[2].z - pos[0].z; + + float s0 = uv[1].x - uv[0].x; + float t0 = uv[1].y - uv[0].y; + + float s1 = uv[2].x - uv[0].x; + float t1 = uv[2].y - uv[0].y; + + float det = (s0 * t1 - t0 * s1); + float r = 1.f / ((std::abs(det) < Dali::Epsilon<1000>::value) ? (Dali::Epsilon<1000>::value * (det > 0.0f ? 1.f : -1.f)) : det); + Vector3 tangent((x0 * t1 - t0 * x1) * r, (y0 * t1 - t0 * y1) * r, (z0 * t1 - t0 * z1) * r); + tangents[indices[0]] += T(tangent); + tangents[indices[1]] += T(tangent); + tangents[indices[2]] += T(tangent); + } } auto* normals = reinterpret_cast(attribs[1].mData.data()); auto iEnd = normals + attribs[1].mNumElements; while(normals != iEnd) { - *tangents -= *normals * normals->Dot(*tangents); - tangents->Normalize(); - - ++tangents; - ++normals; - } - attribs.push_back({"aTangent", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer)}); -} - -void GenerateTangents(MeshDefinition::RawData& raw) -{ - auto& attribs = raw.mAttribs; - DALI_ASSERT_DEBUG(attribs.size() > 1); // positions, normals - - auto* normals = reinterpret_cast(attribs[1].mData.data()); - - std::vector buffer(attribs[0].mNumElements * sizeof(Vector3)); - auto tangents = reinterpret_cast(buffer.data()); - - auto iEnd = normals + attribs[1].mNumElements; - while(normals != iEnd) - { - Vector3 t[]{normals->Cross(Vector3::XAXIS), normals->Cross(Vector3::YAXIS)}; + Vector3 tangentVec3; + if constexpr(hasUvs) + { + // Calculated by indexs + tangentVec3 = Vector3((*tangents).x, (*tangents).y, (*tangents).z); + } + else + { + // Only choiced by normal vector. by indexs + Vector3 t[]{normals->Cross(Vector3::XAXIS), normals->Cross(Vector3::YAXIS)}; + tangentVec3 = t[t[1].LengthSquared() > t[0].LengthSquared()]; + } - *tangents = t[t[1].LengthSquared() > t[0].LengthSquared()]; - *tangents -= *normals * normals->Dot(*tangents); - tangents->Normalize(); + tangentVec3 -= *normals * normals->Dot(tangentVec3); + tangentVec3.Normalize(); + if constexpr(useVec3) + { + *tangents = tangentVec3; + } + else + { + *tangents = Vector4(tangentVec3.x, tangentVec3.y, tangentVec3.z, 1.0f); + } ++tangents; ++normals; } - attribs.push_back({"aTangent", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer)}); + attribs.push_back({"aTangent", useVec3 ? Property::VECTOR3 : Property::VECTOR4, attribs[0].mNumElements, std::move(buffer)}); + + return true; } void CalculateTextureSize(uint32_t totalTextureSize, uint32_t& textureWidth, uint32_t& textureHeight) @@ -347,10 +447,10 @@ void CalculateTextureSize(uint32_t totalTextureSize, uint32_t& textureWidth, uin textureHeight = 1u << powHeight; } -void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::ifstream& binFile, const std::vector& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor) +void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, const std::vector& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor, BufferDefinition::Vector& buffers) { uint32_t geometryBufferIndex = 0u; - float maxDistance = 0.f; + float maxDistanceSquared = 0.f; Vector3* geometryBufferV3 = reinterpret_cast(geometryBuffer); for(const auto& blendShape : blendShapes) { @@ -360,21 +460,41 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::ifstream& binFile, blendShape.deltas.mBlob.mStride >= sizeof(Vector3)) && "Blend Shape position buffer length not a multiple of element size"); - const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize(); - std::vector buffer(bufferSize); - if(ReadAccessor(blendShape.deltas, binFile, buffer.data())) + const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize(); + std::vector buffer(bufferSize); + std::vector sparseIndices{}; + + if(ReadAccessor(blendShape.deltas, buffers[blendShape.deltas.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices)) { - blendShape.deltas.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data())); + blendShape.deltas.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data()), &sparseIndices); + // Calculate the difference with the original mesh. // Find the max distance to normalize the deltas. - const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); + const auto* const deltasBuffer = reinterpret_cast(buffer.data()); - for(uint32_t index = 0u; index < numberOfVertices; ++index) - { - Vector3& delta = geometryBufferV3[geometryBufferIndex++]; - delta = deltasBuffer[index]; + auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) { + Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex]; + delta = deltasBuffer[deltaIndex]; + return std::max(maxDistanceSquared, delta.LengthSquared()); + }; - maxDistance = std::max(maxDistance, delta.LengthSquared()); + if(sparseIndices.empty()) + { + for(uint32_t index = 0u; index < numberOfVertices; ++index) + { + maxDistanceSquared = ProcessVertex(geometryBufferIndex++, index); + } + } + else + { + // initialize blendshape texture + // TODO: there may be a case when sparse accessor uses a base buffer view for initial values. + std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3::ZERO); + for(auto index : sparseIndices) + { + maxDistanceSquared = ProcessVertex(geometryBufferIndex + index, index); + } + geometryBufferIndex += numberOfVertices; } } } @@ -385,20 +505,18 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::ifstream& binFile, blendShape.normals.mBlob.mStride >= sizeof(Vector3)) && "Blend Shape normals buffer length not a multiple of element size"); - const auto bufferSize = blendShape.normals.mBlob.GetBufferSize(); - std::vector buffer(bufferSize); - if(ReadAccessor(blendShape.normals, binFile, buffer.data())) + const auto bufferSize = blendShape.normals.mBlob.GetBufferSize(); + std::vector buffer(bufferSize); + std::vector sparseIndices; + + if(ReadAccessor(blendShape.normals, buffers[blendShape.normals.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices)) { - blendShape.normals.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data())); + blendShape.normals.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data()), &sparseIndices); // Calculate the difference with the original mesh, and translate to make all values positive. - const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); - - for(uint32_t index = 0u; index < numberOfVertices; ++index) - { - Vector3& delta = geometryBufferV3[geometryBufferIndex++]; - delta = deltasBuffer[index]; - + const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); + auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) { + Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex]; delta.x *= 0.5f; delta.y *= 0.5f; delta.z *= 0.5f; @@ -406,6 +524,23 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::ifstream& binFile, delta.x += 0.5f; delta.y += 0.5f; delta.z += 0.5f; + }; + + if(sparseIndices.empty()) + { + for(uint32_t index = 0u; index < numberOfVertices; ++index) + { + ProcessVertex(geometryBufferIndex++, index); + } + } + else + { + std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5)); + for(auto index : sparseIndices) + { + ProcessVertex(geometryBufferIndex + index, index); + } + geometryBufferIndex += numberOfVertices; } } } @@ -416,20 +551,18 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::ifstream& binFile, blendShape.tangents.mBlob.mStride >= sizeof(Vector3)) && "Blend Shape tangents buffer length not a multiple of element size"); - const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize(); - std::vector buffer(bufferSize); - if(ReadAccessor(blendShape.tangents, binFile, buffer.data())) + const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize(); + std::vector buffer(bufferSize); + std::vector sparseIndices; + + if(ReadAccessor(blendShape.tangents, buffers[blendShape.tangents.mBufferIdx].GetBufferStream(), buffer.data(), &sparseIndices)) { - blendShape.tangents.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data())); + blendShape.tangents.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data()), &sparseIndices); // Calculate the difference with the original mesh, and translate to make all values positive. - const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); - - for(uint32_t index = 0u; index < numberOfVertices; ++index) - { - Vector3& delta = geometryBufferV3[geometryBufferIndex++]; - delta = deltasBuffer[index]; - + const Vector3* const deltasBuffer = reinterpret_cast(buffer.data()); + auto ProcessVertex = [&geometryBufferV3, &deltasBuffer, &maxDistanceSquared](uint32_t geometryBufferIndex, uint32_t deltaIndex) { + Vector3& delta = geometryBufferV3[geometryBufferIndex] = deltasBuffer[deltaIndex]; delta.x *= 0.5f; delta.y *= 0.5f; delta.z *= 0.5f; @@ -437,12 +570,37 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::ifstream& binFile, delta.x += 0.5f; delta.y += 0.5f; delta.z += 0.5f; + }; + + if(sparseIndices.empty()) + { + for(uint32_t index = 0u; index < numberOfVertices; ++index) + { + ProcessVertex(geometryBufferIndex++, index); + } + } + else + { + std::fill(geometryBufferV3 + geometryBufferIndex, geometryBufferV3 + geometryBufferIndex + numberOfVertices, Vector3(0.5, 0.5, 0.5)); + for(auto index : sparseIndices) + { + ProcessVertex(geometryBufferIndex + index, index); + } + geometryBufferIndex += numberOfVertices; } } } } geometryBufferIndex = 0u; + + const float maxDistance = sqrtf(maxDistanceSquared); + + const float normalizeFactor = (maxDistanceSquared < Math::MACHINE_EPSILON_100) ? 1.f : (0.5f / maxDistance); + + // Calculate and store the unnormalize factor. + blendShapeUnnormalizeFactor = maxDistance * 2.0f; + for(const auto& blendShape : blendShapes) { // Normalize all the deltas and translate to a possitive value. @@ -450,8 +608,6 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::ifstream& binFile, // whose values that are less than zero are clamped. if(blendShape.deltas.IsDefined()) { - const float normalizeFactor = (fabsf(maxDistance) < Math::MACHINE_EPSILON_1000) ? 1.f : (0.5f / sqrtf(maxDistance)); - for(uint32_t index = 0u; index < numberOfVertices; ++index) { Vector3& delta = geometryBufferV3[geometryBufferIndex++]; @@ -459,9 +615,6 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::ifstream& binFile, delta.y = Clamp(((delta.y * normalizeFactor) + 0.5f), 0.f, 1.f); delta.z = Clamp(((delta.z * normalizeFactor) + 0.5f), 0.f, 1.f); } - - // Calculate and store the unnormalize factor. - blendShapeUnnormalizeFactor = 1.f / normalizeFactor; } if(blendShape.normals.IsDefined()) @@ -476,6 +629,13 @@ void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::ifstream& binFile, } } +std::iostream& GetAvailableData(std::fstream& meshStream, const std::string& meshPath, BufferDefinition& buffer, std::string& availablePath) +{ + auto& stream = (meshStream.is_open()) ? meshStream : buffer.GetBufferStream(); + availablePath = (meshStream.is_open()) ? meshPath : buffer.GetUri(); + return stream; +} + } // namespace MeshDefinition::SparseBlob::SparseBlob(const Blob& indices, const Blob& values, uint32_t count) @@ -493,16 +653,20 @@ MeshDefinition::SparseBlob::SparseBlob(Blob&& indices, Blob&& values, uint32_t c } MeshDefinition::Accessor::Accessor(const MeshDefinition::Blob& blob, - const MeshDefinition::SparseBlob& sparse) + const MeshDefinition::SparseBlob& sparse, + Index bufferIndex) : mBlob{blob}, - mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{sparse} : nullptr} + mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{sparse} : nullptr}, + mBufferIdx(bufferIndex) { } MeshDefinition::Accessor::Accessor(MeshDefinition::Blob&& blob, - MeshDefinition::SparseBlob&& sparse) + MeshDefinition::SparseBlob&& sparse, + Index bufferIndex) : mBlob{std::move(blob)}, - mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{std::move(sparse)} : nullptr} + mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{std::move(sparse)} : nullptr}, + mBufferIdx(bufferIndex) { } @@ -521,21 +685,15 @@ void MeshDefinition::Blob::ComputeMinMax(std::vector& min, std::vector& min, const std::vector& max, uint32_t count, float* values) +void MeshDefinition::Blob::ApplyMinMax(const std::vector& min, const std::vector& max, uint32_t count, float* values, std::vector* sparseIndices) { DALI_ASSERT_DEBUG(max.size() == min.size() || max.size() * min.size() == 0); const auto numComponents = std::max(min.size(), max.size()); using ClampFn = void (*)(const float*, const float*, uint32_t, float&); - ClampFn clampFn = min.empty() ? (max.empty() ? static_cast(nullptr) : [](const float* min, const float* max, uint32_t i, float& value) { - value = std::min(max[i], value); - }) - : (max.empty() ? [](const float* min, const float* max, uint32_t i, float& value) { - value = std::max(min[i], value); - } - : static_cast([](const float* min, const float* max, uint32_t i, float& value) { - value = std::min(std::max(min[i], value), max[i]); - })); + ClampFn clampFn = min.empty() ? (max.empty() ? static_cast(nullptr) : [](const float* min, const float* max, uint32_t i, float& value) { value = std::min(max[i], value); }) + : (max.empty() ? [](const float* min, const float* max, uint32_t i, float& value) { value = std::max(min[i], value); } + : static_cast([](const float* min, const float* max, uint32_t i, float& value) { value = std::min(std::max(min[i], value), max[i]); })); if(!clampFn) { @@ -576,9 +734,9 @@ void MeshDefinition::Blob::ComputeMinMax(uint32_t numComponents, uint32_t count, ComputeMinMax(mMin, mMax, numComponents, count, values); } -void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values) const +void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values, std::vector* sparseIndices) const { - ApplyMinMax(mMin, mMax, count, values); + ApplyMinMax(mMin, mMax, count, values, sparseIndices); } void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const @@ -617,7 +775,7 @@ void MeshDefinition::RequestTangents() } MeshDefinition::RawData -MeshDefinition::LoadRaw(const std::string& modelsPath) +MeshDefinition::LoadRaw(const std::string& modelsPath, BufferDefinition::Vector& buffers) { RawData raw; if(IsQuad()) @@ -625,11 +783,16 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) return raw; } - const std::string meshPath = modelsPath + mUri; - std::ifstream binFile(meshPath, std::ios::binary); - if(!binFile) + std::string meshPath; + meshPath = modelsPath + mUri; + std::fstream fileStream; + if(!mUri.empty()) { - ExceptionFlinger(ASSERT_LOCATION) << "Failed to read geometry data from '" << meshPath << "'"; + fileStream.open(meshPath, std::ios::in | std::ios::binary); + if(!fileStream.is_open()) + { + DALI_LOG_ERROR("Fail to open buffer from %s.\n", meshPath.c_str()); + } } if(mIndices.IsDefined()) @@ -641,22 +804,38 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) "Index buffer length not a multiple of element size"); const auto indexCount = mIndices.mBlob.GetBufferSize() / sizeof(uint32_t); raw.mIndices.resize(indexCount * 2); // NOTE: we need space for uint32_ts initially. - if(!ReadAccessor(mIndices, binFile, reinterpret_cast(raw.mIndices.data()))) + + std::string path; + auto& stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path); + if(!ReadAccessor(mIndices, stream, reinterpret_cast(raw.mIndices.data()))) { - ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << meshPath << "'."; + ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'."; + } + } + else if(MaskMatch(mFlags, U8_INDICES)) + { + DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(uint8_t) == 0) || + mIndices.mBlob.mStride >= sizeof(uint8_t)) && + "Index buffer length not a multiple of element size"); + const auto indexCount = mIndices.mBlob.GetBufferSize() / sizeof(uint8_t); + raw.mIndices.resize(indexCount); // NOTE: we need space for uint16_ts initially. + + std::string path; + auto u8s = reinterpret_cast(raw.mIndices.data()) + indexCount; + auto& stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path); + if(!ReadAccessor(mIndices, stream, u8s)) + { + ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'."; } auto u16s = raw.mIndices.data(); - auto u32s = reinterpret_cast(raw.mIndices.data()); - auto end = u32s + indexCount; - while(u32s != end) + auto end = u8s + indexCount; + while(u8s != end) { - *u16s = static_cast(*u32s); + *u16s = static_cast(*u8s); ++u16s; - ++u32s; + ++u8s; } - - raw.mIndices.resize(indexCount); } else { @@ -664,9 +843,12 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) mIndices.mBlob.mStride >= sizeof(unsigned short)) && "Index buffer length not a multiple of element size"); raw.mIndices.resize(mIndices.mBlob.mLength / sizeof(unsigned short)); - if(!ReadAccessor(mIndices, binFile, reinterpret_cast(raw.mIndices.data()))) + + std::string path; + auto& stream = GetAvailableData(fileStream, meshPath, buffers[mIndices.mBufferIdx], path); + if(!ReadAccessor(mIndices, stream, reinterpret_cast(raw.mIndices.data()))) { - ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << meshPath << "'."; + ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << path << "'."; } } } @@ -679,9 +861,12 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) "Position buffer length not a multiple of element size"); const auto bufferSize = mPositions.mBlob.GetBufferSize(); std::vector buffer(bufferSize); - if(!ReadAccessor(mPositions, binFile, buffer.data())) + + std::string path; + auto& stream = GetAvailableData(fileStream, meshPath, buffers[mPositions.mBufferIdx], path); + if(!ReadAccessor(mPositions, stream, buffer.data())) { - ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << meshPath << "'."; + ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << path << "'."; } uint32_t numVector3 = static_cast(bufferSize / sizeof(Vector3)); @@ -712,9 +897,12 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) "Normal buffer length not a multiple of element size"); const auto bufferSize = mNormals.mBlob.GetBufferSize(); std::vector buffer(bufferSize); - if(!ReadAccessor(mNormals, binFile, buffer.data())) + + std::string path; + auto& stream = GetAvailableData(fileStream, meshPath, buffers[mNormals.mBufferIdx], path); + if(!ReadAccessor(mNormals, stream, buffer.data())) { - ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << meshPath << "'."; + ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << path << "'."; } mNormals.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector3)), reinterpret_cast(buffer.data())); @@ -724,8 +912,20 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) else if(mNormals.mBlob.mLength != 0 && isTriangles) { DALI_ASSERT_DEBUG(mNormals.mBlob.mLength == mPositions.mBlob.GetBufferSize()); - GenerateNormals(raw); - hasNormals = true; + static const std::function GenerateNormalsFunction[2] = + { + GenerateNormals, + GenerateNormals, + }; + const bool generateSuccessed = GenerateNormalsFunction[MaskMatch(mFlags, U32_INDICES)](raw); + if(!generateSuccessed) + { + DALI_LOG_ERROR("Failed to generate normal\n"); + } + else + { + hasNormals = true; + } } const auto hasUvs = mTexCoords.IsDefined(); @@ -736,9 +936,12 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) "Normal buffer length not a multiple of element size"); const auto bufferSize = mTexCoords.mBlob.GetBufferSize(); std::vector buffer(bufferSize); - if(!ReadAccessor(mTexCoords, binFile, buffer.data())) + + std::string path; + auto& stream = GetAvailableData(fileStream, meshPath, buffers[mTexCoords.mBufferIdx], path); + if(!ReadAccessor(mTexCoords, stream, buffer.data())) { - ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << meshPath << "'."; + ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << path << "'."; } const auto uvCount = bufferSize / sizeof(Vector2); @@ -753,7 +956,7 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) } } - mTexCoords.mBlob.ApplyMinMax(static_cast(bufferSize / sizeof(Vector2)), reinterpret_cast(buffer.data())); + mTexCoords.mBlob.ApplyMinMax(static_cast(uvCount), reinterpret_cast(buffer.data())); raw.mAttribs.push_back({"aTexCoord", Property::VECTOR2, static_cast(uvCount), std::move(buffer)}); } @@ -766,9 +969,12 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) "Tangents buffer length not a multiple of element size"); const auto bufferSize = mTangents.mBlob.GetBufferSize(); std::vector buffer(bufferSize); - if(!ReadAccessor(mTangents, binFile, buffer.data())) + + std::string path; + auto& stream = GetAvailableData(fileStream, meshPath, buffers[mTangents.mBufferIdx], path); + if(!ReadAccessor(mTangents, stream, buffer.data())) { - ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << meshPath << "'."; + ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << path << "'."; } mTangents.mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast(buffer.data())); @@ -777,7 +983,33 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) else if(mTangents.mBlob.mLength != 0 && hasNormals && isTriangles) { DALI_ASSERT_DEBUG(mTangents.mBlob.mLength == mNormals.mBlob.GetBufferSize()); - hasUvs ? GenerateTangentsWithUvs(raw) : GenerateTangents(raw); + static const std::function GenerateTangentsFunction[2][2][2] = + { + { + { + GenerateTangents, + GenerateTangents, + }, + { + GenerateTangents, + GenerateTangents, + }, + }, + { + { + GenerateTangents, + GenerateTangents, + }, + { + GenerateTangents, + GenerateTangents, + }, + }}; + const bool generateSuccessed = GenerateTangentsFunction[MaskMatch(mFlags, U32_INDICES)][mTangentType == Property::VECTOR3][hasUvs](raw); + if(!generateSuccessed) + { + DALI_LOG_ERROR("Failed to generate tangents\n"); + } } if(mColors.IsDefined()) @@ -791,42 +1023,62 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) "Colors buffer length not a multiple of element size"); const auto bufferSize = mColors.mBlob.GetBufferSize(); std::vector buffer(bufferSize); - if(!ReadAccessor(mColors, binFile, buffer.data())) + + std::string path; + auto& stream = GetAvailableData(fileStream, meshPath, buffers[mColors.mBufferIdx], path); + if(!ReadAccessor(mColors, stream, buffer.data())) { - ExceptionFlinger(ASSERT_LOCATION) << "Failed to read colors from '" << meshPath << "'."; + ExceptionFlinger(ASSERT_LOCATION) << "Failed to read colors from '" << path << "'."; } mColors.mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast(buffer.data())); raw.mAttribs.push_back({"aVertexColor", propertyType, static_cast(bufferSize / propertySize), std::move(buffer)}); } } + else + { + std::vector buffer(raw.mAttribs[0].mNumElements * sizeof(Vector4)); + auto colors = reinterpret_cast(buffer.data()); + + for(uint32_t i = 0; i < raw.mAttribs[0].mNumElements; i++) + { + colors[i] = Vector4::ONE; + } + + raw.mAttribs.push_back({"aVertexColor", Property::VECTOR4, raw.mAttribs[0].mNumElements, std::move(buffer)}); + } if(IsSkinned()) { + std::string pathJoint; + auto& streamJoint = GetAvailableData(fileStream, meshPath, buffers[mJoints0.mBufferIdx], pathJoint); if(MaskMatch(mFlags, U16_JOINT_IDS)) { - ReadJointAccessor(raw, mJoints0, binFile, meshPath); + ReadJointAccessor(raw, mJoints0, streamJoint, pathJoint); } else if(MaskMatch(mFlags, U8_JOINT_IDS)) { - ReadJointAccessor(raw, mJoints0, binFile, meshPath); + ReadJointAccessor(raw, mJoints0, streamJoint, pathJoint); } else { - ReadJointAccessor(raw, mJoints0, binFile, meshPath); + ReadJointAccessor(raw, mJoints0, streamJoint, pathJoint); } - DALI_ASSERT_ALWAYS(((mWeights0.mBlob.mLength % sizeof(Vector4) == 0) || - mWeights0.mBlob.mStride >= sizeof(Vector4)) && - "Weights buffer length not a multiple of element size"); - const auto bufferSize = mWeights0.mBlob.GetBufferSize(); - std::vector buffer(bufferSize); - if(!ReadAccessor(mWeights0, binFile, buffer.data())) + std::string pathWeight; + auto& streamWeight = GetAvailableData(fileStream, meshPath, buffers[mWeights0.mBufferIdx], pathWeight); + if(MaskMatch(mFlags, U16_WEIGHT)) { - ExceptionFlinger(ASSERT_LOCATION) << "Failed to read weights from '" << meshPath << "'."; + ReadWeightAccessor(raw, mWeights0, streamWeight, pathWeight); + } + else if(MaskMatch(mFlags, U8_WEIGHT)) + { + ReadWeightAccessor(raw, mWeights0, streamWeight, pathWeight); + } + else + { + ReadWeightAccessor(raw, mWeights0, streamWeight, pathWeight); } - - raw.mAttribs.push_back({"aWeights", Property::VECTOR4, static_cast(bufferSize / sizeof(Vector4)), std::move(buffer)}); } // Calculate the Blob for the blend shapes. @@ -865,7 +1117,7 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) else { uint16_t header[2u]; - ReadBlob(mBlendShapeHeader, binFile, reinterpret_cast(header)); + ReadBlob(mBlendShapeHeader, fileStream, reinterpret_cast(header)); textureWidth = header[0u]; textureHeight = header[1u]; } @@ -878,7 +1130,7 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) if(calculateGltf2BlendShapes) { - CalculateGltf2BlendShapes(geometryBuffer, binFile, mBlendShapes, numberOfVertices, raw.mBlendShapeUnnormalizeFactor[0u]); + CalculateGltf2BlendShapes(geometryBuffer, mBlendShapes, numberOfVertices, raw.mBlendShapeUnnormalizeFactor[0u], buffers); } else { @@ -887,7 +1139,7 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) if(blendShapesBlob.IsDefined()) { - if(ReadBlob(blendShapesBlob, binFile, geometryBuffer)) + if(ReadBlob(blendShapesBlob, fileStream, geometryBuffer)) { unnormalizeFactorBlob.mOffset = blendShapesBlob.mOffset + blendShapesBlob.mLength; } @@ -896,7 +1148,7 @@ MeshDefinition::LoadRaw(const std::string& modelsPath) // Read the unnormalize factors. if(unnormalizeFactorBlob.IsDefined()) { - ReadBlob(unnormalizeFactorBlob, binFile, reinterpret_cast(&raw.mBlendShapeUnnormalizeFactor[0u])); + ReadBlob(unnormalizeFactorBlob, fileStream, reinterpret_cast(&raw.mBlendShapeUnnormalizeFactor[0u])); } } raw.mBlendShapeData = Devel::PixelBuffer::Convert(geometryPixelBuffer); @@ -920,7 +1172,15 @@ MeshGeometry MeshDefinition::Load(RawData&& raw) const { if(!raw.mIndices.empty()) { - meshGeometry.geometry.SetIndexBuffer(raw.mIndices.data(), raw.mIndices.size()); + if(MaskMatch(mFlags, U32_INDICES)) + { + // TODO : We can only store indeces as uint16_type. Send Dali::Geometry that we use it as uint32_t actual. + meshGeometry.geometry.SetIndexBuffer(reinterpret_cast(raw.mIndices.data()), raw.mIndices.size() / 2); + } + else + { + meshGeometry.geometry.SetIndexBuffer(raw.mIndices.data(), raw.mIndices.size()); + } } for(auto& a : raw.mAttribs) @@ -944,6 +1204,14 @@ MeshGeometry MeshDefinition::Load(RawData&& raw) const return meshGeometry; } -} // namespace Loader -} // namespace Scene3D -} // namespace Dali +void MeshDefinition::RetrieveBlendShapeComponents(bool& hasPositions, bool& hasNormals, bool& hasTangents) const +{ + for(const auto& blendShape : mBlendShapes) + { + hasPositions = hasPositions || blendShape.deltas.IsDefined(); + hasNormals = hasNormals || blendShape.normals.IsDefined(); + hasTangents = hasTangents || blendShape.tangents.IsDefined(); + } +} + +} // namespace Dali::Scene3D::Loader