2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-scene3d/public-api/loader/shader-definition-factory.h>
22 #include <dali/devel-api/common/map-wrapper.h>
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>
30 namespace Dali::Scene3D::Loader
34 struct ResourceReceiver : IResourceReceiver
36 const ResourceBundle& mResources;
37 const MeshDefinition* mMeshDef = nullptr;
38 const MaterialDefinition* mMaterialDef = nullptr;
40 ResourceReceiver(const ResourceBundle& resources)
41 : mResources(resources)
45 void Register(ResourceType::Value type, Index id) override
49 case ResourceType::Mesh:
50 mMeshDef = &mResources.mMeshes[id].first;
53 case ResourceType::Material:
54 mMaterialDef = &mResources.mMaterials[id].first;
63 void RetrieveBlendShapeComponents(const std::vector<MeshDefinition::BlendShape>& blendShapes, bool& hasPositions, bool& hasNormals, bool& hasTangents)
65 for(const auto& blendShape : blendShapes)
67 hasPositions = hasPositions || blendShape.deltas.IsDefined();
68 hasNormals = hasNormals || blendShape.normals.IsDefined();
69 hasTangents = hasTangents || blendShape.tangents.IsDefined();
73 uint64_t HashNode(const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
77 const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
78 hash.Add(hasTransparency);
81 materialDef.CheckTextures(MaterialDefinition::ALBEDO) ||
82 materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS) ||
83 materialDef.CheckTextures(MaterialDefinition::NORMAL))
87 // For the glTF, each of basecolor, metallic_roughness, normal texture is not essential.
88 if(materialDef.CheckTextures(MaterialDefinition::ALBEDO))
93 if(materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS))
98 if(materialDef.CheckTextures(MaterialDefinition::NORMAL))
104 if(materialDef.GetAlphaCutoff() > 0.f)
106 hash.Add("ALPH" /*A_TEST*/);
109 if(MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
114 if(MaskMatch(materialDef.mFlags, MaterialDefinition::OCCLUSION))
116 hash.Add("OCCL" /*USION*/);
119 if(MaskMatch(materialDef.mFlags, MaterialDefinition::EMISSIVE))
121 hash.Add("EMIS" /*SIVE*/);
124 if(MaskMatch(materialDef.mFlags, MaterialDefinition::SPECULAR))
129 if(MaskMatch(materialDef.mFlags, MaterialDefinition::SPECULAR_COLOR))
131 hash.Add("SPECCOLTEX");
134 if(MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
136 hash.Add("GLTF" /*_CHANNELS*/);
139 if(meshDef.IsSkinned())
141 hash.Add("SKIN" /*NING*/);
144 if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
146 hash.Add("FLIP" /*_V*/);
149 if(meshDef.mColors.IsDefined())
154 if(meshDef.mTangentType == Property::VECTOR4)
159 if(meshDef.HasBlendShapes())
161 bool hasPositions = false;
162 bool hasNormals = false;
163 bool hasTangents = false;
164 RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
167 hash.Add("MORPHPOS");
172 hash.Add("MORPHNOR");
177 hash.Add("MORPHTAN");
180 if(hasPositions || hasNormals || hasTangents)
184 if(BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
195 struct ShaderDefinitionFactory::Impl
197 ResourceBundle* mResources; // no ownership
198 std::map<uint64_t, Index> mShaderMap;
201 ShaderDefinitionFactory::ShaderDefinitionFactory()
206 ShaderDefinitionFactory::~ShaderDefinitionFactory() = default;
208 void ShaderDefinitionFactory::SetResources(ResourceBundle& resources)
210 mImpl->mResources = &resources;
211 mImpl->mShaderMap.clear();
214 Index ShaderDefinitionFactory::ProduceShader(NodeDefinition::Renderable& renderable)
216 auto& resources = *mImpl->mResources;
218 ResourceReceiver receiver{resources};
219 renderable.RegisterResources(receiver);
221 if(!(receiver.mMeshDef && receiver.mMaterialDef))
223 renderable.mShaderIdx = INVALID_INDEX;
224 return INVALID_INDEX;
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())
232 renderable.mShaderIdx = iFind->second;
236 ShaderDefinition shaderDef;
237 shaderDef.mUseBuiltInShader = true;
238 shaderDef.mRendererState = RendererState::DEPTH_TEST;
240 auto& materialDef = *receiver.mMaterialDef;
241 if(!materialDef.mDoubleSided)
243 shaderDef.mRendererState |= RendererState::CULL_BACK;
246 const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
249 // TODO: this requires more granularity
250 shaderDef.mRendererState = (shaderDef.mRendererState | RendererState::ALPHA_BLEND);
253 if(hasTransparency ||
254 !materialDef.CheckTextures(MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC) ||
255 !materialDef.CheckTextures(MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS))
258 shaderDef.mDefines.push_back("THREE_TEX");
260 // For the glTF, each of basecolor, metallic_roughness, normal texture is not essential.
261 if(MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
263 shaderDef.mDefines.push_back("BASECOLOR_TEX");
266 if(materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS))
268 shaderDef.mDefines.push_back("METALLIC_ROUGHNESS_TEX");
271 if(MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
273 shaderDef.mDefines.push_back("NORMAL_TEX");
277 if(materialDef.GetAlphaCutoff() > 0.f)
279 shaderDef.mDefines.push_back("ALPHA_TEST");
282 if(MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
284 shaderDef.mDefines.push_back("SSS");
287 if(MaskMatch(materialDef.mFlags, MaterialDefinition::OCCLUSION))
289 shaderDef.mDefines.push_back("OCCLUSION");
292 if(MaskMatch(materialDef.mFlags, MaterialDefinition::EMISSIVE))
294 shaderDef.mDefines.push_back("EMISSIVE");
297 if(MaskMatch(materialDef.mFlags, MaterialDefinition::SPECULAR))
299 shaderDef.mDefines.push_back("MATERIAL_SPECULAR_TEXTURE");
302 if(MaskMatch(materialDef.mFlags, MaterialDefinition::SPECULAR_COLOR))
304 shaderDef.mDefines.push_back("MATERIAL_SPECULAR_COLOR_TEXTURE");
307 if(MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
309 shaderDef.mDefines.push_back("GLTF_CHANNELS");
312 const auto& meshDef = *receiver.mMeshDef;
313 if(meshDef.IsSkinned())
315 shaderDef.mDefines.push_back("SKINNING");
318 if(MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
320 shaderDef.mDefines.push_back("FLIP_V");
323 if(meshDef.mColors.IsDefined())
325 shaderDef.mDefines.push_back("COLOR_ATTRIBUTE");
328 if(meshDef.mTangentType == Property::VECTOR4)
330 shaderDef.mDefines.push_back("VEC4_TANGENT");
333 if(meshDef.HasBlendShapes())
335 bool hasPositions = false;
336 bool hasNormals = false;
337 bool hasTangents = false;
338 RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
342 shaderDef.mDefines.push_back("MORPH_POSITION");
347 shaderDef.mDefines.push_back("MORPH_NORMAL");
352 shaderDef.mDefines.push_back("MORPH_TANGENT");
355 if(hasPositions || hasNormals || hasTangents)
357 shaderDef.mDefines.push_back("MORPH");
359 if(BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
361 shaderDef.mDefines.push_back("MORPH_VERSION_2_0");
366 shaderDef.mUniforms["uMaxLOD"] = 6.f;
367 shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
369 Index result = resources.mShaders.size();
370 shaderMap[hash] = result;
372 resources.mShaders.emplace_back(std::move(shaderDef), Shader());
374 renderable.mShaderIdx = result;
377 return renderable.mShaderIdx;
380 } // namespace Dali::Scene3D::Loader