Reserved Visual properties on renderer creation
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / mesh / mesh-visual.cpp
1 /*
2  * Copyright (c) 2022 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 "mesh-visual.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/file-loader.h>
23 #include <dali/devel-api/adaptor-framework/image-loading.h>
24 #include <dali/devel-api/common/stage.h>
25 #include <dali/devel-api/scripting/enum-helper.h>
26 #include <dali/devel-api/scripting/scripting.h>
27 #include <dali/integration-api/debug.h>
28
29 //INTERNAL INCLUDES
30 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
31 #include <dali-toolkit/internal/visuals/visual-base-data-impl.h>
32 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
33 #include <dali-toolkit/public-api/visuals/visual-properties.h>
34
35 namespace Dali
36 {
37 namespace
38 {
39 const int CUSTOM_PROPERTY_COUNT(5); // 5 transform properties
40
41 /**
42    * @brief Loads a texture from a file
43    * @param[in] imageUrl The url of the file
44    * @param[in] generateMipmaps Indicates whether to generate mipmaps for the texture
45    * @return A texture if loading succeeds, an empty handle otherwise
46    */
47 Texture LoadTexture(const char* imageUrl, bool generateMipmaps)
48 {
49   Texture texture;
50
51   Devel::PixelBuffer pixelBuffer = LoadImageFromFile(imageUrl);
52   if(pixelBuffer)
53   {
54     texture             = Texture::New(TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight());
55     PixelData pixelData = Devel::PixelBuffer::Convert(pixelBuffer);
56     texture.Upload(pixelData);
57
58     if(generateMipmaps)
59     {
60       texture.GenerateMipmaps();
61     }
62   }
63
64   return texture;
65 }
66 } // unnamed namespace
67
68 namespace Toolkit
69 {
70 namespace Internal
71 {
72 namespace
73 {
74 //Defines ordering of textures for shaders.
75 //All shaders, if including certain texture types, must include them in the same order.
76 //Within the texture set for the renderer, textures are ordered in the same manner.
77 enum TextureIndex
78 {
79   DIFFUSE_INDEX = 0u,
80   NORMAL_INDEX  = 1u,
81   GLOSS_INDEX   = 2u
82 };
83
84 //Shading mode
85 DALI_ENUM_TO_STRING_TABLE_BEGIN(SHADING_MODE)
86   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::MeshVisual::ShadingMode, TEXTURELESS_WITH_DIFFUSE_LIGHTING)
87   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::MeshVisual::ShadingMode, TEXTURED_WITH_SPECULAR_LIGHTING)
88   DALI_ENUM_TO_STRING_WITH_SCOPE(Toolkit::MeshVisual::ShadingMode, TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING)
89 DALI_ENUM_TO_STRING_TABLE_END(SHADING_MODE)
90
91 //Shader properties
92 const char* const OBJECT_MATRIX_UNIFORM_NAME("uObjectMatrix");
93 const char* const STAGE_OFFSET_UNIFORM_NAME("uStageOffset");
94
95 } // unnamed namespace
96
97 MeshVisualPtr MeshVisual::New(VisualFactoryCache& factoryCache, const Property::Map& properties)
98 {
99   MeshVisualPtr meshVisualPtr(new MeshVisual(factoryCache));
100   meshVisualPtr->SetProperties(properties);
101   meshVisualPtr->Initialize();
102   return meshVisualPtr;
103 }
104
105 MeshVisual::MeshVisual(VisualFactoryCache& factoryCache)
106 : Visual::Base(factoryCache, Visual::FittingMode::FIT_KEEP_ASPECT_RATIO, Toolkit::Visual::MESH),
107   mShadingMode(Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING),
108   mUseTexture(true),
109   mUseMipmapping(true),
110   mUseSoftNormals(true)
111 {
112 }
113
114 MeshVisual::~MeshVisual()
115 {
116 }
117
118 void MeshVisual::DoSetProperties(const Property::Map& propertyMap)
119 {
120   for(Property::Map::SizeType iter = 0; iter < propertyMap.Count(); ++iter)
121   {
122     KeyValuePair keyValue = propertyMap.GetKeyValue(iter);
123     if(keyValue.first.type == Property::Key::INDEX)
124     {
125       DoSetProperty(keyValue.first.indexKey, keyValue.second);
126     }
127     else
128     {
129       if(keyValue.first == OBJECT_URL_NAME)
130       {
131         DoSetProperty(Toolkit::MeshVisual::Property::OBJECT_URL, keyValue.second);
132       }
133       else if(keyValue.first == MATERIAL_URL_NAME)
134       {
135         DoSetProperty(Toolkit::MeshVisual::Property::MATERIAL_URL, keyValue.second);
136       }
137       else if(keyValue.first == TEXTURES_PATH_NAME)
138       {
139         DoSetProperty(Toolkit::MeshVisual::Property::TEXTURES_PATH, keyValue.second);
140       }
141       else if(keyValue.first == SHADING_MODE_NAME)
142       {
143         DoSetProperty(Toolkit::MeshVisual::Property::SHADING_MODE, keyValue.second);
144       }
145       else if(keyValue.first == USE_MIPMAPPING_NAME)
146       {
147         DoSetProperty(Toolkit::MeshVisual::Property::USE_MIPMAPPING, keyValue.second);
148       }
149       else if(keyValue.first == USE_SOFT_NORMALS_NAME)
150       {
151         DoSetProperty(Toolkit::MeshVisual::Property::USE_SOFT_NORMALS, keyValue.second);
152       }
153       else if(keyValue.first == LIGHT_POSITION_NAME)
154       {
155         DoSetProperty(Toolkit::MeshVisual::Property::LIGHT_POSITION, keyValue.second);
156       }
157     }
158   }
159
160   if(mMaterialUrl.empty())
161   {
162     mUseTexture = false;
163   }
164
165   if(mLightPosition == Vector3::ZERO)
166   {
167     // Default behaviour is to place the light directly in front of the object,
168     // at a reasonable distance to light everything on screen.
169     Stage stage = Stage::GetCurrent();
170
171     mLightPosition = Vector3(stage.GetSize().width / 2, stage.GetSize().height / 2, stage.GetSize().width * 5);
172   }
173 }
174
175 void MeshVisual::DoSetProperty(Property::Index index, const Property::Value& value)
176 {
177   switch(index)
178   {
179     case Toolkit::MeshVisual::Property::OBJECT_URL:
180     {
181       if(!value.Get(mObjectUrl))
182       {
183         DALI_LOG_ERROR("MeshVisual: property objectUrl is the wrong type, use STRING\n");
184       }
185       break;
186     }
187     case Toolkit::MeshVisual::Property::MATERIAL_URL:
188     {
189       if(!value.Get(mMaterialUrl))
190       {
191         DALI_LOG_ERROR("MeshVisual: property materialUrl is the wrong type, use STRING\n");
192       }
193       break;
194     }
195     case Toolkit::MeshVisual::Property::TEXTURES_PATH:
196     {
197       if(!value.Get(mTexturesPath))
198       {
199         mTexturesPath.clear();
200       }
201       break;
202     }
203     case Toolkit::MeshVisual::Property::SHADING_MODE:
204     {
205       Scripting::GetEnumerationProperty(value, SHADING_MODE_TABLE, SHADING_MODE_TABLE_COUNT, mShadingMode);
206       break;
207     }
208     case Toolkit::MeshVisual::Property::USE_MIPMAPPING:
209     {
210       if(!value.Get(mUseMipmapping))
211       {
212         DALI_LOG_ERROR("MeshVisual: property useMipmapping is the wrong type, use BOOLEAN\n");
213       }
214       break;
215     }
216     case Toolkit::MeshVisual::Property::USE_SOFT_NORMALS:
217     {
218       if(!value.Get(mUseSoftNormals))
219       {
220         DALI_LOG_ERROR("MeshVisual: property useSoftNormals is the wrong type, use BOOLEAN\n");
221       }
222       break;
223     }
224     case Toolkit::MeshVisual::Property::LIGHT_POSITION:
225     {
226       if(!value.Get(mLightPosition))
227       {
228         mLightPosition = Vector3::ZERO;
229         DALI_LOG_ERROR("MeshVisual: property lightPosition is the wrong type, use VECTOR3\n");
230       }
231       break;
232     }
233   }
234 }
235
236 void MeshVisual::OnSetTransform()
237 {
238   if(mImpl->mRenderer)
239   {
240     mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
241   }
242 }
243
244 void MeshVisual::DoSetOnScene(Actor& actor)
245 {
246   actor.AddRenderer(mImpl->mRenderer);
247
248   // Mesh loaded and ready to display
249   ResourceReady(Toolkit::Visual::ResourceStatus::READY);
250 }
251
252 void MeshVisual::DoCreatePropertyMap(Property::Map& map) const
253 {
254   map.Clear();
255   map.Insert(Toolkit::Visual::Property::TYPE, Toolkit::Visual::MESH);
256   map.Insert(Toolkit::MeshVisual::Property::OBJECT_URL, mObjectUrl);
257   map.Insert(Toolkit::MeshVisual::Property::MATERIAL_URL, mMaterialUrl);
258   map.Insert(Toolkit::MeshVisual::Property::TEXTURES_PATH, mTexturesPath);
259   map.Insert(Toolkit::MeshVisual::Property::SHADING_MODE, mShadingMode);
260   map.Insert(Toolkit::MeshVisual::Property::USE_MIPMAPPING, mUseMipmapping);
261   map.Insert(Toolkit::MeshVisual::Property::USE_SOFT_NORMALS, mUseSoftNormals);
262   map.Insert(Toolkit::MeshVisual::Property::LIGHT_POSITION, mLightPosition);
263 }
264
265 void MeshVisual::DoCreateInstancePropertyMap(Property::Map& map) const
266 {
267   // Do nothing
268 }
269
270 void MeshVisual::OnInitialize()
271 {
272   //Try to load the geometry from the file.
273   if(!LoadGeometry())
274   {
275     SupplyEmptyGeometry();
276     return;
277   }
278
279   //If a texture is used by the obj file, load the supplied material file.
280   if(mObjLoader.IsTexturePresent() && !mMaterialUrl.empty())
281   {
282     if(!LoadMaterial())
283     {
284       SupplyEmptyGeometry();
285       return;
286     }
287   }
288
289   //Now that the required parts are loaded, create the geometry for the object.
290   if(!CreateGeometry())
291   {
292     SupplyEmptyGeometry();
293     return;
294   }
295
296   CreateShader();
297
298   //Load the various texture files supplied by the material file.
299   if(!LoadTextures())
300   {
301     SupplyEmptyGeometry();
302     return;
303   }
304
305   mImpl->mRenderer = Renderer::New(mGeometry, mShader);
306   mImpl->mRenderer.ReserveCustomProperties(CUSTOM_PROPERTY_COUNT);
307   mImpl->mRenderer.SetTextures(mTextureSet);
308   mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON);
309   mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);
310
311   //Register transform properties
312   mImpl->mTransform.RegisterUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
313 }
314
315 void MeshVisual::SupplyEmptyGeometry()
316 {
317   mGeometry        = Geometry::New();
318   mShader          = Shader::New(SHADER_MESH_VISUAL_SIMPLE_SHADER_VERT, SHADER_MESH_VISUAL_SIMPLE_SHADER_FRAG);
319   mImpl->mRenderer = Renderer::New(mGeometry, mShader);
320
321   DALI_LOG_ERROR("Initialisation error in mesh visual.\n");
322 }
323
324 void MeshVisual::UpdateShaderUniforms()
325 {
326   Stage stage  = Stage::GetCurrent();
327   float width  = stage.GetSize().width;
328   float height = stage.GetSize().height;
329
330   Matrix scaleMatrix;
331   scaleMatrix.SetIdentityAndScale(Vector3(1.0, -1.0, 1.0));
332
333   mShader.RegisterProperty(STAGE_OFFSET_UNIFORM_NAME, Vector2(width, height) / 2.0f);
334   mShader.RegisterProperty(LIGHT_POSITION_NAME, mLightPosition);
335   mShader.RegisterProperty(OBJECT_MATRIX_UNIFORM_NAME, scaleMatrix);
336 }
337
338 void MeshVisual::CreateShader()
339 {
340   if(mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING)
341   {
342     mShader = Shader::New(SHADER_MESH_VISUAL_NORMAL_MAP_SHADER_VERT, SHADER_MESH_VISUAL_NORMAL_MAP_SHADER_FRAG);
343   }
344   else if(mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_SPECULAR_LIGHTING)
345   {
346     mShader = Shader::New(SHADER_MESH_VISUAL_SHADER_VERT, SHADER_MESH_VISUAL_SHADER_FRAG);
347   }
348   else //Textureless
349   {
350     mShader = Shader::New(SHADER_MESH_VISUAL_SIMPLE_SHADER_VERT, SHADER_MESH_VISUAL_SIMPLE_SHADER_FRAG);
351   }
352
353   UpdateShaderUniforms();
354 }
355
356 bool MeshVisual::CreateGeometry()
357 {
358   //Determine if we need to use a simpler shader to handle the provided data
359   if(!mUseTexture || !mObjLoader.IsDiffuseMapPresent())
360   {
361     mShadingMode = Toolkit::MeshVisual::ShadingMode::TEXTURELESS_WITH_DIFFUSE_LIGHTING;
362   }
363   else if(mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING && (!mObjLoader.IsNormalMapPresent() || !mObjLoader.IsSpecularMapPresent()))
364   {
365     mShadingMode = Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_SPECULAR_LIGHTING;
366   }
367
368   int objectProperties = 0;
369
370   if(mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_SPECULAR_LIGHTING ||
371      mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING)
372   {
373     objectProperties |= ObjLoader::TEXTURE_COORDINATES;
374   }
375
376   if(mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING)
377   {
378     objectProperties |= ObjLoader::TANGENTS | ObjLoader::BINORMALS;
379   }
380
381   //Create geometry with attributes required by shader.
382   mGeometry = mObjLoader.CreateGeometry(objectProperties, mUseSoftNormals);
383
384   if(mGeometry)
385   {
386     return true;
387   }
388
389   DALI_LOG_ERROR("Failed to load geometry in mesh visual.\n");
390   return false;
391 }
392
393 bool MeshVisual::LoadGeometry()
394 {
395   std::streampos     fileSize;
396   Dali::Vector<char> fileContent;
397
398   if(FileLoader::ReadFile(mObjectUrl, fileSize, fileContent, FileLoader::TEXT))
399   {
400     mObjLoader.ClearArrays();
401     mObjLoader.LoadObject(fileContent.Begin(), fileSize);
402
403     //Get size information from the obj loaded
404     mSceneCenter = mObjLoader.GetCenter();
405     mSceneSize   = mObjLoader.GetSize();
406
407     return true;
408   }
409
410   DALI_LOG_ERROR("Failed to find object to load in mesh visual.\n");
411   return false;
412 }
413
414 bool MeshVisual::LoadMaterial()
415 {
416   std::streampos     fileSize;
417   Dali::Vector<char> fileContent;
418
419   if(FileLoader::ReadFile(mMaterialUrl, fileSize, fileContent, FileLoader::TEXT))
420   {
421     //Load data into obj (usable) form
422     mObjLoader.LoadMaterial(fileContent.Begin(), fileSize, mDiffuseTextureUrl, mNormalTextureUrl, mGlossTextureUrl);
423     return true;
424   }
425
426   DALI_LOG_ERROR("Failed to find texture set to load in mesh visual.\n");
427   mUseTexture = false;
428   return false;
429 }
430
431 bool MeshVisual::LoadTextures()
432 {
433   mTextureSet = TextureSet::New();
434
435   if(mShadingMode != Toolkit::MeshVisual::ShadingMode::TEXTURELESS_WITH_DIFFUSE_LIGHTING)
436   {
437     Sampler sampler = Sampler::New();
438     if(mUseMipmapping)
439     {
440       sampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR_MIPMAP_LINEAR);
441     }
442
443     if(!mDiffuseTextureUrl.empty())
444     {
445       std::string imageUrl = mTexturesPath + mDiffuseTextureUrl;
446
447       //Load textures
448       Texture diffuseTexture = LoadTexture(imageUrl.c_str(), mUseMipmapping);
449       if(diffuseTexture)
450       {
451         mTextureSet.SetTexture(DIFFUSE_INDEX, diffuseTexture);
452         mTextureSet.SetSampler(DIFFUSE_INDEX, sampler);
453       }
454       else
455       {
456         DALI_LOG_ERROR("Failed to load diffuse map texture in mesh visual.\n");
457         return false;
458       }
459     }
460
461     if(!mNormalTextureUrl.empty() && (mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING))
462     {
463       std::string imageUrl = mTexturesPath + mNormalTextureUrl;
464
465       //Load textures
466       Texture normalTexture = LoadTexture(imageUrl.c_str(), mUseMipmapping);
467       if(normalTexture)
468       {
469         mTextureSet.SetTexture(NORMAL_INDEX, normalTexture);
470         mTextureSet.SetSampler(NORMAL_INDEX, sampler);
471       }
472       else
473       {
474         DALI_LOG_ERROR("Failed to load normal map texture in mesh visual.\n");
475         return false;
476       }
477     }
478
479     if(!mGlossTextureUrl.empty() && (mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING))
480     {
481       std::string imageUrl = mTexturesPath + mGlossTextureUrl;
482
483       //Load textures
484       Texture glossTexture = LoadTexture(imageUrl.c_str(), mUseMipmapping);
485       if(glossTexture)
486       {
487         mTextureSet.SetTexture(GLOSS_INDEX, glossTexture);
488         mTextureSet.SetSampler(GLOSS_INDEX, sampler);
489       }
490       else
491       {
492         DALI_LOG_ERROR("Failed to load gloss map texture in mesh visual.\n");
493         return false;
494       }
495     }
496   }
497   return true;
498 }
499
500 } // namespace Internal
501
502 } // namespace Toolkit
503
504 } // namespace Dali