Merge branch 'devel/master' into tizen
[platform/core/uifw/dali-demo.git] / examples / shadows-and-lights / shadows-and-lights-example.cpp
1 /*
2  * Copyright (c) 2017 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 // INTERNAL INCLUDES
19 #include "shared/view.h"
20
21 #include <dali/dali.h>
22 #include <dali-toolkit/dali-toolkit.h>
23 #include <dali-toolkit/devel-api/controls/buttons/button-devel.h>
24 #include <dali-toolkit/devel-api/controls/shadow-view/shadow-view.h>
25
26 #include <iostream>
27
28 using namespace Dali;
29 using namespace Dali::Toolkit;
30 using std::string;
31 using namespace DemoHelper;
32
33 namespace
34 {
35 const char* BACKGROUND_IMAGE( DEMO_IMAGE_DIR "background-default.png" );
36 const char* TOOLBAR_IMAGE( DEMO_IMAGE_DIR "top-bar.png" );
37
38 const char* APPLICATION_TITLE_PAN_LIGHT( "Lighting: Pan Light" );
39 const char* APPLICATION_TITLE_ROTATE_OBJECT( "Lighting: Rotate Object" );
40 const char* APPLICATION_TITLE_PAN_SCENE( "Lighting: Pan Scene" );
41 const char* APPLICATION_TITLE_ROTATE_SCENE( "Lighting: Rotate Scene" );
42 const char* CHANGE_EFFECT_IMAGE( DEMO_IMAGE_DIR "icon-change.png" );
43 const char* CHANGE_EFFECT_IMAGE_SELECTED( DEMO_IMAGE_DIR "icon-change-selected.png" );
44 const char* RESET_ICON( DEMO_IMAGE_DIR "icon-reset.png" );
45 const char* RESET_ICON_SELECTED( DEMO_IMAGE_DIR "icon-reset-selected.png" );
46
47 const char* SCENE_IMAGE_1( DEMO_IMAGE_DIR "gallery-small-10.jpg");
48 const char* SCENE_IMAGE_2( DEMO_IMAGE_DIR "gallery-small-42.jpg");
49 const char* SCENE_IMAGE_3( DEMO_IMAGE_DIR "gallery-small-48.jpg");
50
51 const float MIN_PINCH_SCALE( 0.3f );
52 const float MAX_PINCH_SCALE( 2.05f );
53
54 const float R3_2(0.8660254);
55 const Vector3 TOP_POINT(  0.0f, -1.0f,  0.0f);
56 const Vector3 LEFT_POINT( -R3_2, 0.5f,  0.0f);
57 const Vector3 RIGHT_POINT( R3_2, 0.5f,  0.0f);
58 const Vector3 FRONT_POINT( 0.0f, 0.0f, 20.0f);
59
60 const Vector2 DEFAULT_STAGE_SIZE( 480.0f, 800.0f );
61
62 const float X_ROTATION_DISPLACEMENT_FACTOR = 60.f;
63 const float Y_ROTATION_DISPLACEMENT_FACTOR = 60.f;
64 const float LIGHT_PAN_X_DISPLACEMENT_FACTOR = 1/360.f;
65 const float LIGHT_PAN_Y_DISPLACEMENT_FACTOR = 1/360.f;
66
67 }
68
69 /**
70  * This example shows a fixed point light onto an animating set of images
71  * casting a shadow onto a wall. The whole scene can be rotated.
72  */
73
74 class TestApp : public ConnectionTracker
75 {
76 public:
77
78   /**
79    * Constructor
80    * @param application class, stored as reference
81    */
82   TestApp(Application &app)
83   : mApp(app),
84     mView(),
85     mContents(),
86     mSceneActor(),
87     mAnimation(),
88     mSceneAnimation(),
89     mPaused( false ),
90     mShadowView(),
91     mShadowPlaneBg(),
92     mShadowPlane(),
93     mCastingLight(),
94     mLightAnchor(),
95     mImageActor1(),
96     mImageActor2(),
97     mImageActor3(),
98     mPanGestureDetector(),
99     mPinchGestureDetector(),
100     mTapGestureDetector(),
101     mTranslation( 22.0f, -1.0f, 0.0f ),
102     mSceneXRotation( Degree(-6.0f) ), // Initial values give a reasonable off-straight view.
103     mSceneYRotation( Degree(20.0f) ),
104     mLightXRotation( Degree(-1.5f) ),
105     mLightYRotation( Degree(-9.5f) ),
106     mObjectXRotation(0.0f),
107     mObjectYRotation(0.0f),
108     mPinchScale(0.6f),
109     mScaleAtPinchStart(0.6f),
110     mAngle1Index( Property::INVALID_INDEX ),
111     mAngle3Index( Property::INVALID_INDEX ),
112     mTitleActor(),
113     mPanState( PAN_LIGHT )
114   {
115     app.InitSignal().Connect(this, &TestApp::Create);
116     app.TerminateSignal().Connect(this, &TestApp::Terminate);
117   }
118
119   ~TestApp()
120   {
121     // Nothing to do here; All the members of this class get deleted automatically and they delete their children
122   }
123
124 public:
125
126   struct RotationConstraint
127   {
128     RotationConstraint(float sign)
129     : mSign(sign)
130     {
131     }
132
133     void operator()( Quaternion& current, const PropertyInputContainer& inputs )
134     {
135       Radian angle( inputs[0]->GetFloat() );
136       current = Quaternion( angle * mSign, Vector3::YAXIS );
137     }
138
139     float mSign;
140   };
141
142   /**
143    * This method gets called once the main loop of application is up and running
144    */
145   void Create(Application& app)
146   {
147     srand(0); // Want repeatable path
148
149     Stage::GetCurrent().KeyEventSignal().Connect(this, &TestApp::OnKeyEvent);
150
151     CreateToolbarAndView(app);
152     CreateShadowViewAndLights();
153     CreateScene();
154   }
155
156   void CreateToolbarAndView(Application& app)
157   {
158     // Creates a default view with a default tool bar.
159     // The view is added to the stage.
160     Toolkit::ToolBar toolBar;
161     mContents = DemoHelper::CreateView( app,
162                                         mView,
163                                         toolBar,
164                                         BACKGROUND_IMAGE,
165                                         TOOLBAR_IMAGE,
166                                         "" );
167
168     // Add an effect-changing button on the right of the tool bar.
169     Toolkit::PushButton effectChangeButton = Toolkit::PushButton::New();
170     effectChangeButton.SetProperty( Toolkit::DevelButton::Property::UNSELECTED_BACKGROUND_VISUAL, CHANGE_EFFECT_IMAGE );
171     effectChangeButton.SetProperty( Toolkit::DevelButton::Property::SELECTED_BACKGROUND_VISUAL, CHANGE_EFFECT_IMAGE_SELECTED );
172     effectChangeButton.ClickedSignal().Connect( this, &TestApp::OnEffectButtonClicked );
173     toolBar.AddControl( effectChangeButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalRight, DemoHelper::DEFAULT_MODE_SWITCH_PADDING );
174
175     // Add title to the tool bar.
176     mTitleActor = DemoHelper::CreateToolBarLabel( "" );
177     toolBar.AddControl( mTitleActor, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarTitlePercentage, Toolkit::Alignment::HorizontalCenter );
178
179     // Set Title text
180     mTitleActor.SetProperty( TextLabel::Property::TEXT, std::string(APPLICATION_TITLE_PAN_LIGHT) );
181
182     //Add a reset button
183     Toolkit::PushButton resetButton = Toolkit::PushButton::New();
184     resetButton.SetProperty( Toolkit::DevelButton::Property::UNSELECTED_BACKGROUND_VISUAL, RESET_ICON );
185     resetButton.SetProperty( Toolkit::DevelButton::Property::SELECTED_BACKGROUND_VISUAL, RESET_ICON_SELECTED );
186     resetButton.ClickedSignal().Connect( this, &TestApp::OnResetPressed );
187     toolBar.AddControl( resetButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalCenter, DemoHelper::DEFAULT_PLAY_PADDING );
188
189     // Setup
190     mView.SetPosition(Vector3(0.0f, 0.0f, 0.0f));
191
192     mContents.SetBehavior(Layer::LAYER_3D);
193     mContents.SetPosition(mTranslation);
194     mContents.SetOrientation( CalculateWorldRotation( mSceneXRotation, mSceneYRotation ) );
195     mContents.SetScale(mPinchScale, mPinchScale, mPinchScale);
196
197     mPanGestureDetector = PanGestureDetector::New();
198     mPanGestureDetector.Attach( mView );
199     mPanGestureDetector.DetectedSignal().Connect(this, &TestApp::OnPan);
200
201     mPinchGestureDetector = PinchGestureDetector::New();
202     mPinchGestureDetector.Attach( mView );
203     mPinchGestureDetector.DetectedSignal().Connect(this, &TestApp::OnPinch);
204
205     mTapGestureDetector = TapGestureDetector::New();
206     mTapGestureDetector.Attach( mView );
207     mTapGestureDetector.DetectedSignal().Connect(this, &TestApp::OnTap);
208   }
209
210
211
212   void CreateShadowViewAndLights()
213   {
214     mShadowView = Toolkit::ShadowView::New();
215     mShadowView.SetName("Container");
216     mShadowView.SetParentOrigin(ParentOrigin::CENTER);
217     mShadowView.SetAnchorPoint(AnchorPoint::CENTER);
218     mShadowView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
219     mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
220     mContents.Add(mShadowView);
221
222     mShadowPlaneBg = ImageView::New( DEMO_IMAGE_DIR "brick-wall.jpg" );
223     mShadowPlaneBg.SetParentOrigin(ParentOrigin::CENTER);
224     mShadowPlaneBg.SetAnchorPoint(AnchorPoint::CENTER);
225     mShadowPlaneBg.SetName("Plane");
226     mShadowPlaneBg.SetSize(1000.0f, 1000.0f);
227     mContents.Add(mShadowPlaneBg);
228     mShadowPlaneBg.SetPosition(Vector3(50.0f, 50.0f, -200.0f));
229
230     mShadowView.SetShadowPlaneBackground(mShadowPlaneBg);
231     mShadowView.Activate();
232
233     mLightAnchor = Actor::New();
234     mLightAnchor.SetParentOrigin(ParentOrigin::CENTER);
235     mLightAnchor.SetAnchorPoint(AnchorPoint::CENTER);
236     mLightAnchor.SetOrientation( CalculateWorldRotation( mLightXRotation, mLightYRotation ) );
237
238     // Work out a scaling factor as the initial light position was calculated for desktop
239     // Need to scale light position as scene actor size is based on stage size (i.e. much bigger on device)
240     Vector2 stageSize( Stage::GetCurrent().GetSize() );
241     float scaleFactor = stageSize.x / DEFAULT_STAGE_SIZE.x;
242
243     mCastingLight = Actor::New();
244     mCastingLight.SetParentOrigin(ParentOrigin::CENTER);
245     mCastingLight.SetAnchorPoint(AnchorPoint::CENTER);
246     mCastingLight.SetPosition( Vector3( 0.0f, 0.0f, 800.0f ) * scaleFactor );
247
248     TextLabel text = TextLabel::New( "Light" );
249     text.SetProperty( TextLabel::Property::POINT_SIZE, 20.0f );
250     text.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
251     text.SetColor( Color::BLUE );
252
253     mCastingLight.Add(text);
254     mLightAnchor.Add(mCastingLight);
255     mShadowPlaneBg.Add(mLightAnchor);
256
257     text.SetParentOrigin(ParentOrigin::CENTER);
258     mShadowView.SetPointLight(mCastingLight);
259   }
260
261   void CreateScene()
262   {
263     mSceneActor = Actor::New();
264     mSceneActor.SetParentOrigin(ParentOrigin::CENTER);
265
266     // Create and add images to the scene actor:
267     mImageActor1 = ImageView::New( SCENE_IMAGE_1 );
268     mImageActor2 = ImageView::New( SCENE_IMAGE_2 );
269     mImageActor3 = ImageView::New( SCENE_IMAGE_3 );
270
271     mImageActor1.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
272     mImageActor2.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
273     mImageActor3.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
274
275     mImageActor2.SetParentOrigin(ParentOrigin::CENTER);
276
277     mImageActor1.SetParentOrigin(ParentOrigin::CENTER_LEFT);
278     mImageActor1.SetAnchorPoint(AnchorPoint::CENTER_RIGHT);
279
280     mImageActor3.SetParentOrigin(ParentOrigin::CENTER_RIGHT);
281     mImageActor3.SetAnchorPoint(AnchorPoint::CENTER_LEFT);
282
283     mSceneActor.Add(mImageActor2);
284     mImageActor2.Add(mImageActor1);
285     mImageActor2.Add(mImageActor3);
286
287     Property::Index angleIndex = mImageActor2.RegisterProperty("angle", Property::Value( Dali::ANGLE_30 ) );
288     Source angleSrc( mImageActor2, angleIndex );
289
290     Constraint constraint = Constraint::New<Quaternion>( mImageActor1, Actor::Property::ORIENTATION, RotationConstraint(-1.0f) );
291     constraint.AddSource( angleSrc );
292     constraint.Apply();
293
294     constraint = Constraint::New<Quaternion>( mImageActor3, Actor::Property::ORIENTATION, RotationConstraint(+1.0f) );
295     constraint.AddSource( angleSrc );
296     constraint.Apply();
297
298     mSceneAnimation = Animation::New(2.5f);
299
300     // Want to animate angle from 30 => -30 and back again smoothly.
301
302     mSceneAnimation.AnimateTo( Property( mImageActor2, angleIndex ), Property::Value(-Dali::ANGLE_30), AlphaFunction::SIN );
303
304     mSceneAnimation.SetLooping(true);
305     mSceneAnimation.Play();
306
307     mSceneActor.SetSize(250.0f, 250.0f);
308     mSceneActor.SetPosition(0.0f, 0.0f, 130.0f);
309     mShadowView.Add(mSceneActor);
310   }
311
312
313   Quaternion CalculateWorldRotation( Radian XRotation, Radian YRotation )
314   {
315     Quaternion p( XRotation, Vector3::XAXIS );
316     Quaternion q( YRotation, Vector3::YAXIS );
317     return p*q;
318   }
319
320   void OnTap(Dali::Actor actor, const TapGesture& gesture)
321   {
322     if( mSceneAnimation )
323     {
324       if( ! mPaused )
325       {
326         mSceneAnimation.Pause();
327         mPaused = true;
328       }
329       else
330       {
331         mSceneAnimation.Play();
332         mPaused = false;
333       }
334     }
335   }
336
337   void OnPan(Actor actor, const PanGesture& gesture)
338   {
339     switch (gesture.state)
340     {
341       case Gesture::Continuing:
342       {
343         switch(mPanState)
344         {
345           case PAN_LIGHT:
346           {
347             mLightXRotation = mLightXRotation - gesture.displacement.y * LIGHT_PAN_X_DISPLACEMENT_FACTOR; // X displacement rotates around Y axis
348             mLightXRotation = Clamp(mLightXRotation, -Dali::ANGLE_45, Dali::ANGLE_45 );
349             mLightYRotation = mLightYRotation + gesture.displacement.x * LIGHT_PAN_Y_DISPLACEMENT_FACTOR; // Y displacement rotates around X axis
350             mLightYRotation = Clamp(mLightYRotation, -Dali::ANGLE_45, Dali::ANGLE_45 );
351             mLightAnchor.SetOrientation( CalculateWorldRotation( mLightXRotation, mLightYRotation ) );
352             break;
353           }
354
355           case PAN_SCENE:
356           {
357             mTranslation += Vector3(gesture.displacement.x, gesture.displacement.y, 0.f);
358             mContents.SetPosition(mTranslation);
359             break;
360           }
361
362           case ROTATE_SCENE:
363           {
364             mSceneXRotation = mSceneXRotation - gesture.displacement.y / X_ROTATION_DISPLACEMENT_FACTOR; // X displacement rotates around Y axis
365             mSceneXRotation = Clamp( mSceneXRotation, -Dali::ANGLE_90, Dali::ANGLE_90 );
366             mSceneYRotation = mSceneYRotation + gesture.displacement.x / Y_ROTATION_DISPLACEMENT_FACTOR; // Y displacement rotates around X axis
367             mSceneYRotation = Clamp( mSceneYRotation, -Dali::ANGLE_90, Dali::ANGLE_90 );
368             mContents.SetOrientation( CalculateWorldRotation( mSceneXRotation, mSceneYRotation ) );
369             break;
370           }
371
372           case ROTATE_OBJECT:
373           {
374             mObjectXRotation = mObjectXRotation - gesture.displacement.y / X_ROTATION_DISPLACEMENT_FACTOR; // X displacement rotates around Y axis
375             mObjectYRotation = mObjectYRotation + gesture.displacement.x / Y_ROTATION_DISPLACEMENT_FACTOR; // Y displacement rotates around X axis
376             mSceneActor.SetOrientation( CalculateWorldRotation( mObjectXRotation, mObjectYRotation ) );
377             break;
378           }
379         }
380       }
381       break;
382
383       case Gesture::Finished:
384         // Start animation at last known speed
385         break;
386
387       default:
388         break;
389     }
390   }
391
392   void OnPinch(Actor actor, const PinchGesture& gesture)
393   {
394     if (gesture.state == Gesture::Started)
395     {
396       mScaleAtPinchStart = mContents.GetCurrentScale().x;
397     }
398     mPinchScale = Clamp(mScaleAtPinchStart * gesture.scale, MIN_PINCH_SCALE, MAX_PINCH_SCALE);
399
400     mContents.SetScale(mPinchScale, mPinchScale, mPinchScale);
401   }
402
403   void Terminate(Application& app)
404   {
405     if( mSceneActor )
406     {
407       Stage::GetCurrent().Remove(mSceneActor);
408     }
409     if( mView )
410     {
411       Stage::GetCurrent().Remove(mView);
412     }
413   }
414
415   void OnKeyEvent(const KeyEvent& event)
416   {
417     if(event.state == KeyEvent::Down)
418     {
419       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
420       {
421         mApp.Quit();
422       }
423     }
424   }
425
426   bool OnEffectButtonClicked( Toolkit::Button button )
427   {
428     switch(mPanState)
429     {
430       case PAN_LIGHT:
431         mPanState = ROTATE_OBJECT;
432         mTitleActor.SetProperty( TextLabel::Property::TEXT, std::string(APPLICATION_TITLE_ROTATE_OBJECT) );
433         break;
434       case ROTATE_OBJECT:
435         mPanState = ROTATE_SCENE;
436         mTitleActor.SetProperty( TextLabel::Property::TEXT, std::string(APPLICATION_TITLE_ROTATE_SCENE) );
437         break;
438       case ROTATE_SCENE:
439         mPanState = PAN_SCENE;
440         mTitleActor.SetProperty( TextLabel::Property::TEXT, std::string(APPLICATION_TITLE_PAN_SCENE) );
441         break;
442       case PAN_SCENE:
443         mPanState = PAN_LIGHT;
444         mTitleActor.SetProperty( TextLabel::Property::TEXT, std::string(APPLICATION_TITLE_PAN_LIGHT) );
445         break;
446       default:
447         break;
448     }
449
450     return true;
451   }
452
453   bool OnResetPressed( Toolkit::Button button )
454   {
455     // Reset translation
456     mTranslation = Vector3::ZERO;
457     mContents.SetPosition(mTranslation);
458
459     // Align scene so that light anchor orientation is Z Axis
460     mSceneXRotation = -mLightXRotation;
461     mSceneYRotation = -mLightYRotation;
462     mContents.SetOrientation( CalculateWorldRotation( mSceneXRotation, mSceneYRotation ) );
463
464     return true;
465   }
466
467 private:
468   Application&              mApp;
469   Toolkit::Control          mView;
470   Layer                     mContents;
471   Actor                     mSceneActor;
472   Animation                 mAnimation;
473   Animation                 mSceneAnimation;
474   bool                      mPaused;
475   Toolkit::ShadowView       mShadowView;
476   ImageView                 mShadowPlaneBg;
477   ImageView                 mShadowPlane;
478   Actor                     mCastingLight;
479   Actor                     mLightAnchor;
480   ImageView                 mImageActor1;
481   ImageView                 mImageActor2;
482   ImageView                 mImageActor3;
483   PanGestureDetector        mPanGestureDetector;
484   PinchGestureDetector      mPinchGestureDetector;
485   TapGestureDetector        mTapGestureDetector;
486   Vector3                   mTranslation;
487   Radian                    mSceneXRotation;
488   Radian                    mSceneYRotation;
489   Radian                    mLightXRotation;
490   Radian                    mLightYRotation;
491   Radian                    mObjectXRotation;
492   Radian                    mObjectYRotation;
493   float                     mPinchScale;
494   float                     mScaleAtPinchStart;
495
496   Property::Index           mAngle1Index;
497   Property::Index           mAngle3Index;
498
499   Toolkit::TextLabel         mTitleActor;
500
501   enum PanState
502   {
503     PAN_SCENE,
504     ROTATE_SCENE,
505     PAN_LIGHT,
506     ROTATE_OBJECT
507   };
508
509   PanState                  mPanState;
510 };
511
512 /*****************************************************************************/
513
514 int DALI_EXPORT_API main(int argc, char **argv)
515 {
516   Application app = Application::New(&argc, &argv, DEMO_THEME_PATH);
517   TestApp theApp(app);
518   app.MainLoop();
519   return 0;
520 }