DALi Version 2.2.11
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / material-definition.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // INTERNAL INCLUDES
19 #include "dali-scene3d/public-api/loader/material-definition.h"
20
21 // EXTERNAL INCLUDES
22 #include "dali-toolkit/public-api/image-loader/sync-image-loader.h"
23
24 namespace Dali
25 {
26 using namespace Toolkit;
27
28 namespace Scene3D
29 {
30 namespace Loader
31 {
32 namespace
33 {
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,
43 };
44
45 constexpr SamplerFlags::Type WRAP_MODES_FROM_DALI[]{
46   SamplerFlags::WRAP_CLAMP,
47   SamplerFlags::WRAP_CLAMP,
48   SamplerFlags::WRAP_REPEAT,
49   SamplerFlags::WRAP_MIRROR,
50 };
51
52 constexpr FilterMode::Type FILTER_MODES_TO_DALI[]{
53   FilterMode::NEAREST,
54   FilterMode::LINEAR,
55   FilterMode::NEAREST_MIPMAP_NEAREST,
56   FilterMode::LINEAR_MIPMAP_NEAREST,
57   FilterMode::NEAREST_MIPMAP_LINEAR,
58   FilterMode::LINEAR_MIPMAP_LINEAR,
59 };
60
61 constexpr WrapMode::Type WRAP_MODES_TO_DALI[]{
62   WrapMode::REPEAT,
63   WrapMode::CLAMP_TO_EDGE,
64   WrapMode::MIRRORED_REPEAT};
65
66 const SamplerFlags::Type SINGLE_VALUE_SAMPLER = SamplerFlags::Encode(FilterMode::NEAREST, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
67 } // namespace
68
69 SamplerFlags::Type SamplerFlags::Encode(FilterMode::Type minFilter, FilterMode::Type magFilter, WrapMode::Type wrapS, WrapMode::Type wrapT)
70 {
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);
73 }
74
75 FilterMode::Type SamplerFlags::GetMinFilter(Type flags)
76 {
77   return FILTER_MODES_TO_DALI[flags & FILTER_MIN_MASK];
78 }
79
80 FilterMode::Type SamplerFlags::GetMagFilter(Type flags)
81 {
82   return FILTER_MODES_TO_DALI[(flags >> FILTER_MAG_SHIFT) & FILTER_MAG_MASK];
83 }
84
85 WrapMode::Type SamplerFlags::GetWrapS(Type flags)
86 {
87   return WRAP_MODES_TO_DALI[(flags >> WRAP_S_SHIFT) & WRAP_S_MASK];
88 }
89
90 WrapMode::Type SamplerFlags::GetWrapT(Type flags)
91 {
92   return WRAP_MODES_TO_DALI[(flags >> WRAP_T_SHIFT) & WRAP_T_MASK];
93 }
94
95 Sampler SamplerFlags::MakeSampler(Type flags)
96 {
97   auto sampler = Sampler::New();
98   sampler.SetFilterMode(GetMinFilter(flags), GetMagFilter(flags));
99   sampler.SetWrapMode(GetWrapS(flags), GetWrapT(flags));
100   return sampler;
101 }
102
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)
108 {
109 }
110
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)
116 {
117 }
118
119 MaterialDefinition::RawData
120 MaterialDefinition::LoadRaw(const std::string& imagesPath) const
121 {
122   RawData raw;
123
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));
128   if(numBuffers == 0)
129   {
130     return raw;
131   }
132   raw.mTextures.reserve(numBuffers);
133
134   // Load textures
135   auto iTexture   = mTextureStages.cbegin();
136   auto checkStage = [&](uint32_t flags) {
137     return iTexture != mTextureStages.end() && MaskMatch(iTexture->mSemantic, flags);
138   };
139
140   // Check for compulsory textures: Albedo, Metallic, Roughness, Normal
141   if(checkStage(ALBEDO | METALLIC))
142   {
143     raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
144     ++iTexture;
145
146     if(checkStage(NORMAL | ROUGHNESS))
147     {
148       raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
149       ++iTexture;
150     }
151     else // single value normal-roughness
152     {
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});
156     }
157   }
158   else
159   {
160     if(checkStage(ALBEDO))
161     {
162       raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
163       ++iTexture;
164     }
165     else if(mNeedAlbedoTexture) // single value albedo, albedo-alpha or albedo-metallic
166     {
167       uint32_t bufferSize = 4;
168       uint8_t* buffer     = nullptr;
169       auto     format     = Pixel::Format::RGBA8888;
170       if(hasTransparency) // albedo-alpha
171       {
172         buffer    = new uint8_t[bufferSize];
173         buffer[3] = static_cast<uint8_t>(mColor.a * 255.f);
174       }
175       else if(!checkStage(METALLIC | ROUGHNESS)) // albedo-metallic
176       {
177         buffer    = new uint8_t[bufferSize];
178         buffer[3] = 0xff; // metallic of 1.0
179       }
180       else // albedo
181       {
182         bufferSize = 3;
183         buffer     = new uint8_t[bufferSize];
184         format     = Pixel::Format::RGB888;
185       }
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});
190     }
191
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))
195     {
196       raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
197       ++iTexture;
198     }
199     else if(createMetallicRoughnessAndNormal && mNeedMetallicRoughnessTexture)
200     {
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});
206     }
207
208     if(checkStage(NORMAL))
209     {
210       raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
211       ++iTexture;
212     }
213     else if(mNeedNormalTexture)
214     {
215       if(createMetallicRoughnessAndNormal)
216       {
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});
220       }
221       else // single-value normal-roughness
222       {
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});
226       }
227     }
228   }
229
230   // Extra textures.
231   if(checkStage(SUBSURFACE))
232   {
233     raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
234     ++iTexture;
235   }
236
237   if(checkStage(OCCLUSION))
238   {
239     raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
240     ++iTexture;
241   }
242
243   if(checkStage(EMISSIVE))
244   {
245     raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
246     ++iTexture;
247   }
248
249   if(checkStage(SPECULAR))
250   {
251     raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
252     ++iTexture;
253   }
254
255   if(checkStage(SPECULAR_COLOR))
256   {
257     raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri, iTexture->mTexture.mMinImageDimensions, FittingMode::DEFAULT, iTexture->mTexture.mSamplingMode, true), iTexture->mTexture.mSamplerFlags});
258     ++iTexture;
259   }
260
261   return raw;
262 }
263
264 TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environments, RawData&& raw) const
265 {
266   auto textureSet = TextureSet::New();
267
268   uint32_t n = 0;
269   for(auto& tData : raw.mTextures)
270   {
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)
275     {
276       texture.GenerateMipmaps();
277     }
278
279     textureSet.SetTexture(n, texture);
280     textureSet.SetSampler(n, SamplerFlags::MakeSampler(tData.mSamplerFlags));
281
282     ++n;
283   }
284
285   // Assign textures to slots -- starting with 2D ones, then cubemaps, if any.
286   if(mEnvironmentIdx < static_cast<Index>(environments.size()))
287   {
288     auto& envTextures = environments[mEnvironmentIdx].second;
289     // If pre-computed brdf texture is defined, set the texture.
290     if(envTextures.mBrdf)
291     {
292       textureSet.SetTexture(n, envTextures.mBrdf);
293       ++n;
294     }
295
296     if(envTextures.mDiffuse)
297     {
298       textureSet.SetTexture(n, envTextures.mDiffuse);
299       ++n;
300     }
301
302     if(envTextures.mSpecular)
303     {
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);
307
308       textureSet.SetTexture(n, envTextures.mSpecular);
309       textureSet.SetSampler(n, specularSampler);
310       ++n;
311     }
312   }
313   else
314   {
315     ExceptionFlinger(ASSERT_LOCATION) << "Environment index (" << mEnvironmentIdx << ") out of bounds (" << environments.size() << ").";
316   }
317
318   return textureSet;
319 }
320
321 bool MaterialDefinition::CheckTextures(uint32_t flags) const
322 {
323   return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) {
324            return MaskMatch(ts.mSemantic, flags);
325          }) != mTextureStages.end();
326 }
327
328 } // namespace Loader
329 } // namespace Scene3D
330 } // namespace Dali