2 * Copyright (c) 2022 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/material-definition.h"
22 #include "dali-toolkit/public-api/image-loader/sync-image-loader.h"
26 using namespace Toolkit;
34 constexpr SamplerFlags::Type FILTER_MODES_FROM_DALI[]{
35 SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_NEAREST,
36 SamplerFlags::FILTER_LINEAR,
37 SamplerFlags::FILTER_NEAREST,
38 SamplerFlags::FILTER_LINEAR,
39 SamplerFlags::FILTER_NEAREST | SamplerFlags::FILTER_MIPMAP_NEAREST,
40 SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_NEAREST,
41 SamplerFlags::FILTER_NEAREST | SamplerFlags::FILTER_MIPMAP_LINEAR,
42 SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_LINEAR,
45 constexpr SamplerFlags::Type WRAP_MODES_FROM_DALI[]{
46 SamplerFlags::WRAP_CLAMP,
47 SamplerFlags::WRAP_CLAMP,
48 SamplerFlags::WRAP_REPEAT,
49 SamplerFlags::WRAP_MIRROR,
52 constexpr FilterMode::Type FILTER_MODES_TO_DALI[]{
55 FilterMode::NEAREST_MIPMAP_NEAREST,
56 FilterMode::LINEAR_MIPMAP_NEAREST,
57 FilterMode::NEAREST_MIPMAP_LINEAR,
58 FilterMode::LINEAR_MIPMAP_LINEAR,
61 constexpr WrapMode::Type WRAP_MODES_TO_DALI[]{
63 WrapMode::CLAMP_TO_EDGE,
64 WrapMode::MIRRORED_REPEAT};
66 const SamplerFlags::Type SINGLE_VALUE_SAMPLER = SamplerFlags::Encode(FilterMode::NEAREST, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
69 SamplerFlags::Type SamplerFlags::Encode(FilterMode::Type minFilter, FilterMode::Type magFilter, WrapMode::Type wrapS, WrapMode::Type wrapT)
71 return FILTER_MODES_FROM_DALI[minFilter] | ((FILTER_MODES_FROM_DALI[magFilter] & FILTER_MAG_BITS) << FILTER_MAG_SHIFT) |
72 (WRAP_MODES_FROM_DALI[wrapS] << WRAP_S_SHIFT) | (WRAP_MODES_FROM_DALI[wrapT] << WRAP_T_SHIFT);
75 FilterMode::Type SamplerFlags::GetMinFilter(Type flags)
77 return FILTER_MODES_TO_DALI[flags & FILTER_MIN_MASK];
80 FilterMode::Type SamplerFlags::GetMagFilter(Type flags)
82 return FILTER_MODES_TO_DALI[(flags >> FILTER_MAG_SHIFT) & FILTER_MAG_MASK];
85 WrapMode::Type SamplerFlags::GetWrapS(Type flags)
87 return WRAP_MODES_TO_DALI[(flags >> WRAP_S_SHIFT) & WRAP_S_MASK];
90 WrapMode::Type SamplerFlags::GetWrapT(Type flags)
92 return WRAP_MODES_TO_DALI[(flags >> WRAP_T_SHIFT) & WRAP_T_MASK];
95 Sampler SamplerFlags::MakeSampler(Type flags)
97 auto sampler = Sampler::New();
98 sampler.SetFilterMode(GetMinFilter(flags), GetMagFilter(flags));
99 sampler.SetWrapMode(GetWrapS(flags), GetWrapT(flags));
103 TextureDefinition::TextureDefinition(const std::string& imageUri, SamplerFlags::Type samplerFlags, ImageDimensions minImageDimensions, SamplingMode::Type samplingMode)
104 : mImageUri(imageUri),
105 mSamplerFlags(samplerFlags),
106 mMinImageDimensions(minImageDimensions),
107 mSamplingMode(samplingMode)
111 TextureDefinition::TextureDefinition(std::string&& imageUri, SamplerFlags::Type samplerFlags, ImageDimensions minImageDimensions, SamplingMode::Type samplingMode)
112 : mImageUri(std::move(imageUri)),
113 mSamplerFlags(samplerFlags),
114 mMinImageDimensions(minImageDimensions),
115 mSamplingMode(samplingMode)
119 MaterialDefinition::RawData
120 MaterialDefinition::LoadRaw(const std::string& imagesPath) const
124 const bool hasTransparency = MaskMatch(mFlags, TRANSPARENCY);
125 // Why we add additional count here?
126 uint32_t numBuffers = static_cast<uint32_t>(mTextureStages.size()) + (hasTransparency ? !CheckTextures(ALBEDO) + !CheckTextures(METALLIC | ROUGHNESS) + !CheckTextures(NORMAL)
127 : !CheckTextures(ALBEDO | METALLIC) + !CheckTextures(NORMAL | ROUGHNESS));
132 raw.mTextures.reserve(numBuffers);
135 auto iTexture = mTextureStages.cbegin();
136 auto checkStage = [&](uint32_t flags) {
137 return iTexture != mTextureStages.end() && MaskMatch(iTexture->mSemantic, flags);
140 // Check for compulsory textures: Albedo, Metallic, Roughness, Normal
141 if(checkStage(ALBEDO | METALLIC))
143 raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
146 if(checkStage(NORMAL | ROUGHNESS))
148 raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
151 else // single value normal-roughness
153 const auto bufferSize = 4;
154 uint8_t* buffer = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1
155 raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
160 if(checkStage(ALBEDO))
162 raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
165 else if(mNeedAlbedoTexture) // single value albedo, albedo-alpha or albedo-metallic
167 uint32_t bufferSize = 4;
168 uint8_t* buffer = nullptr;
169 auto format = Pixel::Format::RGBA8888;
170 if(hasTransparency) // albedo-alpha
172 buffer = new uint8_t[bufferSize];
173 buffer[3] = static_cast<uint8_t>(mColor.a * 255.f);
175 else if(!checkStage(METALLIC | ROUGHNESS)) // albedo-metallic
177 buffer = new uint8_t[bufferSize];
178 buffer[3] = 0xff; // metallic of 1.0
183 buffer = new uint8_t[bufferSize];
184 format = Pixel::Format::RGB888;
186 buffer[0] = static_cast<uint8_t>(mColor.r * 255.f);
187 buffer[1] = static_cast<uint8_t>(mColor.g * 255.f);
188 buffer[2] = static_cast<uint8_t>(mColor.b * 255.f);
189 raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, format, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
192 // If we have transparency, or an image based albedo map, we will have to continue with separate metallicRoughness + normal.
193 const bool createMetallicRoughnessAndNormal = hasTransparency || std::distance(mTextureStages.begin(), iTexture) > 0;
194 if(checkStage(METALLIC | ROUGHNESS))
196 raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
199 else if(createMetallicRoughnessAndNormal && mNeedMetallicRoughnessTexture)
201 // NOTE: we want to set both metallic and roughness to 1.0; dli uses the R & A channels,
202 // glTF2 uses B & G, so we might as well just set all components to 1.0.
203 const auto bufferSize = 4;
204 uint8_t* buffer = new uint8_t[bufferSize]{0xff, 0xff, 0xff, 0xff};
205 raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
208 if(checkStage(NORMAL))
210 raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
213 else if(mNeedNormalTexture)
215 if(createMetallicRoughnessAndNormal)
217 const auto bufferSize = 3;
218 uint8_t* buffer = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff}; // normal of (0, 0, 1)
219 raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
221 else // single-value normal-roughness
223 const auto bufferSize = 4;
224 uint8_t* buffer = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1.0
225 raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
231 if(checkStage(SUBSURFACE))
233 raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
237 if(checkStage(OCCLUSION))
239 raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
243 if(checkStage(EMISSIVE))
245 raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
249 if(checkStage(SPECULAR))
251 raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
255 if(checkStage(SPECULAR_COLOR))
257 raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
264 TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environments, RawData&& raw) const
266 auto textureSet = TextureSet::New();
269 for(auto& tData : raw.mTextures)
271 auto& pixels = tData.mPixels;
272 auto texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
273 texture.Upload(tData.mPixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
274 if(tData.mSamplerFlags & SamplerFlags::MIPMAP_MASK)
276 texture.GenerateMipmaps();
279 textureSet.SetTexture(n, texture);
280 textureSet.SetSampler(n, SamplerFlags::MakeSampler(tData.mSamplerFlags));
285 // Assign textures to slots -- starting with 2D ones, then cubemaps, if any.
286 if(mEnvironmentIdx < static_cast<Index>(environments.size()))
288 auto& envTextures = environments[mEnvironmentIdx].second;
289 // If pre-computed brdf texture is defined, set the texture.
290 if(envTextures.mBrdf)
292 textureSet.SetTexture(n, envTextures.mBrdf);
296 if(envTextures.mDiffuse)
298 textureSet.SetTexture(n, envTextures.mDiffuse);
302 if(envTextures.mSpecular)
304 auto specularSampler = Sampler::New();
305 specularSampler.SetWrapMode(WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
306 specularSampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR);
308 textureSet.SetTexture(n, envTextures.mSpecular);
309 textureSet.SetSampler(n, specularSampler);
315 ExceptionFlinger(ASSERT_LOCATION) << "Environment index (" << mEnvironmentIdx << ") out of bounds (" << environments.size() << ").";
321 bool MaterialDefinition::CheckTextures(uint32_t flags) const
323 return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) {
324 return MaskMatch(ts.mSemantic, flags);
325 }) != mTextureStages.end();
328 } // namespace Loader
329 } // namespace Scene3D