/*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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-toolkit/public-api/image-loader/sync-image-loader.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
namespace 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())
+ {
+ uint32_t 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 = Devel::PixelBuffer::Convert(pixelBuffer);
+ }
+ }
+ }
+ else
+ {
+ textureDefinition.mDirectoryPath = resourcePath;
+ pixelData = SyncImageLoader::Load(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)
{
}
+TextureDefinition::TextureDefinition(std::vector<uint8_t>&& textureBuffer, SamplerFlags::Type samplerFlags, ImageDimensions minImageDimensions, SamplingMode::Type samplingMode)
+: mImageUri(),
+ mSamplerFlags(samplerFlags),
+ mMinImageDimensions(minImageDimensions),
+ mSamplingMode(samplingMode),
+ mTextureBuffer(std::move(textureBuffer))
+{
+}
+
MaterialDefinition::RawData
-MaterialDefinition::LoadRaw(const std::string& imagesPath) const
+MaterialDefinition::LoadRaw(const std::string& imagesPath)
{
RawData raw;
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);
};
// Check for compulsory textures: Albedo, Metallic, Roughness, Normal
if(checkStage(ALBEDO | METALLIC))
{
- raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), 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.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), 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({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
}
}
else
{
if(checkStage(ALBEDO))
{
- raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), 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
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({PixelData::New(buffer, bufferSize, 1, 1, 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.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+ raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
++iTexture;
}
else if(createMetallicRoughnessAndNormal && mNeedMetallicRoughnessTexture)
// 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({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
}
if(checkStage(NORMAL))
{
- raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+ raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
++iTexture;
}
else if(mNeedNormalTexture)
{
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({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), 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({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), GetSingleValueSampler()});
}
}
}
// Extra textures.
if(checkStage(SUBSURFACE))
{
- raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), 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.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), 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.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), 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({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+ raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
++iTexture;
}
if(checkStage(SPECULAR_COLOR))
{
- raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
+ raw.mTextures.push_back({LoadImageResource(imagesPath, iTexture->mTexture, FittingMode::DEFAULT, true), iTexture->mTexture.mSamplerFlags});
++iTexture;
}
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 = 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)
+ {
+ texture.GenerateMipmaps();
+ }
}
textureSet.SetTexture(n, texture);
++n;
}
+ if(mShadowAvailable)
+ {
+ PixelData shadowMapPixelData = PixelData::New(new uint8_t[3]{0xff, 0xff, 0xff}, 3, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY);
+ Texture shadowMapTexture = Texture::New(TextureType::TEXTURE_2D, shadowMapPixelData.GetPixelFormat(), shadowMapPixelData.GetWidth(), shadowMapPixelData.GetHeight());
+ shadowMapTexture.Upload(shadowMapPixelData, 0, 0, 0, 0, shadowMapPixelData.GetWidth(), shadowMapPixelData.GetHeight());
+ textureSet.SetTexture(n++, shadowMapTexture);
+ }
+
// Assign textures to slots -- starting with 2D ones, then cubemaps, if any.
if(mEnvironmentIdx < static_cast<Index>(environments.size()))
{
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