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