Added support for setting light position in mesh renderer. 69/79969/11
authorAndrew Poor <andrew.poor@samsung.com>
Thu, 7 Jul 2016 14:00:01 +0000 (15:00 +0100)
committerAndrew Poor <andrew.poor@samsung.com>
Fri, 22 Jul 2016 14:54:03 +0000 (07:54 -0700)
Change-Id: I1d0298c1e0cd30ac370bc27f45ffe0ade5d2d616

demo/dali-demo.cpp
examples/mesh-renderer/mesh-renderer-example.cpp

index aff5d5f..8f60cfb 100644 (file)
@@ -42,7 +42,6 @@ int DALI_EXPORT_API main(int argc, char **argv)
   demo.AddExample(Example("dissolve-effect.example", DALI_DEMO_STR_TITLE_DISSOLVE_TRANSITION));
   demo.AddExample(Example("item-view.example", DALI_DEMO_STR_TITLE_ITEM_VIEW));
   demo.AddExample(Example("magnifier.example", DALI_DEMO_STR_TITLE_MAGNIFIER));
-  demo.AddExample(Example("model3d-view.example", DALI_DEMO_STR_TITLE_MODEL_3D_VIEWER));
   demo.AddExample(Example("motion-blur.example", DALI_DEMO_STR_TITLE_MOTION_BLUR));
   demo.AddExample(Example("motion-stretch.example", DALI_DEMO_STR_TITLE_MOTION_STRETCH));
   demo.AddExample(Example("page-turn-view.example", DALI_DEMO_STR_TITLE_PAGE_TURN_VIEW));
index 2c322ce..b9286fa 100644 (file)
@@ -47,6 +47,11 @@ namespace
   const float MODEL_SCALE = 0.75f;
   const int NUM_MESHES = 3;
 
+  //Used to identify actors.
+  const int MODEL_TAG = 0;
+  const int LIGHT_TAG = 1;
+  const int LAYER_TAG = 2;
+
 } //End namespace
 
 class MeshRendererController : public ConnectionTracker
@@ -57,7 +62,9 @@ public:
   : mApplication( application ),   //Store handle to the application.
     mModelIndex( 1 ),              //Start with metal robot.
     mShaderIndex( 0 ),             //Start with all textures.
