ModelView using scene-loader
[platform/core/uifw/dali-toolkit.git] / dali-scene-loader / public-api / 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-scene-loader/public-api/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 SceneLoader
29 {
30 namespace
31 {
32 constexpr SamplerFlags::Type FILTER_MODES_FROM_DALI[]{
33   SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_NEAREST,
34   SamplerFlags::FILTER_LINEAR,
35   SamplerFlags::FILTER_NEAREST,
36   SamplerFlags::FILTER_LINEAR,
37   SamplerFlags::FILTER_NEAREST | SamplerFlags::FILTER_MIPMAP_NEAREST,
38   SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_NEAREST,
39   SamplerFlags::FILTER_NEAREST | SamplerFlags::FILTER_MIPMAP_LINEAR,
40   SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_LINEAR,
41 };
42
43 constexpr SamplerFlags::Type WRAP_MODES_FROM_DALI[]{
44   SamplerFlags::WRAP_CLAMP,
45   SamplerFlags::WRAP_CLAMP,
46   SamplerFlags::WRAP_REPEAT,
47   SamplerFlags::WRAP_MIRROR,
48 };
49
50 constexpr FilterMode::Type FILTER_MODES_TO_DALI[]{
51   FilterMode::NEAREST,
52   FilterMode::LINEAR,
53   FilterMode::NEAREST_MIPMAP_NEAREST,
54   FilterMode::LINEAR_MIPMAP_NEAREST,
55   FilterMode::NEAREST_MIPMAP_LINEAR,
56   FilterMode::LINEAR_MIPMAP_LINEAR,
57 };
58
59 constexpr WrapMode::Type WRAP_MODES_TO_DALI[]{
60   WrapMode::REPEAT,
61   WrapMode::CLAMP_TO_EDGE,
62   WrapMode::MIRRORED_REPEAT};
63
64 const SamplerFlags::Type SINGLE_VALUE_SAMPLER = SamplerFlags::Encode(FilterMode::NEAREST, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
65 } // namespace
66
67 SamplerFlags::Type SamplerFlags::Encode(FilterMode::Type minFilter, FilterMode::Type magFilter, WrapMode::Type wrapS, WrapMode::Type wrapT)
68 {
69   return FILTER_MODES_FROM_DALI[minFilter] | ((FILTER_MODES_FROM_DALI[magFilter] & FILTER_MAG_BITS) << FILTER_MAG_SHIFT) |
70          (WRAP_MODES_FROM_DALI[wrapS] << WRAP_S_SHIFT) | (WRAP_MODES_FROM_DALI[wrapT] << WRAP_T_SHIFT);
71 }
72
73 FilterMode::Type SamplerFlags::GetMinFilter(Type flags)
74 {
75   return FILTER_MODES_TO_DALI[flags & FILTER_MIN_MASK];
76 }
77
78 FilterMode::Type SamplerFlags::GetMagFilter(Type flags)
79 {
80   return FILTER_MODES_TO_DALI[(flags >> FILTER_MAG_SHIFT) & FILTER_MAG_MASK];
81 }
82
83 WrapMode::Type SamplerFlags::GetWrapS(Type flags)
84 {
85   return WRAP_MODES_TO_DALI[(flags >> WRAP_S_SHIFT) & WRAP_S_MASK];
86 }
87
88 WrapMode::Type SamplerFlags::GetWrapT(Type flags)
89 {
90   return WRAP_MODES_TO_DALI[(flags >> WRAP_T_SHIFT) & WRAP_T_MASK];
91 }
92
93 Sampler SamplerFlags::MakeSampler(Type flags)
94 {
95   auto sampler = Sampler::New();
96   sampler.SetFilterMode(GetMinFilter(flags), GetMagFilter(flags));
97   sampler.SetWrapMode(GetWrapS(flags), GetWrapT(flags));
98   return sampler;
99 }
100
101 TextureDefinition::TextureDefinition(const std::string& imageUri, SamplerFlags::Type samplerFlags)
102 : mImageUri(imageUri),
103   mSamplerFlags(samplerFlags)
104 {
105 }
106
107 MaterialDefinition::RawData
108 MaterialDefinition::LoadRaw(const std::string& imagesPath) const
109 {
110   RawData raw;
111
112   const bool hasTransparency = MaskMatch(mFlags, TRANSPARENCY);
113   // Why we add additional count here?
114   uint32_t numBuffers = mTextureStages.size() + (hasTransparency ? !CheckTextures(ALBEDO) + !CheckTextures(METALLIC | ROUGHNESS) + !CheckTextures(NORMAL)
115                                                                  : !CheckTextures(ALBEDO | METALLIC) + !CheckTextures(NORMAL | ROUGHNESS));
116   if(numBuffers == 0)
117   {
118     return raw;
119   }
120   raw.mTextures.reserve(numBuffers);
121
122   // Load textures
123   auto iTexture   = mTextureStages.cbegin();
124   auto checkStage = [&](uint32_t flags) {
125     return iTexture != mTextureStages.end() && MaskMatch(iTexture->mSemantic, flags);
126   };
127
128   // Check for compulsory textures: Albedo, Metallic, Roughness, Normal
129   if(checkStage(ALBEDO | METALLIC))
130   {
131     raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
132     ++iTexture;
133
134     if(checkStage(NORMAL | ROUGHNESS))
135     {
136       raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
137       ++iTexture;
138     }
139     else // single value normal-roughness
140     {
141       const auto bufferSize = 4;
142       uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1
143       raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
144     }
145   }
146   else
147   {
148     if(checkStage(ALBEDO))
149     {
150       raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
151       ++iTexture;
152     }
153     else if(mNeedAlbedoTexture) // single value albedo, albedo-alpha or albedo-metallic
154     {
155       uint32_t bufferSize = 4;
156       uint8_t* buffer     = nullptr;
157       auto     format     = Pixel::Format::RGBA8888;
158       if(hasTransparency) // albedo-alpha
159       {
160         buffer    = new uint8_t[bufferSize];
161         buffer[3] = static_cast<uint8_t>(mColor.a * 255.f);
162       }
163       else if(!checkStage(METALLIC | ROUGHNESS)) // albedo-metallic
164       {
165         buffer    = new uint8_t[bufferSize];
166         buffer[3] = 0xff; // metallic of 1.0
167       }
168       else // albedo
169       {
170         bufferSize = 3;
171         buffer     = new uint8_t[bufferSize];
172         format     = Pixel::Format::RGB888;
173       }
174       buffer[0] = static_cast<uint8_t>(mColor.r * 255.f);
175       buffer[1] = static_cast<uint8_t>(mColor.g * 255.f);
176       buffer[2] = static_cast<uint8_t>(mColor.b * 255.f);
177       raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, format, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
178     }
179
180     // If we have transparency, or an image based albedo map, we will have to continue with separate metallicRoughness + normal.
181     const bool createMetallicRoughnessAndNormal = hasTransparency || std::distance(mTextureStages.begin(), iTexture) > 0;
182     if(checkStage(METALLIC | ROUGHNESS))
183     {
184       raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
185       ++iTexture;
186     }
187     else if(createMetallicRoughnessAndNormal && mNeedMetallicRoughnessTexture)
188     {
189       // NOTE: we want to set both metallic and roughness to 1.0; dli uses the R & A channels,
190       // glTF2 uses B & G, so we might as well just set all components to 1.0.
191       const auto bufferSize = 4;
192       uint8_t*   buffer     = new uint8_t[bufferSize]{0xff, 0xff, 0xff, 0xff};
193       raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
194     }
195
196     if(checkStage(NORMAL))
197     {
198       raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
199       ++iTexture;
200     }
201     else if(mNeedNormalTexture)
202     {
203       if(createMetallicRoughnessAndNormal)
204       {
205         const auto bufferSize = 3;
206         uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff}; // normal of (0, 0, 1)
207         raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
208       }
209       else // single-value normal-roughness
210       {
211         const auto bufferSize = 4;
212         uint8_t*   buffer     = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1.0
213         raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
214       }
215     }
216   }
217
218   // Extra textures.
219   if(checkStage(SUBSURFACE))
220   {
221     raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
222     ++iTexture;
223   }
224
225   if(checkStage(OCCLUSION))
226   {
227     raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
228     ++iTexture;
229   }
230
231   if(checkStage(EMISSIVE))
232   {
233     raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
234     ++iTexture;
235   }
236
237   return raw;
238 }
239
240 TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environments, RawData&& raw) const
241 {
242   auto textureSet = TextureSet::New();
243
244   uint32_t n = 0;
245   for(auto& tData : raw.mTextures)
246   {
247     auto& pixels  = tData.mPixels;
248     auto  texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
249     texture.Upload(tData.mPixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
250     if(tData.mSamplerFlags & SamplerFlags::MIPMAP_MASK)
251     {
252       texture.GenerateMipmaps();
253     }
254
255     textureSet.SetTexture(n, texture);
256     textureSet.SetSampler(n, SamplerFlags::MakeSampler(tData.mSamplerFlags));
257
258     ++n;
259   }
260
261   // Assign textures to slots -- starting with 2D ones, then cubemaps, if any.
262   if(mEnvironmentIdx < environments.size())
263   {
264     auto& envTextures = environments[mEnvironmentIdx].second;
265     // If pre-computed brdf texture is defined, set the texture.
266     if(envTextures.mBrdf)
267     {
268       textureSet.SetTexture(n, envTextures.mBrdf);
269       ++n;
270     }
271
272     if(envTextures.mDiffuse)
273     {
274       textureSet.SetTexture(n, envTextures.mDiffuse);
275       ++n;
276     }
277
278     if(envTextures.mSpecular)
279     {
280       auto specularSampler = Sampler::New();
281       specularSampler.SetWrapMode(WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
282       specularSampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR);
283
284       textureSet.SetTexture(n, envTextures.mSpecular);
285       textureSet.SetSampler(n, specularSampler);
286       ++n;
287     }
288   }
289   else
290   {
291     ExceptionFlinger(ASSERT_LOCATION) << "Environment index (" << mEnvironmentIdx << ") out of bounds (" << environments.size() << ").";
292   }
293
294   return textureSet;
295 }
296
297 bool MaterialDefinition::CheckTextures(uint32_t flags) const
298 {
299   return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) {
300            return MaskMatch(ts.mSemantic, flags);
301          }) != mTextureStages.end();
302 }
303
304 } // namespace SceneLoader
305 } // namespace Dali