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