-    mSelectedModelIndex( 0 )       //Non-valid default, which will get set to a correct value when used.
+    mTag( -1 ),                    //Non-valid default, which will get set to a correct value when used.
+    mSelectedModelIndex( -1 ),     //Non-valid default, which will get set to a correct value when used.
+    mPaused( false )               //Animations play by default.
   {
     // Connect to the Application's Init signal
     mApplication.InitSignal().Connect( this, &MeshRendererController::Create );
@@ -90,25 +97,24 @@ public:
   {
     Stage stage = Stage::GetCurrent();
 
-    //Set up 3D layer to place objects on.
-    Layer layer = Layer::New();
-    layer.SetParentOrigin( ParentOrigin::CENTER );
-    layer.SetAnchorPoint( AnchorPoint::CENTER );
-    layer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
-    layer.SetBehavior( Layer::LAYER_2D ); //We use a 2D layer as this is closer to UI work than full 3D scene creation.
-    layer.SetDepthTestDisabled( false ); //Enable depth testing, as otherwise the 2D layer would not do so.
-    stage.Add( layer );
-
-    //Create gesture detector for panning of models.
-    mPanGestureDetector = PanGestureDetector::New();
-    mPanGestureDetector.DetectedSignal().Connect( this, &MeshRendererController::OnPan );
+    //Set up layer to place objects on.
+    Layer baseLayer = Layer::New();
+    baseLayer.SetParentOrigin( ParentOrigin::CENTER );
+    baseLayer.SetAnchorPoint( AnchorPoint::CENTER );
+    baseLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
+    baseLayer.SetBehavior( Layer::LAYER_2D ); //We use a 2D layer as this is closer to UI work than full 3D scene creation.
+    baseLayer.SetDepthTestDisabled( false ); //Enable depth testing, as otherwise the 2D layer would not do so.
+    baseLayer.RegisterProperty( "Tag", LAYER_TAG ); //Used to differentiate between different kinds of actor.
+    baseLayer.TouchedSignal().Connect( this, &MeshRendererController::OnTouch );
+    stage.Add( baseLayer );
 
     //Add containers to house each renderer-holding-actor.
     for( int i = 0; i < NUM_MESHES; i++ )
     {
       mContainers[i] = Actor::New();
       mContainers[i].SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
-      mContainers[i].RegisterProperty( "Tag", Property::Value( i ) ); //Used to identify the actor and index into the model.
+      mContainers[i].RegisterProperty( "Tag", MODEL_TAG ); //Used to differentiate between different kinds of actor.
+      mContainers[i].RegisterProperty( "Model", Property::Value( i ) ); //Used to index into the model.
 
       //Position each container on screen
       if( i == 0 )
@@ -133,8 +139,8 @@ public:
         mContainers[i].SetAnchorPoint( AnchorPoint::TOP_RIGHT );
       }
 
-      mPanGestureDetector.Attach( mContainers[i] );
-      layer.Add( mContainers[i] );
+      mContainers[i].TouchedSignal().Connect( this, &MeshRendererController::OnTouch );
+      baseLayer.Add( mContainers[i] );
     }
 
     //Set up models
@@ -169,19 +175,65 @@ public:
     Toolkit::PushButton modelButton = Toolkit::PushButton::New();
     modelButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
     modelButton.ClickedSignal().Connect( this, &MeshRendererController::OnChangeModelClicked );
-    modelButton.SetParentOrigin( Vector3( 0.1, 0.95, 0.5 ) ); //Offset from bottom left
+    modelButton.SetParentOrigin( Vector3( 0.05, 0.95, 0.5 ) ); //Offset from bottom left
     modelButton.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
     modelButton.SetLabelText( "Change Model" );
-    layer.Add( modelButton );
+    baseLayer.Add( modelButton );
 
     //Create button for shader changing
     Toolkit::PushButton shaderButton = Toolkit::PushButton::New();
     shaderButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
     shaderButton.ClickedSignal().Connect( this, &MeshRendererController::OnChangeShaderClicked );
-    shaderButton.SetParentOrigin( Vector3( 0.9, 0.95, 0.5 ) ); //Offset from bottom right
+    shaderButton.SetParentOrigin( Vector3( 0.95, 0.95, 0.5 ) ); //Offset from bottom right
     shaderButton.SetAnchorPoint( AnchorPoint::BOTTOM_RIGHT );
     shaderButton.SetLabelText( "Change Shader" );
-    layer.Add( shaderButton );
+    baseLayer.Add( shaderButton );
+
+    //Create button for pausing animations
+    Toolkit::PushButton pauseButton = Toolkit::PushButton::New();
+    pauseButton.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
+    pauseButton.ClickedSignal().Connect( this, &MeshRendererController::OnPauseClicked );
+    pauseButton.SetParentOrigin( Vector3( 0.5, 0.95, 0.5 ) ); //Offset from bottom center
+    pauseButton.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
+    pauseButton.SetLabelText( " || " );
+    baseLayer.Add( pauseButton );
+
+    //Create control to act as light source of scene.
+    mLightSource = Control::New();
+    mLightSource.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::WIDTH );
+    mLightSource.SetResizePolicy( ResizePolicy::DIMENSION_DEPENDENCY, Dimension::HEIGHT );
+    mLightSource.RegisterProperty( "Tag", LIGHT_TAG );
+
+    //Set position relative to top left, as the light source property is also relative to the top left.
+    mLightSource.SetParentOrigin( ParentOrigin::TOP_LEFT );
+    mLightSource.SetAnchorPoint( AnchorPoint::CENTER );
+    mLightSource.SetPosition( Stage::GetCurrent().GetSize().x * 0.5f, Stage::GetCurrent().GetSize().y * 0.1f );
+
+    //Make white background.
+    Property::Map lightMap;
+    lightMap.Insert( "rendererType", "COLOR" );
+    lightMap.Insert( "mixColor", Color::WHITE );
+    mLightSource.SetProperty( Control::Property::BACKGROUND, Property::Value( lightMap ) );
+
+    //Label to show what this actor is for the user.
+    TextLabel lightLabel = TextLabel::New( "Light" );
+    lightLabel.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
+    lightLabel.SetParentOrigin( ParentOrigin::CENTER );
+    lightLabel.SetAnchorPoint( AnchorPoint::CENTER );
+    float padding = 5.0f;
+    lightLabel.SetPadding( Padding( padding, padding, padding, padding ) );
+    mLightSource.Add( lightLabel );
+
+    //Connect to touch signal for dragging.
+    mLightSource.TouchedSignal().Connect( this, &MeshRendererController::OnTouch );
+
+    //Place the light source on a layer above the base, so that it is rendered above everything else.
+    Layer upperLayer = Layer::New();
+    baseLayer.Add( upperLayer );
+    upperLayer.Add( mLightSource );
+
+    //Calling this sets the light position of each model to that of the light source control.
+    UpdateLight();
   }
 
   //Updates the displayed models to account for parameter changes.
