Added libdli to dali-toolkit as dali-scene-loader.
[platform/core/uifw/dali-toolkit.git] / dali-scene-loader / public-api / shader-definition-factory.cpp
1 /*
2  * Copyright (c) 2020 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-scene-loader/internal/hash.h"
18 #include "dali-scene-loader/public-api/shader-definition-factory.h"
19 #include "dali-scene-loader/public-api/node-definition.h"
20 #include "dali-scene-loader/public-api/blend-shape-details.h"
21 #include "dali/devel-api/common/map-wrapper.h"
22 #include <cstring>
23
24 namespace Dali
25 {
26 namespace SceneLoader
27 {
28 namespace
29 {
30 struct ResourceReceiver : IResourceReceiver
31 {
32   const ResourceBundle& mResources;
33   const MeshDefinition* mMeshDef = nullptr;
34   const MaterialDefinition* mMaterialDef = nullptr;
35
36   ResourceReceiver(const ResourceBundle& resources)
37   : mResources(resources)
38   {}
39
40   void Register(ResourceType::Value type, Index id) override
41   {
42     switch (type)
43     {
44     case ResourceType::Mesh:
45       mMeshDef = &mResources.mMeshes[id].first;
46       break;
47
48     case ResourceType::Material:
49       mMaterialDef = &mResources.mMaterials[id].first;
50       break;
51
52     default:
53       break;
54     }
55   }
56 };
57
58 const std::string PBR_SHADER_NAME = "dli_pbr";
59
60 void RetrieveBlendShapeComponents(const std::vector<MeshDefinition::BlendShape>& blendShapes, bool& hasPositions, bool& hasNormals, bool& hasTangents)
61 {
62   for (const auto& blendShape : blendShapes)
63   {
64     hasPositions = hasPositions || blendShape.deltas.IsDefined();
65     hasNormals = hasNormals || blendShape.normals.IsDefined();
66     hasTangents = hasTangents || blendShape.tangents.IsDefined();
67   }
68 }
69
70 uint64_t HashNode(const NodeDefinition& nodeDef, const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
71 {
72   Hash hash;
73
74   // note: could be per vertex / fragment component - in WatchViewer, these have the same name.
75   hash.Add(PBR_SHADER_NAME);
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
88   if (materialDef.GetAlphaCutoff() > 0.f)
89   {
90     hash.Add("ALPH"/*A_TEST*/);
91   }
92
93   if (MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
94   {
95     hash.Add("SSS");
96   }
97
98   if (MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
99   {
100     hash.Add("GLTF"/*_CHANNELS*/);
101   }
102
103   if (meshDef.IsSkinned())
104   {
105     hash.Add("SKIN"/*NING*/);
106   }
107
108   if (MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
109   {
110     hash.Add("FLIP"/*_V*/);
111   }
112
113   if (meshDef.HasBlendShapes())
114   {
115     bool hasPositions = false;
116     bool hasNormals = false;
117     bool hasTangents = false;
118     RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
119     if (hasPositions)
120     {
121       hash.Add("MORPHPOS");
122     }
123
124     if (hasNormals)
125     {
126       hash.Add("MORPHNOR");
127     }
128
129     if (hasTangents)
130     {
131       hash.Add("MORPHTAN");
132     }
133
134     if (hasPositions || hasNormals || hasTangents)
135     {
136       hash.Add("MORPH");
137
138       if (BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
139       {
140         hash.Add("MORPHV2");
141       }
142     }
143   }
144
145   return hash;
146 }
147 }
148
149 struct ShaderDefinitionFactory::Impl
150 {
151   ResourceBundle* mResources;  // no ownership
152   std::map<uint64_t, Index> mShaderMap;
153 };
154
155 ShaderDefinitionFactory::ShaderDefinitionFactory()
156 : mImpl{ new Impl() }
157 {}
158
159 ShaderDefinitionFactory::~ShaderDefinitionFactory() = default;
160
161 void ShaderDefinitionFactory::SetResources(ResourceBundle& resources)
162 {
163   mImpl->mResources = &resources;
164   mImpl->mShaderMap.clear();
165 }
166
167 Index ShaderDefinitionFactory::ProduceShader(const NodeDefinition& nodeDef)
168 {
169   DALI_ASSERT_DEBUG(nodeDef.mRenderable);
170
171   auto& resources = *mImpl->mResources;
172   ResourceReceiver receiver{ resources };
173   nodeDef.mRenderable->RegisterResources(receiver);
174   if (!(receiver.mMeshDef && receiver.mMaterialDef))
175   {
176     return INVALID_INDEX;
177   }
178
179   auto& shaderMap = mImpl->mShaderMap;
180   uint64_t hash = HashNode(nodeDef, *receiver.mMaterialDef, *receiver.mMeshDef);
181   auto iFind = shaderMap.find(hash);
182   if (iFind != shaderMap.end())
183   {
184     return iFind->second;
185   }
186
187   ShaderDefinition shaderDef;
188   shaderDef.mVertexShaderPath = PBR_SHADER_NAME + ".vsh";
189   shaderDef.mFragmentShaderPath = PBR_SHADER_NAME + ".fsh";
190   shaderDef.mRendererState = RendererState::DEPTH_TEST | RendererState::DEPTH_WRITE | RendererState::CULL_BACK;
191
192   auto& materialDef = *receiver.mMaterialDef;
193   const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
194   if (hasTransparency)
195   {
196     // TODO: this requires more granularity
197     shaderDef.mRendererState = (shaderDef.mRendererState | RendererState::ALPHA_BLEND) & ~RendererState::DEPTH_WRITE;
198   }
199
200   if (hasTransparency ||
201     materialDef.CheckTextures(MaterialDefinition::ALBEDO) ||
202     materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS) ||
203     materialDef.CheckTextures(MaterialDefinition::NORMAL))
204   {
205     shaderDef.mDefines.push_back("THREE_TEX");
206   }
207
208   if (materialDef.GetAlphaCutoff() > 0.f)
209   {
210     shaderDef.mDefines.push_back("ALPHA_TEST");
211   }
212
213   if (MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
214   {
215     shaderDef.mDefines.push_back("SSS");
216   }
217
218   if (MaskMatch(receiver.mMaterialDef->mFlags, MaterialDefinition::GLTF_CHANNELS))
219   {
220     shaderDef.mDefines.push_back("GLTF_CHANNELS");
221   }
222
223   const auto& meshDef = *receiver.mMeshDef;
224   if (meshDef.IsSkinned())
225   {
226     shaderDef.mDefines.push_back("SKINNING");
227   }
228
229   if (MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
230   {
231     shaderDef.mDefines.push_back("FLIP_V");
232   }
233
234   if (meshDef.HasBlendShapes())
235   {
236     bool hasPositions = false;
237     bool hasNormals = false;
238     bool hasTangents = false;
239     RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
240
241     if (hasPositions)
242     {
243       shaderDef.mDefines.push_back("MORPH_POSITION");
244     }
245
246     if (hasNormals)
247     {
248       shaderDef.mDefines.push_back("MORPH_NORMAL");
249     }
250
251     if (hasTangents)
252     {
253       shaderDef.mDefines.push_back("MORPH_TANGENT");
254     }
255
256     if (hasPositions || hasNormals || hasTangents)
257     {
258       shaderDef.mDefines.push_back("MORPH");
259
260       if (BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
261       {
262         shaderDef.mDefines.push_back("MORPH_VERSION_2_0");
263       }
264     }
265   }
266
267   shaderDef.mUniforms["uMaxLOD"] = 6.f;
268   shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
269
270   Index result = resources.mShaders.size();
271   shaderMap[hash] = result;
272
273   resources.mShaders.emplace_back(std::move(shaderDef), Shader());
274
275   return result;
276 }
277
278 }
279 }