[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / model-components / model-primitive-impl.cpp
1 /*
2  * Copyright (c) 2024 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/internal/model-components/model-primitive-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/public-api/animation/constraint.h>
24 #include <dali/public-api/object/type-registry-helper.h>
25 #include <dali/public-api/object/type-registry.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-scene3d/internal/common/image-resource-loader.h>
29 #include <dali-scene3d/internal/light/light-impl.h>
30 #include <dali-scene3d/internal/model-components/material-impl.h>
31 #include <dali-scene3d/public-api/loader/environment-definition.h>
32
33 #include <dali/integration-api/debug.h>
34 #include <dali/public-api/object/property-array.h>
35 #include <dali/public-api/object/property-map.h>
36
37 #if defined(DEBUG_ENABLED)
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <filesystem>
41 namespace fs = std::filesystem;
42 #endif
43
44 namespace Dali
45 {
46 namespace Scene3D
47 {
48 namespace Internal
49 {
50 namespace
51 {
52 #if defined(DEBUG_ENABLED)
53 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_SCENE3D_MODEL_PRIMITIVE");
54
55 std::string tmpFilename(std::string prefix, std::string suffix)
56 {
57   static int id = 0;
58   id++;
59   std::ostringstream oss;
60   oss << prefix << getpid() << "_" << std::setfill('0') << std::setw(4) << id << suffix;
61   return oss.str();
62 }
63
64 #define DALI_LOG_WRITE_FILE(filename, stream) \
65   {                                           \
66     fs::path tmp = fs::temp_directory_path(); \
67     tmp /= filename;                          \
68     std::ofstream ostrm(tmp, std::ios::out);  \
69     ostrm << stream;                          \
70     ostrm.flush();                            \
71   }
72
73 inline Property::Map GetMap(Shader shader)
74 {
75   Property::Value program = shader[Shader::Property::PROGRAM];
76   Property::Map*  map{nullptr};
77   if(program.GetType() == Property::ARRAY)
78   {
79     Property::Array* array = program.GetArray();
80     if(array)
81     {
82       Property::Value& value = array->GetElementAt(0);
83       if(value.GetType() == Property::MAP)
84       {
85         map = value.GetMap();
86       }
87     }
88   }
89   else if(program.GetType() == Property::MAP)
90   {
91     map = program.GetMap();
92   }
93   if(map)
94   {
95     return *map;
96   }
97   return Property::Map();
98 }
99
100 #endif
101 /**
102  * Creates control through type registry
103  */
104 BaseHandle Create()
105 {
106   return Scene3D::ModelPrimitive::New();
107 }
108
109 // Setup properties, signals and actions using the type-registry.
110 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::ModelPrimitive, Dali::BaseHandle, Create);
111 DALI_TYPE_REGISTRATION_END()
112
113 static constexpr uint32_t INDEX_FOR_LIGHT_CONSTRAINT_TAG = 10;
114
115 constexpr uint32_t MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE = 300;
116
117 } // unnamed namespace
118
119 ModelPrimitivePtr ModelPrimitive::New()
120 {
121   ModelPrimitivePtr primitive = new ModelPrimitive();
122
123   primitive->Initialize();
124
125   return primitive;
126 }
127
128 ModelPrimitive::ModelPrimitive()
129 : mShaderManager(new Scene3D::Loader::ShaderManager())
130 {
131 }
132
133 ModelPrimitive::~ModelPrimitive()
134 {
135   if(mMaterial)
136   {
137     GetImplementation(mMaterial).RemoveObserver(this);
138   }
139   mMaterial.Reset();
140 }
141
142 void ModelPrimitive::Initialize()
143 {
144 }
145
146 void ModelPrimitive::SetRenderer(Dali::Renderer renderer)
147 {
148   mRenderer   = renderer;
149   mGeometry   = renderer.GetGeometry();
150   mTextureSet = renderer.GetTextures();
151   mShader     = renderer.GetShader();
152 }
153
154 Dali::Renderer ModelPrimitive::GetRenderer() const
155 {
156   return mRenderer;
157 }
158
159 void ModelPrimitive::SetGeometry(Dali::Geometry geometry)
160 {
161   mGeometry = geometry;
162   CreateRenderer();
163 }
164
165 Dali::Geometry ModelPrimitive::GetGeometry() const
166 {
167   return mGeometry;
168 }
169
170 void ModelPrimitive::SetMaterial(Dali::Scene3D::Material material, bool updateRenderer)
171 {
172   if(!material)
173   {
174     return;
175   }
176
177   if(mMaterial != material)
178   {
179     // Stop observe from previous material.
180     if(mMaterial)
181     {
182       GetImplementation(mMaterial).RemoveObserver(this);
183     }
184
185     mMaterial = material;
186
187     // Start observe from new material.
188
189     if(mMaterial)
190     {
191       GetImplementation(mMaterial).AddObserver(this);
192     }
193
194     if(updateRenderer)
195     {
196       mIsMaterialChanged = true;
197       if(GetImplementation(mMaterial).IsResourceReady())
198       {
199         GetImplementation(mMaterial).UpdateMaterialData();
200         ApplyMaterialToRenderer();
201       }
202     }
203     UpdateShadowMapTexture();
204     UpdateImageBasedLightTexture();
205   }
206 }
207
208 Dali::Scene3D::Material ModelPrimitive::GetMaterial() const
209 {
210   return mMaterial;
211 }
212
213 void ModelPrimitive::AddPrimitiveObserver(ModelPrimitiveModifyObserver* observer)
214 {
215   mObservers.insert(observer);
216 }
217
218 void ModelPrimitive::RemovePrimitiveObserver(ModelPrimitiveModifyObserver* observer)
219 {
220   mObservers.erase(observer);
221 }
222
223 void ModelPrimitive::SetShadowMapTexture(Dali::Texture shadowMapTexture)
224 {
225   mShadowMapTexture = shadowMapTexture;
226   UpdateShadowMapTexture();
227 }
228
229 void ModelPrimitive::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
230 {
231   mDiffuseTexture       = diffuseTexture;
232   mSpecularTexture      = specularTexture;
233   mIblScaleFactor       = iblScaleFactor;
234   mSpecularMipmapLevels = specularMipmapLevels;
235
236   UpdateImageBasedLightTexture();
237 }
238
239 void ModelPrimitive::SetImageBasedLightScaleFactor(float iblScaleFactor)
240 {
241   mIblScaleFactor = iblScaleFactor;
242   if(mRenderer && mMaterial)
243   {
244     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), iblScaleFactor);
245   }
246 }
247
248 void ModelPrimitive::UpdateShader(Scene3D::Loader::ShaderManagerPtr shaderManager, Loader::ShaderOption::HashType hash)
249 {
250   if(mShaderManager != shaderManager)
251   {
252     mShaderManager = (shaderManager) ? shaderManager : new Scene3D::Loader::ShaderManager();
253     if(mMaterial && GetImplementation(mMaterial).IsResourceReady())
254     {
255       ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag::SHADER, hash);
256     }
257   }
258 }
259
260 void ModelPrimitive::SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data)
261 {
262   mBlendShapeData = std::move(data);
263   Scene3D::Loader::BlendShapes::ConfigureProperties(mBlendShapeData, mRenderer);
264 }
265
266 void ModelPrimitive::SetBlendShapeGeometry(Dali::Texture blendShapeGeometry)
267 {
268   mBlendShapeGeometry = blendShapeGeometry;
269   if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE))
270   {
271     if(mRenderer && mBlendShapeGeometry)
272     {
273       mRenderer.RegisterProperty("uBlendShapeGeometryWidth", static_cast<int>(mBlendShapeGeometry.GetWidth()));
274       mRenderer.RegisterProperty("uBlendShapeGeometryHeight", static_cast<int>(mBlendShapeGeometry.GetHeight()));
275     }
276   }
277 }
278
279 void ModelPrimitive::SetBlendShapeOptions(bool hasPositions, bool hasNormals, bool hasTangents, Scene3D::Loader::BlendShapes::Version version)
280 {
281   mHasPositions      = hasPositions;
282   mHasNormals        = hasNormals;
283   mHasTangents       = hasTangents;
284   mBlendShapeVersion = version;
285 }
286
287 void ModelPrimitive::SetSkinned(bool isSkinned, uint32_t numberOfJointSets)
288 {
289   mHasSkinning       = isSkinned;
290   mNumberOfJointSets = numberOfJointSets;
291 }
292
293 void ModelPrimitive::SetVertexColor(bool hasVertexColor)
294 {
295   mHasVertexColor = hasVertexColor;
296 }
297
298 // From MaterialModifyObserver
299
300 void ModelPrimitive::OnMaterialModified(Dali::Scene3D::Material material, MaterialModifyObserver::ModifyFlag flag)
301 {
302   ApplyMaterialToRenderer(flag);
303 }
304
305 void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag flag, Loader::ShaderOption::HashType oldHash)
306 {
307   if(!mMaterial)
308   {
309     return;
310   }
311
312   uint32_t shaderFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::SHADER));
313   if(mIsMaterialChanged || shaderFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::SHADER))
314   {
315     Scene3D::Loader::ShaderOption shaderOption = GetImplementation(mMaterial).GetShaderOption();
316
317     shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::VEC4_TANGENT);
318     if(mHasSkinning)
319     {
320       shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::SKINNING);
321       shaderOption.AddJointMacros(mNumberOfJointSets);
322     }
323     else
324     {
325       shaderOption.AddJointMacros(0);
326     }
327     if(mHasVertexColor)
328     {
329       shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::COLOR_ATTRIBUTE);
330     }
331     if(mHasPositions || mHasNormals || mHasTangents)
332     {
333       if(mHasPositions)
334       {
335         shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_POSITION);
336       }
337       if(mHasNormals)
338       {
339         shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_NORMAL);
340       }
341       if(mHasTangents)
342       {
343         shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_TANGENT);
344       }
345       if(mBlendShapeVersion == Scene3D::Loader::BlendShapes::Version::VERSION_2_0)
346       {
347         shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_VERSION_2_0);
348       }
349     }
350     if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE))
351     {
352       shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::SL_VERSION_LOW);
353     }
354
355     Shader newShader = mShaderManager->ProduceShader(shaderOption);
356     if(mShader != newShader)
357     {
358       DALI_LOG_INFO(gLogFilter, Debug::General, "Warning!  Model primitive shader changed: OldHash:%x NewHash:%x\n", oldHash, shaderOption.GetOptionHash());
359
360 #if defined(DEBUG_ENABLED)
361       if(mShader)
362       {
363         Property::Map oldMap = GetMap(mShader);
364         DALI_LOG_WRITE_FILE(tmpFilename("oldShader", ".txt"), "Vertex Shader:\n"
365                                                                 << oldMap["vertex"] << "\n\nFragmentShader: " << oldMap["fragment"] << "\n");
366       }
367       if(newShader)
368       {
369         Property::Map newMap = GetMap(newShader);
370         DALI_LOG_WRITE_FILE(tmpFilename("newShader", ".txt"), "Vertex Shader:\n"
371                                                                 << newMap["vertex"] << "\n\nFragmentShader: " << newMap["fragment"] << "\n");
372       }
373 #endif
374     }
375     mShader = newShader;
376
377     if(!mRenderer)
378     {
379       CreateRenderer();
380     }
381     else
382     {
383       mRenderer.SetShader(mShader);
384     }
385   }
386
387   uint32_t textureFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::TEXTURE));
388   if(mIsMaterialChanged || textureFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::TEXTURE))
389   {
390     mTextureSet = GetImplementation(mMaterial).GetTextureSet();
391
392     if(mBlendShapeGeometry)
393     {
394       TextureSet newTextureSet = TextureSet::New();
395       newTextureSet.SetTexture(0u, mBlendShapeGeometry);
396
397       Sampler blendShapeSampler = Sampler::New();
398       blendShapeSampler.SetFilterMode(Dali::FilterMode::NEAREST, Dali::FilterMode::NEAREST);
399       newTextureSet.SetSampler(0u, blendShapeSampler);
400
401       const unsigned int numberOfTextures = mTextureSet.GetTextureCount();
402       for(unsigned int index = 0u; index < numberOfTextures; ++index)
403       {
404         const unsigned int newIndex = index + 1u;
405         newTextureSet.SetTexture(newIndex, mTextureSet.GetTexture(index));
406         newTextureSet.SetSampler(newIndex, mTextureSet.GetSampler(index));
407       }
408
409       mTextureSet = newTextureSet;
410     }
411
412     uint32_t textureCount = mTextureSet.GetTextureCount();
413
414     if(!mShadowMapTexture)
415     {
416       mShadowMapTexture = Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyTextureWhiteRGB();
417     }
418     mTextureSet.SetTexture(textureCount++, mShadowMapTexture);
419
420     Texture brdfTexture = Scene3D::Loader::EnvironmentDefinition::GetBrdfTexture();
421     if(!mSpecularTexture || !mDiffuseTexture)
422     {
423       Texture iblTexture = Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyCubeTextureWhiteRGB();
424       mDiffuseTexture    = iblTexture;
425       mSpecularTexture   = iblTexture;
426     }
427
428     mTextureSet.SetTexture(textureCount++, brdfTexture);
429     mTextureSet.SetTexture(textureCount++, mDiffuseTexture);
430     mTextureSet.SetTexture(textureCount, mSpecularTexture);
431
432     auto specularSampler = Sampler::New();
433     specularSampler.SetWrapMode(WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
434     specularSampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR);
435     mTextureSet.SetSampler(textureCount, specularSampler);
436
437     if(!mRenderer)
438     {
439       CreateRenderer();
440     }
441     else
442     {
443       mRenderer.SetTextures(mTextureSet);
444     }
445   }
446
447   uint32_t uniformFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::UNIFORM));
448   if(mIsMaterialChanged || uniformFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::UNIFORM))
449   {
450     if(mRenderer)
451     {
452       UpdateRendererUniform();
453     }
454   }
455
456   uint32_t propertyFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::PROPERTY));
457   if(mIsMaterialChanged || propertyFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::PROPERTY))
458   {
459     if(mRenderer)
460     {
461       UpdateRendererProperty();
462     }
463   }
464   mIsMaterialChanged = false;
465 }
466
467 void ModelPrimitive::CreateRenderer()
468 {
469   if(!mShader || !mGeometry || !mTextureSet || mRenderer)
470   {
471     return;
472   }
473
474   mRenderer = Renderer::New(mGeometry, mShader);
475   mRenderer.SetTextures(mTextureSet);
476   UpdateRendererUniform();
477   UpdateRendererProperty();
478
479   for(auto* observer : mObservers)
480   {
481     observer->OnRendererCreated(mRenderer);
482   }
483 }
484
485 void ModelPrimitive::UpdateShadowMapTexture()
486 {
487   if(mRenderer && mMaterial)
488   {
489     Dali::TextureSet textures = mRenderer.GetTextures();
490     if(!textures)
491     {
492       return;
493     }
494
495     uint32_t textureCount = textures.GetTextureCount();
496     if(mShadowMapTexture &&
497        textureCount >= GetImplementation(mMaterial).GetShadowMapTextureOffset() &&
498        textures.GetTexture(textureCount - GetImplementation(mMaterial).GetShadowMapTextureOffset()) != mShadowMapTexture)
499     {
500       Dali::TextureSet newTextures = Dali::TextureSet::New();
501
502       for(uint32_t index = 0u; index < textureCount; ++index)
503       {
504         Dali::Texture texture = textures.GetTexture(index);
505         if(index == textureCount - GetImplementation(mMaterial).GetShadowMapTextureOffset())
506         {
507           texture = (!!mShadowMapTexture) ? mShadowMapTexture : Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyTextureWhiteRGB();
508           if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE))
509           {
510             mRenderer.RegisterProperty("uShadowMapWidth", static_cast<int>(texture.GetWidth()));
511             mRenderer.RegisterProperty("uShadowMapHeight", static_cast<int>(texture.GetHeight()));
512           }
513         }
514
515         newTextures.SetTexture(index, texture);
516         newTextures.SetSampler(index, textures.GetSampler(index));
517       }
518
519       mRenderer.SetTextures(newTextures);
520     }
521   }
522 }
523
524 void ModelPrimitive::UpdateImageBasedLightTexture()
525 {
526   if(mRenderer && mMaterial)
527   {
528     Dali::TextureSet textures = mRenderer.GetTextures();
529     if(!textures)
530     {
531       return;
532     }
533
534     uint32_t textureCount = textures.GetTextureCount();
535     if(textureCount > 2u &&
536        (textures.GetTexture(textureCount - GetImplementation(mMaterial).GetDiffuseImageBasedLightTextureOffset()) != mDiffuseTexture ||
537         textures.GetTexture(textureCount - GetImplementation(mMaterial).GetSpecularImageBasedLightTextureOffset()) != mSpecularTexture))
538     {
539       Dali::TextureSet newTextures = Dali::TextureSet::New();
540
541       for(uint32_t index = 0u; index < textureCount; ++index)
542       {
543         Dali::Texture texture = textures.GetTexture(index);
544         if(index == textureCount - GetImplementation(mMaterial).GetDiffuseImageBasedLightTextureOffset())
545         {
546           texture = mDiffuseTexture;
547         }
548         else if(index == textureCount - GetImplementation(mMaterial).GetSpecularImageBasedLightTextureOffset())
549         {
550           texture = mSpecularTexture;
551         }
552
553         newTextures.SetTexture(index, texture);
554         newTextures.SetSampler(index, textures.GetSampler(index));
555       }
556
557       mRenderer.SetTextures(newTextures);
558     }
559     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), mIblScaleFactor);
560     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightMaxLodUniformName().data(), static_cast<float>(mSpecularMipmapLevels));
561   }
562 }
563
564 void ModelPrimitive::UpdateRendererUniform()
565 {
566   if(mMaterial)
567   {
568     GetImplementation(mMaterial).SetRendererUniform(mRenderer);
569     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), mIblScaleFactor);
570     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightMaxLodUniformName().data(), static_cast<float>(mSpecularMipmapLevels));
571
572     if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE))
573     {
574       if(mShadowMapTexture)
575       {
576         mRenderer.RegisterProperty("uShadowMapWidth", static_cast<int>(mShadowMapTexture.GetWidth()));
577         mRenderer.RegisterProperty("uShadowMapHeight", static_cast<int>(mShadowMapTexture.GetHeight()));
578       }
579       if(mBlendShapeGeometry)
580       {
581         mRenderer.RegisterProperty("uBlendShapeGeometryWidth", static_cast<int>(mBlendShapeGeometry.GetWidth()));
582         mRenderer.RegisterProperty("uBlendShapeGeometryHeight", static_cast<int>(mBlendShapeGeometry.GetHeight()));
583       }
584     }
585   }
586 }
587
588 void ModelPrimitive::UpdateRendererProperty()
589 {
590   if(mMaterial)
591   {
592     GetImplementation(mMaterial).SetRendererProperty(mRenderer);
593   }
594 }
595
596 } // namespace Internal
597
598 } // namespace Scene3D
599
600 } // namespace Dali