Changed to Apache 2.0 License
[platform/core/uifw/dali-demo.git] / examples / shadows / shadow-bone-lighting-example.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 // INTERNAL INCLUDES
19 #include "../shared/view.h"
20
21 #include <dali/dali.h>
22 #include <dali-toolkit/dali-toolkit.h>
23
24 using namespace Dali;
25 using namespace Dali::Toolkit;
26 using std::string;
27 using namespace DemoHelper;
28
29 namespace
30 {
31 const char* gModelFile = DALI_MODEL_DIR "AlbumCute.dae";
32 const char* gBinaryModelFile = DALI_MODEL_DIR "AlbumCute.dali-bin";
33
34 const char* BACKGROUND_IMAGE( DALI_IMAGE_DIR "background-default.png" );
35 const char* TOOLBAR_IMAGE( DALI_IMAGE_DIR "top-bar.png" );
36
37 const char* APPLICATION_TITLE_PAN_LIGHT( "Lighting: Pan Light" );
38 const char* APPLICATION_TITLE_PAN_OBJECT( "Lighting: Pan 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( DALI_IMAGE_DIR "icon-change.png" );
42 const char* RESET_ICON( DALI_IMAGE_DIR "icon-reset.png" );
43
44 const Quaternion JAUNTY_ROTATION(Math::PI/5.0f, Math::PI/5.0f, 0.0f); // Euler angles
45 const float MIN_PINCH_SCALE( 0.3f );
46 const float MAX_PINCH_SCALE( 2.05f );
47
48 const float R3_2(0.8660254);
49 const Vector3 TOP_POINT(  0.0f, -1.0f,  0.0f);
50 const Vector3 LEFT_POINT( -R3_2, 0.5f,  0.0f);
51 const Vector3 RIGHT_POINT( R3_2, 0.5f,  0.0f);
52 const Vector3 FRONT_POINT( 0.0f, 0.0f, 20.0f);
53
54 const Vector2 DEFAULT_STAGE_SIZE( 480.0f, 800.0f );
55
56 }
57
58 /**
59  * This example shows a fixed point light onto an animating model
60  * casting a shadow onto a wall. The whole scene can be rotated.
61  */
62
63 class TestApp : public ConnectionTracker
64 {
65 public:
66
67   /**
68    * Constructor
69    * @param application class, stored as reference
70    */
71   TestApp(Application &app)
72   : mApp(app),
73     mPaused(false),
74     mTranslation(Vector3::ZERO),
75     mLongitudinal(15.0f),
76     mAxisTilt(30.0f),
77     mLightLongitudinal(0.0f),
78     mLightAxisTilt(0.0f),
79     mObjectLongitudinal(0.0f),
80     mObjectAxisTilt(0.0f),
81     mPinchScale(0.5f),
82     mScaleAtPinchStart(0.5f),
83     mPanState(PAN_SCENE)
84   {
85     app.InitSignal().Connect(this, &TestApp::Create);
86     app.TerminateSignal().Connect(this, &TestApp::Terminate);
87   }
88
89   ~TestApp()
90   {
91     // Nothing to do here; All the members of this class get deleted automatically and they delete their children
92   }
93
94 public:
95   struct PositionInFrontOf
96   {
97     PositionInFrontOf()
98     {
99     }
100
101     Vector3 operator()( const Vector3& current, const PropertyInput& property )
102     {
103       Vector3 position = property.GetVector3();
104       position.z += 1.0f;
105       return position;
106     }
107   };
108
109   struct QuaternionEqualToConstraint
110   {
111     QuaternionEqualToConstraint()
112     {
113     }
114
115     Quaternion operator()( const Quaternion& current, const PropertyInput& property )
116     {
117       return property.GetQuaternion();
118     }
119   };
120
121
122   /**
123    * This method gets called once the main loop of application is up and running
124    */
125   void Create(Application& app)
126   {
127     srand(0); // Want repeatable path
128
129     Stage::GetCurrent().KeyEventSignal().Connect(this, &TestApp::OnKeyEvent);
130
131     mModel = Model::New(gBinaryModelFile);  // trigger model load
132     mModel.LoadingFinishedSignal().Connect(this, &TestApp::BinaryModelLoaded);
133
134     CreateToolbarAndView(app);
135     CreateShadowViewAndLights();
136   }
137
138   void CreateToolbarAndView(Application& app)
139   {
140     // Creates a default view with a default tool bar.
141     // The view is added to the stage.
142     Toolkit::ToolBar toolBar;
143     mContents = DemoHelper::CreateView( app,
144                                         mView,
145                                         toolBar,
146                                         BACKGROUND_IMAGE,
147                                         TOOLBAR_IMAGE,
148                                         "" );
149
150     // Add an effect-changing button on the right of the tool bar.
151     Image imageChangeEffect = Image::New( CHANGE_EFFECT_IMAGE );
152     Toolkit::PushButton effectChangeButton = Toolkit::PushButton::New();
153     effectChangeButton.SetBackgroundImage(imageChangeEffect);
154     effectChangeButton.ClickedSignal().Connect( this, &TestApp::OnEffectButtonClicked );
155     toolBar.AddControl( effectChangeButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalRight, DemoHelper::DEFAULT_MODE_SWITCH_PADDING );
156
157     // Add title to the tool bar.
158     mTitleActor = Toolkit::TextView::New();
159     toolBar.AddControl( mTitleActor, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarTitlePercentage, Toolkit::Alignment::HorizontalCenter );
160
161     // Set Title text
162     mTitleActor.SetText( APPLICATION_TITLE_PAN_SCENE );
163     mTitleActor.SetSize( Font::New().MeasureText( APPLICATION_TITLE_PAN_SCENE ) );
164     mTitleActor.SetStyleToCurrentText( DemoHelper::GetDefaultTextStyle() );
165
166     //Add a reset button
167     Image resetImage = Image::New( RESET_ICON );
168     Toolkit::PushButton resetButton = Toolkit::PushButton::New();
169     resetButton.SetBackgroundImage( resetImage );
170     resetButton.ClickedSignal().Connect( this, &TestApp::OnResetPressed );
171     toolBar.AddControl( resetButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalCenter, DemoHelper::DEFAULT_PLAY_PADDING );
172
173     // Setup
174     mView.SetPosition(Vector3(0.0f, 0.0f, -50));
175
176     mContents.SetPosition(mTranslation);
177     mContents.SetRotation(CalculateWorldRotation(Radian(mLongitudinal), Radian(mAxisTilt)));
178     mContents.SetScale(mPinchScale, mPinchScale, mPinchScale);
179
180     mPanGestureDetector = PanGestureDetector::New();
181     mPanGestureDetector.Attach( mView );
182     mPanGestureDetector.DetectedSignal().Connect(this, &TestApp::OnPan);
183
184     mPinchGestureDetector = PinchGestureDetector::New();
185     mPinchGestureDetector.Attach( mView );
186     mPinchGestureDetector.DetectedSignal().Connect(this, &TestApp::OnPinch);
187
188     mTapGestureDetector = TapGestureDetector::New(1, 1);
189     mTapGestureDetector.Attach( mView );
190     mTapGestureDetector.DetectedSignal().Connect(this, &TestApp::OnTap);
191   }
192
193   /**
194    * This method gets called once the model is loaded by the resource manager
195    */
196   void BinaryModelLoaded(Model model)
197   {
198     if( model.GetLoadingState() == ResourceLoadingSucceeded )
199     {
200       std::cout << "Succeeded loading binary model" << std::endl;
201
202       ModelReady();
203     }
204     else
205     {
206       std::cout << "Failed loading binary model" << std::endl;
207
208       mModel = Model::New(gModelFile);
209       mModel.LoadingFinishedSignal().Connect(this, &TestApp::ModelLoaded);
210     }
211   }
212
213   void ModelLoaded(Model model)
214   {
215     if( model.GetLoadingState() == ResourceLoadingSucceeded )
216     {
217       std::cout << "Succeeded loading collada model" << std::endl;
218
219       model.Save(gBinaryModelFile);
220       ModelReady();
221     }
222     else
223     {
224       std::cout << "Failed loading collada model" << std::endl;
225
226       mApp.Quit();
227     }
228   }
229
230   void CreateShadowViewAndLights()
231   {
232     mShadowView = Toolkit::ShadowView::New();
233     mShadowView.SetName("Container");
234     mShadowView.SetParentOrigin(ParentOrigin::CENTER);
235     mShadowView.SetAnchorPoint(AnchorPoint::CENTER);
236     mShadowView.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) );
237     mShadowView.SetPointLightFieldOfView( Math::PI / 2.0f);
238     mContents.Add(mShadowView);
239
240     Image brickWall = Image::New(DALI_IMAGE_DIR "brick-wall.jpg");
241     mShadowPlaneBg = ImageActor::New(brickWall);
242     mShadowPlaneBg.SetParentOrigin(ParentOrigin::CENTER);
243     mShadowPlaneBg.SetAnchorPoint(AnchorPoint::CENTER);
244     mShadowPlaneBg.SetName("Plane");
245     mShadowPlaneBg.SetSize(1000.0f, 1000.0f);
246     mContents.Add(mShadowPlaneBg);
247     mShadowPlaneBg.SetPosition(Vector3(50.0f, 50.0f, -200.0f));
248
249     mShadowView.SetShadowPlane(mShadowPlaneBg);
250     mShadowView.Activate();
251
252     mLightAnchor = Actor::New();
253     mLightAnchor.SetParentOrigin(ParentOrigin::CENTER);
254     mLightAnchor.SetAnchorPoint(AnchorPoint::CENTER);
255     mLightAnchor.SetRotation(CalculateWorldRotation(Radian(mLightLongitudinal), Radian(mLightAxisTilt)));
256
257     // Work out a scaling factor as the initial light position was calculated for desktop
258     // Need to scale light position as model size is based on stage size (i.e. much bigger on device)
259     Vector2 stageSize( Stage::GetCurrent().GetSize() );
260     float scaleFactor = stageSize.x / DEFAULT_STAGE_SIZE.x;
261
262     mCastingLight = Actor::New();
263     mCastingLight.SetParentOrigin(ParentOrigin::CENTER);
264     mCastingLight.SetAnchorPoint(AnchorPoint::CENTER);
265     mCastingLight.SetPosition( Vector3( 0.0f, 0.0f, 800.0f ) * scaleFactor );
266
267     TextActor text = TextActor::New("Light", Font::New(FontParameters("Times New Roman", "Book", PointSize(20.0f))));
268     text.SetColor(Color::BLUE);
269
270     mCastingLight.Add(text);
271     mLightAnchor.Add(mCastingLight);
272     mShadowPlaneBg.Add(mLightAnchor);
273
274     text.SetParentOrigin(ParentOrigin::CENTER);
275     mShadowView.SetPointLight(mCastingLight);
276   }
277
278   void ModelReady()
279   {
280     mModelActor = ModelActorFactory::BuildActorTree(mModel, "");  // Gets root actor
281
282     if (mModelActor)
283     {
284       Vector2 stageSize(Stage::GetCurrent().GetSize());
285
286       mModelActor.SetSize(250.0f, 250.0f);
287       mModelActor.SetPosition(0.0f, 0.0f, 130.0f);
288
289       //Create a Key light
290       Light keylight = Light::New("KeyLight");
291       keylight.SetFallOff(Vector2(10000.0f, 10000.0f));
292       //keylight.SetSpecularColor(Vector3::ZERO);
293       mKeyLightActor = LightActor::New();
294       mKeyLightActor.SetParentOrigin(ParentOrigin::CENTER);
295       mKeyLightActor.SetName(keylight.GetName());
296
297       //Add all the actors to the stage
298       mCastingLight.Add(mKeyLightActor);
299       mKeyLightActor.SetLight(keylight);
300
301       mShadowView.Add(mModelActor);
302
303
304       if (mModel.NumberOfAnimations())
305       {
306         mModelAnimation = ModelActorFactory::BuildAnimation(mModel, mModelActor, 0);
307         mModelAnimation.SetDuration(4.0f);
308         mModelAnimation.SetLooping(true);
309         mModelAnimation.Play();
310       }
311
312       //StartAnimation();
313     }
314   }
315
316   Quaternion CalculateWorldRotation(Radian longitude, Radian axisTilt )
317   {
318     Quaternion q(longitude, Vector3::YAXIS);
319     Quaternion p(axisTilt, Vector3::XAXIS);
320     return p*q;
321   }
322
323   void OnTap(Dali::Actor actor, TapGesture gesture)
324   {
325     if( ! mPaused )
326     {
327       //mAnimation.Pause();
328       mModelAnimation.Pause();
329       mPaused = true;
330     }
331     else
332     {
333       //mAnimation.Play();
334       mModelAnimation.Play();
335       mPaused = false;
336     }
337   }
338
339   void OnPan(Actor actor, PanGesture gesture)
340   {
341     switch (gesture.state)
342     {
343       case Gesture::Continuing:
344       {
345         switch(mPanState)
346         {
347           case PAN_LIGHT:
348           {
349             mLightLongitudinal += gesture.displacement.x/4.0f;
350             mLightAxisTilt -= gesture.displacement.y/6.0f;
351             mLightAxisTilt = Clamp<float>(mLightAxisTilt, -90.0f, 90.0f);
352             mLightAnchor.SetRotation(CalculateWorldRotation(Radian(mLightLongitudinal), Radian(mLightAxisTilt)));
353             break;
354           }
355
356           case PAN_SCENE:
357           {
358             mTranslation += Vector3(gesture.displacement.x, gesture.displacement.y, 0.f);
359             mContents.SetPosition(mTranslation);
360             break;
361           }
362
363           case ROTATE_SCENE:
364           {
365             mLongitudinal += gesture.displacement.x/4.0f;
366             mAxisTilt -= gesture.displacement.y/6.0f;
367             mAxisTilt = Clamp<float>(mAxisTilt, -90.0f, 90.0f);
368             mContents.SetRotation(CalculateWorldRotation(Radian(mLongitudinal), Radian(mAxisTilt)));
369             break;
370           }
371
372           case PAN_OBJECT:
373           {
374             mObjectLongitudinal += gesture.displacement.x/4.0f;
375             mObjectAxisTilt -= gesture.displacement.y/6.0f;
376             mObjectAxisTilt = Clamp<float>(mObjectAxisTilt, -90.0f, 90.0f);
377             mModelActor.SetRotation(CalculateWorldRotation(Radian(mObjectLongitudinal), Radian(mObjectAxisTilt)));
378             break;
379           }
380         }
381       }
382       break;
383
384       case Gesture::Finished:
385         // Start animation at last known speed
386         break;
387
388       default:
389         break;
390     }
391   }
392
393   void OnPinch(Actor actor, PinchGesture gesture)
394   {
395     if (gesture.state == Gesture::Started)
396     {
397       mScaleAtPinchStart = mContents.GetCurrentScale().x;
398     }
399     mPinchScale = Clamp(mScaleAtPinchStart * gesture.scale, MIN_PINCH_SCALE, MAX_PINCH_SCALE);
400
401     mContents.SetScale(mPinchScale, mPinchScale, mPinchScale);
402   }
403
404   void Terminate(Application& app)
405   {
406     if( mModelActor )
407     {
408       Stage::GetCurrent().Remove(mModelActor);
409     }
410     if( mKeyLightActor )
411     {
412       Stage::GetCurrent().Remove(mKeyLightActor);
413     }
414     if( mView )
415     {
416       Stage::GetCurrent().Remove(mView);
417     }
418   }
419
420   void OnKeyEvent(const KeyEvent& event)
421   {
422     if(event.state == KeyEvent::Down)
423     {
424       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
425       {
426         mApp.Quit();
427       }
428     }
429   }
430
431   bool OnEffectButtonClicked( Toolkit::Button button )
432   {
433     switch(mPanState)
434     {
435       case PAN_SCENE:
436         mPanState = ROTATE_SCENE;
437         mTitleActor.SetText( APPLICATION_TITLE_ROTATE_SCENE );
438         mTitleActor.SetSize( Font::New().MeasureText( APPLICATION_TITLE_ROTATE_SCENE ) );
439         break;
440       case ROTATE_SCENE:
441         mPanState = PAN_LIGHT;
442         mTitleActor.SetText( APPLICATION_TITLE_PAN_LIGHT );
443         mTitleActor.SetSize( Font::New().MeasureText( APPLICATION_TITLE_PAN_LIGHT ) );
444         break;
445       case PAN_LIGHT:
446         mPanState = PAN_OBJECT;
447         mTitleActor.SetText( APPLICATION_TITLE_PAN_OBJECT );
448         mTitleActor.SetSize( Font::New().MeasureText( APPLICATION_TITLE_PAN_OBJECT ) );
449         break;
450       case PAN_OBJECT:
451         mPanState = PAN_SCENE;
452         mTitleActor.SetText( APPLICATION_TITLE_PAN_SCENE );
453         mTitleActor.SetSize( Font::New().MeasureText( APPLICATION_TITLE_PAN_SCENE ) );
454         break;
455       default:
456         break;
457     }
458
459     mTitleActor.SetStyleToCurrentText(DemoHelper::GetDefaultTextStyle());
460
461     return true;
462   }
463
464   bool OnResetPressed( Toolkit::Button button )
465   {
466     // Reset translation
467     mTranslation = Vector3::ZERO;
468     mContents.SetPosition(mTranslation);
469     // Align scene so that light anchor orientation is Z Axis
470     mAxisTilt = -mLightAxisTilt;
471     mLongitudinal = -mLightLongitudinal;
472     mContents.SetRotation(CalculateWorldRotation(Radian(mLongitudinal), Radian(mAxisTilt)));
473
474     return true;
475   }
476
477 private:
478   Application&              mApp;
479   Toolkit::View             mView;
480   Layer                     mContents;
481   Model                     mModel;
482   Actor                     mModelActor;
483   LightActor                mKeyLightActor;
484   Animation                 mAnimation;
485   Animation                 mModelAnimation;
486   bool                      mPaused;
487   Toolkit::ShadowView       mShadowView;
488   ImageActor                mShadowPlaneBg;
489   ImageActor                mShadowPlane;
490   Actor                     mCastingLight;
491   Actor                     mLightAnchor;
492
493   PanGestureDetector        mPanGestureDetector;
494   PinchGestureDetector      mPinchGestureDetector;
495   TapGestureDetector        mTapGestureDetector;
496   Vector3                   mTranslation;
497   Degree                    mLongitudinal;
498   Degree                    mAxisTilt;
499   Degree                    mLightLongitudinal;
500   Degree                    mLightAxisTilt;
501   Degree                    mObjectLongitudinal;
502   Degree                    mObjectAxisTilt;
503   float                     mPinchScale;
504   float                     mScaleAtPinchStart;
505
506   Toolkit::TextView         mTitleActor;
507
508   enum PanState
509   {
510     PAN_SCENE,
511     ROTATE_SCENE,
512     PAN_LIGHT,
513     PAN_OBJECT
514   };
515
516   PanState                  mPanState;
517 };
518
519 /*****************************************************************************/
520
521 static void
522 RunTest(Application& app)
523 {
524   TestApp theApp(app);
525   app.MainLoop();
526 }
527
528 /*****************************************************************************/
529
530 int
531 main(int argc, char **argv)
532 {
533   Application app = Application::New(&argc, &argv);
534
535   RunTest(app);
536
537   return 0;
538 }