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