[dali_2.3.5] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / internal / model-components / model-primitive-impl.cpp
1 /*
2  * Copyright (c) 2023 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 } // unnamed namespace
116
117 ModelPrimitivePtr ModelPrimitive::New()
118 {
119   ModelPrimitivePtr primitive = new ModelPrimitive();
120
121   primitive->Initialize();
122
123   return primitive;
124 }
125
126 ModelPrimitive::ModelPrimitive()
127 : mShaderManager(new Scene3D::Loader::ShaderManager())
128 {
129 }
130
131 ModelPrimitive::~ModelPrimitive()
132 {
133   if(mMaterial)
134   {
135     GetImplementation(mMaterial).RemoveObserver(this);
136   }
137   mMaterial.Reset();
138 }
139
140 void ModelPrimitive::Initialize()
141 {
142 }
143
144 void ModelPrimitive::SetRenderer(Dali::Renderer renderer)
145 {
146   mRenderer   = renderer;
147   mGeometry   = renderer.GetGeometry();
148   mTextureSet = renderer.GetTextures();
149   mShader     = renderer.GetShader();
150 }
151
152 Dali::Renderer ModelPrimitive::GetRenderer() const
153 {
154   return mRenderer;
155 }
156
157 void ModelPrimitive::SetGeometry(Dali::Geometry geometry)
158 {
159   mGeometry = geometry;
160   CreateRenderer();
161 }
162
163 Dali::Geometry ModelPrimitive::GetGeometry() const
164 {
165   return mGeometry;
166 }
167
168 void ModelPrimitive::SetMaterial(Dali::Scene3D::Material material, bool updateRenderer)
169 {
170   if(!material)
171   {
172     return;
173   }
174
175   if(mMaterial != material)
176   {
177     // Stop observe from previous material.
178     if(mMaterial)
179     {
180       GetImplementation(mMaterial).RemoveObserver(this);
181     }
182
183     mMaterial = material;
184
185     // Start observe from new material.
186
187     if(mMaterial)
188     {
189       GetImplementation(mMaterial).AddObserver(this);
190     }
191
192     if(updateRenderer)
193     {
194       mIsMaterialChanged = true;
195       if(GetImplementation(mMaterial).IsResourceReady())
196       {
197         GetImplementation(mMaterial).UpdateMaterialData();
198         ApplyMaterialToRenderer();
199       }
200     }
201     UpdateShadowMapTexture();
202     UpdateImageBasedLightTexture();
203   }
204 }
205
206 Dali::Scene3D::Material ModelPrimitive::GetMaterial() const
207 {
208   return mMaterial;
209 }
210
211 void ModelPrimitive::AddPrimitiveObserver(ModelPrimitiveModifyObserver* observer)
212 {
213   mObservers.insert(observer);
214 }
215
216 void ModelPrimitive::RemovePrimitiveObserver(ModelPrimitiveModifyObserver* observer)
217 {
218   mObservers.erase(observer);
219 }
220
221 void ModelPrimitive::SetShadowMapTexture(Dali::Texture shadowMapTexture)
222 {
223   mShadowMapTexture = shadowMapTexture;
224   UpdateShadowMapTexture();
225 }
226
227 void ModelPrimitive::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
228 {
229   mDiffuseTexture       = diffuseTexture;
230   mSpecularTexture      = specularTexture;
231   mIblScaleFactor       = iblScaleFactor;
232   mSpecularMipmapLevels = specularMipmapLevels;
233
234   UpdateImageBasedLightTexture();
235 }
236
237 void ModelPrimitive::SetImageBasedLightScaleFactor(float iblScaleFactor)
238 {
239   mIblScaleFactor = iblScaleFactor;
240   if(mRenderer && mMaterial)
241   {
242     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), iblScaleFactor);
243   }
244 }
245
246 void ModelPrimitive::UpdateShader(Scene3D::Loader::ShaderManagerPtr shaderManager, Loader::ShaderOption::HashType hash)
247 {
248   if(mShaderManager != shaderManager)
249   {
250     mShaderManager = (shaderManager) ? shaderManager : new Scene3D::Loader::ShaderManager();
251     if(mMaterial && GetImplementation(mMaterial).IsResourceReady())
252     {
253       ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag::SHADER, hash);
254     }
255   }
256 }
257
258 void ModelPrimitive::SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data)
259 {
260   mBlendShapeData = std::move(data);
261   Scene3D::Loader::BlendShapes::ConfigureProperties(mBlendShapeData, mRenderer);
262 }
263
264 void ModelPrimitive::SetBlendShapeGeometry(Dali::Texture blendShapeGeometry)
265 {
266   mBlendShapeGeometry = blendShapeGeometry;
267 }
268
269 void ModelPrimitive::SetBlendShapeOptions(bool hasPositions, bool hasNormals, bool hasTangents, Scene3D::Loader::BlendShapes::Version version)
270 {
271   mHasPositions      = hasPositions;
272   mHasNormals        = hasNormals;
273   mHasTangents       = hasTangents;
274   mBlendShapeVersion = version;
275 }
276
277 void ModelPrimitive::SetSkinned(bool isSkinned, uint32_t numberOfJointSets)
278 {
279   mHasSkinning       = isSkinned;
280   mNumberOfJointSets = numberOfJointSets;
281 }
282
283 void ModelPrimitive::SetVertexColor(bool hasVertexColor)
284 {
285   mHasVertexColor = hasVertexColor;
286 }
287
288 // From MaterialModifyObserver
289
290 void ModelPrimitive::OnMaterialModified(Dali::Scene3D::Material material, MaterialModifyObserver::ModifyFlag flag)
291 {
292   ApplyMaterialToRenderer(flag);
293 }
294
295 void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag flag, Loader::ShaderOption::HashType oldHash)
296 {
297   if(!mMaterial)
298   {
299     return;
300   }
301
302   uint32_t shaderFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::SHADER));
303   if(mIsMaterialChanged || shaderFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::SHADER))
304   {
305     Scene3D::Loader::ShaderOption shaderOption = GetImplementation(mMaterial).GetShaderOption();
306
307     shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::VEC4_TANGENT);
308     if(mHasSkinning)
309     {
310       shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::SKINNING);
311       shaderOption.AddJointMacros(mNumberOfJointSets);
312     }
313     else
314     {
315       shaderOption.AddJointMacros(0);
316     }
317     if(mHasVertexColor)
318     {
319       shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::COLOR_ATTRIBUTE);
320     }
321     if(mHasPositions || mHasNormals || mHasTangents)
322     {
323       if(mHasPositions)
324       {
325         shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_POSITION);
326       }
327       if(mHasNormals)
328       {
329         shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_NORMAL);
330       }
331       if(mHasTangents)
332       {
333         shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_TANGENT);
334       }
335       if(mBlendShapeVersion == Scene3D::Loader::BlendShapes::Version::VERSION_2_0)
336       {
337         shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_VERSION_2_0);
338       }
339     }
340
341     Shader newShader = mShaderManager->ProduceShader(shaderOption);
342     if(mShader != newShader)
343     {
344       DALI_LOG_INFO(gLogFilter, Debug::General, "Warning!  Model primitive shader changed: OldHash:%x NewHash:%x\n", oldHash, shaderOption.GetOptionHash());
345
346 #if defined(DEBUG_ENABLED)
347       if(mShader)
348       {
349         Property::Map oldMap = GetMap(mShader);
350         DALI_LOG_WRITE_FILE(tmpFilename("oldShader", ".txt"), "Vertex Shader:\n"
351                                                                 << oldMap["vertex"] << "\n\nFragmentShader: " << oldMap["fragment"] << "\n");
352       }
353       if(newShader)
354       {
355         Property::Map newMap = GetMap(newShader);
356         DALI_LOG_WRITE_FILE(tmpFilename("newShader", ".txt"), "Vertex Shader:\n"
357                                                                 << newMap["vertex"] << "\n\nFragmentShader: " << newMap["fragment"] << "\n");
358       }
359 #endif
360     }
361     mShader = newShader;
362
363     if(!mRenderer)
364     {
365       CreateRenderer();
366     }
367     else
368     {
369       mRenderer.SetShader(mShader);
370     }
371   }
372
373   uint32_t textureFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::TEXTURE));
374   if(mIsMaterialChanged || textureFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::TEXTURE))
375   {
376     mTextureSet = GetImplementation(mMaterial).GetTextureSet();
377
378     if(mBlendShapeGeometry)
379     {
380       TextureSet newTextureSet = TextureSet::New();
381       newTextureSet.SetTexture(0u, mBlendShapeGeometry);
382
383       const unsigned int numberOfTextures = mTextureSet.GetTextureCount();
384       for(unsigned int index = 0u; index < numberOfTextures; ++index)
385       {
386         const unsigned int newIndex = index + 1u;
387         newTextureSet.SetTexture(newIndex, mTextureSet.GetTexture(index));
388         newTextureSet.SetSampler(newIndex, mTextureSet.GetSampler(index));
389       }
390
391       mTextureSet = newTextureSet;
392     }
393
394     uint32_t textureCount = mTextureSet.GetTextureCount();
395
396     if(!mShadowMapTexture)
397     {
398       mShadowMapTexture = Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyTextureWhiteRGB();
399     }
400     mTextureSet.SetTexture(textureCount++, mShadowMapTexture);
401
402     Texture brdfTexture = Scene3D::Loader::EnvironmentDefinition::GetBrdfTexture();
403     if(!mSpecularTexture || !mDiffuseTexture)
404     {
405       Scene3D::Loader::EnvironmentMapData environmentMapData;
406       environmentMapData.mPixelData.resize(6);
407       for(auto& face : environmentMapData.mPixelData)
408       {
409         face.push_back(PixelData::New(new uint8_t[3]{0xff, 0xff, 0xff}, 3, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY));
410       }
411       environmentMapData.SetEnvironmentMapType(Dali::Scene3D::EnvironmentMapType::CUBEMAP);
412       Texture iblTexture = environmentMapData.GetTexture();
413       mDiffuseTexture    = iblTexture;
414       mSpecularTexture   = iblTexture;
415     }
416
417     mTextureSet.SetTexture(textureCount++, brdfTexture);
418     mTextureSet.SetTexture(textureCount++, mDiffuseTexture);
419     mTextureSet.SetTexture(textureCount, mSpecularTexture);
420
421     auto specularSampler = Sampler::New();
422     specularSampler.SetWrapMode(WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
423     specularSampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR);
424     mTextureSet.SetSampler(textureCount, specularSampler);
425
426     if(!mRenderer)
427     {
428       CreateRenderer();
429     }
430     else
431     {
432       mRenderer.SetTextures(mTextureSet);
433     }
434   }
435
436   uint32_t uniformFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::UNIFORM));
437   if(mIsMaterialChanged || uniformFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::UNIFORM))
438   {
439     if(mRenderer)
440     {
441       UpdateRendererUniform();
442     }
443   }
444
445   uint32_t propertyFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::PROPERTY));
446   if(mIsMaterialChanged || propertyFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::PROPERTY))
447   {
448     if(mRenderer)
449     {
450       UpdateRendererProperty();
451     }
452   }
453   mIsMaterialChanged = false;
454 }
455
456 void ModelPrimitive::CreateRenderer()
457 {
458   if(!mShader || !mGeometry || !mTextureSet || mRenderer)
459   {
460     return;
461   }
462
463   mRenderer = Renderer::New(mGeometry, mShader);
464   mRenderer.SetTextures(mTextureSet);
465   UpdateRendererUniform();
466   UpdateRendererProperty();
467
468   for(auto* observer : mObservers)
469   {
470     observer->OnRendererCreated(mRenderer);
471   }
472 }
473
474 void ModelPrimitive::UpdateShadowMapTexture()
475 {
476   if(mRenderer && mMaterial)
477   {
478     Dali::TextureSet textures = mRenderer.GetTextures();
479     if(!textures)
480     {
481       return;
482     }
483
484     uint32_t textureCount = textures.GetTextureCount();
485     if(mShadowMapTexture &&
486        textureCount >= GetImplementation(mMaterial).GetShadowMapTextureOffset() &&
487        textures.GetTexture(textureCount - GetImplementation(mMaterial).GetShadowMapTextureOffset()) != mShadowMapTexture)
488     {
489       Dali::TextureSet newTextures = Dali::TextureSet::New();
490
491       for(uint32_t index = 0u; index < textureCount; ++index)
492       {
493         Dali::Texture texture = textures.GetTexture(index);
494         if(index == textureCount - GetImplementation(mMaterial).GetShadowMapTextureOffset())
495         {
496           texture = (!!mShadowMapTexture) ? mShadowMapTexture : Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyTextureWhiteRGB();
497         }
498
499         newTextures.SetTexture(index, texture);
500         newTextures.SetSampler(index, textures.GetSampler(index));
501       }
502
503       mRenderer.SetTextures(newTextures);
504     }
505   }
506 }
507
508 void ModelPrimitive::UpdateImageBasedLightTexture()
509 {
510   if(mRenderer && mMaterial)
511   {
512     Dali::TextureSet textures = mRenderer.GetTextures();
513     if(!textures)
514     {
515       return;
516     }
517
518     uint32_t textureCount = textures.GetTextureCount();
519     if(textureCount > 2u &&
520        (textures.GetTexture(textureCount - GetImplementation(mMaterial).GetDiffuseImageBasedLightTextureOffset()) != mDiffuseTexture ||
521         textures.GetTexture(textureCount - GetImplementation(mMaterial).GetSpecularImageBasedLightTextureOffset()) != mSpecularTexture))
522     {
523       Dali::TextureSet newTextures = Dali::TextureSet::New();
524
525       for(uint32_t index = 0u; index < textureCount; ++index)
526       {
527         Dali::Texture texture = textures.GetTexture(index);
528         if(index == textureCount - GetImplementation(mMaterial).GetDiffuseImageBasedLightTextureOffset())
529         {
530           texture = mDiffuseTexture;
531         }
532         else if(index == textureCount - GetImplementation(mMaterial).GetSpecularImageBasedLightTextureOffset())
533         {
534           texture = mSpecularTexture;
535         }
536
537         newTextures.SetTexture(index, texture);
538         newTextures.SetSampler(index, textures.GetSampler(index));
539       }
540
541       mRenderer.SetTextures(newTextures);
542     }
543     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), mIblScaleFactor);
544     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightMaxLodUniformName().data(), static_cast<float>(mSpecularMipmapLevels));
545   }
546 }
547
548 void ModelPrimitive::UpdateRendererUniform()
549 {
550   if(mMaterial)
551   {
552     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), mIblScaleFactor);
553     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightMaxLodUniformName().data(), static_cast<float>(mSpecularMipmapLevels));
554     GetImplementation(mMaterial).SetRendererUniform(mRenderer);
555   }
556 }
557
558 void ModelPrimitive::UpdateRendererProperty()
559 {
560   if(mMaterial)
561   {
562     GetImplementation(mMaterial).SetRendererProperty(mRenderer);
563   }
564 }
565
566 } // namespace Internal
567
568 } // namespace Scene3D
569
570 } // namespace Dali