Allow to create shader effect which is type-registered by its type name in JavaScript
[platform/core/uifw/dali-toolkit.git] / plugins / dali-script-v8 / src / shader-effects / shader-effect-api.cpp
1 /*
2  * Copyright (c) 2015 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
19 // CLASS HEADER
20 #include "shader-effect-api.h"
21
22 // EXTERNAL INCLUDES
23 #include <dali/public-api/object/type-registry.h>
24
25 // INTERNAL INCLUDES
26 #include <v8-utils.h>
27 #include <shader-effects/shader-effect-wrapper.h>
28
29 namespace Dali
30 {
31
32 namespace V8Plugin
33 {
34
35 namespace // un named namespace
36 {
37
38 typedef  std::vector< std::string > HintsArray;
39
40
41 struct GeometryTypePair
42 {
43   const char* name;
44   GeometryType type;
45 };
46
47 const GeometryTypePair GeometryTypeTable[]=
48 {
49     {"image",         GEOMETRY_TYPE_IMAGE },
50     {"text",          GEOMETRY_TYPE_TEXT  },
51     {"mesh",          GEOMETRY_TYPE_UNTEXTURED_MESH  },
52     {"textured-mesh", GEOMETRY_TYPE_TEXTURED_MESH },
53 };
54
55 const unsigned int GeometryTypeTableCount = sizeof(GeometryTypeTable)/sizeof(GeometryTypeTable[0]);
56
57 struct GeometryHintPair
58 {
59   const char* name;
60   ShaderEffect::GeometryHints hint;
61 };
62
63 const GeometryHintPair GeometryHintTable[]=
64 {
65     {"gridX",        ShaderEffect::HINT_GRID_X },
66     {"gridY",        ShaderEffect::HINT_GRID_Y },
67     {"grid",         ShaderEffect::HINT_GRID   },
68     {"depthBuffer",  ShaderEffect::HINT_DEPTH_BUFFER },
69     {"blending",     ShaderEffect::HINT_BLENDING },
70     {"doesntModifyGeometry", ShaderEffect::HINT_DOESNT_MODIFY_GEOMETRY }
71 };
72
73 const unsigned int GeometryHintTableCount = sizeof(GeometryHintTable)/sizeof(GeometryHintTable[0]);
74
75
76
77
78 struct ShaderParameters
79 {
80   ShaderParameters()
81       : mType( GEOMETRY_TYPE_IMAGE),
82         mHints( ShaderEffect::HINT_NONE )
83   {
84   }
85
86   void SetGeometryType( v8::Isolate* isolate, const std::string& typeName )
87   {
88      for( unsigned int i = 0; i < GeometryTypeTableCount; ++i )
89      {
90        if( typeName == GeometryTypeTable[i].name )
91        {
92          mType = GeometryTypeTable[i].type;
93          return;
94        }
95      }
96      DALI_SCRIPT_EXCEPTION( isolate, "Geometry type not found\n");
97   }
98
99   ShaderEffect::GeometryHints GetGeometryHint( const std::string& hint )
100   {
101     for( unsigned int i = 0 ; i < GeometryHintTableCount; ++i )
102     {
103       if( hint == GeometryHintTable[i].name )
104       {
105         return GeometryHintTable[i].hint;
106       }
107     }
108     return ShaderEffect::HINT_NONE;
109   }
110
111   void ProcessHintsArray( const HintsArray& hintsArray )
112   {
113     for( HintsArray::const_iterator iter  = hintsArray.begin(); iter != hintsArray.end() ; ++iter )
114     {
115       mHints= static_cast<ShaderEffect::GeometryHints>( mHints | GetGeometryHint( *iter ) );
116     }
117   }
118
119
120
121   ShaderEffect NewShader()
122   {
123     return ShaderEffect::NewWithPrefix( mVertexPrefix ,
124                               mVertex,
125                               mFragmentPrefix,
126                               mFragment,
127                               mType,
128                               mHints);
129   }
130   std::string mVertexPrefix;
131   std::string mVertex;
132   std::string mFragmentPrefix;
133   std::string mFragment;
134   GeometryType mType;
135   ShaderEffect::GeometryHints mHints;
136 };
137
138 ShaderEffect GetShaderEffect( v8::Isolate* isolate, const v8::FunctionCallbackInfo< v8::Value >& args )
139 {
140   v8::HandleScope handleScope( isolate );
141
142   v8::Local<v8::Object> object = args.This();
143   v8::Local<v8::External> field = v8::Local<v8::External>::Cast( object->GetInternalField(0) );
144   void* ptr = field->Value();
145
146   ShaderEffectWrapper* wrapper = static_cast< ShaderEffectWrapper *>(ptr);
147   return wrapper->GetShaderEffect();
148 }
149
150 } // unnamed space
151
152 /**
153  * Create a new ShaderEffect
154  * @constructor
155  * @for ShaderEffect
156  * @method ShaderEffect
157  * @param {Object} shaderOptions
158  * @param {String} [shaderOptions.geometryType] Type of geometry to be rendered with the effect. "image", "text", "mesh", "textured-mesh", default is image.
159  * @param {String} [shaderOptions.vertexShaderPrefix] This string will be inserted before the default uniforms for the vertex shader(ideal for #defines)
160  * @param {String} [shaderOptions.vertexShader] VertexShader code for the effect. If not defined, the default version will be used
161  * @param {String} [shaderOptions.fragmentShaderPrefix] This string will be inserted before the default uniforms for the fragment shader(ideal for #defines)
162  * @param {String} [shaderOptions.fragmentShader] FragmentShader code for the effect. If not defined, the default version will be used
163  * @param {Array}  [shaderOptions.geometryHints] Hints for rendering the geometry, e.g.  [ "gridX", "gridY", "grid","depthBuffer","blending","doesntModifyGeometry" ]
164  * @return {Object} ShaderEffect
165  * @example
166
167       // this will match the default shaders for image
168       var vertexShader = " void main() \
169       { \
170         gl_Position = uProjection * uModelView * vec4(aPosition, 1.0); \
171         vTexCoord = aTexCoord; \
172       }; "
173      var fragShader = " void main() \
174       { \
175        gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor;\
176       }; "
177       var shaderOptions = {
178             geometryType: "image",
179             vertexShader: vertexShader,
180             fragmentShader: fragShader
181       };
182
183       var shader = new dali.ShaderEffect(shaderOptions);
184
185       imageActor.setShaderEffect( shader );
186
187  *
188  *
189  *
190  */
191 ShaderEffect ShaderEffectApi::New(  v8::Isolate* isolate, const v8::FunctionCallbackInfo< v8::Value >& args )
192 {
193   v8::HandleScope handleScope( isolate );
194
195   if( args[0]->IsObject() )
196   {
197     ShaderParameters shaderParams;
198
199     v8::Local<v8::Object > obj = args[0]->ToObject();
200
201     v8::Local<v8::Value> geometryTypeValue = obj->Get(v8::String::NewFromUtf8( isolate, "geometryType"));
202     if( geometryTypeValue->IsString() )
203     {
204       std::string geometryTypeName = V8Utils::v8StringToStdString( geometryTypeValue );
205      // printf(" geometry type found %s \n", geometryTypeName.c_str() );
206       shaderParams.SetGeometryType( isolate, geometryTypeName );
207     }
208
209     v8::Local<v8::Value> vertexPrefixValue = obj->Get(v8::String::NewFromUtf8( isolate, "vertexShaderPrefix"));
210     if( vertexPrefixValue->IsString() )
211     {
212       shaderParams.mVertexPrefix = V8Utils::v8StringToStdString( vertexPrefixValue );
213     }
214
215     v8::Local<v8::Value> fragmentPrefixValue = obj->Get(v8::String::NewFromUtf8( isolate, "fragmentShaderPrefix"));
216     if( fragmentPrefixValue->IsString() )
217     {
218       shaderParams.mFragmentPrefix = V8Utils::v8StringToStdString( fragmentPrefixValue );
219     }
220
221     v8::Local<v8::Value> vertexValue = obj->Get(v8::String::NewFromUtf8( isolate, "vertexShader"));
222     if( vertexValue->IsString() )
223     {
224       shaderParams.mVertex = V8Utils::v8StringToStdString( vertexValue );
225     }
226
227     v8::Local<v8::Value> fragmentValue = obj->Get(v8::String::NewFromUtf8( isolate, "fragmentShader"));
228     if( fragmentValue->IsString() )
229     {
230       shaderParams.mFragment = V8Utils::v8StringToStdString( fragmentValue );
231     }
232     // Get any hints
233     v8::Local<v8::Value> hints = obj->Get(v8::String::NewFromUtf8( isolate, "geometryHints"));
234     if( hints->IsArray() )
235     {
236       HintsArray hintsArray;
237       v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast( hints );
238       for( uint32_t i=0; i <  array->Length(); ++i)
239       {
240         v8::Handle<v8::Value> entry = array->Get(  v8::Integer::New( isolate, i) );
241         if( entry->IsString() )
242         {
243           std::string entryString = V8Utils::v8StringToStdString( entry );
244           hintsArray.push_back( entryString );
245         }
246       }
247       shaderParams.ProcessHintsArray( hintsArray );
248     }
249
250     return shaderParams.NewShader();
251   }
252   else
253   {
254     ShaderEffect effect;
255
256     bool found( false );
257     std::string typeName = V8Utils::GetStringParameter( PARAMETER_0, found, isolate, args );
258     if( !found )
259     {
260       DALI_SCRIPT_EXCEPTION( isolate, "string parameter missing" );
261     }
262     else
263     {
264       // create a new shader effect based on type, using the type registry.
265       Dali::TypeInfo typeInfo = Dali::TypeRegistry::Get().GetTypeInfo( typeName );
266       if( typeInfo ) // handle, check if it has a value
267       {
268         Dali::BaseHandle handle = typeInfo.CreateInstance();
269         if( handle )
270         {
271           effect = ShaderEffect::DownCast( handle );
272         }
273       }
274       else
275       {
276         DALI_SCRIPT_EXCEPTION(isolate,"Unknown shader effect type");
277       }
278     }
279
280     return effect;
281   }
282 }
283
284 ShaderEffect ShaderEffectApi::GetShaderEffectFromParams( int paramIndex,
285                           bool& found,
286                           v8::Isolate* isolate,
287                           const v8::FunctionCallbackInfo< v8::Value >& args )
288 {
289   found = false;
290
291   v8::HandleScope handleScope( isolate );
292   BaseWrappedObject* wrappedObject = V8Utils::GetWrappedDaliObjectParameter( paramIndex, BaseWrappedObject::SHADER_EFFECT, isolate, args );
293   if( wrappedObject )
294   {
295     found = true;
296     ShaderEffectWrapper* wrapper = static_cast< ShaderEffectWrapper *>(wrappedObject);
297     return wrapper->GetShaderEffect();
298   }
299   else
300   {
301     return ShaderEffect();
302   }
303 }
304
305 /**
306  * Set a shader effect image.
307  * This image texture will be bound to the "sEffect" sampler
308  * so it can be used in fragment shader for effects
309  * @method setEffectImage
310  * @for ShaderEffect
311  * @param {Object} image
312  * @example
313  *
314  *      shader.setEffectImage( image );
315  *
316  *      // example of a fragment shader than can use the effect image (sEffect and main texture sTexture)
317  *
318  *      "void main() \
319  *       { \
320  *        vec4 v4Color  = (texture2D(sTexture, vTexCoord) * uColor); \
321  *        v4Color = v4Color*  texture2D(sEffect, vTexCoord);\
322  *        gl_FragColor = v4Color; \"
323  *      }";
324  */
325 void ShaderEffectApi::SetEffectImage( const v8::FunctionCallbackInfo< v8::Value >& args )
326 {
327   v8::Isolate* isolate = args.GetIsolate();
328   v8::HandleScope handleScope( isolate );
329
330   bool found( false );
331   Image image = V8Utils::GetImageParameter( PARAMETER_0, found, isolate, args );
332   if( !found )
333   {
334     DALI_SCRIPT_EXCEPTION( isolate, " Image::SetEffectImage invalid params");
335     return;
336   }
337   ShaderEffect effect =  GetShaderEffect( isolate, args );
338   effect.SetEffectImage( image );
339 }
340
341
342 /**
343  * Sets and registers a uniform property.
344  * If name matches a uniform in the shader source, this value will be uploaded when rendering.
345  * This uniform can then be animated / modified and the change will be made to the shader.
346  * @method setUniform
347  * @for ShaderEffect
348  * @param {String} name
349  * @param {Object} value must be a: float, vector2 vector3, vector4, matrix
350  * @param {String} [uniformCoordinateType] The coordinate type of the uniform.
351  * either "viewPortPosition" or "viewPortDirection"
352  * @example
353
354       var fragShader =
355       " uniform lowp vec4 uColorShift; \
356       \
357        void main()             \
358        {                  \
359
360          gl_FragColor = texture2D( sTexture, vTexCoord ) * uColor + uColorShift; \
361        }"
362
363       var shaderOptions = {
364           geometryType: "image",
365           fragmentShader: fragShader
366       };
367
368       // create a new shader effect
369       var shader = new dali.ShaderEffect(shaderOptions);
370
371       // add the color shift uniform so we can animate it
372       // default the color shift to zero, so it has no effect
373       shader.setUniform("uColorShift", [0.0, 0.0, 0.0, 0]);
374  */
375 void ShaderEffectApi::SetUniform( const v8::FunctionCallbackInfo< v8::Value >& args )
376 {
377
378   v8::Isolate* isolate = args.GetIsolate();
379   v8::HandleScope handleScope( isolate );
380   ShaderEffect effect =  GetShaderEffect( isolate, args );
381
382   bool found(false);
383   std::string uniformName = V8Utils::GetStringParameter( PARAMETER_0 , found, isolate, args );
384   if( !found )
385   {
386     DALI_SCRIPT_EXCEPTION( isolate, "SetUniform: uniform name not found\n");
387     return;
388   }
389
390   Property::Value propValue = V8Utils::GetPropertyValueParameter( PARAMETER_1 , found, isolate, args );
391   if( !found )
392   {
393     DALI_SCRIPT_EXCEPTION( isolate, "SetUniform: value not found\n");
394     return;
395   }
396
397   std::string coordinateType = V8Utils::GetStringParameter( PARAMETER_2, found, isolate, args );
398   ShaderEffect::UniformCoordinateType uniformCoordinateType( ShaderEffect::COORDINATE_TYPE_DEFAULT );
399   if( found )
400   {
401     if( coordinateType == "viewPortPosition")
402     {
403       uniformCoordinateType = ShaderEffect::COORDINATE_TYPE_VIEWPORT_POSITION;
404     }
405     else if ( coordinateType == "viewPortDirection ")
406     {
407       uniformCoordinateType = ShaderEffect::COORDINATE_TYPE_VIEWPORT_DIRECTION;
408     }
409   }
410
411   switch( propValue.GetType() )
412   {
413     case Property::FLOAT:
414     {
415       effect.SetUniform( uniformName, propValue.Get<float>(), uniformCoordinateType );
416       break;
417     }
418     case Property::VECTOR2:
419     {
420       effect.SetUniform( uniformName, propValue.Get<Vector2>(), uniformCoordinateType );
421       break;
422     }
423     case Property::VECTOR3:
424     {
425       effect.SetUniform( uniformName, propValue.Get<Vector3>(), uniformCoordinateType );
426       break;
427     }
428     case Property::VECTOR4:
429     {
430       effect.SetUniform( uniformName, propValue.Get<Vector4>(), uniformCoordinateType );
431       break;
432     }
433     case Property::MATRIX:
434     {
435       effect.SetUniform( uniformName, propValue.Get<Matrix>(), uniformCoordinateType );
436       break;
437     }
438     case Property::MATRIX3:
439     {
440       effect.SetUniform( uniformName, propValue.Get<Matrix3>(), uniformCoordinateType );
441       break;
442     }
443     default:
444     {
445       DALI_SCRIPT_EXCEPTION( isolate, "value type not recognised \n");
446       break;
447     }
448   }
449 }
450
451
452 } // namespace V8Plugin
453
454 } // namespace Dali