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