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