[dali_2.3.24] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / material-definition.cpp
index 9becc3a..b61005b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  *
  */
 
-// INTERNAL INCLUDES
-#include "dali-scene3d/public-api/loader/material-definition.h"
+// CLASS HEADER
+#include <dali-scene3d/public-api/loader/material-definition.h>
 
 // EXTERNAL INCLUDES
-#include "dali-toolkit/public-api/image-loader/sync-image-loader.h"
+#include <dali-toolkit/devel-api/builder/base64-encoding.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/integration-api/pixel-data-integ.h>
+
+// INTERNAL INCLUDES
+#include <dali-scene3d/internal/common/image-resource-loader.h>
 
 namespace Dali
 {
@@ -29,6 +35,8 @@ namespace Scene3D
 {
 namespace Loader
 {
+const Matrix3 TextureDefinition::DEFAULT_TRANSFORM = Matrix3::IDENTITY;
+
 namespace
 {
 constexpr SamplerFlags::Type FILTER_MODES_FROM_DALI[]{
@@ -63,7 +71,55 @@ constexpr WrapMode::Type WRAP_MODES_TO_DALI[]{
   WrapMode::CLAMP_TO_EDGE,
   WrapMode::MIRRORED_REPEAT};
 
-const SamplerFlags::Type SINGLE_VALUE_SAMPLER = SamplerFlags::Encode(FilterMode::NEAREST, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
+const SamplerFlags::Type GetSingleValueSampler()
+{
+  static const SamplerFlags::Type SINGLE_VALUE_SAMPLER = SamplerFlags::Encode(FilterMode::NEAREST, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
+  return SINGLE_VALUE_SAMPLER;
+}
+
+static constexpr std::string_view EMBEDDED_DATA_PREFIX               = "data:";
+static constexpr std::string_view EMBEDDED_DATA_IMAGE_MEDIA_TYPE     = "image/";
+static constexpr std::string_view EMBEDDED_DATA_BASE64_ENCODING_TYPE = "base64,";
+
+Dali::PixelData LoadImageResource(const std::string& resourcePath,
+                                  TextureDefinition& textureDefinition,
+                                  FittingMode::Type  fittingMode,
+                                  bool               orientationCorrection)
+{
+  Dali::PixelData pixelData;
+  if(!textureDefinition.mTextureBuffer.empty())
+  {
+    Dali::Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromBuffer(textureDefinition.mTextureBuffer.data(), textureDefinition.mTextureBuffer.size(), textureDefinition.mMinImageDimensions, fittingMode, textureDefinition.mSamplingMode, orientationCorrection);
+    if(pixelBuffer)
+    {
+      pixelData = Devel::PixelBuffer::Convert(pixelBuffer);
+    }
+  }
+  else if(textureDefinition.mImageUri.find(EMBEDDED_DATA_PREFIX.data()) == 0 && textureDefinition.mImageUri.find(EMBEDDED_DATA_IMAGE_MEDIA_TYPE.data(), EMBEDDED_DATA_PREFIX.length()) == EMBEDDED_DATA_PREFIX.length())
+  {
+    auto position = textureDefinition.mImageUri.find(EMBEDDED_DATA_BASE64_ENCODING_TYPE.data(), EMBEDDED_DATA_PREFIX.length() + EMBEDDED_DATA_IMAGE_MEDIA_TYPE.length());
+    if(position != std::string::npos)
+    {
+      position += EMBEDDED_DATA_BASE64_ENCODING_TYPE.length();
+      std::string_view     data = std::string_view(textureDefinition.mImageUri).substr(position);
+      std::vector<uint8_t> buffer;
+      Dali::Toolkit::DecodeBase64FromString(data, buffer);
+      uint32_t bufferSize = buffer.size();
+
+      Dali::Devel::PixelBuffer pixelBuffer = Dali::LoadImageFromBuffer(reinterpret_cast<uint8_t*>(buffer.data()), bufferSize, textureDefinition.mMinImageDimensions, fittingMode, textureDefinition.mSamplingMode, orientationCorrection);
+      if(pixelBuffer)
+      {
+        pixelData = Dali::Devel::PixelBuffer::Convert(pixelBuffer, true);
+      }
+    }
+  }
+  else
+  {
+    textureDefinition.mDirectoryPath = resourcePath;
+    pixelData                        = Internal::ImageResourceLoader::GetCachedPixelData(resourcePath + textureDefinition.mImageUri, textureDefinition.mMinImageDimensions, fittingMode, textureDefinition.mSamplingMode, orientationCorrection);
+  }
+  return pixelData;
+}
 } // namespace
 
 SamplerFlags::Type SamplerFlags::Encode(FilterMode::Type minFilter, FilterMode::Type magFilter, WrapMode::Type wrapS, WrapMode::Type wrapT)
@@ -100,21 +156,43 @@ Sampler SamplerFlags::MakeSampler(Type flags)
   return sampler;
 }
 
-TextureDefinition::TextureDefinition(const std::string& imageUri, SamplerFlags::Type samplerFlags)
+TextureDefinition::TextureDefinition(const std::string& imageUri, SamplerFlags::Type samplerFlags, ImageDimensions minImageDimensions, SamplingMode::Type samplingMode, Matrix3 transform)
 : mImageUri(imageUri),
-  mSamplerFlags(samplerFlags)
+  mSamplerFlags(samplerFlags),
+  mMinImageDimensions(minImageDimensions),
+  mSamplingMode(samplingMode),
+  mTransform(transform)
+{
+}
+
+TextureDefinition::TextureDefinition(std::string&& imageUri, SamplerFlags::Type samplerFlags, ImageDimensions minImageDimensions, SamplingMode::Type samplingMode, Matrix3 transform)
+: mImageUri(std::move(imageUri)),
+  mSamplerFlags(samplerFlags),
+  mMinImageDimensions(minImageDimensions),
+  mSamplingMode(samplingMode),
+  mTransform(transform)
+{
+}
+
+TextureDefinition::TextureDefinition(std::vector<uint8_t>&& textureBuffer, SamplerFlags::Type samplerFlags, ImageDimensions minImageDimensions, SamplingMode::Type samplingMode, Matrix3 transform)
+: mImageUri(),
+  mSamplerFlags(samplerFlags),
+  mMinImageDimensions(minImageDimensions),
+  mSamplingMode(samplingMode),
+  mTransform(transform),
+  mTextureBuffer(std::move(textureBuffer))
 {
 }
 
 MaterialDefinition::RawData
-MaterialDefinition::LoadRaw(const std::string& imagesPath) const
+MaterialDefinition::LoadRaw(const std::string& imagesPath)
 {
   RawData raw;
 
   const bool hasTransparency = MaskMatch(mFlags, TRANSPARENCY);
   // Why we add additional count here?
-  uint32_t numBuffers = mTextureStages.size() + (hasTransparency ? !CheckTextures(ALBEDO) + !CheckTextures(METALLIC | ROUGHNESS) + !CheckTextures(NORMAL)
-                                                                 : !CheckTextures(ALBEDO | METALLIC) + !CheckTextures(NORMAL | ROUGHNESS));
+  uint32_t numBuffers = static_cast<uint32_t>(mTextureStages.size()) + (hasTransparency ? !CheckTextures(ALBEDO) + !CheckTextures(METALLIC | ROUGHNESS) + !CheckTextures(NORMAL)
+                                                                                        : !CheckTextures(ALBEDO | METALLIC) + !CheckTextures(NORMAL | ROUGHNESS));
   if(numBuffers == 0)
   {
     return raw;
@@ -122,7 +200,7 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath) const
   raw.mTextures.reserve(numBuffers);
 
   // Load textures
-  auto iTexture   = mTextureStages.cbegin();
+  auto iTexture   = mTextureStages.begin();
   auto checkStage = [&](uint32_t flags) {
     return iTexture != mTextureStages.end() && MaskMatch(iTexture->mSemantic, flags);
   };
@@ -130,26 +208,24 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath) const
   // Check for compulsory textures: Albedo, Metallic, Roughness, Normal
   if(checkStage(ALBEDO | METALLIC))
   {
-    raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
     ++iTexture;
 
     if(checkStage(NORMAL | ROUGHNESS))
     {
-      raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
+      raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
       ++iTexture;
     }
     else // single value normal-roughness
     {
-      const auto bufferSize = 4;
-      uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1
-      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+      raw.mTextures.push_back({Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyPixelDataZAxisAndAlphaRGBA(), GetSingleValueSampler()});
     }
   }
   else
   {
     if(checkStage(ALBEDO))
     {
-      raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
+      raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
       ++iTexture;
     }
     else if(mNeedAlbedoTexture) // single value albedo, albedo-alpha or albedo-metallic
@@ -176,43 +252,37 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath) const
       buffer[0] = static_cast<uint8_t>(mColor.r * 255.f);
       buffer[1] = static_cast<uint8_t>(mColor.g * 255.f);
       buffer[2] = static_cast<uint8_t>(mColor.b * 255.f);
-      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, format, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+      raw.mTextures.push_back({Dali::Integration::NewPixelDataWithReleaseAfterUpload(buffer, bufferSize, 1, 1, 0, format, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
     }
 
     // If we have transparency, or an image based albedo map, we will have to continue with separate metallicRoughness + normal.
     const bool createMetallicRoughnessAndNormal = hasTransparency || std::distance(mTextureStages.begin(), iTexture) > 0;
     if(checkStage(METALLIC | ROUGHNESS))
     {
-      raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
+      raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
       ++iTexture;
     }
     else if(createMetallicRoughnessAndNormal && mNeedMetallicRoughnessTexture)
     {
       // NOTE: we want to set both metallic and roughness to 1.0; dli uses the R & A channels,
       // glTF2 uses B & G, so we might as well just set all components to 1.0.
-      const auto bufferSize = 4;
-      uint8_t*   buffer     = new uint8_t[bufferSize]{0xff, 0xff, 0xff, 0xff};
-      raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+      raw.mTextures.push_back({Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyPixelDataWhiteRGBA(), GetSingleValueSampler()});
     }
 
     if(checkStage(NORMAL))
     {
-      raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
+      raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
       ++iTexture;
     }
     else if(mNeedNormalTexture)
     {
       if(createMetallicRoughnessAndNormal)
       {
-        const auto bufferSize = 3;
-        uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff}; // normal of (0, 0, 1)
-        raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+        raw.mTextures.push_back({Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyPixelDataZAxisRGB(), GetSingleValueSampler()});
       }
       else // single-value normal-roughness
       {
-        const auto bufferSize = 4;
-        uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1.0
-        raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+        raw.mTextures.push_back({Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyPixelDataZAxisAndAlphaRGBA(), GetSingleValueSampler()});
       }
     }
   }
@@ -220,19 +290,31 @@ MaterialDefinition::LoadRaw(const std::string& imagesPath) const
   // Extra textures.
   if(checkStage(SUBSURFACE))
   {
-    raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
     ++iTexture;
   }
 
   if(checkStage(OCCLUSION))
   {
-    raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
     ++iTexture;
   }
 
   if(checkStage(EMISSIVE))
   {
-    raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
+    ++iTexture;
+  }
+
+  if(checkStage(SPECULAR))
+  {
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
+    ++iTexture;
+  }
+
+  if(checkStage(SPECULAR_COLOR))
+  {
+    raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
     ++iTexture;
   }
 
@@ -246,12 +328,11 @@ TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environ
   uint32_t n = 0;
   for(auto& tData : raw.mTextures)
   {
-    auto& pixels  = tData.mPixels;
-    auto  texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
-    texture.Upload(tData.mPixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
-    if(tData.mSamplerFlags & SamplerFlags::MIPMAP_MASK)
+    auto&   pixels = tData.mPixels;
+    Texture texture;
+    if(pixels)
     {
-      texture.GenerateMipmaps();
+      texture = Dali::Scene3D::Internal::ImageResourceLoader::GetCachedTexture(pixels, tData.mSamplerFlags & SamplerFlags::MIPMAP_MASK);
     }
 
     textureSet.SetTexture(n, texture);
@@ -260,8 +341,13 @@ TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environ
     ++n;
   }
 
+  if(mShadowAvailable)
+  {
+    textureSet.SetTexture(n++, Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyTextureWhiteRGB());
+  }
+
   // Assign textures to slots -- starting with 2D ones, then cubemaps, if any.
-  if(mEnvironmentIdx < environments.size())
+  if(mEnvironmentIdx < static_cast<Index>(environments.size()))
   {
     auto& envTextures = environments[mEnvironmentIdx].second;
     // If pre-computed brdf texture is defined, set the texture.
@@ -298,9 +384,7 @@ TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environ
 
 bool MaterialDefinition::CheckTextures(uint32_t flags) const
 {
-  return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) {
-           return MaskMatch(ts.mSemantic, flags);
-         }) != mTextureStages.end();
+  return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) { return MaskMatch(ts.mSemantic, flags); }) != mTextureStages.end();
 }
 
 } // namespace Loader