33408388fec2222ce938e449264e40d577497d01
[platform/core/uifw/dali-demo.git] / examples / mesh-visual / mesh-visual-example.cpp
1 #include <dali-toolkit/dali-toolkit.h>
2 #include <dali/public-api/object/property-map.h>
3
4 using namespace Dali;
5 using namespace Dali::Toolkit;
6
7 namespace
8 {
9   //Keeps information about each model for access.
10   struct Model
11   {
12     Control control; // Control housing the mesh visual of the model.
13     Vector2 rotation; // Keeps track of rotation about x and y axis for manual rotation.
14     Animation rotationAnimation; // Automatically rotates when left alone.
15   };
16
17   //Files for meshes
18   const char * const MODEL_FILE_TABLE[] =
19   {
20       DEMO_MODEL_DIR "Dino.obj",
21       DEMO_MODEL_DIR "ToyRobot-Metal.obj",
22       DEMO_MODEL_DIR "Toyrobot-Plastic.obj"
23   };
24
25   const char * const MATERIAL_FILE_TABLE[] =
26   {
27       DEMO_MODEL_DIR "Dino.mtl",
28       DEMO_MODEL_DIR "ToyRobot-Metal.mtl",
29       DEMO_MODEL_DIR "Toyrobot-Plastic.mtl"
30   };
31
32   const char * const TEXTURES_PATH( DEMO_IMAGE_DIR "" );
33
34   //Possible shading modes.
35   MeshVisual::ShadingMode::Value SHADING_MODE_TABLE[] =
36   {
37     MeshVisual::ShadingMode::TEXTURED_WITH_DETAILED_SPECULAR_LIGHTING,
38     MeshVisual::ShadingMode::TEXTURED_WITH_SPECULAR_LIGHTING,
39     MeshVisual::ShadingMode::TEXTURELESS_WITH_DIFFUSE_LIGHTING
40   };
41
42   const float X_ROTATION_DISPLACEMENT_FACTOR = 60.0f;
43   const float Y_ROTATION_DISPLACEMENT_FACTOR = 60.0f;
44   const float MODEL_SCALE =                    0.75f;
45   const float LIGHT_SCALE =                    0.15f;
46   const float BUTTONS_OFFSET_BOTTOM =          0.9f;
47   const int   NUM_MESHES =                     2;
48
49   //Used to identify actors.
50   const int MODEL_TAG = 0;
51   const int LIGHT_TAG = 1;
52   const int LAYER_TAG = 2;
53
54 } //End namespace
55
56 class MeshVisualController : public ConnectionTracker
57 {
58 public:
59
60   MeshVisualController( Application& application )
61   : mApplication( application ),   //Store handle to the application.
62     mModelIndex( 1 ),              //Start with metal robot.
63     mShadingModeIndex( 0 ),        //Start with textured with detailed specular lighting.
64     mTag( -1 ),                    //Non-valid default, which will get set to a correct value when used.
65     mSelectedModelIndex( -1 ),     //Non-valid default, which will get set to a correct value when used.
66     mPaused( false ),              //Animations play by default.
67     mLightFixed( true )            //The light is fixed by default.
68   {
69     // Connect to the Application's Init signal
70     mApplication.InitSignal().Connect( this, &MeshVisualController::Create );
71   }
72
73   ~MeshVisualController()
74   {
75   }
76
77   // The Init signal is received once (only) during the Application lifetime
78   void Create( Application& application )
79   {
80     // Get a handle to the stage
81     Stage stage = Stage::GetCurrent();
82     stage.SetBackgroundColor( Vector4( 0.0, 0.5, 1.0, 1.0 ) );
83
84     //Set up layer to place objects on.
85     Layer baseLayer = Layer::New();
86     baseLayer.SetParentOrigin( ParentOrigin::CENTER );
87     baseLayer.SetAnchorPoint( AnchorPoint::CENTER );
88     baseLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
89     baseLayer.SetBehavior( Layer::LAYER_2D ); //We use a 2D layer as this is closer to UI work than full 3D scene creation.
90     baseLayer.SetDepthTestDisabled( false ); //Enable depth testing, as otherwise the 2D layer would not do so.
91     baseLayer.RegisterProperty( "Tag", LAYER_TAG ); //Used to differentiate between different kinds of actor.
92     baseLayer.TouchedSignal().Connect( this, &MeshVisualController::OnTouch );
93     stage.Add( baseLayer );
94
95     //Place models on the scene.
96     SetupModels( baseLayer );
97
98     //Place buttons on the scene.
99     SetupButtons( baseLayer );
100
101     //Add a light to the scene.
102     SetupLight( baseLayer );
103
104     //Allow for exiting of the application via key presses.
105     stage.KeyEventSignal().Connect( this, &MeshVisualController::OnKeyEvent );
106   }
107
108   //Loads and adds the models to the scene, inside containers for hit detection.
109   void SetupModels( Layer layer )
110   {
111     //Add containers to house each renderer-holding-actor.
112     for( int i = 0; i < NUM_MESHES; i++ )
113     {
114       mContainers[i] = Actor::New();
115       mContainers[i].SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
116       mContainers[i].RegisterProperty( "Tag", MODEL_TAG ); //Used to differentiate between different kinds of actor.
117       mContainers[i].RegisterProperty( "Model", Property::Value( i ) ); //Used to index into the model.
118       mContainers[i].TouchedSignal().Connect( this, &MeshVisualController::OnTouch );
119       layer.Add( mContainers[i] );
120     }
121
122     //Position each container individually on screen
123
124     //Main, central model
125     mContainers[0].SetSizeModeFactor( Vector3( MODEL_SCALE, MODEL_SCALE, 0.0f ) );
126     mContainers[0].SetParentOrigin( ParentOrigin::CENTER );
127     mContainers[0].SetAnchorPoint( AnchorPoint::CENTER );
128
129     //Top left model
130     mContainers[1].SetSizeModeFactor( Vector3( MODEL_SCALE / 3.0f, MODEL_SCALE / 3.0f, 0.0f ) );
131     mContainers[1].SetParentOrigin( Vector3( 0.05, 0.03, 0.5 ) ); //Offset from top left
132     mContainers[1].SetAnchorPoint( AnchorPoint::TOP_LEFT );
133
134     //Set up models
135     for( int i = 0; i < NUM_MESHES; i++ )
136     {
137       //Create control to display model
138       Control control = Control::New();
139       control.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
140       control.SetParentOrigin( ParentOrigin::CENTER );
141       control.SetAnchorPoint( AnchorPoint::CENTER );
142       mContainers[i].Add( control );
143
144       //Make model spin to demonstrate 3D
145       Animation rotationAnimation = Animation::New( 15.0f );
146       float spin = i % 2 == 0 ? 1.0f : -1.0f; //Make actors spin in different directions to better show independence.
147       rotationAnimation.AnimateBy( Property( control, Actor::Property::ORIENTATION ),
148                                    Quaternion( Degree( 0.0f ), Degree( spin * 360.0f ), Degree( 0.0f ) ) );
149       rotationAnimation.SetLooping( true );
150       rotationAnimation.Play();
151
152       //Store model information in corresponding structs.
153       mModels[i].control = control;
154       mModels[i].rotation.x = 0.0f;
155       mModels[i].rotation.y = 0.0f;
156       mModels[i].rotationAnimation = rotationAnimation;
157     }
158
159     //Calling this sets the model in the controls.
160     ReloadModel();
161   }
162
163   //Place the various buttons on the bottom of the screen, with title labels where necessary.
164   void SetupButtons( Layer layer )
165   {
166     //Text label title for changing model or shader.
167     TextLabel changeTitleLabel = TextLabel::New( "Switch" );
168     changeTitleLabel.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
169     changeTitleLabel.SetProperty( TextLabel::Property::UNDERLINE, "{\"thickness\":\"2.0\"}" );
170     changeTitleLabel.SetParentOrigin( Vector3( 0.2, BUTTONS_OFFSET_BOTTOM, 0.5 ) );
171     changeTitleLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
172     layer.Add( changeTitleLabel );
173
174     //Create button for model changing
175     PushButton modelButton = Toolkit::PushButton::New();
176     modelButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
177     modelButton.ClickedSignal().Connect( this, &MeshVisualController::OnChangeModelClicked );
178     modelButton.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
179     modelButton.SetAnchorPoint( AnchorPoint::TOP_RIGHT );
180     modelButton.SetLabelText( "Model" );
181     changeTitleLabel.Add( modelButton );
182
183     //Create button for shader changing
184     PushButton shaderButton = Toolkit::PushButton::New();
185     shaderButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
186     shaderButton.ClickedSignal().Connect( this, &MeshVisualController::OnChangeShaderClicked );
187     shaderButton.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
188     shaderButton.SetAnchorPoint( AnchorPoint::TOP_LEFT );
189     shaderButton.SetLabelText( "Shader" );
190     changeTitleLabel.Add( shaderButton );
191
192     //Create button for pausing animations
193     PushButton pauseButton = Toolkit::PushButton::New();
194     pauseButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
195     pauseButton.ClickedSignal().Connect( this, &MeshVisualController::OnPauseClicked );
196     pauseButton.SetParentOrigin( Vector3( 0.5, BUTTONS_OFFSET_BOTTOM, 0.5 ) );
197     pauseButton.SetAnchorPoint( AnchorPoint::TOP_CENTER );
198     pauseButton.SetLabelText( " || " );
199     layer.Add( pauseButton );
200
201     //Text label title for light position mode.
202     TextLabel lightTitleLabel = TextLabel::New( "Light Position" );
203     lightTitleLabel.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
204     lightTitleLabel.SetProperty( TextLabel::Property::UNDERLINE, "{\"thickness\":\"2.0\"}" );
205     lightTitleLabel.SetParentOrigin( Vector3( 0.8, BUTTONS_OFFSET_BOTTOM, 0.5 ) );
206     lightTitleLabel.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
207     layer.Add( lightTitleLabel );
208
209     //Create button for switching between manual and fixed light position.
210     PushButton lightButton = Toolkit::PushButton::New();
211     lightButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
212     lightButton.ClickedSignal().Connect( this, &MeshVisualController::OnChangeLightModeClicked );
213     lightButton.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
214     lightButton.SetAnchorPoint( AnchorPoint::TOP_CENTER );
215     lightButton.SetLabelText( "FIXED" );
216     lightTitleLabel.Add( lightButton );
217   }
218
219   //Add a point light source the the scene, on a layer above the first.
220   void SetupLight( Layer baseLayer )
221   {
222     //Create control to act as light source of scene.
223     mLightSource = Control::New();
224     mLightSource.RegisterProperty( "Tag", LIGHT_TAG );
225
226     //Set size of control based on screen dimensions.
227     Stage stage = Stage::GetCurrent();
228     if( stage.GetSize().width < stage.GetSize().height )
229     {
230       //Scale to width.
231       mLightSource.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
232       mLightSource.SetResizePolicy( ResizePolicy::DIMENSION_DEPENDENCY, Dimension::HEIGHT );
233       mLightSource.SetSizeModeFactor( Vector3( LIGHT_SCALE, 0.0f, 0.0f ) );
234     }
235     else
236     {
237       //Scale to height.
238       mLightSource.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::HEIGHT );
239       mLightSource.SetResizePolicy( ResizePolicy::DIMENSION_DEPENDENCY, Dimension::WIDTH );
240       mLightSource.SetSizeModeFactor( Vector3( 0.0f, LIGHT_SCALE, 0.0f ) );
241     }
242
243     //Set position relative to top left, as the light source property is also relative to the top left.
244     mLightSource.SetParentOrigin( ParentOrigin::TOP_LEFT );
245     mLightSource.SetAnchorPoint( AnchorPoint::CENTER );
246     mLightSource.SetPosition( Stage::GetCurrent().GetSize().x * 0.85f, Stage::GetCurrent().GetSize().y * 0.125 );
247
248     //Supply an image to represent the light.
249     Property::Map lightMap;
250     lightMap.Insert( Visual::Property::TYPE, Visual::IMAGE );
251     lightMap.Insert( ImageVisual::Property::URL, DEMO_IMAGE_DIR "light-icon.png" );
252     mLightSource.SetProperty( Control::Property::BACKGROUND, Property::Value( lightMap ) );
253
254     //Connect to touch signal for dragging.
255     mLightSource.TouchedSignal().Connect( this, &MeshVisualController::OnTouch );
256
257     //Place the light source on a layer above the base, so that it is rendered above everything else.
258     Layer upperLayer = Layer::New();
259     upperLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
260     upperLayer.SetParentOrigin( ParentOrigin::CENTER );
261     upperLayer.SetAnchorPoint( AnchorPoint::CENTER );
262
263     baseLayer.Add( upperLayer );
264     upperLayer.Add( mLightSource );
265
266     //Decide which light to use to begin with.
267     if( mLightFixed )
268     {
269       UseFixedLight();
270     }
271     else
272     {
273       UseManualLight();
274     }
275   }
276
277   //Updates the displayed models to account for parameter changes.
278   void ReloadModel()
279   {
280     //Create mesh property map
281     Property::Map map;
282     map.Insert( Visual::Property::TYPE,  Visual::MESH );
283     map.Insert( MeshVisual::Property::OBJECT_URL, MODEL_FILE_TABLE[mModelIndex] );
284     map.Insert( MeshVisual::Property::MATERIAL_URL, MATERIAL_FILE_TABLE[mModelIndex] );
285     map.Insert( MeshVisual::Property::TEXTURES_PATH, TEXTURES_PATH );
286     map.Insert( MeshVisual::Property::SHADING_MODE, SHADING_MODE_TABLE[mShadingModeIndex] );
287
288     //Set the two controls to use the mesh
289     for( int i = 0; i < NUM_MESHES; i++ )
290     {
291       mModels[i].control.SetProperty( Control::Property::BACKGROUND, Property::Value( map ) );
292     }
293   }
294
295   void UseFixedLight()
296   {
297     //Hide draggable source
298     mLightSource.SetVisible( false );
299
300     //Use stage dimensions to place light at center, offset outwards in z axis.
301     Stage stage = Stage::GetCurrent();
302     float width = stage.GetSize().width;
303     float height = stage.GetSize().height;
304     Vector3 lightPosition = Vector3( width / 2.0f, height / 2.0f, std::max( width, height ) * 5.0f );
305
306     //Set global light position
307     for( int i = 0; i < NUM_MESHES; ++i )
308     {
309       mModels[i].control.RegisterProperty( "lightPosition", lightPosition, Property::ANIMATABLE );
310     }
311   }
312
313   void UseManualLight()
314   {
315     //Show draggable source
316     mLightSource.SetVisible( true );
317
318     //Update to switch light position to that off the source.
319     UpdateLight();
320   }
321
322   //Updates the light position for each model to account for changes in the source on screen.
323   void UpdateLight()
324   {
325     //Set light position to the x and y of the light control, offset out of the screen.
326     Vector3 controlPosition = mLightSource.GetCurrentPosition();
327     Vector3 lightPosition = Vector3( controlPosition.x, controlPosition.y, Stage::GetCurrent().GetSize().x / 2.0f );
328
329     for( int i = 0; i < NUM_MESHES; ++i )
330     {
331       mModels[i].control.RegisterProperty( "lightPosition", lightPosition, Property::ANIMATABLE );
332     }
333   }
334
335   //If the light source is touched, move it by dragging it.
336   //If a model is touched, rotate it by panning around.
337   bool OnTouch( Actor actor, const TouchEvent& event )
338   {
339     //Get primary touch point.
340     const Dali::TouchPoint& point = event.GetPoint( 0 );
341
342     switch( point.state )
343     {
344       case TouchPoint::Down:
345       {
346         //Determine what was touched.
347         actor.GetProperty( actor.GetPropertyIndex( "Tag" ) ).Get( mTag );
348
349         if( mTag == MODEL_TAG )
350         {
351           //Find out which model has been selected
352           actor.GetProperty( actor.GetPropertyIndex( "Model" ) ).Get( mSelectedModelIndex );
353
354           //Pause current animation, as the touch gesture will be used to manually rotate the model
355           mModels[mSelectedModelIndex].rotationAnimation.Pause();
356
357           //Store start points.
358           mPanStart = point.screen;
359           mRotationStart = mModels[mSelectedModelIndex].rotation;
360         }
361
362         break;
363       }
364       case TouchPoint::Motion:
365       {
366         //Switch on the kind of actor we're interacting with.
367         switch( mTag )
368         {
369           case MODEL_TAG: //Rotate model
370           {
371             //Calculate displacement and corresponding rotation.
372             Vector2 displacement = point.screen - mPanStart;
373             mModels[mSelectedModelIndex].rotation = Vector2( mRotationStart.x - displacement.y / Y_ROTATION_DISPLACEMENT_FACTOR,   // Y displacement rotates around X axis
374                                                              mRotationStart.y + displacement.x / X_ROTATION_DISPLACEMENT_FACTOR ); // X displacement rotates around Y axis
375             Quaternion rotation = Quaternion( Radian( mModels[mSelectedModelIndex].rotation.x ), Vector3::XAXIS) *
376                                   Quaternion( Radian( mModels[mSelectedModelIndex].rotation.y ), Vector3::YAXIS);
377
378             //Apply rotation.
379             mModels[mSelectedModelIndex].control.SetOrientation( rotation );
380
381             break;
382           }
383           case LIGHT_TAG: //Drag light
384           {
385             //Set light source to new position and update the models accordingly.
386             mLightSource.SetPosition( Vector3( point.screen ) );
387             UpdateLight();
388
389             break;
390           }
391         }
392
393         break;
394       }
395       case TouchPoint::Interrupted: //Same as finished.
396       case TouchPoint::Finished:
397       {
398         if( mTag == MODEL_TAG )
399         {
400           //Return to automatic animation
401           if( !mPaused )
402           {
403             mModels[mSelectedModelIndex].rotationAnimation.Play();
404           }
405         }
406
407         break;
408       }
409       default:
410       {
411         //Other touch states do nothing.
412         break;
413       }
414     }
415
416     return true;
417   }
418
419   //Cycle through the list of models.
420   bool OnChangeModelClicked( Toolkit::Button button )
421   {
422     ++mModelIndex %= 3;
423
424     ReloadModel();
425
426     return true;
427   }
428
429   //Cycle through the list of shaders.
430   bool OnChangeShaderClicked( Toolkit::Button button )
431   {
432     ++mShadingModeIndex %= 3;
433
434     ReloadModel();
435
436     return true;
437   }
438
439   //Pause all animations, and keep them paused even after user panning.
440   //This button is a toggle, so pressing again will start the animations again.
441   bool OnPauseClicked( Toolkit::Button button )
442   {
443     //Toggle pause state.
444     mPaused = !mPaused;
445
446     //If we wish to pause animations, do so and keep them paused.
447     if( mPaused )
448     {
449       for( int i = 0; i < NUM_MESHES ; ++i )
450       {
451         mModels[i].rotationAnimation.Pause();
452       }
453
454       button.SetLabelText( " > " );
455     }
456     else //Unpause all animations again.
457     {
458       for( int i = 0; i < NUM_MESHES ; ++i )
459       {
460         mModels[i].rotationAnimation.Play();
461       }
462
463       button.SetLabelText( " || " );
464     }
465
466     return true;
467   }
468
469
470   //Switch between a fixed light source in front of the screen, and a light source the user can drag around.
471   bool OnChangeLightModeClicked( Toolkit::Button button )
472   {
473     //Toggle state.
474     mLightFixed = !mLightFixed;
475
476     if( mLightFixed )
477     {
478       UseFixedLight();
479
480       button.SetLabelText( "FIXED" );
481     }
482     else
483     {
484       UseManualLight();
485
486       button.SetLabelText( "MANUAL" );
487     }
488
489     return true;
490   }
491
492   //If escape or the back button is pressed, quit the application (and return to the launcher)
493   void OnKeyEvent( const KeyEvent& event )
494   {
495     if( event.state == KeyEvent::Down )
496     {
497       if( IsKey( event, DALI_KEY_ESCAPE) || IsKey( event, DALI_KEY_BACK ) )
498       {
499         mApplication.Quit();
500       }
501     }
502   }
503
504 private:
505   Application&  mApplication;
506
507   //The models displayed on screen, including information about rotation.
508   Model mModels[NUM_MESHES];
509   Actor mContainers[NUM_MESHES];
510
511   //Acts as a global light source, which can be dragged around.
512   Control mLightSource;
513
514   //Used to detect panning to rotate the selected model.
515   Vector2 mPanStart;
516   Vector2 mRotationStart;
517
518   int mModelIndex; //Index of model to load.
519   int mShadingModeIndex; //Index of shader type to use.
520   int mTag; //Identifies what kind of actor has been selected in OnTouch.
521   int mSelectedModelIndex; //Index of model selected on screen.
522   bool mPaused; //If true, all animations are paused and should stay so.
523   bool mLightFixed; //If false, the light is in manual.
524 };
525
526 // Entry point for Linux & Tizen applications
527 //
528 int main( int argc, char **argv )
529 {
530   Application application = Application::New( &argc, &argv );
531   MeshVisualController test( application );
532   application.MainLoop();
533   return 0;
534 }