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