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