diff --git a/demo/dali-demo.cpp b/demo/dali-demo.cpp index aff5d5f..8f60cfb 100644 --- a/demo/dali-demo.cpp +++ b/demo/dali-demo.cpp @@ -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)); diff --git a/examples/mesh-renderer/mesh-renderer-example.cpp b/examples/mesh-renderer/mesh-renderer-example.cpp index 2c322ce..b9286fa 100644 --- a/examples/mesh-renderer/mesh-renderer-example.cpp +++ b/examples/mesh-renderer/mesh-renderer-example.cpp @@ -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 )