[3.0] Remove/move experimental features
[platform/core/uifw/dali-core.git] / dali / internal / event / effects / shader-effect-impl.cpp
1 /*
2  * Copyright (c) 2014 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 <dali/internal/event/effects/shader-effect-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23
24 // INTERNAL INCLUDES
25 #include <dali/public-api/math/matrix.h>
26 #include <dali/public-api/math/matrix3.h>
27 #include <dali/public-api/math/vector2.h>
28 #include <dali/public-api/object/type-registry.h>
29 #include <dali/devel-api/scripting/scripting.h>
30 #include <dali/devel-api/shader-effects/shader-effect.h>
31 #include <dali/internal/event/common/property-helper.h>
32 #include <dali/internal/event/images/image-impl.h>
33 #include "dali-shaders.h"
34
35 using std::string;
36
37 namespace Dali
38 {
39
40 namespace Internal
41 {
42
43 namespace
44 {
45
46 // Properties
47
48 //              Name             Type   writable animatable constraint-input  enum for index-checking
49 DALI_PROPERTY_TABLE_BEGIN
50 DALI_PROPERTY( "gridDensity",    FLOAT,   true,    false,   false,   Dali::ShaderEffect::Property::GRID_DENSITY   )
51 DALI_PROPERTY( "image",          MAP,     true,    false,   false,   Dali::ShaderEffect::Property::IMAGE          )
52 DALI_PROPERTY( "program",        MAP,     true,    false,   false,   Dali::ShaderEffect::Property::PROGRAM        )
53 DALI_PROPERTY( "geometryHints",  STRING,  true,    false,   false,   Dali::ShaderEffect::Property::GEOMETRY_HINTS )
54 DALI_PROPERTY_TABLE_END( DEFAULT_ACTOR_PROPERTY_START_INDEX )
55
56 BaseHandle Create()
57 {
58   Internal::ShaderEffectPtr internal = Internal::ShaderEffect::New();
59
60   return Dali::ShaderEffect(internal.Get());
61 }
62
63 TypeRegistration mType( typeid(Dali::ShaderEffect), typeid(Dali::Handle), Create );
64
65 struct WrapperStrings
66 {
67   const char* vertexShaderPrefix;
68   const char* fragmentShaderPrefix;
69   const char* vertexShaderPostfix;
70   const char* fragmentShaderPostfix;
71 };
72
73 WrapperStrings customImageShaderWrappers =
74 {
75   CustomImagePrefixVertex, CustomImagePrefixFragment,
76   CustomImagePostfixVertex, CustomImagePostfixFragment
77 };
78
79 /**
80  * Helper to wrap the program with our default pre and postfix if needed
81  * @param[in] vertexPrefix from application
82  * @param[in] vertexBody from application
83  */
84 std::string WrapVertexShader( const std::string& vertexPrefix, const std::string& vertexBody )
85 {
86   std::string vertexSource = vertexPrefix + customImageShaderWrappers.vertexShaderPrefix;
87
88   // Append the custom vertex shader code if supplied, otherwise append the default
89   if ( vertexBody.length() > 0 )
90   {
91     vertexSource.append( vertexBody );
92   }
93   else
94   {
95     vertexSource.append( customImageShaderWrappers.vertexShaderPostfix );
96   }
97
98   return vertexSource;
99 }
100
101 /**
102  * Helper to wrap the program with our default pre and postfix if needed
103  * @param[in] fragmentPrefix from application
104  * @param[in] fragmentBody from application
105  */
106 std::string WrapFragmentShader( const std::string& fragmentPrefix, const std::string& fragmentBody )
107 {
108   std::string fragmentSource = fragmentPrefix + customImageShaderWrappers.fragmentShaderPrefix;
109
110   // Append the custom fragment shader code if supplied, otherwise append the default
111   if ( fragmentBody.length() > 0 )
112   {
113     fragmentSource.append( fragmentBody );
114   }
115   else
116   {
117     fragmentSource.append( customImageShaderWrappers.fragmentShaderPostfix );
118   }
119
120   return fragmentSource;
121 }
122
123 std::string GetStringProperty(const std::string& field, const Property::Value& property)
124 {
125   std::string retval;
126   const Property::Map* map = property.GetMap();
127   if( map )
128   {
129     const Property::Value* value = map->Find( field );
130     if( value )
131     {
132       value->Get( retval );
133     }
134   }
135
136   return retval;
137 }
138
139 Dali::Shader::Hint::Value ConvertHints( Dali::ShaderEffect::GeometryHints hints)
140 {
141   int convertedHints = Dali::Shader::Hint::NONE;
142
143   if( hints & Dali::ShaderEffect::HINT_BLENDING )
144   {
145     convertedHints |= Dali::Shader::Hint::OUTPUT_IS_TRANSPARENT;
146   }
147   if( !(hints & Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY) )
148   {
149     convertedHints |= Dali::Shader::Hint::MODIFIES_GEOMETRY;
150   }
151
152   return Dali::Shader::Hint::Value( convertedHints );
153 }
154
155 } // unnamed namespace
156
157 ShaderEffectPtr ShaderEffect::New( Dali::ShaderEffect::GeometryHints hints )
158 {
159   ShaderEffectPtr shaderEffect( new ShaderEffect( hints ) );
160   shaderEffect->RegisterObject();
161   return shaderEffect;
162 }
163
164 ShaderEffect::ShaderEffect( Dali::ShaderEffect::GeometryHints hints )
165 : mGridDensity( Dali::ShaderEffect::DEFAULT_GRID_DENSITY ),
166   mGeometryHints( hints )
167 {
168 }
169
170 ShaderEffect::~ShaderEffect()
171 {
172   // Guard to allow handle destruction after Core has been destroyed
173   UnregisterObject();
174 }
175
176 void ShaderEffect::SetEffectImage( Dali::Image image )
177 {
178   // if images are the same, do nothing
179   if ( mEffectImage == image )
180   {
181     return;
182   }
183
184   if ( mEffectImage && mConnectedActors.size() > 0 )
185   {
186     // unset previous image
187     GetImplementation( mEffectImage ).Disconnect();
188   }
189
190   // in case image is empty this will reset our image handle
191   mEffectImage = image;
192
193   if( image )
194   {
195     // tell image that we're using it
196     if (mConnectedActors.size() > 0)
197     {
198       GetImplementation( mEffectImage ).Connect();
199     }
200   }
201
202   //inform connected actors the image has been unset
203   // TODO or CHECK: ImageActor part was removed.
204 }
205
206 void ShaderEffect::SetUniform( const std::string& name, Property::Value value, UniformCoordinateType uniformCoordinateType )
207 {
208   // Register the property if it does not exist
209   mShader->RegisterProperty( name, value );
210 }
211
212 void ShaderEffect::SetPrograms( const string& vertexSource, const string& fragmentSource )
213 {
214   SetPrograms( "", "", vertexSource, fragmentSource );
215 }
216
217 void ShaderEffect::SetPrograms( const std::string& vertexPrefix, const std::string& fragmentPrefix,
218                                 const std::string& vertexSource, const std::string& fragmentSource )
219 {
220   mShader = Shader::New( WrapVertexShader( vertexPrefix, vertexSource ),
221                          WrapFragmentShader( fragmentPrefix, fragmentSource ),
222                          ConvertHints( mGeometryHints ) );
223 }
224
225 Vector2 ShaderEffect::GetGridSize( const Vector2& size )
226 {
227   Vector2 gridSize( 1.f, 1.f );
228
229   if( mGridDensity > 0 )
230   {
231     if( ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_X ) )
232     {
233       gridSize.x = ceil( size.width / mGridDensity );
234     }
235     if( ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_Y ) )
236     {
237       gridSize.y = ceil( size.height / mGridDensity );
238     }
239   }
240
241   return gridSize;
242 }
243
244 void ShaderEffect::Connect( ActorPtr actor )
245 {
246   if( !actor )
247   {
248     return;
249   }
250
251   std::vector< ActorPtr >::const_iterator it = std::find( mConnectedActors.begin(), mConnectedActors.end(), actor );
252   if( it == mConnectedActors.end() )
253   {
254     mConnectedActors.push_back( actor );
255   }
256
257   if( mEffectImage && mConnectedActors.size() == 1 )
258   {
259     GetImplementation( mEffectImage ).Connect();
260   }
261 }
262 void ShaderEffect::Disconnect( ActorPtr actor )
263 {
264   if( !actor )
265   {
266     return;
267   }
268
269   DALI_ASSERT_DEBUG(mConnectedActors.size() > 0);
270   std::vector< ActorPtr >::iterator match( std::remove( mConnectedActors.begin(), mConnectedActors.end(), actor ) );
271   mConnectedActors.erase( match, mConnectedActors.end() );
272
273   if (mEffectImage && mConnectedActors.size() == 0)
274   {
275      GetImplementation(mEffectImage).Disconnect();
276   }
277 }
278
279 unsigned int ShaderEffect::GetPropertyCount() const
280 {
281   return GetDefaultPropertyCount() + mShader->GetPropertyCount();
282 }
283
284 std::string ShaderEffect::GetPropertyName( Property::Index index ) const
285 {
286   if ( index < DEFAULT_PROPERTY_COUNT )
287   {
288     return GetDefaultPropertyName( index );
289   }
290   else
291   {
292     return mShader->GetPropertyName( index );
293   }
294 }
295
296 Property::Index ShaderEffect::GetPropertyIndex( const std::string& name ) const
297 {
298   Property::Index index = GetDefaultPropertyIndex( name );
299   if( index == Property::INVALID_INDEX )
300   {
301     return mShader->GetPropertyIndex( name );
302   }
303   else
304   {
305     return index;
306   }
307 }
308
309 bool ShaderEffect::IsPropertyWritable( Property::Index index ) const
310 {
311   if ( index < DEFAULT_PROPERTY_COUNT )
312   {
313     return IsDefaultPropertyWritable( index );
314   }
315   else
316   {
317     return mShader->IsPropertyWritable( index );
318   }
319 }
320
321 bool ShaderEffect::IsPropertyAnimatable( Property::Index index ) const
322 {
323   if ( index < DEFAULT_PROPERTY_COUNT )
324   {
325     return IsDefaultPropertyAnimatable( index );
326   }
327   else
328   {
329     return mShader->IsPropertyAnimatable( index );
330   }
331 }
332
333 bool ShaderEffect::IsPropertyAConstraintInput( Property::Index index ) const
334 {
335   if ( index < DEFAULT_PROPERTY_COUNT )
336   {
337     return IsDefaultPropertyAConstraintInput( index );
338   }
339   else
340   {
341     return mShader->IsPropertyAConstraintInput( index );
342   }
343 }
344
345 Property::Type ShaderEffect::GetPropertyType( Property::Index index ) const
346 {
347   if ( index < DEFAULT_PROPERTY_COUNT )
348   {
349     return GetDefaultPropertyType( index );
350   }
351   return mShader->GetPropertyType( index );
352 }
353
354 void ShaderEffect::SetProperty( Property::Index index, const Property::Value& propertyValue )
355 {
356   if ( index < DEFAULT_PROPERTY_COUNT )
357   {
358     SetDefaultProperty( index, propertyValue );
359   }
360   else
361   {
362     mShader->SetProperty( index, propertyValue );
363   }
364 }
365
366 Property::Value ShaderEffect::GetProperty( Property::Index index ) const
367 {
368   if ( index < DEFAULT_PROPERTY_COUNT )
369   {
370     return GetDefaultProperty( index );
371   }
372   return mShader->GetProperty( index );
373 }
374
375 void ShaderEffect::GetPropertyIndices( Property::IndexContainer& indices ) const
376 {
377   mShader->GetPropertyIndices( indices );
378   GetDefaultPropertyIndices( indices );
379 }
380
381 Property::Index ShaderEffect::RegisterProperty( const std::string& name, const Property::Value& propertyValue )
382 {
383   return mShader->RegisterProperty( name, propertyValue );
384 }
385
386 Property::Index ShaderEffect::RegisterProperty( const std::string& name, const Property::Value& propertyValue, Property::AccessMode accessMode )
387 {
388   return mShader->RegisterProperty( name, propertyValue, accessMode );
389 }
390
391 Dali::PropertyNotification ShaderEffect::AddPropertyNotification( Property::Index index,
392                                                                   int componentIndex,
393                                                                   const Dali::PropertyCondition& condition )
394 {
395   return mShader->AddPropertyNotification( index, componentIndex, condition );
396 }
397
398 void ShaderEffect::RemovePropertyNotification( Dali::PropertyNotification propertyNotification )
399 {
400   mShader->RemovePropertyNotification( propertyNotification );
401 }
402
403 void ShaderEffect::RemovePropertyNotifications()
404 {
405   mShader->RemovePropertyNotifications();
406 }
407
408 unsigned int ShaderEffect::GetDefaultPropertyCount() const
409 {
410   return DEFAULT_PROPERTY_COUNT;
411 }
412
413 void ShaderEffect::GetDefaultPropertyIndices( Property::IndexContainer& indices ) const
414 {
415   indices.Reserve( indices.Size() + DEFAULT_PROPERTY_COUNT );
416
417   for ( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
418   {
419     indices.PushBack( i );
420   }
421 }
422
423 const char* ShaderEffect::GetDefaultPropertyName(Property::Index index) const
424 {
425   if( index < DEFAULT_PROPERTY_COUNT )
426   {
427     return DEFAULT_PROPERTY_DETAILS[index].name;
428   }
429
430   return NULL;
431 }
432
433 Property::Index ShaderEffect::GetDefaultPropertyIndex(const std::string& name) const
434 {
435   Property::Index index = Property::INVALID_INDEX;
436
437   // Look for name in default properties
438   for( int i = 0; i < DEFAULT_PROPERTY_COUNT; ++i )
439   {
440     const Internal::PropertyDetails* property = &DEFAULT_PROPERTY_DETAILS[ i ];
441     if( 0 == strcmp( name.c_str(), property->name ) ) // dont want to convert rhs to string
442     {
443       index = i;
444       break;
445     }
446   }
447
448   return index;
449 }
450
451 bool ShaderEffect::IsDefaultPropertyWritable(Property::Index index) const
452 {
453   return DEFAULT_PROPERTY_DETAILS[ index ].writable;
454 }
455
456 bool ShaderEffect::IsDefaultPropertyAnimatable(Property::Index index) const
457 {
458   return DEFAULT_PROPERTY_DETAILS[ index ].animatable;
459 }
460
461 bool ShaderEffect::IsDefaultPropertyAConstraintInput( Property::Index index ) const
462 {
463   return DEFAULT_PROPERTY_DETAILS[ index ].constraintInput;
464 }
465
466 Property::Type ShaderEffect::GetDefaultPropertyType(Property::Index index) const
467 {
468   if( index < DEFAULT_PROPERTY_COUNT )
469   {
470     return DEFAULT_PROPERTY_DETAILS[index].type;
471   }
472
473   // index out of range...return Property::NONE
474   return Property::NONE;
475 }
476
477 void ShaderEffect::SetDefaultProperty( Property::Index index, const Property::Value& propertyValue )
478 {
479   switch ( index )
480   {
481     case Dali::ShaderEffect::Property::GRID_DENSITY:
482     {
483       propertyValue.Get( mGridDensity );
484       if( ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_X ) ||
485           ( mGeometryHints & Dali::ShaderEffect::HINT_GRID_Y ) )
486       {
487         //inform all the connected actors
488         for(std::vector< ActorPtr >::iterator it = mConnectedActors.begin(); it != mConnectedActors.end(); ++it )
489         {
490           (*it)->RelayoutRequest();
491         }
492       }
493       break;
494     }
495
496     case Dali::ShaderEffect::Property::IMAGE:
497     {
498       Dali::Image img(Scripting::NewImage( propertyValue ));
499       if(img)
500       {
501         SetEffectImage( img );
502       }
503       else
504       {
505         DALI_LOG_WARNING("Cannot create image from property value for ShaderEffect image\n");
506       }
507       break;
508     }
509
510     case Dali::ShaderEffect::Property::PROGRAM:
511     {
512       std::string vertexPrefix   = GetStringProperty("vertexPrefix",  propertyValue);
513       std::string fragmentPrefix = GetStringProperty("fragmentPrefix",  propertyValue);
514       std::string vertex         = GetStringProperty("vertex", propertyValue);
515       std::string fragment       = GetStringProperty("fragment", propertyValue);
516
517       SetPrograms( vertexPrefix, fragmentPrefix, vertex, fragment );
518       break;
519     }
520
521     case Dali::ShaderEffect::Property::GEOMETRY_HINTS:
522     {
523       mGeometryHints = Dali::ShaderEffect::HINT_NONE;
524       std::string s = propertyValue.Get<std::string>();
525       if(s == "HINT_NONE")
526       {
527         mGeometryHints = Dali::ShaderEffect::HINT_NONE;
528       }
529       else if(s == "HINT_GRID_X")
530       {
531         mGeometryHints = Dali::ShaderEffect::HINT_GRID_X;
532       }
533       else if(s == "HINT_GRID_Y")
534       {
535         mGeometryHints = Dali::ShaderEffect::HINT_GRID_Y;
536       }
537       else if(s == "HINT_GRID")
538       {
539         mGeometryHints = Dali::ShaderEffect::HINT_GRID;
540       }
541       else if(s == "HINT_DEPTH_BUFFER")
542       {
543         mGeometryHints = Dali::ShaderEffect::HINT_DEPTH_BUFFER;
544       }
545       else if(s == "HINT_BLENDING")
546       {
547         mGeometryHints = Dali::ShaderEffect::HINT_BLENDING;
548       }
549       else if(s == "HINT_DOESNT_MODIFY_GEOMETRY")
550       {
551         mGeometryHints = Dali::ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY;
552       }
553       else
554       {
555         DALI_ASSERT_ALWAYS(!"Geometry hint unknown" );
556       }
557       break;
558     }
559
560     default:
561     {
562       // nothing to do
563       break;
564     }
565   }
566 }
567
568 Property::Value ShaderEffect::GetDefaultProperty(Property::Index /*index*/) const
569 {
570   // none of our properties are readable so return empty
571   return Property::Value();
572 }
573
574 const SceneGraph::PropertyOwner* ShaderEffect::GetSceneObject() const
575 {
576   return mShader->GetSceneObject();
577 }
578
579 const SceneGraph::PropertyBase* ShaderEffect::GetSceneObjectAnimatableProperty( Property::Index index ) const
580 {
581   return mShader->GetSceneObjectAnimatableProperty( index );
582 }
583
584 const PropertyInputImpl* ShaderEffect::GetSceneObjectInputProperty( Property::Index index ) const
585 {
586   return mShader->GetSceneObjectInputProperty( index );
587 }
588
589 int ShaderEffect::GetPropertyComponentIndex( Property::Index index ) const
590 {
591   return mShader->GetPropertyComponentIndex( index );
592 }
593
594 } // namespace Internal
595
596 } // namespace Dali