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