@@ -194,6 +246,7 @@ public:
     map.Insert( "materialUrl", MATERIAL_FILE[mModelIndex] );
     map.Insert( "texturesPath", TEXTURES_PATH );
     map.Insert( "shaderType", SHADER_TYPE[mShaderIndex] );
+    map.Insert( "useSoftNormals", false );
 
     //Set the two controls to use the mesh
     for( int i = 0; i < NUM_MESHES; i++ )
@@ -202,53 +255,101 @@ public:
     }
   }
 
-  //Rotates the panned model based on the gesture.
-  void OnPan( Actor actor, const PanGesture& gesture )
+  //Updates the light position for each model to account for changes in the source on screen.
+  void UpdateLight()
   {
-    switch( gesture.state )
+    //Set light position to the x and y of the light control, offset out of the screen.
+    Vector3 controlPosition = mLightSource.GetCurrentPosition();
+    Vector3 lightPosition = Vector3( controlPosition.x, controlPosition.y, Stage::GetCurrent().GetSize().x * 2.0f );
+
+    for( int i = 0; i < NUM_MESHES; ++i )
     {
-      case Gesture::Started:
-      {
-        //Find out which model has been selected
-        actor.GetProperty( actor.GetPropertyIndex( "Tag" ) ).Get( mSelectedModelIndex );
+      mModels[i].control.RegisterProperty( "lightPosition", lightPosition, Property::ANIMATABLE );
+    }
+  }
 
-        //Pause current animation, as the gesture will be used to manually rotate the model
-        mModels[mSelectedModelIndex].rotationAnimation.Pause();
+  //If the light source is touched, move it by dragging it.
+  //If a model is touched, rotate it by panning around.
+  bool OnTouch( Actor actor, const TouchEvent& event )
+  {
+    //Get primary touch point.
+    const Dali::TouchPoint& point = event.GetPoint( 0 );
 
-        break;
-      }
-      case Gesture::Continuing:
+    switch( point.state )
+    {
+      case TouchPoint::Down:
       {
-        //Rotate based off the gesture.
-        mModels[mSelectedModelIndex].rotation.x -= gesture.displacement.y / X_ROTATION_DISPLACEMENT_FACTOR; // Y displacement rotates around X axis
-        mModels[mSelectedModelIndex].rotation.y += gesture.displacement.x / Y_ROTATION_DISPLACEMENT_FACTOR; // X displacement rotates around Y axis
-        Quaternion rotation = Quaternion( Radian( mModels[mSelectedModelIndex].rotation.x ), Vector3::XAXIS) *
-                              Quaternion( Radian( mModels[mSelectedModelIndex].rotation.y ), Vector3::YAXIS);
+        //Determine what was touched.
+        actor.GetProperty( actor.GetPropertyIndex( "Tag" ) ).Get( mTag );
+
+        if( mTag == MODEL_TAG )
+        {
+          //Find out which model has been selected
+          actor.GetProperty( actor.GetPropertyIndex( "Model" ) ).Get( mSelectedModelIndex );
+
+          //Pause current animation, as the touch gesture will be used to manually rotate the model
+          mModels[mSelectedModelIndex].rotationAnimation.Pause();
 
-        mModels[mSelectedModelIndex].control.SetOrientation( rotation );
+          //Store start points.
+          mPanStart = point.screen;
+          mRotationStart = mModels[mSelectedModelIndex].rotation;
+        }
 
         break;
       }
-      case Gesture::Finished:
+      case TouchPoint::Motion:
       {
-        //Return to automatic animation
-        mModels[mSelectedModelIndex].rotationAnimation.Play();
+        //Switch on the kind of actor we're interacting with.
+        switch( mTag )
+        {
+          case MODEL_TAG: //Rotate model
+          {
+            //Calculate displacement and corresponding rotation.
+            Vector2 displacement = point.screen - mPanStart;
+            mModels[mSelectedModelIndex].rotation = Vector2( mRotationStart.x - displacement.y / Y_ROTATION_DISPLACEMENT_FACTOR,   // Y displacement rotates around X axis
+                                                             mRotationStart.y + displacement.x / X_ROTATION_DISPLACEMENT_FACTOR ); // X displacement rotates around Y axis
+            Quaternion rotation = Quaternion( Radian( mModels[mSelectedModelIndex].rotation.x ), Vector3::XAXIS) *
+                                  Quaternion( Radian( mModels[mSelectedModelIndex].rotation.y ), Vector3::YAXIS);
+
+            //Apply rotation.
+            mModels[mSelectedModelIndex].control.SetOrientation( rotation );
+
+            break;
+          }
+          case LIGHT_TAG: //Drag light
+          {
+            //Set light source to new position and update the models accordingly.
+            mLightSource.SetPosition( Vector3( point.screen ) );
+            UpdateLight();
+
+            break;
+          }
+        }
 
         break;
       }
-      case Gesture::Cancelled:
+      case TouchPoint::Interrupted: //Same as finished.
+      case TouchPoint::Finished:
       {
-        //Return to automatic animation
-        mModels[mSelectedModelIndex].rotationAnimation.Play();
+        if( mTag == MODEL_TAG )
+        {
+          //Return to automatic animation
+          if( !mPaused )
+          {
+            mModels[mSelectedModelIndex].rotationAnimation.Play();
+          }
+        }
 
         break;
       }
       default:
       {
-        //We can ignore other gestures and gesture states.
+        //Other touch states do nothing.
         break;
       }
     }
+
+    return true;
   }
 
   //Cycle through the list of models.
