Merge branch 'devel/master' into tizen
[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/light/light-impl.h>
29 #include <dali-scene3d/internal/model-components/material-impl.h>
30 #include <dali-scene3d/public-api/loader/environment-definition.h>
31
32 namespace Dali
33 {
34 namespace Scene3D
35 {
36 namespace Internal
37 {
38 namespace
39 {
40 /**
41  * Creates control through type registry
42  */
43 BaseHandle Create()
44 {
45   return Scene3D::ModelPrimitive::New();
46 }
47
48 // Setup properties, signals and actions using the type-registry.
49 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::ModelPrimitive, Dali::BaseHandle, Create);
50 DALI_TYPE_REGISTRATION_END()
51
52 constexpr std::string_view MORPH_POSITION_KEYWORD    = "MORPH_POSITION";
53 constexpr std::string_view MORPH_NORMAL_KEYWORD      = "MORPH_NORMAL";
54 constexpr std::string_view MORPH_TANGENT_KEYWORD     = "MORPH_TANGENT";
55 constexpr std::string_view MORPH_VERSION_2_0_KEYWORD = "MORPH_VERSION_2_0";
56
57 static constexpr uint32_t INDEX_FOR_LIGHT_CONSTRAINT_TAG = 10;
58 } // unnamed namespace
59
60 ModelPrimitivePtr ModelPrimitive::New()
61 {
62   ModelPrimitivePtr primitive = new ModelPrimitive();
63
64   primitive->Initialize();
65
66   return primitive;
67 }
68
69 ModelPrimitive::ModelPrimitive()
70 {
71 }
72
73 ModelPrimitive::~ModelPrimitive()
74 {
75   if(mMaterial)
76   {
77     GetImplementation(mMaterial).RemoveObserver(this);
78   }
79   mMaterial.Reset();
80 }
81
82 void ModelPrimitive::Initialize()
83 {
84   mLights.resize(Scene3D::Internal::Light::GetMaximumEnabledLightCount());
85 }
86
87 void ModelPrimitive::SetRenderer(Dali::Renderer renderer)
88 {
89   mRenderer   = renderer;
90   mGeometry   = renderer.GetGeometry();
91   mTextureSet = renderer.GetTextures();
92   mShader     = renderer.GetShader();
93 }
94
95 Dali::Renderer ModelPrimitive::GetRenderer() const
96 {
97   return mRenderer;
98 }
99
100 void ModelPrimitive::SetGeometry(Dali::Geometry geometry)
101 {
102   mGeometry = geometry;
103   CreateRenderer();
104 }
105
106 Dali::Geometry ModelPrimitive::GetGeometry() const
107 {
108   return mGeometry;
109 }
110
111 void ModelPrimitive::SetMaterial(Dali::Scene3D::Material material, bool updateRenderer)
112 {
113   if(!material)
114   {
115     return;
116   }
117
118   if(mMaterial != material)
119   {
120     // Stop observe from previous material.
121     if(mMaterial)
122     {
123       GetImplementation(mMaterial).RemoveObserver(this);
124     }
125
126     mMaterial = material;
127
128     // Start observe from new material.
129
130     if(mMaterial)
131     {
132       GetImplementation(mMaterial).AddObserver(this);
133     }
134
135     if(updateRenderer)
136     {
137       mIsMaterialChanged = true;
138       if(GetImplementation(mMaterial).IsResourceReady())
139       {
140         GetImplementation(mMaterial).UpdateMaterialData();
141         ApplyMaterialToRenderer();
142       }
143     }
144     UpdateImageBasedLightTexture();
145   }
146 }
147
148 Dali::Scene3D::Material ModelPrimitive::GetMaterial() const
149 {
150   return mMaterial;
151 }
152
153 void ModelPrimitive::AddPrimitiveObserver(ModelPrimitiveModifyObserver* observer)
154 {
155   mObservers.insert(observer);
156 }
157
158 void ModelPrimitive::RemovePrimitiveObserver(ModelPrimitiveModifyObserver* observer)
159 {
160   mObservers.erase(observer);
161 }
162
163 void ModelPrimitive::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
164 {
165   mDiffuseTexture       = diffuseTexture;
166   mSpecularTexture      = specularTexture;
167   mIblScaleFactor       = iblScaleFactor;
168   mSpecularMipmapLevels = specularMipmapLevels;
169
170   UpdateImageBasedLightTexture();
171 }
172
173 void ModelPrimitive::SetImageBasedLightScaleFactor(float iblScaleFactor)
174 {
175   mIblScaleFactor = iblScaleFactor;
176   if(mRenderer && mMaterial)
177   {
178     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), iblScaleFactor);
179   }
180 }
181
182 void ModelPrimitive::AddLight(Scene3D::Light light, uint32_t lightIndex)
183 {
184   if(mLights[lightIndex])
185   {
186     RemoveLight(lightIndex);
187   }
188
189   mLights[lightIndex] = light;
190   // TODO  Remove light at lightIndex if it is already set.
191   if(mRenderer && mMaterial)
192   {
193     mLightCount++;
194     std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
195     mRenderer.RegisterProperty(lightCountPropertyName, mLightCount);
196
197     std::string lightDirectionPropertyName(Scene3D::Internal::Light::GetLightDirectionUniformName());
198     lightDirectionPropertyName += "[" + std::to_string(lightIndex) + "]";
199     auto             lightDirectionPropertyIndex = mRenderer.RegisterProperty(lightDirectionPropertyName, Vector3::ZAXIS);
200     Dali::Constraint lightDirectionConstraint    = Dali::Constraint::New<Vector3>(mRenderer, lightDirectionPropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs)
201                                                                                { output = inputs[0]->GetQuaternion().Rotate(Vector3::ZAXIS); });
202     lightDirectionConstraint.AddSource(Source{light, Dali::Actor::Property::WORLD_ORIENTATION});
203     lightDirectionConstraint.ApplyPost();
204     lightDirectionConstraint.SetTag(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
205
206     std::string lightColorPropertyName(Scene3D::Internal::Light::GetLightColorUniformName());
207     lightColorPropertyName += "[" + std::to_string(lightIndex) + "]";
208     auto             lightColorPropertyIndex = mRenderer.RegisterProperty(lightColorPropertyName, Vector3(Color::WHITE));
209     Dali::Constraint lightColorConstraint    = Dali::Constraint::New<Vector3>(mRenderer, lightColorPropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs)
210                                                                            { output = Vector3(inputs[0]->GetVector4()); });
211     lightColorConstraint.AddSource(Source{light, Dali::Actor::Property::COLOR});
212     lightColorConstraint.ApplyPost();
213     lightColorConstraint.SetTag(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
214   }
215 }
216
217 void ModelPrimitive::RemoveLight(uint32_t lightIndex)
218 {
219   mLightCount--;
220   std::string lightCountPropertyName(Scene3D::Internal::Light::GetLightCountUniformName());
221   mRenderer.RegisterProperty(lightCountPropertyName, mLightCount);
222
223   mRenderer.RemoveConstraints(INDEX_FOR_LIGHT_CONSTRAINT_TAG + lightIndex);
224
225   mLights[lightIndex].Reset();
226 }
227
228 void ModelPrimitive::SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data)
229 {
230   mBlendShapeData = std::move(data);
231   Scene3D::Loader::BlendShapes::ConfigureProperties(mBlendShapeData, mRenderer);
232 }
233
234 void ModelPrimitive::SetBlendShapeGeometry(Dali::Texture blendShapeGeometry)
235 {
236   mBlendShapeGeometry = blendShapeGeometry;
237 }
238
239 void ModelPrimitive::SetBlendShapeOptions(bool hasPositions, bool hasNormals, bool hasTangents)
240 {
241   mHasPositions = hasPositions;
242   mHasNormals   = hasNormals;
243   mHasTangents  = hasTangents;
244 }
245
246 void ModelPrimitive::SetSkinned(bool isSkinned)
247 {
248   mHasSkinning = isSkinned;
249 }
250
251 // From MaterialModifyObserver
252
253 void ModelPrimitive::OnMaterialModified(Dali::Scene3D::Material material, MaterialModifyObserver::ModifyFlag flag)
254 {
255   ApplyMaterialToRenderer(flag);
256 }
257
258 void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag flag)
259 {
260   uint32_t shaderFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::SHADER));
261   if(mIsMaterialChanged || shaderFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::SHADER))
262   {
263     std::string vertexShader   = GetImplementation(mMaterial).GetVertexShader();
264     std::string fragmentShader = GetImplementation(mMaterial).GetFragmentShader();
265
266     std::vector<std::string> defines;
267     defines.push_back("VEC4_TANGENT");
268     if(mHasSkinning)
269     {
270       defines.push_back("SKINNING");
271     }
272     if(mHasPositions || mHasNormals || mHasTangents)
273     {
274       if(mHasPositions)
275       {
276         defines.push_back(MORPH_POSITION_KEYWORD.data());
277       }
278       if(mHasNormals)
279       {
280         defines.push_back(MORPH_NORMAL_KEYWORD.data());
281       }
282       if(mHasTangents)
283       {
284         defines.push_back(MORPH_TANGENT_KEYWORD.data());
285       }
286       if(mBlendShapeData.version == Scene3D::Loader::BlendShapes::Version::VERSION_2_0)
287       {
288         defines.push_back(MORPH_VERSION_2_0_KEYWORD.data());
289       }
290     }
291     for(const auto& define : defines)
292     {
293       Scene3D::Loader::ShaderDefinition::ApplyDefine(vertexShader, define);
294     }
295
296     mShader.Reset();
297     mShader = Shader::New(vertexShader, fragmentShader);
298
299     if(!mRenderer)
300     {
301       CreateRenderer();
302     }
303     else
304     {
305       mRenderer.SetShader(mShader);
306     }
307   }
308
309   uint32_t textureFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::TEXTURE));
310   if(mIsMaterialChanged || textureFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::TEXTURE))
311   {
312     mTextureSet = GetImplementation(mMaterial).GetTextureSet();
313
314     if(mBlendShapeGeometry)
315     {
316       TextureSet newTextureSet = TextureSet::New();
317       newTextureSet.SetTexture(0u, mBlendShapeGeometry);
318
319       const unsigned int numberOfTextures = mTextureSet.GetTextureCount();
320       for(unsigned int index = 0u; index < numberOfTextures; ++index)
321       {
322         const unsigned int newIndex = index + 1u;
323         newTextureSet.SetTexture(newIndex, mTextureSet.GetTexture(index));
324         newTextureSet.SetSampler(newIndex, mTextureSet.GetSampler(index));
325       }
326
327       mTextureSet = newTextureSet;
328     }
329
330     uint32_t textureCount = mTextureSet.GetTextureCount();
331     Texture  brdfTexture  = Scene3D::Loader::EnvironmentDefinition::GetBrdfTexture();
332     if(!mSpecularTexture || !mDiffuseTexture)
333     {
334       Scene3D::Loader::EnvironmentMapData environmentMapData;
335       environmentMapData.mPixelData.resize(6);
336       for(auto& face : environmentMapData.mPixelData)
337       {
338         face.push_back(PixelData::New(new uint8_t[3]{0xff, 0xff, 0xff}, 3, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY));
339       }
340       environmentMapData.SetEnvironmentMapType(Dali::Scene3D::EnvironmentMapType::CUBEMAP);
341       Texture iblTexture = environmentMapData.GetTexture();
342       mDiffuseTexture    = iblTexture;
343       mSpecularTexture   = iblTexture;
344     }
345
346     mTextureSet.SetTexture(textureCount++, brdfTexture);
347     mTextureSet.SetTexture(textureCount++, mDiffuseTexture);
348     mTextureSet.SetTexture(textureCount, mSpecularTexture);
349
350     auto specularSampler = Sampler::New();
351     specularSampler.SetWrapMode(WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
352     specularSampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR);
353     mTextureSet.SetSampler(textureCount, specularSampler);
354
355     if(!mRenderer)
356     {
357       CreateRenderer();
358     }
359     else
360     {
361       mRenderer.SetTextures(mTextureSet);
362     }
363   }
364
365   uint32_t uniformFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::UNIFORM));
366   if(mIsMaterialChanged || uniformFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::UNIFORM))
367   {
368     if(!mRenderer)
369     {
370       mNeedToSetRendererUniform = true;
371     }
372     else
373     {
374       UpdateRendererUniform();
375     }
376   }
377   mIsMaterialChanged = false;
378 }
379
380 void ModelPrimitive::CreateRenderer()
381 {
382   if(!mShader || !mGeometry || !mTextureSet || mRenderer)
383   {
384     return;
385   }
386
387   mRenderer = Renderer::New(mGeometry, mShader);
388   mRenderer.SetTextures(mTextureSet);
389   UpdateRendererUniform();
390
391   uint32_t maxLightCount = Scene3D::Internal::Light::GetMaximumEnabledLightCount();
392   for(uint32_t i = 0; i < maxLightCount; ++i)
393   {
394     if(mLights[i])
395     {
396       AddLight(mLights[i], i);
397     }
398   }
399
400   for(auto* observer : mObservers)
401   {
402     observer->OnRendererCreated(mRenderer);
403   }
404 }
405
406 void ModelPrimitive::UpdateImageBasedLightTexture()
407 {
408   if(mRenderer && mMaterial)
409   {
410     Dali::TextureSet textures = mRenderer.GetTextures();
411     if(!textures)
412     {
413       return;
414     }
415
416     uint32_t textureCount = textures.GetTextureCount();
417     if(textureCount > 2u &&
418        (textures.GetTexture(textureCount - GetImplementation(mMaterial).GetDiffuseImageBasedLightTextureOffset()) != mDiffuseTexture ||
419         textures.GetTexture(textureCount - GetImplementation(mMaterial).GetSpecularImageBasedLightTextureOffset()) != mSpecularTexture))
420     {
421       Dali::TextureSet newTextures = Dali::TextureSet::New();
422
423       for(uint32_t index = 0u; index < textureCount; ++index)
424       {
425         Dali::Texture texture = textures.GetTexture(index);
426         if(index == textureCount - GetImplementation(mMaterial).GetDiffuseImageBasedLightTextureOffset())
427         {
428           texture = mDiffuseTexture;
429         }
430         else if(index == textureCount - GetImplementation(mMaterial).GetSpecularImageBasedLightTextureOffset())
431         {
432           texture = mSpecularTexture;
433         }
434
435         newTextures.SetTexture(index, texture);
436         newTextures.SetSampler(index, textures.GetSampler(index));
437       }
438
439       mRenderer.SetTextures(newTextures);
440     }
441     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), mIblScaleFactor);
442     mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightMaxLodUniformName().data(), static_cast<float>(mSpecularMipmapLevels));
443   }
444 }
445
446 void ModelPrimitive::UpdateRendererUniform()
447 {
448   mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), mIblScaleFactor);
449   mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightMaxLodUniformName().data(), static_cast<float>(mSpecularMipmapLevels));
450   GetImplementation(mMaterial).SetRendererUniform(mRenderer);
451 }
452
453 } // namespace Internal
454
455 } // namespace Scene3D
456
457 } // namespace Dali