Merge "Change dali-scene-loader to dali-scene3d" 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 NodeDefinition& nodeDef, 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(const NodeDefinition& nodeDef)
177 {
178   DALI_ASSERT_DEBUG(nodeDef.mRenderable);
179
180   auto&            resources = *mImpl->mResources;
181   ResourceReceiver receiver{resources};
182   nodeDef.mRenderable->RegisterResources(receiver);
183   if(!(receiver.mMeshDef && receiver.mMaterialDef))
184   {
185     return INVALID_INDEX;
186   }
187
188   auto&    shaderMap = mImpl->mShaderMap;
189   uint64_t hash      = HashNode(nodeDef, *receiver.mMaterialDef, *receiver.mMeshDef);
190   auto     iFind     = shaderMap.find(hash);
191   if(iFind != shaderMap.end())
192   {
193     return iFind->second;
194   }
195
196   ShaderDefinition shaderDef;
197   shaderDef.mUseBuiltInShader = true;
198   shaderDef.mRendererState    = RendererState::DEPTH_TEST | RendererState::DEPTH_WRITE | RendererState::CULL_BACK;
199
200   auto&      materialDef     = *receiver.mMaterialDef;
201   const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
202   if(hasTransparency)
203   {
204     // TODO: this requires more granularity
205     shaderDef.mRendererState = (shaderDef.mRendererState | RendererState::ALPHA_BLEND) & ~RendererState::DEPTH_WRITE;
206   }
207
208   if(hasTransparency ||
209      !materialDef.CheckTextures(MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC) ||
210      !materialDef.CheckTextures(MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS))
211
212   {
213     shaderDef.mDefines.push_back("THREE_TEX");
214
215     // For the glTF, each of basecolor, metallic_roughness, normal texture is not essential.
216     if(MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
217     {
218       shaderDef.mDefines.push_back("BASECOLOR_TEX");
219     }
220
221     if(materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS))
222     {
223       shaderDef.mDefines.push_back("METALLIC_ROUGHNESS_TEX");
224     }
225
226     if(MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
227     {
228       shaderDef.mDefines.push_back("NORMAL_TEX");
229     }
230   }
231
232   if(materialDef.GetAlphaCutoff() > 0.f)
233   {
234     shaderDef.mDefines.push_back("ALPHA_TEST");
235   }
236
237   if(MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
238   {
239     shaderDef.mDefines.push_back("SSS");
240   }
241
242   if(MaskMatch(materialDef.mFlags, MaterialDefinition::OCCLUSION))
243   {
244     shaderDef.mDefines.push_back("OCCLUSION");
245   }
246
247   if(MaskMatch(materialDef.mFlags, MaterialDefinition::EMISSIVE))
248   {
249     shaderDef.mDefines.push_back("EMISSIVE");
250   }
251
252   if(MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
253   {
254     shaderDef.mDefines.push_back("GLTF_CHANNELS");
255   }
256
257   const auto& meshDef = *receiver.mMeshDef;
258   if(meshDef.IsSkinned())
259   {
260     shaderDef.mDefines.push_back("SKINNING");
261   }
262
263   if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
264   {
265     shaderDef.mDefines.push_back("FLIP_V");
266   }
267
268   if(meshDef.HasBlendShapes())
269   {
270     bool hasPositions = false;
271     bool hasNormals   = false;
272     bool hasTangents  = false;
273     RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
274
275     if(hasPositions)
276     {
277       shaderDef.mDefines.push_back("MORPH_POSITION");
278     }
279
280     if(hasNormals)
281     {
282       shaderDef.mDefines.push_back("MORPH_NORMAL");
283     }
284
285     if(hasTangents)
286     {
287       shaderDef.mDefines.push_back("MORPH_TANGENT");
288     }
289
290     if(hasPositions || hasNormals || hasTangents)
291     {
292       shaderDef.mDefines.push_back("MORPH");
293
294       if(BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
295       {
296         shaderDef.mDefines.push_back("MORPH_VERSION_2_0");
297       }
298     }
299   }
300
301   if(meshDef.mTangentType == Property::VECTOR4)
302   {
303     shaderDef.mDefines.push_back("VEC4_TANGENT");
304   }
305
306   shaderDef.mUniforms["uMaxLOD"]     = 6.f;
307   shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
308
309   Index result    = resources.mShaders.size();
310   shaderMap[hash] = result;
311
312   resources.mShaders.emplace_back(std::move(shaderDef), Shader());
313
314   return result;
315 }
316
317 } // namespace Loader
318 } // namespace Scene3D
319 } // namespace Dali