@@ -271,6 +372,36 @@ public:
     return true;
   }
 
+  //Pause all animations, and keep them paused even after user panning.
+  //This button is a toggle, so pressing again will start the animations again.
+  bool OnPauseClicked( Toolkit::Button button )
+  {
+    //Toggle pause state.
+    mPaused = !mPaused;
+
+    //If we wish to pause animations, do so and keep them paused.
+    if( mPaused )
+    {
+      for( int i = 0; i < NUM_MESHES ; ++i )
+      {
+        mModels[i].rotationAnimation.Pause();
+      }
+
+      button.SetLabelText( " > " );
+    }
+    else //Unpause all animations again.
+    {
+      for( int i = 0; i < NUM_MESHES ; ++i )
+      {
+        mModels[i].rotationAnimation.Play();
+      }
+
+      button.SetLabelText( " || " );
+    }
+
+    return true;
+  }
+
   //If escape or the back button is pressed, quit the application (and return to the launcher)
   void OnKeyEvent( const KeyEvent& event )
   {
@@ -290,12 +421,18 @@ private:
   Model mModels[NUM_MESHES];
   Actor mContainers[NUM_MESHES];
 
+  //Acts as a global light source, which can be dragged around.
+  Control mLightSource;
+
   //Used to detect panning to rotate the selected model.
-  PanGestureDetector mPanGestureDetector;
+  Vector2 mPanStart;
+  Vector2 mRotationStart;
 
   int mModelIndex; //Index of model to load.
   int mShaderIndex; //Index of shader type to use.
+  int mTag; //Identifies what kind of actor has been selected in OnTouch.
   int mSelectedModelIndex; //Index of model selected on screen.
+  bool mPaused; //If true, all animations are paused and should stay so.
 };
 
 void RunTest( Application& application )