Merge changes I776588c1,I7292a2fb into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / shader-manager.cpp
1 /*
2  * Copyright (c) 2023 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 // CLASS HEADER
19 #include <dali-scene3d/public-api/loader/shader-manager.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/common/map-wrapper.h>
23 #include <dali/public-api/animation/constraint.h>
24 #include <cstring>
25
26 // INTERNAL INCLUDES
27 #include <dali-scene3d/internal/light/light-impl.h>
28 #include <dali-scene3d/internal/loader/hash.h>
29 #include <dali-scene3d/public-api/loader/blend-shape-details.h>
30 #include <dali-scene3d/public-api/loader/node-definition.h>
31
32 #include <dali/integration-api/debug.h>
33
34 namespace Dali::Scene3D::Loader
35 {
36 namespace
37 {
38 static constexpr uint32_t INDEX_FOR_LIGHT_CONSTRAINT_TAG  = 10;
39 static constexpr uint32_t INDEX_FOR_SHADOW_CONSTRAINT_TAG = 100;
40
41 ShaderOption MakeOption(const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
42 {
43   ShaderOption option;
44
45   const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
46   if(hasTransparency)
47   {
48     option.SetTransparency();
49   }
50
51   if(hasTransparency ||
52      !materialDef.CheckTextures(MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC) ||
53      !materialDef.CheckTextures(MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS))
54   {
55     option.AddOption(ShaderOption::Type::THREE_TEXTURE);
56
57     // For the glTF, each of basecolor, metallic_roughness, normal texture is not essential.
58     if(MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
59     {
60       option.AddOption(ShaderOption::Type::BASE_COLOR_TEXTURE);
61     }
62
63     if(materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS))
64     {
65       option.AddOption(ShaderOption::Type::METALLIC_ROUGHNESS_TEXTURE);
66     }
67
68     if(MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
69     {
70       option.AddOption(ShaderOption::Type::NORMAL_TEXTURE);
71     }
72   }
73
74   if(materialDef.GetAlphaCutoff() > 0.f)
75   {
76     option.AddOption(ShaderOption::Type::ALPHA_TEST);
77   }
78
79   if(MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
80   {
81     option.AddOption(ShaderOption::Type::SUBSURFACE);
82   }
83
84   if(MaskMatch(materialDef.mFlags, MaterialDefinition::OCCLUSION))
85   {
86     option.AddOption(ShaderOption::Type::OCCLUSION);
87   }
88
89   if(MaskMatch(materialDef.mFlags, MaterialDefinition::EMISSIVE))
90   {
91     option.AddOption(ShaderOption::Type::EMISSIVE);
92   }
93
94   if(MaskMatch(materialDef.mFlags, MaterialDefinition::SPECULAR))
95   {
96     option.AddOption(ShaderOption::Type::SPECULAR);
97   }
98
99   if(MaskMatch(materialDef.mFlags, MaterialDefinition::SPECULAR_COLOR))
100   {
101     option.AddOption(ShaderOption::Type::SPECULAR_COLOR);
102   }
103
104   if(MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
105   {
106     option.AddOption(ShaderOption::Type::GLTF_CHANNELS);
107   }
108
109   if(meshDef.IsSkinned())
110   {
111     option.AddOption(ShaderOption::Type::SKINNING);
112   }
113
114   if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
115   {
116     option.AddOption(ShaderOption::Type::FLIP_UVS_VERTICAL);
117   }
118
119   if(meshDef.mColors.IsDefined())
120   {
121     option.AddOption(ShaderOption::Type::COLOR_ATTRIBUTE);
122   }
123
124   if(meshDef.mTangentType == Property::VECTOR4)
125   {
126     option.AddOption(ShaderOption::Type::VEC4_TANGENT);
127   }
128
129   if(meshDef.HasBlendShapes())
130   {
131     bool hasPositions = false;
132     bool hasNormals   = false;
133     bool hasTangents  = false;
134     meshDef.RetrieveBlendShapeComponents(hasPositions, hasNormals, hasTangents);
135     if(hasPositions)
136     {
137       option.AddOption(ShaderOption::Type::MORPH_POSITION);
138     }
139
140     if(hasNormals)
141     {
142       option.AddOption(ShaderOption::Type::MORPH_NORMAL);
143     }
144
145     if(hasTangents)
146     {
147       option.AddOption(ShaderOption::Type::MORPH_TANGENT);
148     }
149
150     if(hasPositions || hasNormals || hasTangents)
151     {
152       if(BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
153       {
154         option.AddOption(ShaderOption::Type::MORPH_VERSION_2_0);
155       }
156     }
157   }
158
159   return option;
160 }
161 } // namespace
162
163 struct ShaderManager::Impl
164 {
165   std::map<uint64_t, Index>   mShaderMap;
166   std::vector<Dali::Shader>   mShaders;
167   std::vector<Scene3D::Light> mLights;
168
169   Scene3D::Light mShadowLight;
170 };
171
172 ShaderManager::ShaderManager()
173 : mImpl{new Impl()}
174 {
175 }
176
177 ShaderManager::~ShaderManager() = default;
178
179 Dali::Shader ShaderManager::ProduceShader(const MaterialDefinition& materialDefinition, const MeshDefinition& meshDefinition)
180 {
181   ShaderOption option = MakeOption(materialDefinition, meshDefinition);
182   return ProduceShader(option);
183 }
184
185 Dali::Shader ShaderManager::ProduceShader(const ShaderOption& shaderOption)
186 {
187   Dali::Shader result;
188
189   auto&    shaderMap = mImpl->mShaderMap;
190   uint64_t hash      = shaderOption.GetOptionHash();
191   auto     iFind     = shaderMap.find(hash);
192   if(iFind != shaderMap.end())
193   {
194     result = mImpl->mShaders[iFind->second];
195   }
196   else
197   {
198     ShaderDefinition shaderDef;
199     shaderDef.mUseBuiltInShader = true;
200
201     shaderOption.GetDefines(shaderDef.mDefines);
202     shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
203
204     shaderMap[hash] = mImpl->mShaders.size();
205
206     auto raw = shaderDef.LoadRaw("");
207     mImpl->mShaders.emplace_back(shaderDef.Load(std::move(raw)));
208     result = mImpl->mShaders.back();
209
210     std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
211     result.RegisterProperty(lightCountPropertyName, static_cast<int32_t>(mImpl->mLights.size()));
212
213     for(uint32_t index = 0; index < mImpl->mLights.size(); ++index)
214     {
215       SetLightConstraintToShader(index, result);
216     }
217
218     result.RegisterProperty("uIsShadowEnabled", static_cast<int32_t>(!!mImpl->mShadowLight));
219     if(!!mImpl->mShadowLight)
220     {
221       SetShadowConstraintToShader(result);
222       SetShadowUniformToShader(result);
223     }
224   }
225
226   return result;
227 }
228
229 RendererState::Type ShaderManager::GetRendererState(const MaterialDefinition& materialDefinition)
230 {
231   RendererState::Type rendererState = RendererState::DEPTH_TEST;
232
233   if(!materialDefinition.mDoubleSided)
234   {
235     rendererState |= RendererState::CULL_BACK;
236   }
237
238   const bool hasTransparency = MaskMatch(materialDefinition.mFlags, MaterialDefinition::TRANSPARENCY);
239   if(hasTransparency)
240   {
241     // TODO: this requires more granularity
242     rendererState = (rendererState | RendererState::ALPHA_BLEND);
243   }
244   return rendererState;
245 }
246
247 bool ShaderManager::AddLight(Scene3D::Light light)
248 {
249   if(!light || mImpl->mLights.size() >= Scene3D::Internal::Light::GetMaximumEnabledLightCount())
250   {
251     return false;
252   }
253
254   uint32_t lightIndex = mImpl->mLights.size();
255   mImpl->mLights.push_back(light);
256
257   for(auto&& shader : mImpl->mShaders)
258   {
259     std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
260     shader.RegisterProperty(lightCountPropertyName, static_cast<int32_t>(mImpl->mLights.size()));
261   }
262
263   SetLightConstraint(lightIndex);
264
265   return true;
266 }
267
268 void ShaderManager::RemoveLight(Scene3D::Light light)
269 {
270   uint32_t lightCount = mImpl->mLights.size();
271   for(uint32_t index = 0; index < lightCount; ++index)
272   {
273     if(mImpl->mLights[index] != light)
274     {
275       continue;
276     }
277
278     RemoveLightConstraint(index);
279
280     if(!mImpl->mLights.empty() && light != mImpl->mLights.back())
281     {
282       RemoveLightConstraint(mImpl->mLights.size() - 1);
283       mImpl->mLights[index] = mImpl->mLights.back();
284       SetLightConstraint(index);
285     }
286
287     mImpl->mLights.pop_back();
288
289     for(auto&& shader : mImpl->mShaders)
290     {
291       std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
292       shader.RegisterProperty(lightCountPropertyName, static_cast<int32_t>(mImpl->mLights.size()));
293     }
294     break;
295   }
296 }
297
298 uint32_t ShaderManager::GetLightCount() const
299 {
300   return mImpl->mLights.size();
301 }
302
303 void ShaderManager::SetShadow(Scene3D::Light light)
304 {
305   mImpl->mShadowLight = light;
306   for(auto&& shader : mImpl->mShaders)
307   {
308     std::string shadowEnabledPropertyName(Scene3D::Internal::Light::GetShadowEnabledUniformName());
309     shader.RegisterProperty(shadowEnabledPropertyName, static_cast<int32_t>(true));
310   }
311
312   SetShadowProperty();
313 }
314
315 void ShaderManager::RemoveShadow()
316 {
317   for(auto&& shader : mImpl->mShaders)
318   {
319     std::string shadowEnabledPropertyName(Scene3D::Internal::Light::GetShadowEnabledUniformName());
320     shader.RegisterProperty(shadowEnabledPropertyName, static_cast<int32_t>(false));
321     shader.RemoveConstraints(INDEX_FOR_SHADOW_CONSTRAINT_TAG);
322   }
323   mImpl->mShadowLight.Reset();
324 }
325
326 void ShaderManager::UpdateShadowUniform(Scene3D::Light light)
327 {
328   if(light != mImpl->mShadowLight)
329   {
330     return;
331   }
332
333   for(auto&& shader : mImpl->mShaders)
334   {
335     SetShadowUniformToShader(shader);
336   }
337 }
338
339 void ShaderManager::SetLightConstraint(uint32_t lightIndex)
340 {
341   for(auto&& shader : mImpl->mShaders)
342   {
343     SetLightConstraintToShader(lightIndex, shader);
344   }
345 }
346
347 void ShaderManager::SetLightConstraintToShader(uint32_t lightIndex, Dali::Shader shader)
348 {
349   std::string lightDirectionPropertyName(Scene3D::Internal::Light::GetLightDirectionUniformName());
350   lightDirectionPropertyName += "[" + std::to_string(lightIndex) + "]";
351   auto             lightDirectionPropertyIndex = shader.RegisterProperty(lightDirectionPropertyName, Vector3::ZAXIS);
352   Dali::Constraint lightDirectionConstraint    = Dali::Constraint::New<Vector3>(shader, lightDirectionPropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs) { output = inputs[0]->GetQuaternion().Rotate(Vector3::ZAXIS); });
353   lightDirectionConstraint.AddSource(Source{mImpl->mLights[lightIndex], Dali::Actor::Property::WORLD_ORIENTATION});
354   lightDirectionConstraint.ApplyPost();
355   lightDirectionConstraint.SetTag(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
356
357   std::string lightColorPropertyName(Scene3D::Internal::Light::GetLightColorUniformName());
358   lightColorPropertyName += "[" + std::to_string(lightIndex) + "]";
359   auto             lightColorPropertyIndex = shader.RegisterProperty(lightColorPropertyName, Vector3(Color::WHITE));
360   Dali::Constraint lightColorConstraint    = Dali::Constraint::New<Vector3>(shader, lightColorPropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs) { output = Vector3(inputs[0]->GetVector4()); });
361   lightColorConstraint.AddSource(Source{mImpl->mLights[lightIndex], Dali::Actor::Property::COLOR});
362   lightColorConstraint.ApplyPost();
363   lightColorConstraint.SetTag(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
364 }
365
366 void ShaderManager::RemoveLightConstraint(uint32_t lightIndex)
367 {
368   for(auto&& shader : mImpl->mShaders)
369   {
370     shader.RemoveConstraints(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
371   }
372 }
373
374 void ShaderManager::SetShadowUniformToShader(Dali::Shader shader)
375 {
376   shader.RegisterProperty("uShadowIntensity", mImpl->mShadowLight.GetShadowIntensity());
377   shader.RegisterProperty("uShadowBias", mImpl->mShadowLight.GetShadowBias());
378   shader.RegisterProperty("uEnableShadowSoftFiltering", static_cast<int>(mImpl->mShadowLight.IsShadowSoftFilteringEnabled()));
379 }
380
381 void ShaderManager::SetShadowProperty()
382 {
383   for(auto&& shader : mImpl->mShaders)
384   {
385     SetShadowUniformToShader(shader);
386     SetShadowConstraintToShader(shader);
387   }
388 }
389
390 void ShaderManager::SetShadowConstraintToShader(Dali::Shader shader)
391 {
392   // Constraint is applied before View/Projection Matrix is computed in update thread.
393   // So, it could show not plausible result if camera properties are changed discontinuesly.
394   // If we want to make it be synchronized, View/Projection matrix are needed to be conputed in below constraint.
395
396   std::string       shadowViewProjectionPropertyName(Scene3D::Internal::Light::GetShadowViewProjectionMatrixUniformName());
397   auto              shadowViewProjectionPropertyIndex = shader.RegisterProperty(shadowViewProjectionPropertyName, Matrix::IDENTITY);
398   Dali::CameraActor shadowLightCamera                 = Dali::Scene3D::Internal::GetImplementation(mImpl->mShadowLight).GetCamera();
399   auto              tempViewMatrixIndex               = shadowLightCamera.GetPropertyIndex("tempViewMatrix");
400   if(tempViewMatrixIndex != Dali::Property::INVALID_INDEX)
401   {
402     tempViewMatrixIndex = shadowLightCamera.RegisterProperty("tempViewMatrix", Matrix::IDENTITY);
403   }
404   auto tempProjectionMatrixIndex = shadowLightCamera.GetPropertyIndex("tempProjectionMatrix");
405   if(tempProjectionMatrixIndex != Dali::Property::INVALID_INDEX)
406   {
407     tempProjectionMatrixIndex = shadowLightCamera.RegisterProperty("tempProjectionMatrix", Matrix::IDENTITY);
408   }
409   Dali::Constraint shadowViewProjectionConstraint = Dali::Constraint::New<Matrix>(shader, shadowViewProjectionPropertyIndex, [](Matrix& output, const PropertyInputContainer& inputs)
410                                                                                   {
411                                                                                     output = inputs[1]->GetMatrix() * inputs[0]->GetMatrix(); });
412
413   shadowViewProjectionConstraint.AddSource(Source{shadowLightCamera, tempViewMatrixIndex});
414   shadowViewProjectionConstraint.AddSource(Source{shadowLightCamera, tempProjectionMatrixIndex});
415   shadowViewProjectionConstraint.ApplyPost();
416   shadowViewProjectionConstraint.SetTag(INDEX_FOR_SHADOW_CONSTRAINT_TAG);
417 }
418
419 } // namespace Dali::Scene3D::Loader