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