ca1244b8dd7eefc941cf8e195b6b0f80388c5297
[platform/core/uifw/dali-toolkit.git] / dali-scene-loader / public-api / material-definition.cpp
1 /*
2  * Copyright (c) 2020 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
33 constexpr SamplerFlags::Type FILTER_MODES_FROM_DALI[]{
34   SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_NEAREST,
35   SamplerFlags::FILTER_LINEAR,
36   SamplerFlags::FILTER_NEAREST,
37   SamplerFlags::FILTER_LINEAR,
38   SamplerFlags::FILTER_NEAREST | SamplerFlags::FILTER_MIPMAP_NEAREST,
39   SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_NEAREST,
40   SamplerFlags::FILTER_NEAREST | SamplerFlags::FILTER_MIPMAP_LINEAR,
41   SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_LINEAR,
42 };
43
44 constexpr SamplerFlags::Type WRAP_MODES_FROM_DALI[]{
45   SamplerFlags::WRAP_CLAMP,
46   SamplerFlags::WRAP_CLAMP,
47   SamplerFlags::WRAP_REPEAT,
48   SamplerFlags::WRAP_MIRROR,
49 };
50
51 constexpr FilterMode::Type  FILTER_MODES_TO_DALI[]{
52   FilterMode::NEAREST,
53   FilterMode::LINEAR,
54   FilterMode::NEAREST_MIPMAP_NEAREST,
55   FilterMode::LINEAR_MIPMAP_NEAREST,
56   FilterMode::NEAREST_MIPMAP_LINEAR,
57   FilterMode::LINEAR_MIPMAP_LINEAR,
58 };
59
60 constexpr WrapMode::Type WRAP_MODES_TO_DALI[]{
61   WrapMode::REPEAT,
62   WrapMode::CLAMP_TO_EDGE,
63   WrapMode::MIRRORED_REPEAT
64 };
65
66 const SamplerFlags::Type SINGLE_VALUE_SAMPLER = SamplerFlags::Encode(FilterMode::NEAREST, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
67 }
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 MaterialDefinition::RawData
104   MaterialDefinition::LoadRaw(const std::string& imagesPath) const
105 {
106   RawData raw;
107
108   const bool hasTransparency = MaskMatch(mFlags, TRANSPARENCY);
109   uint32_t numBuffers = mTextureStages.size() + (hasTransparency ?
110     !CheckTextures(ALBEDO) + !CheckTextures(METALLIC | ROUGHNESS) + !CheckTextures(NORMAL) :
111     !CheckTextures(ALBEDO | METALLIC) + !CheckTextures(NORMAL | ROUGHNESS));
112   if (numBuffers == 0)
113   {
114     return raw;
115   }
116   raw.mTextures.reserve(numBuffers);
117
118   // Load textures
119   auto iTexture = mTextureStages.cbegin();
120   auto checkStage = [&](uint32_t flags) {
121     return iTexture != mTextureStages.end() && MaskMatch(iTexture->mSemantic, flags);
122   };
123
124   // Check for compulsory textures: Albedo, Metallic, Roughness, Normal
125   if (checkStage(ALBEDO | METALLIC))
126   {
127     raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
128     ++iTexture;
129
130     if (checkStage(NORMAL | ROUGHNESS))
131     {
132       raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
133       ++iTexture;
134     }
135     else // single value normal-roughness
136     {
137       const auto bufferSize = 4;
138       uint8_t* buffer = new uint8_t[bufferSize]{ 0x7f, 0x7f, 0xff, 0xff }; // normal of (0, 0, 1), roughness of 1
139       raw.mTextures.push_back({ PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER });
140     }
141   }
142   else
143   {
144     if (checkStage(ALBEDO))
145     {
146       raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
147       ++iTexture;
148     }
149     else // single value albedo, albedo-alpha or albedo-metallic
150     {
151       uint32_t bufferSize = 4;
152       uint8_t* buffer = nullptr;
153       auto format = Pixel::Format::RGBA8888;
154       if (hasTransparency)  // albedo-alpha
155       {
156         buffer = new uint8_t[bufferSize];
157         buffer[3] = static_cast<uint8_t>(mColor.a * 255.f);
158       }
159       else if (!checkStage(METALLIC | ROUGHNESS))  // albedo-metallic
160       {
161         buffer = new uint8_t[bufferSize];
162         buffer[3] = 0xff;  // metallic of 1.0
163       }
164       else  // albedo
165       {
166         bufferSize = 3;
167         buffer = new uint8_t[bufferSize];
168         format = Pixel::Format::RGB888;
169       }
170       buffer[0] = static_cast<uint8_t>(mColor.r * 255.f);
171       buffer[1] = static_cast<uint8_t>(mColor.g * 255.f);
172       buffer[2] = static_cast<uint8_t>(mColor.b * 255.f);
173       raw.mTextures.push_back({ PixelData::New(buffer, bufferSize, 1, 1, format, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER });
174     }
175
176     // If we have transparency, or an image based albedo map, we will have to continue with separate metallicRoughness + normal.
177     const bool createMetallicRoughnessAndNormal = hasTransparency || std::distance(mTextureStages.begin(), iTexture) > 0;
178     if (checkStage(METALLIC | ROUGHNESS))
179     {
180       raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
181       ++iTexture;
182     }
183     else if (createMetallicRoughnessAndNormal)
184     {
185       // NOTE: we want to set both metallic and roughness to 1.0; dli uses the R & A channels,
186       // glTF2 uses B & G, so we might as well just set all components to 1.0.
187       const auto bufferSize = 4;
188       uint8_t* buffer = new uint8_t[bufferSize]{ 0xff, 0xff, 0xff, 0xff };
189       raw.mTextures.push_back({ PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER });
190     }
191
192     if (checkStage(NORMAL))
193     {
194       raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
195       ++iTexture;
196     }
197     else if (createMetallicRoughnessAndNormal)
198     {
199       const auto bufferSize = 3;
200       uint8_t* buffer = new uint8_t[bufferSize]{ 0x7f, 0x7f, 0xff };  // normal of (0, 0, 1)
201       raw.mTextures.push_back({ PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER });
202     }
203     else // single-value normal-roughness
204     {
205       const auto bufferSize = 4;
206       uint8_t* buffer = new uint8_t[bufferSize]{ 0x7f, 0x7f, 0xff, 0xff };  // normal of (0, 0, 1), roughness of 1.0
207       raw.mTextures.push_back({ PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER });
208     }
209   }
210
211   // Extra textures. TODO: emissive, occlusion etc.
212   if (checkStage(SUBSURFACE))
213   {
214     raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
215     ++iTexture;
216   }
217
218   return raw;
219 }
220
221 TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environments, RawData&& raw) const
222 {
223   auto textureSet = TextureSet::New();
224
225   uint32_t n = 0;
226   for (auto& tData : raw.mTextures)
227   {
228     auto& pixels = tData.mPixels;
229     auto texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
230     texture.Upload(tData.mPixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
231     if (tData.mSamplerFlags & SamplerFlags::MIPMAP_MASK)
232     {
233       texture.GenerateMipmaps();
234     }
235
236     textureSet.SetTexture(n, texture);
237     textureSet.SetSampler(n, SamplerFlags::MakeSampler(tData.mSamplerFlags));
238
239     ++n;
240   }
241
242   // Assign textures to slots -- starting with 2D ones, then cubemaps, if any.
243   if (mEnvironmentIdx < environments.size())
244   {
245     auto& envTextures = environments[mEnvironmentIdx].second;
246     if (envTextures.mDiffuse)
247     {
248       textureSet.SetTexture(n, envTextures.mDiffuse);
249       ++n;
250     }
251
252     if (envTextures.mSpecular)
253     {
254       auto specularSampler = Sampler::New();
255       specularSampler.SetWrapMode(WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
256       specularSampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR);
257
258       textureSet.SetTexture(n, envTextures.mSpecular);
259       textureSet.SetSampler(n, specularSampler);
260     }
261   }
262   else
263   {
264     ExceptionFlinger(ASSERT_LOCATION) << "Environment index (" << mEnvironmentIdx << ") out of bounds (" <<
265       environments.size() << ").";
266   }
267
268   return textureSet;
269 }
270
271 bool MaterialDefinition::CheckTextures(uint32_t flags) const
272 {
273   return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) {
274     return MaskMatch(ts.mSemantic, flags);
275   }) != mTextureStages.end();
276 }
277
278 }
279 }