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