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