e025ebe40df14f897efbb4f27fdf629e79d6a89e
[platform/core/uifw/dali-toolkit.git] / dali-scene-loader / public-api / shader-definition-factory.cpp
1 /*
2  * Copyright (c) 2022 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 #include "dali-scene-loader/public-api/shader-definition-factory.h"
18 #include <cstring>
19 #include "dali-scene-loader/internal/hash.h"
20 #include "dali-scene-loader/public-api/blend-shape-details.h"
21 #include "dali-scene-loader/public-api/node-definition.h"
22 #include "dali/devel-api/common/map-wrapper.h"
23
24 namespace Dali
25 {
26 namespace SceneLoader
27 {
28 namespace
29 {
30 struct ResourceReceiver : IResourceReceiver
31 {
32   const ResourceBundle&     mResources;
33   const MeshDefinition*     mMeshDef     = nullptr;
34   const MaterialDefinition* mMaterialDef = nullptr;
35
36   ResourceReceiver(const ResourceBundle& resources)
37   : mResources(resources)
38   {
39   }
40
41   void Register(ResourceType::Value type, Index id) override
42   {
43     switch(type)
44     {
45       case ResourceType::Mesh:
46         mMeshDef = &mResources.mMeshes[id].first;
47         break;
48
49       case ResourceType::Material:
50         mMaterialDef = &mResources.mMaterials[id].first;
51         break;
52
53       default:
54         break;
55     }
56   }
57 };
58
59 void RetrieveBlendShapeComponents(const std::vector<MeshDefinition::BlendShape>& blendShapes, bool& hasPositions, bool& hasNormals, bool& hasTangents)
60 {
61   for(const auto& blendShape : blendShapes)
62   {
63     hasPositions = hasPositions || blendShape.deltas.IsDefined();
64     hasNormals   = hasNormals || blendShape.normals.IsDefined();
65     hasTangents  = hasTangents || blendShape.tangents.IsDefined();
66   }
67 }
68
69 uint64_t HashNode(const NodeDefinition& nodeDef, const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
70 {
71   Hash hash;
72
73   const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
74   hash.Add(hasTransparency);
75
76   if(hasTransparency ||
77      materialDef.CheckTextures(MaterialDefinition::ALBEDO) ||
78      materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS) ||
79      materialDef.CheckTextures(MaterialDefinition::NORMAL))
80   {
81     hash.Add("3TEX");
82   }
83
84   if(materialDef.GetAlphaCutoff() > 0.f)
85   {
86     hash.Add("ALPH" /*A_TEST*/);
87   }
88
89   if(MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
90   {
91     hash.Add("SSS");
92   }
93
94   if(MaskMatch(materialDef.mFlags, MaterialDefinition::OCCLUSION))
95   {
96     hash.Add("OCCL" /*USION*/);
97   }
98
99   if(MaskMatch(materialDef.mFlags, MaterialDefinition::EMISSIVE))
100   {
101     hash.Add("EMIS" /*SIVE*/);
102   }
103
104   if(MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
105   {
106     hash.Add("GLTF" /*_CHANNELS*/);
107   }
108
109   if(meshDef.IsSkinned())
110   {
111     hash.Add("SKIN" /*NING*/);
112   }
113
114   if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
115   {
116     hash.Add("FLIP" /*_V*/);
117   }
118
119   if(meshDef.HasBlendShapes())
120   {
121     bool hasPositions = false;
122     bool hasNormals   = false;
123     bool hasTangents  = false;
124     RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
125     if(hasPositions)
126     {
127       hash.Add("MORPHPOS");
128     }
129
130     if(hasNormals)
131     {
132       hash.Add("MORPHNOR");
133     }
134
135     if(hasTangents)
136     {
137       hash.Add("MORPHTAN");
138     }
139
140     if(hasPositions || hasNormals || hasTangents)
141     {
142       hash.Add("MORPH");
143
144       if(BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
145       {
146         hash.Add("MORPHV2");
147       }
148     }
149   }
150
151   return hash;
152 }
153 } // namespace
154
155 struct ShaderDefinitionFactory::Impl
156 {
157   ResourceBundle*           mResources; // no ownership
158   std::map<uint64_t, Index> mShaderMap;
159 };
160
161 ShaderDefinitionFactory::ShaderDefinitionFactory()
162 : mImpl{new Impl()}
163 {
164 }
165
166 ShaderDefinitionFactory::~ShaderDefinitionFactory() = default;
167
168 void ShaderDefinitionFactory::SetResources(ResourceBundle& resources)
169 {
170   mImpl->mResources = &resources;
171   mImpl->mShaderMap.clear();
172 }
173
174 Index ShaderDefinitionFactory::ProduceShader(const NodeDefinition& nodeDef)
175 {
176   DALI_ASSERT_DEBUG(nodeDef.mRenderable);
177
178   auto&            resources = *mImpl->mResources;
179   ResourceReceiver receiver{resources};
180   nodeDef.mRenderable->RegisterResources(receiver);
181   if(!(receiver.mMeshDef && receiver.mMaterialDef))
182   {
183     return INVALID_INDEX;
184   }
185
186   auto&    shaderMap = mImpl->mShaderMap;
187   uint64_t hash      = HashNode(nodeDef, *receiver.mMaterialDef, *receiver.mMeshDef);
188   auto     iFind     = shaderMap.find(hash);
189   if(iFind != shaderMap.end())
190   {
191     return iFind->second;
192   }
193
194   ShaderDefinition shaderDef;
195   shaderDef.mUseBuiltInShader = true;
196   shaderDef.mRendererState      = RendererState::DEPTH_TEST | RendererState::DEPTH_WRITE | RendererState::CULL_BACK;
197
198   auto&      materialDef     = *receiver.mMaterialDef;
199   const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
200   if(hasTransparency)
201   {
202     // TODO: this requires more granularity
203     shaderDef.mRendererState = (shaderDef.mRendererState | RendererState::ALPHA_BLEND) & ~RendererState::DEPTH_WRITE;
204   }
205
206   if(hasTransparency ||
207      !materialDef.CheckTextures(MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC) ||
208      !materialDef.CheckTextures(MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS))
209
210   {
211     shaderDef.mDefines.push_back("THREE_TEX");
212
213     // For the glTF, each of basecolor, metallic_roughness, normal texture is not essential.
214     if(MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
215     {
216       shaderDef.mDefines.push_back("BASECOLOR_TEX");
217     }
218
219     if(materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS))
220     {
221       shaderDef.mDefines.push_back("METALLIC_ROUGHNESS_TEX");
222     }
223
224     if(MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
225     {
226       shaderDef.mDefines.push_back("NORMAL_TEX");
227     }
228   }
229
230   if(materialDef.GetAlphaCutoff() > 0.f)
231   {
232     shaderDef.mDefines.push_back("ALPHA_TEST");
233   }
234
235   if(MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
236   {
237     shaderDef.mDefines.push_back("SSS");
238   }
239
240   if(MaskMatch(materialDef.mFlags, MaterialDefinition::OCCLUSION))
241   {
242     shaderDef.mDefines.push_back("OCCLUSION");
243   }
244
245   if(MaskMatch(materialDef.mFlags, MaterialDefinition::EMISSIVE))
246   {
247     shaderDef.mDefines.push_back("EMISSIVE");
248   }
249
250   if(MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
251   {
252     shaderDef.mDefines.push_back("GLTF_CHANNELS");
253   }
254
255   const auto& meshDef = *receiver.mMeshDef;
256   if(meshDef.IsSkinned())
257   {
258     shaderDef.mDefines.push_back("SKINNING");
259   }
260
261   if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
262   {
263     shaderDef.mDefines.push_back("FLIP_V");
264   }
265
266   if(meshDef.HasBlendShapes())
267   {
268     bool hasPositions = false;
269     bool hasNormals   = false;
270     bool hasTangents  = false;
271     RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
272
273     if(hasPositions)
274     {
275       shaderDef.mDefines.push_back("MORPH_POSITION");
276     }
277
278     if(hasNormals)
279     {
280       shaderDef.mDefines.push_back("MORPH_NORMAL");
281     }
282
283     if(hasTangents)
284     {
285       shaderDef.mDefines.push_back("MORPH_TANGENT");
286     }
287
288     if(hasPositions || hasNormals || hasTangents)
289     {
290       shaderDef.mDefines.push_back("MORPH");
291
292       if(BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
293       {
294         shaderDef.mDefines.push_back("MORPH_VERSION_2_0");
295       }
296     }
297   }
298
299   if(meshDef.mTangentType == Property::VECTOR4)
300   {
301     shaderDef.mDefines.push_back("VEC4_TANGENT");
302   }
303
304   shaderDef.mUniforms["uMaxLOD"]     = 6.f;
305   shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
306
307   Index result    = resources.mShaders.size();
308   shaderMap[hash] = result;
309
310   resources.mShaders.emplace_back(std::move(shaderDef), Shader());
311
312   return result;
313 }
314
315 } // namespace SceneLoader
316 } // namespace Dali