2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-scene3d/public-api/loader/shader-manager.h>
22 #include <dali/devel-api/common/map-wrapper.h>
23 #include <dali/public-api/animation/constraint.h>
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>
32 #include <dali/integration-api/debug.h>
36 #if defined(DEBUG_ENABLED)
37 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_MODEL_SHADER_MANAGER");
41 namespace Dali::Scene3D::Loader
45 static constexpr uint32_t INDEX_FOR_LIGHT_CONSTRAINT_TAG = 10;
46 static constexpr uint32_t INDEX_FOR_SHADOW_CONSTRAINT_TAG = 100;
48 static const char* ADD_EXTRA_SKINNING_ATTRIBUTES{"ADD_EXTRA_SKINNING_ATTRIBUTES"};
49 static const char* ADD_EXTRA_WEIGHTS{"ADD_EXTRA_WEIGHTS"};
51 ShaderOption MakeOption(const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
55 const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
58 option.SetTransparency();
62 !materialDef.CheckTextures(MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC) ||
63 !materialDef.CheckTextures(MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS))
65 option.AddOption(ShaderOption::Type::THREE_TEXTURE);
67 // For the glTF, each of basecolor, metallic_roughness, normal texture is not essential.
68 if(MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
70 option.AddOption(ShaderOption::Type::BASE_COLOR_TEXTURE);
73 if(materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS))
75 option.AddOption(ShaderOption::Type::METALLIC_ROUGHNESS_TEXTURE);
78 if(MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
80 option.AddOption(ShaderOption::Type::NORMAL_TEXTURE);
84 if(materialDef.GetAlphaCutoff() > 0.f)
86 option.AddOption(ShaderOption::Type::ALPHA_TEST);
89 if(MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
91 option.AddOption(ShaderOption::Type::SUBSURFACE);
94 if(MaskMatch(materialDef.mFlags, MaterialDefinition::OCCLUSION))
96 option.AddOption(ShaderOption::Type::OCCLUSION);
99 if(MaskMatch(materialDef.mFlags, MaterialDefinition::EMISSIVE))
101 option.AddOption(ShaderOption::Type::EMISSIVE);
104 if(MaskMatch(materialDef.mFlags, MaterialDefinition::SPECULAR))
106 option.AddOption(ShaderOption::Type::SPECULAR);
109 if(MaskMatch(materialDef.mFlags, MaterialDefinition::SPECULAR_COLOR))
111 option.AddOption(ShaderOption::Type::SPECULAR_COLOR);
114 if(MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
116 option.AddOption(ShaderOption::Type::GLTF_CHANNELS);
119 if(meshDef.IsSkinned())
121 option.AddOption(ShaderOption::Type::SKINNING);
123 // Add options for ADD_EXTRA_SKINNING_ATTRIBUTES and ADD_EXTRA_WEIGHTS:
124 size_t numberOfSets = meshDef.mJoints.size();
127 std::ostringstream attributes;
128 std::ostringstream weights;
129 for(size_t i = 1; i < numberOfSets; ++i)
131 attributes << "in vec4 aJoints" << i << ";\n";
132 attributes << "in vec4 aWeights" << i << ";\n";
134 weights << "bone +=\n"
135 << "uBone[int(aJoints" << i << ".x)] * aWeights" << i << ".x +\n"
136 << "uBone[int(aJoints" << i << ".y)] * aWeights" << i << ".y +\n"
137 << "uBone[int(aJoints" << i << ".z)] * aWeights" << i << ".z +\n"
138 << "uBone[int(aJoints" << i << ".w)] * aWeights" << i << ".w;\n";
140 option.AddMacroDefinition(ADD_EXTRA_SKINNING_ATTRIBUTES, attributes.str());
141 option.AddMacroDefinition(ADD_EXTRA_WEIGHTS, weights.str());
145 if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
147 option.AddOption(ShaderOption::Type::FLIP_UVS_VERTICAL);
150 if(!meshDef.mColors.empty() && meshDef.mColors[0].IsDefined())
152 option.AddOption(ShaderOption::Type::COLOR_ATTRIBUTE);
155 if(meshDef.mTangentType == Property::VECTOR4)
157 option.AddOption(ShaderOption::Type::VEC4_TANGENT);
160 if(meshDef.HasBlendShapes())
162 bool hasPositions = false;
163 bool hasNormals = false;
164 bool hasTangents = false;
165 meshDef.RetrieveBlendShapeComponents(hasPositions, hasNormals, hasTangents);
168 option.AddOption(ShaderOption::Type::MORPH_POSITION);
173 option.AddOption(ShaderOption::Type::MORPH_NORMAL);
178 option.AddOption(ShaderOption::Type::MORPH_TANGENT);
181 if(hasPositions || hasNormals || hasTangents)
183 if(BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
185 option.AddOption(ShaderOption::Type::MORPH_VERSION_2_0);
194 struct ShaderManager::Impl
196 std::map<uint64_t, Index> mShaderMap;
197 std::vector<Dali::Shader> mShaders;
198 std::vector<Scene3D::Light> mLights;
200 Scene3D::Light mShadowLight;
203 ShaderManager::ShaderManager()
208 ShaderManager::~ShaderManager() = default;
210 Dali::Shader ShaderManager::ProduceShader(const MaterialDefinition& materialDefinition, const MeshDefinition& meshDefinition)
212 DALI_LOG_INFO(gLogFilter, Debug::Concise, "Defining shader from mat/mesh definitions\n");
213 ShaderOption option = MakeOption(materialDefinition, meshDefinition);
214 return ProduceShader(option);
217 Dali::Shader ShaderManager::ProduceShader(const ShaderOption& shaderOption)
221 auto& shaderMap = mImpl->mShaderMap;
222 uint64_t hash = shaderOption.GetOptionHash();
224 #if defined(DEBUG_ENABLED)
225 std::ostringstream oss;
226 oss << " ShaderOption defines:";
227 std::vector<std::string> defines;
228 shaderOption.GetDefines(defines);
229 for(auto& def : defines)
234 << " ShaderOption macro definitions:" << std::endl;
235 for(auto& macro : shaderOption.GetMacroDefinitions())
237 oss << macro.macro << " : " << macro.definition << std::endl;
239 DALI_LOG_INFO(gLogFilter, Debug::Concise, "ShaderOption:\n%s\n", oss.str().c_str());
242 auto iFind = shaderMap.find(hash);
243 if(iFind != shaderMap.end())
245 DALI_LOG_INFO(gLogFilter, Debug::Concise, "Defining Shader found: hash: %lx", hash);
246 result = mImpl->mShaders[iFind->second];
250 DALI_LOG_INFO(gLogFilter, Debug::Concise, "Creating new shader: hash: %lx\n", hash);
251 ShaderDefinition shaderDef;
252 shaderDef.mUseBuiltInShader = true;
254 shaderOption.GetDefines(shaderDef.mDefines);
255 shaderDef.mMacros = shaderOption.GetMacroDefinitions();
256 shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
258 shaderMap[hash] = mImpl->mShaders.size();
260 auto raw = shaderDef.LoadRaw("");
261 mImpl->mShaders.emplace_back(shaderDef.Load(std::move(raw)));
262 result = mImpl->mShaders.back();
264 std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
265 result.RegisterProperty(lightCountPropertyName, static_cast<int32_t>(mImpl->mLights.size()));
267 for(uint32_t index = 0; index < mImpl->mLights.size(); ++index)
269 SetLightConstraintToShader(index, result);
272 result.RegisterProperty("uIsShadowEnabled", static_cast<int32_t>(!!mImpl->mShadowLight));
273 if(!!mImpl->mShadowLight)
275 SetShadowConstraintToShader(result);
276 SetShadowUniformToShader(result);
283 RendererState::Type ShaderManager::GetRendererState(const MaterialDefinition& materialDefinition)
285 RendererState::Type rendererState = RendererState::DEPTH_TEST;
287 if(!materialDefinition.mDoubleSided)
289 rendererState |= RendererState::CULL_BACK;
292 const bool hasTransparency = MaskMatch(materialDefinition.mFlags, MaterialDefinition::TRANSPARENCY);
295 // TODO: this requires more granularity
296 rendererState = (rendererState | RendererState::ALPHA_BLEND);
298 return rendererState;
301 bool ShaderManager::AddLight(Scene3D::Light light)
303 if(!light || mImpl->mLights.size() >= Scene3D::Internal::Light::GetMaximumEnabledLightCount())
308 uint32_t lightIndex = mImpl->mLights.size();
309 mImpl->mLights.push_back(light);
311 for(auto&& shader : mImpl->mShaders)
313 std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
314 shader.RegisterProperty(lightCountPropertyName, static_cast<int32_t>(mImpl->mLights.size()));
317 SetLightConstraint(lightIndex);
322 void ShaderManager::RemoveLight(Scene3D::Light light)
324 uint32_t lightCount = mImpl->mLights.size();
325 for(uint32_t index = 0; index < lightCount; ++index)
327 if(mImpl->mLights[index] != light)
332 RemoveLightConstraint(index);
334 if(!mImpl->mLights.empty() && light != mImpl->mLights.back())
336 RemoveLightConstraint(mImpl->mLights.size() - 1);
337 mImpl->mLights[index] = mImpl->mLights.back();
338 SetLightConstraint(index);
341 mImpl->mLights.pop_back();
343 for(auto&& shader : mImpl->mShaders)
345 std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
346 shader.RegisterProperty(lightCountPropertyName, static_cast<int32_t>(mImpl->mLights.size()));
352 uint32_t ShaderManager::GetLightCount() const
354 return mImpl->mLights.size();
357 void ShaderManager::SetShadow(Scene3D::Light light)
359 mImpl->mShadowLight = light;
360 for(auto&& shader : mImpl->mShaders)
362 std::string shadowEnabledPropertyName(Scene3D::Internal::Light::GetShadowEnabledUniformName());
363 shader.RegisterProperty(shadowEnabledPropertyName, static_cast<int32_t>(true));
369 void ShaderManager::RemoveShadow()
371 for(auto&& shader : mImpl->mShaders)
373 std::string shadowEnabledPropertyName(Scene3D::Internal::Light::GetShadowEnabledUniformName());
374 shader.RegisterProperty(shadowEnabledPropertyName, static_cast<int32_t>(false));
375 shader.RemoveConstraints(INDEX_FOR_SHADOW_CONSTRAINT_TAG);
377 mImpl->mShadowLight.Reset();
380 void ShaderManager::UpdateShadowUniform(Scene3D::Light light)
382 if(light != mImpl->mShadowLight)
387 for(auto&& shader : mImpl->mShaders)
389 SetShadowUniformToShader(shader);
393 void ShaderManager::SetLightConstraint(uint32_t lightIndex)
395 for(auto&& shader : mImpl->mShaders)
397 SetLightConstraintToShader(lightIndex, shader);
401 void ShaderManager::SetLightConstraintToShader(uint32_t lightIndex, Dali::Shader shader)
403 std::string lightDirectionPropertyName(Scene3D::Internal::Light::GetLightDirectionUniformName());
404 lightDirectionPropertyName += "[" + std::to_string(lightIndex) + "]";
405 auto lightDirectionPropertyIndex = shader.RegisterProperty(lightDirectionPropertyName, Vector3::ZAXIS);
406 Dali::Constraint lightDirectionConstraint = Dali::Constraint::New<Vector3>(shader, lightDirectionPropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs) { output = inputs[0]->GetQuaternion().Rotate(Vector3::ZAXIS); });
407 lightDirectionConstraint.AddSource(Source{mImpl->mLights[lightIndex], Dali::Actor::Property::WORLD_ORIENTATION});
408 lightDirectionConstraint.ApplyPost();
409 lightDirectionConstraint.SetTag(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
411 std::string lightColorPropertyName(Scene3D::Internal::Light::GetLightColorUniformName());
412 lightColorPropertyName += "[" + std::to_string(lightIndex) + "]";
413 auto lightColorPropertyIndex = shader.RegisterProperty(lightColorPropertyName, Vector3(Color::WHITE));
414 Dali::Constraint lightColorConstraint = Dali::Constraint::New<Vector3>(shader, lightColorPropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs) { output = Vector3(inputs[0]->GetVector4()); });
415 lightColorConstraint.AddSource(Source{mImpl->mLights[lightIndex], Dali::Actor::Property::COLOR});
416 lightColorConstraint.ApplyPost();
417 lightColorConstraint.SetTag(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
420 void ShaderManager::RemoveLightConstraint(uint32_t lightIndex)
422 for(auto&& shader : mImpl->mShaders)
424 shader.RemoveConstraints(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
428 void ShaderManager::SetShadowUniformToShader(Dali::Shader shader)
430 shader.RegisterProperty("uShadowIntensity", mImpl->mShadowLight.GetShadowIntensity());
431 shader.RegisterProperty("uShadowBias", mImpl->mShadowLight.GetShadowBias());
432 shader.RegisterProperty("uEnableShadowSoftFiltering", static_cast<int>(mImpl->mShadowLight.IsShadowSoftFilteringEnabled()));
435 void ShaderManager::SetShadowProperty()
437 for(auto&& shader : mImpl->mShaders)
439 SetShadowUniformToShader(shader);
440 SetShadowConstraintToShader(shader);
444 void ShaderManager::SetShadowConstraintToShader(Dali::Shader shader)
446 // Constraint is applied before View/Projection Matrix is computed in update thread.
447 // So, it could show not plausible result if camera properties are changed discontinuesly.
448 // If we want to make it be synchronized, View/Projection matrix are needed to be conputed in below constraint.
450 std::string shadowViewProjectionPropertyName(Scene3D::Internal::Light::GetShadowViewProjectionMatrixUniformName());
451 auto shadowViewProjectionPropertyIndex = shader.RegisterProperty(shadowViewProjectionPropertyName, Matrix::IDENTITY);
452 Dali::CameraActor shadowLightCamera = Dali::Scene3D::Internal::GetImplementation(mImpl->mShadowLight).GetCamera();
453 auto tempViewProjectionMatrixIndex = shadowLightCamera.GetPropertyIndex("tempViewProjectionMatrix");
454 if(tempViewProjectionMatrixIndex != Dali::Property::INVALID_INDEX)
456 tempViewProjectionMatrixIndex = shadowLightCamera.RegisterProperty("tempViewProjectionMatrix", Matrix::IDENTITY);
458 Dali::Constraint shadowViewProjectionConstraint = Dali::Constraint::New<Matrix>(shader, shadowViewProjectionPropertyIndex, [](Matrix& output, const PropertyInputContainer& inputs) { output = inputs[0]->GetMatrix(); });
459 shadowViewProjectionConstraint.AddSource(Source{shadowLightCamera, tempViewProjectionMatrixIndex});
460 shadowViewProjectionConstraint.ApplyPost();
461 shadowViewProjectionConstraint.SetTag(INDEX_FOR_SHADOW_CONSTRAINT_TAG);
464 } // namespace Dali::Scene3D::Loader