Updated visuals to use VisualRenderer
[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.SetUniforms(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 = VisualRenderer::New(mGeometry, mShader);
306   mImpl->mRenderer.SetTextures(mTextureSet);
307   mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::ON);
308   mImpl->mRenderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);
309
310   //Register transform properties
311   mImpl->mTransform.SetUniforms(mImpl->mRenderer, Direction::LEFT_TO_RIGHT);
312 }
313
314 void MeshVisual::SupplyEmptyGeometry()
315 {
316   mGeometry        = Geometry::New();
317   mShader          = Shader::New(SHADER_MESH_VISUAL_SIMPLE_SHADER_VERT, SHADER_MESH_VISUAL_SIMPLE_SHADER_FRAG);
318   mImpl->mRenderer = VisualRenderer::New(mGeometry, mShader);
319
320   DALI_LOG_ERROR("Initialisation error in mesh visual.\n");
321 }
322
323 void MeshVisual::UpdateShaderUniforms()
324 {
325   Stage stage  = Stage::GetCurrent();
326   float width  = stage.GetSize().width;
327   float height = stage.GetSize().height;
328
329   Matrix scaleMatrix;
330   scaleMatrix.SetIdentityAndScale(Vector3(1.0, -1.0, 1.0));
331
332   mShader.RegisterProperty(STAGE_OFFSET_UNIFORM_NAME, Vector2(width, height) / 2.0f);
333   mShader.RegisterProperty(LIGHT_POSITION_NAME, mLightPosition);
334   mShader.RegisterProperty(OBJECT_MATRIX_UNIFORM_NAME, scaleMatrix);
335 }
336
337 void MeshVisual::CreateShader()
338 {
339   if(mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING)
340   {
341     mShader = Shader::New(SHADER_MESH_VISUAL_NORMAL_MAP_SHADER_VERT, SHADER_MESH_VISUAL_NORMAL_MAP_SHADER_FRAG);
342   }
343   else if(mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_SPECULAR_LIGHTING)
344   {
345     mShader = Shader::New(SHADER_MESH_VISUAL_SHADER_VERT, SHADER_MESH_VISUAL_SHADER_FRAG);
346   }
347   else //Textureless
348   {
349     mShader = Shader::New(SHADER_MESH_VISUAL_SIMPLE_SHADER_VERT, SHADER_MESH_VISUAL_SIMPLE_SHADER_FRAG);
350   }
351
352   UpdateShaderUniforms();
353 }
354
355 bool MeshVisual::CreateGeometry()
356 {
357   //Determine if we need to use a simpler shader to handle the provided data
358   if(!mUseTexture || !mObjLoader.IsDiffuseMapPresent())
359   {
360     mShadingMode = Toolkit::MeshVisual::ShadingMode::TEXTURELESS_WITH_DIFFUSE_LIGHTING;
361   }
362   else if(mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING && (!mObjLoader.IsNormalMapPresent() || !mObjLoader.IsSpecularMapPresent()))
363   {
364     mShadingMode = Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_SPECULAR_LIGHTING;
365   }
366
367   int objectProperties = 0;
368
369   if(mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_SPECULAR_LIGHTING ||
370      mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING)
371   {
372     objectProperties |= ObjLoader::TEXTURE_COORDINATES;
373   }
374
375   if(mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING)
376   {
377     objectProperties |= ObjLoader::TANGENTS | ObjLoader::BINORMALS;
378   }
379
380   //Create geometry with attributes required by shader.
381   mGeometry = mObjLoader.CreateGeometry(objectProperties, mUseSoftNormals);
382
383   if(mGeometry)
384   {
385     return true;
386   }
387
388   DALI_LOG_ERROR("Failed to load geometry in mesh visual.\n");
389   return false;
390 }
391
392 bool MeshVisual::LoadGeometry()
393 {
394   std::streampos     fileSize;
395   Dali::Vector<char> fileContent;
396
397   if(FileLoader::ReadFile(mObjectUrl, fileSize, fileContent, FileLoader::TEXT))
398   {
399     mObjLoader.ClearArrays();
400     mObjLoader.LoadObject(fileContent.Begin(), fileSize);
401
402     //Get size information from the obj loaded
403     mSceneCenter = mObjLoader.GetCenter();
404     mSceneSize   = mObjLoader.GetSize();
405
406     return true;
407   }
408
409   DALI_LOG_ERROR("Failed to find object to load in mesh visual.\n");
410   return false;
411 }
412
413 bool MeshVisual::LoadMaterial()
414 {
415   std::streampos     fileSize;
416   Dali::Vector<char> fileContent;
417
418   if(FileLoader::ReadFile(mMaterialUrl, fileSize, fileContent, FileLoader::TEXT))
419   {
420     //Load data into obj (usable) form
421     mObjLoader.LoadMaterial(fileContent.Begin(), fileSize, mDiffuseTextureUrl, mNormalTextureUrl, mGlossTextureUrl);
422     return true;
423   }
424
425   DALI_LOG_ERROR("Failed to find texture set to load in mesh visual.\n");
426   mUseTexture = false;
427   return false;
428 }
429
430 bool MeshVisual::LoadTextures()
431 {
432   mTextureSet = TextureSet::New();
433
434   if(mShadingMode != Toolkit::MeshVisual::ShadingMode::TEXTURELESS_WITH_DIFFUSE_LIGHTING)
435   {
436     Sampler sampler = Sampler::New();
437     if(mUseMipmapping)
438     {
439       sampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR_MIPMAP_LINEAR);
440     }
441
442     if(!mDiffuseTextureUrl.empty())
443     {
444       std::string imageUrl = mTexturesPath + mDiffuseTextureUrl;
445
446       //Load textures
447       Texture diffuseTexture = LoadTexture(imageUrl.c_str(), mUseMipmapping);
448       if(diffuseTexture)
449       {
450         mTextureSet.SetTexture(DIFFUSE_INDEX, diffuseTexture);
451         mTextureSet.SetSampler(DIFFUSE_INDEX, sampler);
452       }
453       else
454       {
455         DALI_LOG_ERROR("Failed to load diffuse map texture in mesh visual.\n");
456         return false;
457       }
458     }
459
460     if(!mNormalTextureUrl.empty() && (mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING))
461     {
462       std::string imageUrl = mTexturesPath + mNormalTextureUrl;
463
464       //Load textures
465       Texture normalTexture = LoadTexture(imageUrl.c_str(), mUseMipmapping);
466       if(normalTexture)
467       {
468         mTextureSet.SetTexture(NORMAL_INDEX, normalTexture);
469         mTextureSet.SetSampler(NORMAL_INDEX, sampler);
470       }
471       else
472       {
473         DALI_LOG_ERROR("Failed to load normal map texture in mesh visual.\n");
474         return false;
475       }
476     }
477
478     if(!mGlossTextureUrl.empty() && (mShadingMode == Toolkit::MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING))
479     {
480       std::string imageUrl = mTexturesPath + mGlossTextureUrl;
481
482       //Load textures
483       Texture glossTexture = LoadTexture(imageUrl.c_str(), mUseMipmapping);
484       if(glossTexture)
485       {
486         mTextureSet.SetTexture(GLOSS_INDEX, glossTexture);
487         mTextureSet.SetSampler(GLOSS_INDEX, sampler);
488       }
489       else
490       {
491         DALI_LOG_ERROR("Failed to load gloss map texture in mesh visual.\n");
492         return false;
493       }
494     }
495   }
496   return true;
497 }
498
499 } // namespace Internal
500
501 } // namespace Toolkit
502
503 } // namespace Dali