b9286fa08e57c986ae48d7f8285bd60ce58d2764
[platform/core/uifw/dali-demo.git] / examples / mesh-renderer / mesh-renderer-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 renderer 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[] =
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[] =
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 shader options.
35   const char * const SHADER_TYPE[] =
36   {
37     "ALL_TEXTURES",
38     "DIFFUSE_TEXTURE",
39     "TEXTURELESS"
40   };
41
42   //Files for background and toolbar
43   const char * const BACKGROUND_IMAGE( DEMO_IMAGE_DIR "background-1.jpg");
44
45   const float X_ROTATION_DISPLACEMENT_FACTOR = 60.0f;
46   const float Y_ROTATION_DISPLACEMENT_FACTOR = 60.0f;
47   const float MODEL_SCALE = 0.75f;
48   const int NUM_MESHES = 3;
49
50   //Used to identify actors.
51   const int MODEL_TAG = 0;
52   const int LIGHT_TAG = 1;
53   const int LAYER_TAG = 2;
54
55 } //End namespace
56
57 class MeshRendererController : public ConnectionTracker
58 {
59 public:
60
61   MeshRendererController( Application& application )
62   : mApplication( application ),   //Store handle to the application.
63     mModelIndex( 1 ),              //Start with metal robot.
64     mShaderIndex( 0 ),             //Start with all textures.
65     mTag( -1 ),                    //Non-valid default, which will get set to a correct value when used.
66     mSelectedModelIndex( -1 ),     //Non-valid default, which will get set to a correct value when used.
67     mPaused( false )               //Animations play by default.
68   {
69     // Connect to the Application's Init signal
70     mApplication.InitSignal().Connect( this, &MeshRendererController::Create );
71   }
72
73   ~MeshRendererController()
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
83     //Add background
84     ImageView backView = ImageView::New( BACKGROUND_IMAGE );
85     backView.SetAnchorPoint( AnchorPoint::TOP_LEFT );
86     stage.Add( backView );
87
88     //Setup and load the 3D models and buttons
89     LoadScene();
90
91     //Allow for exiting of the application via key presses.
92     stage.KeyEventSignal().Connect( this, &MeshRendererController::OnKeyEvent );
93   }
94
95   //Sets up the on-screen elements.
96   void LoadScene()
97   {
98     Stage stage = Stage::GetCurrent();
99
100     //Set up layer to place objects on.
101     Layer baseLayer = Layer::New();
102     baseLayer.SetParentOrigin( ParentOrigin::CENTER );
103     baseLayer.SetAnchorPoint( AnchorPoint::CENTER );
104     baseLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
105     baseLayer.SetBehavior( Layer::LAYER_2D ); //We use a 2D layer as this is closer to UI work than full 3D scene creation.
106     baseLayer.SetDepthTestDisabled( false ); //Enable depth testing, as otherwise the 2D layer would not do so.
107     baseLayer.RegisterProperty( "Tag", LAYER_TAG ); //Used to differentiate between different kinds of actor.
108     baseLayer.TouchedSignal().Connect( this, &MeshRendererController::OnTouch );
109     stage.Add( baseLayer );
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
119       //Position each container on screen
120       if( i == 0 )
121       {
122         //Main, central model
123         mContainers[i].SetSizeModeFactor( Vector3( MODEL_SCALE, MODEL_SCALE, 0.0f ) );
124         mContainers[i].SetParentOrigin( ParentOrigin::CENTER );
125         mContainers[i].SetAnchorPoint( AnchorPoint::CENTER );
126       }
127       else if( i == 1 )
128       {
129         //Top left model
130         mContainers[i].SetSizeModeFactor( Vector3( MODEL_SCALE / 3.0f, MODEL_SCALE / 3.0f, 0.0f ) );
131         mContainers[i].SetParentOrigin( Vector3( 0.05, 0.03, 0.5 ) ); //Offset from top left
132         mContainers[i].SetAnchorPoint( AnchorPoint::TOP_LEFT );
133       }
134       else if( i == 2 )
135       {
136         //Top right model
137         mContainers[i].SetSizeModeFactor( Vector3( MODEL_SCALE / 3.0f, MODEL_SCALE / 3.0f, 0.0f ) );
138         mContainers[i].SetParentOrigin( Vector3( 0.95, 0.03, 0.5 ) ); //Offset from top right
139         mContainers[i].SetAnchorPoint( AnchorPoint::TOP_RIGHT );
140       }
141
142       mContainers[i].TouchedSignal().Connect( this, &MeshRendererController::OnTouch );
143       baseLayer.Add( mContainers[i] );
144     }
145
146     //Set up models
147     for( int i = 0; i < NUM_MESHES; i++ )
148     {
149       //Create control to display model
150       Control control = Control::New();
151       control.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
152       control.SetParentOrigin( ParentOrigin::CENTER );
153       control.SetAnchorPoint( AnchorPoint::CENTER );
154       mContainers[i].Add( control );
155
156       //Make model spin to demonstrate 3D
157       Animation rotationAnimation = Animation::New( 15.0f );
158       float spin = i % 2 == 0 ? 1.0f : -1.0f; //Make actors spin in different directions to better show independence.
159       rotationAnimation.AnimateBy( Property( control, Actor::Property::ORIENTATION ),
160                                    Quaternion( Degree( 0.0f ), Degree( spin * 360.0f ), Degree( 0.0f ) ) );
161       rotationAnimation.SetLooping( true );
162       rotationAnimation.Play();
163
164       //Store model information in corresponding structs.
165       mModels[i].control = control;
166       mModels[i].rotation.x = 0.0f;
167       mModels[i].rotation.y = 0.0f;
168       mModels[i].rotationAnimation = rotationAnimation;
169     }
170
171     //Calling this sets the model in the controls.
172     ReloadModel();
173
174     //Create button for model changing
175     Toolkit::PushButton modelButton = Toolkit::PushButton::New();
176     modelButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
177     modelButton.ClickedSignal().Connect( this, &MeshRendererController::OnChangeModelClicked );
178     modelButton.SetParentOrigin( Vector3( 0.05, 0.95, 0.5 ) ); //Offset from bottom left
179     modelButton.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
180     modelButton.SetLabelText( "Change Model" );
181     baseLayer.Add( modelButton );
182
183     //Create button for shader changing
184     Toolkit::PushButton shaderButton = Toolkit::PushButton::New();
185     shaderButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
186     shaderButton.ClickedSignal().Connect( this, &MeshRendererController::OnChangeShaderClicked );
187     shaderButton.SetParentOrigin( Vector3( 0.95, 0.95, 0.5 ) ); //Offset from bottom right
188     shaderButton.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
189     shaderButton.SetLabelText( "Change Shader" );
190     baseLayer.Add( shaderButton );
191
192     //Create button for pausing animations
193     Toolkit::PushButton pauseButton = Toolkit::PushButton::New();
194     pauseButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
195     pauseButton.ClickedSignal().Connect( this, &MeshRendererController::OnPauseClicked );
196     pauseButton.SetParentOrigin( Vector3( 0.5, 0.95, 0.5 ) ); //Offset from bottom center
197     pauseButton.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
198     pauseButton.SetLabelText( " || " );
199     baseLayer.Add( pauseButton );
200
201     //Create control to act as light source of scene.
202     mLightSource = Control::New();
203     mLightSource.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::WIDTH );
204     mLightSource.SetResizePolicy( ResizePolicy::DIMENSION_DEPENDENCY, Dimension::HEIGHT );
205     mLightSource.RegisterProperty( "Tag", LIGHT_TAG );
206
207     //Set position relative to top left, as the light source property is also relative to the top left.
208     mLightSource.SetParentOrigin( ParentOrigin::TOP_LEFT );
209     mLightSource.SetAnchorPoint( AnchorPoint::CENTER );
210     mLightSource.SetPosition( Stage::GetCurrent().GetSize().x * 0.5f, Stage::GetCurrent().GetSize().y * 0.1f );
211
212     //Make white background.
213     Property::Map lightMap;
214     lightMap.Insert( "rendererType", "COLOR" );
215     lightMap.Insert( "mixColor", Color::WHITE );
216     mLightSource.SetProperty( Control::Property::BACKGROUND, Property::Value( lightMap ) );
217
218     //Label to show what this actor is for the user.
219     TextLabel lightLabel = TextLabel::New( "Light" );
220     lightLabel.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
221     lightLabel.SetParentOrigin( ParentOrigin::CENTER );
222     lightLabel.SetAnchorPoint( AnchorPoint::CENTER );
223     float padding = 5.0f;
224     lightLabel.SetPadding( Padding( padding, padding, padding, padding ) );
225     mLightSource.Add( lightLabel );
226
227     //Connect to touch signal for dragging.
228     mLightSource.TouchedSignal().Connect( this, &MeshRendererController::OnTouch );
229
230     //Place the light source on a layer above the base, so that it is rendered above everything else.
231     Layer upperLayer = Layer::New();
232     baseLayer.Add( upperLayer );
233     upperLayer.Add( mLightSource );
234
235     //Calling this sets the light position of each model to that of the light source control.
236     UpdateLight();
237   }
238
239   //Updates the displayed models to account for parameter changes.
240   void ReloadModel()
241   {
242     //Create mesh property map
243     Property::Map map;
244     map.Insert( "rendererType", "MESH" );
245     map.Insert( "objectUrl", MODEL_FILE[mModelIndex] );
246     map.Insert( "materialUrl", MATERIAL_FILE[mModelIndex] );
247     map.Insert( "texturesPath", TEXTURES_PATH );
248     map.Insert( "shaderType", SHADER_TYPE[mShaderIndex] );
249     map.Insert( "useSoftNormals", false );
250
251     //Set the two controls to use the mesh
252     for( int i = 0; i < NUM_MESHES; i++ )
253     {
254       mModels[i].control.SetProperty( Control::Property::BACKGROUND, Property::Value( map ) );
255     }
256   }
257
258   //Updates the light position for each model to account for changes in the source on screen.
259   void UpdateLight()
260   {
261     //Set light position to the x and y of the light control, offset out of the screen.
262     Vector3 controlPosition = mLightSource.GetCurrentPosition();
263     Vector3 lightPosition = Vector3( controlPosition.x, controlPosition.y, Stage::GetCurrent().GetSize().x * 2.0f );
264
265     for( int i = 0; i < NUM_MESHES; ++i )
266     {
267       mModels[i].control.RegisterProperty( "lightPosition", lightPosition, Property::ANIMATABLE );
268     }
269   }
270
271   //If the light source is touched, move it by dragging it.
272   //If a model is touched, rotate it by panning around.
273   bool OnTouch( Actor actor, const TouchEvent& event )
274   {
275     //Get primary touch point.
276     const Dali::TouchPoint& point = event.GetPoint( 0 );
277
278     switch( point.state )
279     {
280       case TouchPoint::Down:
281       {
282         //Determine what was touched.
283         actor.GetProperty( actor.GetPropertyIndex( "Tag" ) ).Get( mTag );
284
285         if( mTag == MODEL_TAG )
286         {
287           //Find out which model has been selected
288           actor.GetProperty( actor.GetPropertyIndex( "Model" ) ).Get( mSelectedModelIndex );
289
290           //Pause current animation, as the touch gesture will be used to manually rotate the model
291           mModels[mSelectedModelIndex].rotationAnimation.Pause();
292
293           //Store start points.
294           mPanStart = point.screen;
295           mRotationStart = mModels[mSelectedModelIndex].rotation;
296         }
297
298         break;
299       }
300       case TouchPoint::Motion:
301       {
302         //Switch on the kind of actor we're interacting with.
303         switch( mTag )
304         {
305           case MODEL_TAG: //Rotate model
306           {
307             //Calculate displacement and corresponding rotation.
308             Vector2 displacement = point.screen - mPanStart;
309             mModels[mSelectedModelIndex].rotation = Vector2( mRotationStart.x - displacement.y / Y_ROTATION_DISPLACEMENT_FACTOR,   // Y displacement rotates around X axis
310                                                              mRotationStart.y + displacement.x / X_ROTATION_DISPLACEMENT_FACTOR ); // X displacement rotates around Y axis
311             Quaternion rotation = Quaternion( Radian( mModels[mSelectedModelIndex].rotation.x ), Vector3::XAXIS) *
312                                   Quaternion( Radian( mModels[mSelectedModelIndex].rotation.y ), Vector3::YAXIS);
313
314             //Apply rotation.
315             mModels[mSelectedModelIndex].control.SetOrientation( rotation );
316
317             break;
318           }
319           case LIGHT_TAG: //Drag light
320           {
321             //Set light source to new position and update the models accordingly.
322             mLightSource.SetPosition( Vector3( point.screen ) );
323             UpdateLight();
324
325             break;
326           }
327         }
328
329         break;
330       }
331       case TouchPoint::Interrupted: //Same as finished.
332       case TouchPoint::Finished:
333       {
334         if( mTag == MODEL_TAG )
335         {
336           //Return to automatic animation
337           if( !mPaused )
338           {
339             mModels[mSelectedModelIndex].rotationAnimation.Play();
340           }
341         }
342
343         break;
344       }
345       default:
346       {
347         //Other touch states do nothing.
348         break;
349       }
350     }
351
352     return true;
353   }
354
355   //Cycle through the list of models.
356   bool OnChangeModelClicked( Toolkit::Button button )
357   {
358     ++mModelIndex %= 3;
359
360     ReloadModel();
361
362     return true;
363   }
364
365   //Cycle through the list of shaders.
366   bool OnChangeShaderClicked( Toolkit::Button button )
367   {
368     ++mShaderIndex %= 3;
369
370     ReloadModel();
371
372     return true;
373   }
374
375   //Pause all animations, and keep them paused even after user panning.
376   //This button is a toggle, so pressing again will start the animations again.
377   bool OnPauseClicked( Toolkit::Button button )
378   {
379     //Toggle pause state.
380     mPaused = !mPaused;
381
382     //If we wish to pause animations, do so and keep them paused.
383     if( mPaused )
384     {
385       for( int i = 0; i < NUM_MESHES ; ++i )
386       {
387         mModels[i].rotationAnimation.Pause();
388       }
389
390       button.SetLabelText( " > " );
391     }
392     else //Unpause all animations again.
393     {
394       for( int i = 0; i < NUM_MESHES ; ++i )
395       {
396         mModels[i].rotationAnimation.Play();
397       }
398
399       button.SetLabelText( " || " );
400     }
401
402     return true;
403   }
404
405   //If escape or the back button is pressed, quit the application (and return to the launcher)
406   void OnKeyEvent( const KeyEvent& event )
407   {
408     if( event.state == KeyEvent::Down )
409     {
410       if( IsKey( event, DALI_KEY_ESCAPE) || IsKey( event, DALI_KEY_BACK ) )
411       {
412         mApplication.Quit();
413       }
414     }
415   }
416
417 private:
418   Application&  mApplication;
419
420   //The models displayed on screen, including information about rotation.
421   Model mModels[NUM_MESHES];
422   Actor mContainers[NUM_MESHES];
423
424   //Acts as a global light source, which can be dragged around.
425   Control mLightSource;
426
427   //Used to detect panning to rotate the selected model.
428   Vector2 mPanStart;
429   Vector2 mRotationStart;
430
431   int mModelIndex; //Index of model to load.
432   int mShaderIndex; //Index of shader type to use.
433   int mTag; //Identifies what kind of actor has been selected in OnTouch.
434   int mSelectedModelIndex; //Index of model selected on screen.
435   bool mPaused; //If true, all animations are paused and should stay so.
436 };
437
438 void RunTest( Application& application )
439 {
440   MeshRendererController test( application );
441
442   application.MainLoop();
443 }
444
445 // Entry point for Linux & Tizen applications
446 //
447 int main( int argc, char **argv )
448 {
449   Application application = Application::New( &argc, &argv );
450
451   RunTest( application );
452
453   return 0;
454 }