From 12fd4d7a32767503adf87fba1178e5ac0c30d896 Mon Sep 17 00:00:00 2001 From: Andrew Poor Date: Thu, 2 Jun 2016 15:00:29 +0100 Subject: [PATCH] Implemented a primitive shape renderer to display simple shapes like cubes. Change-Id: I2431290a111de586f6440f58d17ffb5473f400bf --- com.samsung.dali-demo.xml | 9 +- demo/dali-demo.cpp | 1 + .../primitive-shapes/primitive-shapes-example.cpp | 651 +++++++++++++++++++++ resources/images/bevelled-cube-button.png | Bin 0 -> 4074 bytes resources/images/cone-button.png | Bin 0 -> 3564 bytes resources/images/conical-frustrum-button.png | Bin 0 -> 3944 bytes resources/images/cube-button.png | Bin 0 -> 2690 bytes resources/images/cylinder-button.png | Bin 0 -> 3858 bytes resources/images/octahedron-button.png | Bin 0 -> 3320 bytes resources/images/sphere-button.png | Bin 0 -> 5265 bytes shared/dali-demo-strings.h | 1 + 11 files changed, 659 insertions(+), 3 deletions(-) create mode 100644 examples/primitive-shapes/primitive-shapes-example.cpp create mode 100644 resources/images/bevelled-cube-button.png create mode 100644 resources/images/cone-button.png create mode 100644 resources/images/conical-frustrum-button.png create mode 100644 resources/images/cube-button.png create mode 100644 resources/images/cylinder-button.png create mode 100644 resources/images/octahedron-button.png create mode 100644 resources/images/sphere-button.png diff --git a/com.samsung.dali-demo.xml b/com.samsung.dali-demo.xml index 001e2d3..d04bb09 100644 --- a/com.samsung.dali-demo.xml +++ b/com.samsung.dali-demo.xml @@ -160,7 +160,10 @@ - - - + + + + + + diff --git a/demo/dali-demo.cpp b/demo/dali-demo.cpp index 7c6d372..aff5d5f 100644 --- a/demo/dali-demo.cpp +++ b/demo/dali-demo.cpp @@ -79,6 +79,7 @@ int DALI_EXPORT_API main(int argc, char **argv) demo.AddExample(Example("effects-view.example", DALI_DEMO_STR_TITLE_EFFECTS_VIEW)); demo.AddExample(Example("native-image-source.example", DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE)); demo.AddExample(Example("mesh-renderer.example", DALI_DEMO_STR_TITLE_MESH_RENDERER)); + demo.AddExample(Example("primitive-shapes.example", DALI_DEMO_STR_TITLE_PRIMITIVE_SHAPES)); demo.SortAlphabetically( true ); diff --git a/examples/primitive-shapes/primitive-shapes-example.cpp b/examples/primitive-shapes/primitive-shapes-example.cpp new file mode 100644 index 0000000..7ac2870 --- /dev/null +++ b/examples/primitive-shapes/primitive-shapes-example.cpp @@ -0,0 +1,651 @@ +#include +#include +#include + +using namespace Dali; +using namespace Dali::Toolkit; + +namespace +{ + //Button image urls + const char* BUTTON_IMAGE_URL[] = + { + DEMO_IMAGE_DIR "sphere-button.png", + DEMO_IMAGE_DIR "cone-button.png", + DEMO_IMAGE_DIR "conical-frustrum-button.png", + DEMO_IMAGE_DIR "cylinder-button.png", + DEMO_IMAGE_DIR "cube-button.png", + DEMO_IMAGE_DIR "bevelled-cube-button.png", + DEMO_IMAGE_DIR "octahedron-button.png" + }; + + //Shape names + const char * const SHAPE_SPHERE = "SPHERE"; + const char * const SHAPE_CONE = "CONE"; + const char * const SHAPE_CONICAL_FRUSTRUM = "CONICAL_FRUSTRUM"; + const char * const SHAPE_CYLINDER = "CYLINDER"; + const char * const SHAPE_CUBE = "CUBE"; + const char * const SHAPE_BEVELLED_CUBE = "BEVELLED_CUBE"; + const char * const SHAPE_OCTAHEDRON = "OCTAHEDRON"; + + //Shape property defaults + const int DEFAULT_SLICES = 32; + const int DEFAULT_STACKS = 32; + const float DEFAULT_SCALE_HEIGHT = 16.0f; + const float DEFAULT_SCALE_BOTTOM_RADIUS = 8.0f; + const float DEFAULT_SCALE_TOP_RADIUS = 4.0f; + const float DEFAULT_SCALE_RADIUS = 8.0f; + const float DEFAULT_BEVEL_PERCENTAGE = 0.3f; + const float DEFAULT_BEVEL_SMOOTHNESS = 0.0f; + + //Shape property limits + const int SLICES_LOWER_BOUND = 3; + const int SLICES_UPPER_BOUND = 16; + const int STACKS_LOWER_BOUND = 2; + const int STACKS_UPPER_BOUND = 16; + + //Used to the control rate of rotation when panning an object. + const float X_ROTATION_DISPLACEMENT_FACTOR = 60.0f; + const float Y_ROTATION_DISPLACEMENT_FACTOR = 60.0f; + + const int NUM_MODELS = 7; //Total number of possible base shapes. + const int MAX_PROPERTIES = 3; //Maximum number of properties a shape may require. (For displaying sliders.) + +} //End namespace + +class PrimitiveShapesController : public ConnectionTracker +{ +public: + + PrimitiveShapesController( Application& application ) + : mApplication( application ), + mColor( Vector4( 0.3f, 0.7f, 1.0f, 1.0f ) ), + mRotation( Vector2::ZERO ) + { + // Connect to the Application's Init signal + mApplication.InitSignal().Connect( this, &PrimitiveShapesController::Create ); + } + + ~PrimitiveShapesController() + { + } + + // The Init signal is received once (only) during the Application lifetime + void Create( Application& application ) + { + // Get a handle to the stage + Stage stage = Stage::GetCurrent(); + stage.SetBackgroundColor( Color::WHITE ); + + //Set up layer to place UI 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 ); + + //Set up model selection buttons. + SetupButtons( layer ); + + //Set up model parameter sliders. + SetupSliders( layer ); + + //Set up 3D model. + SetupModel( layer ); + + //Allow for exiting of the application via key presses. + stage.KeyEventSignal().Connect( this, &PrimitiveShapesController::OnKeyEvent ); + } + + //Place buttons on the top of the screen, which allow for selection of the shape to be displayed. + //The buttons are laid out like so: + // + // ^ +--------------------------------+ + // | | | + // | | +----+ +----+ +----+ +----+ | + // | | | | | | | | | | | + // 30% | | +----+ +----+ +----+ +----+ | + // | | | + // | | +----+ +----+ +----+ | + // | | | | | | | | | + // v | +----+ +----+ +----+ | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // +--------------------------------+ + // + void SetupButtons( Layer layer ) + { + float containerPadding = 10.0f; + float buttonPadding = 5.0f; + + //Create a variable-length container that can wrap buttons around as more are added. + FlexContainer buttonContainer = FlexContainer::New(); + buttonContainer.SetParentOrigin( ParentOrigin::TOP_CENTER ); + buttonContainer.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + buttonContainer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH ); + buttonContainer.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::HEIGHT ); + buttonContainer.SetSizeModeFactor( Vector3( 0.0, 0.3, 0.0 ) ); //30% of height. + buttonContainer.SetPadding( Padding( containerPadding, containerPadding, containerPadding, containerPadding ) ); + buttonContainer.SetProperty( FlexContainer::Property::FLEX_DIRECTION, FlexContainer::ROW ); + buttonContainer.SetProperty( FlexContainer::Property::FLEX_WRAP, FlexContainer::WRAP ); + + layer.Add( buttonContainer ); + + //Create buttons and place them in the container. + for( int modelNumber = 0; modelNumber < NUM_MODELS; modelNumber++ ) + { + PushButton button = Toolkit::PushButton::New(); + button.SetParentOrigin( ParentOrigin::CENTER ); + button.SetAnchorPoint( AnchorPoint::CENTER ); + button.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS ); + button.SetPadding( Padding( buttonPadding, buttonPadding, buttonPadding, buttonPadding ) ); + button.SetProperty( Button::Property::UNSELECTED_STATE_IMAGE, Property::Value( BUTTON_IMAGE_URL[modelNumber] ) ); + button.SetProperty( Button::Property::SELECTED_STATE_IMAGE, Property::Value( BUTTON_IMAGE_URL[modelNumber] ) ); + button.RegisterProperty( "modelNumber", Property::Value( modelNumber ) ); + button.ClickedSignal().Connect( this, &PrimitiveShapesController::OnChangeShapeClicked ); + + buttonContainer.Add( button ); + } + } + + //Add sliders to the bottom of the screen, which allow for control of shape properties such as radius. + //Each slider is placed next to a label that states the property it affects. + //The sliders are laid out like so: + // + // +--------------------------------+ + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // ^ | Label +----------O-----------+ | + // | | | + // | | | + // | | Label +--O-------------------+ | + // 30% | | | + // | | | + // | | Label +----------------------O | + // | | | + // v +--------------------------------+ + // + void SetupSliders( Layer layer ) + { + //Create table to hold sliders and their corresponding labels. + mSliderTable = Toolkit::TableView::New( MAX_PROPERTIES, 2 ); + mSliderTable.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); + mSliderTable.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + mSliderTable.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS ); + mSliderTable.SetSizeModeFactor( Vector3( 0.9, 0.3, 0.0 ) ); //90% of width, 30% of height. + mSliderTable.SetFitWidth( 0 ); //Label column should fit to natural size of label. + mSliderTable.SetRelativeWidth( 1, 1.0f ); //Slider column should fill remaining space. + mSliderTable.SetCellPadding( Vector2( 10.0f, 0.0f ) ); //Leave a gap between the slider and its label. + layer.Add( mSliderTable ); + + //Set up sliders, and place labels next to them. + for( int i = 0; i < MAX_PROPERTIES; i++ ) + { + //Create slider + Slider slider = Slider::New(); + slider.SetParentOrigin( ParentOrigin::CENTER ); + slider.SetAnchorPoint( AnchorPoint::CENTER ); + slider.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH ); + slider.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT ); + slider.ValueChangedSignal().Connect( this, &PrimitiveShapesController::OnSliderValueChanged ); + mSliders.push_back( slider ); + + //Setup slider cell properties + mSliderTable.AddChild( slider, TableView::CellPosition( i, 1 ) ); + mSliderTable.SetCellAlignment( TableView::CellPosition( i, 1 ), HorizontalAlignment::CENTER, VerticalAlignment::CENTER ); + + //Create slider label + TextLabel sliderLabel = TextLabel::New(); + sliderLabel.SetParentOrigin( ParentOrigin::CENTER ); + sliderLabel.SetAnchorPoint( AnchorPoint::CENTER ); + sliderLabel.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS ); + mSliderLabels.push_back( sliderLabel ); + + //Setup slider-label cell properties + mSliderTable.AddChild( sliderLabel, TableView::CellPosition( i, 0 ) ); + mSliderTable.SetCellAlignment( TableView::CellPosition( i, 0 ), HorizontalAlignment::LEFT, VerticalAlignment::CENTER ); + } + } + + //Adds a control to the centre of the stage to display the 3D shapes. + //The model is placed in the center of the screen, like so: + // + // +--------------------------------+ + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // ^ | ---------- | + // | | / \ | + // | | / \ | + // | | | | | + // 30% | | | | | + // | | | | | + // | | \ / | + // | | \ / | + // v | ---------- | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // +--------------------------------+ + // + void SetupModel( Layer layer ) + { + //Create a container to house the renderer-holding actor, to provide a constant hitbox. + Actor container = Actor::New(); + container.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS ); + container.SetSizeModeFactor( Vector3( 0.9, 0.3, 0.0 ) ); //90% of width, 30% of height. + container.SetParentOrigin( ParentOrigin::CENTER ); + container.SetAnchorPoint( AnchorPoint::CENTER ); + layer.Add( container ); + + //Create control to display the 3D primitive. + mModel = Control::New(); + mModel.SetParentOrigin( ParentOrigin::CENTER ); + mModel.SetAnchorPoint( AnchorPoint::CENTER); + mModel.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS ); + container.Add( mModel ); + + //Load default shape. + LoadCube(); + + //Make model spin to demonstrate 3D. + mRotationAnimation = Animation::New(15.0f); + mRotationAnimation.AnimateBy( Property( mModel, Actor::Property::ORIENTATION ), + Quaternion( Degree( 0.0f ), Degree( 360.0f ), Degree( 0.0f ) ) ); + mRotationAnimation.SetLooping(true); + mRotationAnimation.Play(); + + //Attach gesture detector to pan models when rotated. + mPanGestureDetector = PanGestureDetector::New(); + mPanGestureDetector.Attach( container ); + mPanGestureDetector.DetectedSignal().Connect( this, &PrimitiveShapesController::OnPan ); + } + + //Clears all sliders and resets the primitive renderer property map. + void InitialiseSlidersAndModel() + { + //Sliders + for( unsigned i = 0; i < mSliders.size(); i++ ) + { + mSliders.at( i ).SetProperty( Slider::Property::MARKS, Property::Value( 0 ) ); //Remove marks + mSliders.at( i ).SetVisible( false ); + mSliderLabels.at( i ).SetProperty( TextLabel::Property::TEXT, Property::Value( "Default" ) ); + mSliderLabels.at( i ).SetVisible( false ); + } + + //Renderer map for model + mRendererMap.Clear(); + mRendererMap[ "rendererType" ] = "PRIMITIVE"; + mRendererMap[ "color" ] = mColor; + } + + //Sets the 3D model to a sphere and modifies the sliders appropriately. + void LoadSphere() + { + InitialiseSlidersAndModel(); + + //Set up specific renderer properties. + mRendererMap[ "shape" ] = SHAPE_SPHERE; + mRendererMap[ "slices" ] = DEFAULT_SLICES; + mRendererMap[ "stacks" ] = DEFAULT_STACKS; + + //Set up sliders. + SetupSlider( 0, SLICES_LOWER_BOUND, SLICES_UPPER_BOUND, DEFAULT_STACKS, "slices" ); + SetupMarks( mSliders.at( 0 ), SLICES_LOWER_BOUND, SLICES_UPPER_BOUND ); + SetupSlider( 1, STACKS_LOWER_BOUND, STACKS_UPPER_BOUND, DEFAULT_STACKS, "stacks" ); + SetupMarks( mSliders.at( 1 ), STACKS_LOWER_BOUND, STACKS_UPPER_BOUND ); + + //Set model in control. + mModel.SetProperty( Control::Property::BACKGROUND, Property::Value( mRendererMap ) ); + } + + //Sets the 3D model to a cone and modifies the sliders appropriately. + void LoadCone() + { + InitialiseSlidersAndModel(); + + //Set up specific renderer properties. + mRendererMap[ "shape" ] = SHAPE_CONE; + mRendererMap[ "scaleHeight" ] = DEFAULT_SCALE_HEIGHT; + mRendererMap[ "scaleBottomRadius" ] = DEFAULT_SCALE_BOTTOM_RADIUS; + mRendererMap[ "slices" ] = DEFAULT_SLICES; + + //Set up sliders. + SetupSlider( 0, 1.0f, 32.0f, DEFAULT_SCALE_HEIGHT, "scaleHeight" ); + SetupSlider( 1, 1.0f, 32.0f, DEFAULT_SCALE_BOTTOM_RADIUS, "scaleBottomRadius" ); + SetupSlider( 2, SLICES_LOWER_BOUND, SLICES_UPPER_BOUND, DEFAULT_STACKS, "slices" ); + SetupMarks( mSliders.at( 2 ), SLICES_LOWER_BOUND, SLICES_UPPER_BOUND ); + + //Set model in control. + mModel.SetProperty( Control::Property::BACKGROUND, Property::Value( mRendererMap ) ); + } + + //Sets the 3D model to a conical frustrum and modifies the sliders appropriately. + void LoadConicalFrustrum() + { + InitialiseSlidersAndModel(); + + //Set up specific renderer properties. + mRendererMap[ "shape" ] = SHAPE_CONICAL_FRUSTRUM; + mRendererMap[ "scaleTopRadius" ] = DEFAULT_SCALE_TOP_RADIUS; + mRendererMap[ "scaleBottomRadius" ] = DEFAULT_SCALE_BOTTOM_RADIUS; + mRendererMap[ "scaleHeight" ] = DEFAULT_SCALE_HEIGHT; + mRendererMap[ "slices" ] = DEFAULT_SLICES; + + //Set up used sliders. + SetupSlider( 0, 1.0f, 32.0f, DEFAULT_SCALE_HEIGHT, "scaleHeight" ); + SetupSlider( 1, 0.0f, 32.0f, DEFAULT_SCALE_BOTTOM_RADIUS, "scaleBottomRadius" ); + SetupSlider( 2, 0.0f, 32.0f, DEFAULT_SCALE_TOP_RADIUS, "scaleTopRadius" ); + + //Set model in control. + mModel.SetProperty( Control::Property::BACKGROUND, Property::Value( mRendererMap ) ); + } + + //Sets the 3D model to a cylinder and modifies the sliders appropriately. + void LoadCylinder() + { + InitialiseSlidersAndModel(); + + //Set up specific renderer properties. + mRendererMap[ "shape" ] = SHAPE_CYLINDER; + mRendererMap[ "scaleHeight" ] = DEFAULT_SCALE_HEIGHT; + mRendererMap[ "scaleRadius" ] = DEFAULT_SCALE_RADIUS; + mRendererMap[ "slices" ] = DEFAULT_SLICES; + + //Set up used sliders. + SetupSlider( 0, 1.0f, 32.0f, DEFAULT_SCALE_HEIGHT, "scaleHeight" ); + SetupSlider( 1, 1.0f, 32.0f, DEFAULT_SCALE_RADIUS, "scaleRadius" ); + SetupSlider( 2, SLICES_LOWER_BOUND, SLICES_UPPER_BOUND, DEFAULT_STACKS, "slices" ); + SetupMarks( mSliders.at( 2 ), SLICES_LOWER_BOUND, SLICES_UPPER_BOUND ); + + //Set model in control. + mModel.SetProperty( Control::Property::BACKGROUND, Property::Value( mRendererMap ) ); + } + + //Sets the 3D model to a cube and modifies the sliders appropriately. + void LoadCube() + { + InitialiseSlidersAndModel(); + + //Set up specific renderer properties. + mRendererMap[ "shape" ] = SHAPE_CUBE; + + //Set model in control. + mModel.SetProperty( Control::Property::BACKGROUND, Property::Value( mRendererMap ) ); + } + + //Sets the 3D model to a bevelled cube and modifies the sliders appropriately. + void LoadBevelledCube() + { + InitialiseSlidersAndModel(); + + //Set up specific renderer properties. + mRendererMap[ "shape" ] = SHAPE_BEVELLED_CUBE; + mRendererMap[ "bevelPercentage" ] = DEFAULT_BEVEL_PERCENTAGE; + mRendererMap[ "bevelSmoothness" ] = DEFAULT_BEVEL_SMOOTHNESS; + + //Set up used sliders. + SetupSlider( 0, 0.0f, 1.0f, DEFAULT_BEVEL_PERCENTAGE, "bevelPercentage" ); + SetupSlider( 1, 0.0f, 1.0f, DEFAULT_BEVEL_SMOOTHNESS, "bevelSmoothness" ); + + //Set model in control. + mModel.SetProperty( Control::Property::BACKGROUND, Property::Value( mRendererMap ) ); + } + + //Sets the 3D model to an octahedron and modifies the sliders appropriately. + void LoadOctahedron() + { + InitialiseSlidersAndModel(); + + //Set up specific renderer properties. + mRendererMap[ "shape" ] = SHAPE_OCTAHEDRON; + + //Set model in control. + mModel.SetProperty( Control::Property::BACKGROUND, Property::Value( mRendererMap ) ); + } + + //Sets up the slider at the given index for the supplied property, and labels it appropriately. + // rendererPropertyLabel is the property that will be set by this slider. + void SetupSlider( int sliderIndex, float lowerBound, float upperBound, float startPoint, + std::string rendererPropertyLabel ) + { + //Set up the slider itself. + mSliders.at( sliderIndex ).RegisterProperty( "rendererProperty", Property::Value( rendererPropertyLabel ), Property::READ_WRITE ); + mSliders.at( sliderIndex ).SetProperty( Slider::Property::LOWER_BOUND, Property::Value( lowerBound ) ); + mSliders.at( sliderIndex ).SetProperty( Slider::Property::UPPER_BOUND, Property::Value( upperBound ) ); + mSliders.at( sliderIndex ).SetProperty( Slider::Property::VALUE, Property::Value( startPoint ) ); + mSliders.at( sliderIndex ).SetVisible( true ); + + //Label the slider with the property. + //We reset the TextLabel to force a relayout of the table. + mSliderTable.RemoveChildAt( TableView::CellPosition(sliderIndex, 0) ); + + TextLabel sliderLabel = TextLabel::New( rendererPropertyLabel ); + sliderLabel.SetParentOrigin( ParentOrigin::CENTER ); + sliderLabel.SetAnchorPoint( AnchorPoint::CENTER ); + sliderLabel.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS ); + + mSliderTable.AddChild( sliderLabel, TableView::CellPosition( sliderIndex, 0 ) ); + mSliderTable.SetCellAlignment( TableView::CellPosition( sliderIndex, 0 ), HorizontalAlignment::LEFT, VerticalAlignment::CENTER ); + + mSliderLabels.at( sliderIndex ).SetVisible( true ); + mSliderLabels.at( sliderIndex) = sliderLabel; + } + + //Setup snapping to integer values between the two given values. + void SetupMarks( Slider& slider, int lower, int upper ) + { + Property::Array marks; + + for( int mark = lower; mark <= upper; mark++ ) + { + marks.PushBack( Property::Value( mark ) ); + } + + slider.SetProperty( Slider::Property::MARKS, Property::Value( marks ) ); + slider.SetProperty( Slider::Property::SNAP_TO_MARKS, Property::Value( true ) ); + } + + //When a shape button is tapped, switch to the corresponding shape. + bool OnChangeShapeClicked( Button button ) + { + //Get the model number from the button. + int modelNumber; + button.GetProperty( button.GetPropertyIndex( "modelNumber" ) ).Get( modelNumber ); + + //Switch to the shape that corresponds to the model number. + switch( modelNumber ) + { + case 0: + { + LoadSphere(); + break; + } + case 1: + { + LoadCone(); + break; + } + case 2: + { + LoadConicalFrustrum(); + break; + } + case 3: + { + LoadCylinder(); + break; + } + case 4: + { + LoadCube(); + break; + } + case 5: + { + LoadBevelledCube(); + break; + } + case 6: + { + LoadOctahedron(); + break; + } + } + + return true; + } + + //When the slider is adjusted, change the corresponding shape property accordingly. + bool OnSliderValueChanged( Slider slider, float value ) + { + //Update property map to reflect the change to the specific renderer property. + std::string rendererPropertyLabel; + slider.GetProperty( slider.GetPropertyIndex( "rendererProperty" ) ).Get( rendererPropertyLabel ); + mRendererMap[ rendererPropertyLabel ] = value; + + //Reload the model to display the change. + mModel.SetProperty( Control::Property::BACKGROUND, Property::Value( mRendererMap ) ); + + return true; + } + + //Panning around the shape rotates it. + void OnPan( Actor actor, const PanGesture& gesture ) + { + switch( gesture.state ) + { + case Gesture::Started: + { + //Pause animation, as the gesture will be used to manually rotate the model + mRotationAnimation.Pause(); + + break; + } + case Gesture::Continuing: + { + //Rotate based off the gesture. + mRotation.x -= gesture.displacement.y / X_ROTATION_DISPLACEMENT_FACTOR; // Y displacement rotates around X axis + mRotation.y += gesture.displacement.x / Y_ROTATION_DISPLACEMENT_FACTOR; // X displacement rotates around Y axis + Quaternion rotation = Quaternion( Radian( mRotation.x ), Vector3::XAXIS) * + Quaternion( Radian( mRotation.y ), Vector3::YAXIS); + + mModel.SetOrientation( rotation ); + + break; + } + case Gesture::Finished: + { + //Return to automatic animation + mRotationAnimation.Play(); + + break; + } + case Gesture::Cancelled: + { + //Return to automatic animation + mRotationAnimation.Play(); + + break; + } + default: + { + break; + } + } + } + + //If escape or the back button is pressed, quit the application (and return to the launcher) + void OnKeyEvent( const KeyEvent& event ) + { + if( event.state == KeyEvent::Down ) + { + if( IsKey( event, DALI_KEY_ESCAPE) || IsKey( event, DALI_KEY_BACK ) ) + { + mApplication.Quit(); + } + } + } + +private: + Application& mApplication; + + std::vector mSliders; ///< Holds the sliders on screen that each shape accesses. + std::vector mSliderLabels; ///< Holds the labels to each slider. + TableView mSliderTable; ///< A table to layout the sliders next to their labels. + + Property::Map mRendererMap; ///< Property map to create a primitive renderer. + Control mModel; ///< Control to house the primitive renderer. + + PanGestureDetector mPanGestureDetector; ///< Detects pan gestures for rotation of the model. + Animation mRotationAnimation; ///< Automatically rotates the model, unless it is being panned. + + Vector4 mColor; ///< Color to set all shapes. + Vector2 mRotation; ///< Keeps track of model rotation. +}; + +void RunTest( Application& application ) +{ + PrimitiveShapesController test( application ); + + application.MainLoop(); +} + +// Entry point for Linux & Tizen applications +// +int main( int argc, char **argv ) +{ + Application application = Application::New( &argc, &argv ); + + RunTest( application ); + + return 0; +} diff --git a/resources/images/bevelled-cube-button.png b/resources/images/bevelled-cube-button.png new file mode 100644 index 0000000000000000000000000000000000000000..77b7ed6a6eae08878f52a834ef94686e019c8d0a GIT binary patch literal 4074 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 a4%lDtEG%@b**fFtfZIqDy?O*H?*9N+s?Dwd literal 0 HcmV?d00001 diff --git a/resources/images/cone-button.png b/resources/images/cone-button.png new file mode 100644 index 0000000000000000000000000000000000000000..6ad6c0ba9a554ba1808791f8de0e740051074291 GIT binary patch literal 3564 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 DKg-H$ literal 0 HcmV?d00001 diff --git a/resources/images/conical-frustrum-button.png b/resources/images/conical-frustrum-button.png new file mode 100644 index 0000000000000000000000000000000000000000..be1756310101172d0ba629b8800d5f1c4b96ab42 GIT binary patch literal 3944 zcmahM_dnE+`+euQv+p<~8E5aAE}4fT!Z|B@WHiV~LJ5b%S;?-fLy}|^vbT_(m8_7g zjAUQp>+=_UpXc?A*Ym^kdS1_bJu!IPHAXrvIsgC|_4Tl(=aTRr!NBL-(C#4XT+sUH zSw8>(de;941iZ}RJU2r9^o?|&^H3H>7PK^)r~v@XP<^bXdEnHh{cIxlHAMOPi>A!k zCN)Pb^=x7fmI!D)gXJyJ^79^39qk4ZECa(VA$odPY!WyLk7WI;q;kV=Zf0d}c57n~ z$*eYNW-I>S`w=%@y$0sqGTpISX9 z*(4JxhDd{$vw%>}AJbmlRnSXGzLVrZ_ATzqA11wlBS45Cpn1z8{gnY_V(w^}KIOK5 zbWlUb#guyrysv2C7t8@56)b=uK4My_{IubFUpbQxC(EmiqZ>q_uo7xDCDn!|jiv`@Fj5XiC`SsQvu~!1%q7k} zS(_-i(%a4PSdxEBzdAG{tdTYy)80+LyF7+Gl>uAZh=98j%N8ZC?^X#5Hk$1(m?4s7(fBw zdtv~o)=mb5(r3|LJp1`iC6qFK`o+3ggcI4W8XaV&^b$1T1hfaLZpktD>32zI)V&9O zM5bT|WOiFZe7pk`>i2t>syVtlq@~t=`R3EzEM7RF8!RUmUxfo81Y82BkA-A|$kig? z!e@>&Q92GY>Pmn2oIciMs;PW?d`n?6lR?)O10;5K^rQlGjJw=rzE(*59l6EJ%>3ZA zG=vy&CvfH_H?G0#Oc)_vXwDeEV=$pv81qtgkE8F?f__-7p6TToT(qrHZ?a;WdDwN7 zCKi%U_dJMu-1M|AAxh^3BTtgtV zx$|c87Yh#o5f7g%wh26*k@l>L=|{!-Fjxw>v8c{qw`Dhl#r9xhfUy}TSuZ<{(70vM zxQ>eujCHz=N5Nk-gHLJAORqny9+2fc-5VQGv^1+8O`y+BCA0v(jy&*|%8(jwFk$lK zzBVTj5xfuAbc5(p@c;a>v!laS!ja*o5`7;AzyKme$V8-3d5f3$I!OLPLIcWG83#B- zT|eNdt*$*58}nA+O+-@z@1!xg&UbUs~$E=_KNgPAYge*wQ1*>b8y{oR+5biEz zrbJzyy=a&7In5%%>QPDiaY#}hspMmtNTjL7o)l&zA5c+IG1dTdOgML9_Z(qU`ihd{ zua$k}B4BJySqbEq1i^B4)o)ty*cHNvU}*=xT^eo2Fdeh@KOjAN=il`d0Pssn+!qMp-E-)o@BX%?d0r7U^5E2_3^Yqs@x=;F z0blN?P=aS?%}u917x9v=zXjTn&S^s^@7S>ZYmu+x8ugO32uZ&ddgRIaVNZ+>+ofoi z>u{zQ=`m$JV}4*)>E%D{S-IqT{)o$0x$1b&eN9ZAbO#Fd7hY&@(vOSv9H7s|p$hfm z6SWg-P>HZb-HL2CiA9lsM&sVynVv7Fhgj%qkxwUGLPTxF`b+Rhq;uGfWlQo>|127n^vmaN&B4(Xxmg$ClKC~}-vb(R$+QlK_pXtco5=%c3Og}Bjuzr6MUb65OmR2AW$bFg;16+ zDE1s)w=2F{TW}`^&j8$m(3B%?ZMLqcY#NsF=q!{z>Km~Biy>NgW!{Kfe68@Njl>=; zXid*o9#LdOs+dg`SGV67mwb_oV?u z7+0yNuPWoQh0Lx-*r2e1LxQ}Tg?#Iw2H@L1Tuwu5eoz+PJ@k7p7}@tubn9Yhti1d=C;eF6Z~`t;10&0FQ6YXE@++A?_|1 zaz_Xub?gJqNIgS!|p90_8v7t5yW z&hU;0P>-KOL%vSH*<*^?pyPe|V(S3GB8x_0E9^z2SfXdgy!d@h%`>XJPl6!ZzNgPs z(sXba& z=gWp$fuzHZoA7iv5cTK82P%>ZLrhfG1DtMnuO?vfrXT;m0~h{R-M2?p4&gUfqCvh?RGBy8J7A?8fM9SN_}EA$X;;oEFc5>AL?lff&~3L>r8P64uN=D(A()r1 zr>CdCauSrmH6uR#Ez|LpT#Y|%r^4fC8al8F)LK>+ywFzEF`O?|(B?0BW(ow9@2}pb z%#>81fk|!CCzLmIL#wX&fKf2&W6klUVjj42W{E^^QiunHT*?T4*RI-v_2r*t9J>Q0 z3?D1Ay3WlkNUTa;0Gaa8tgC$GiFWBEu>EaqKI9O}9@0Ky`MGN|@MG1Y=SVN%?ps%t zc)tXYT;=faT(F;+VUCBd8?$Tvn4(Vy12ZMUdwAq|XKS|J0Qj(;qkGI|Jonl>ilZ|^ zPLf_?%PcERIM`BJT;qpbTW{Ww1zBovVVWWq5^-k%|y-x_+pXm5?e)QtnV_HMzhNMyE;gK~}*~WPkZgr8h@n6Z|QUV#Q z)y(8fSg9<-yK%7Z?x>?= z2e+cvsA~UidtF_2O+QJ1-6$$pgmYst;fu;(3^qcsS_c^QK*qQ6Tfv~Dt7vi-!G&)Aj~5~<*6s{ZohMJK7>Y-aJZqwMB_5~ zi`TIo3qGIfhSjCrn-~Q}G5tjZOU#a9s&P%9OO@8jI>ol+mfYg%(q2L6+3i{SAbFZf zhK!LBXKdMqI&Wzt=S2feI?>bl-0!m??u;w^__mDhqBF`-3hY<%&Y+H1pF$PacFrsF z7~37kI^C9ggYTsC)=<6)Yn-p=N?Pi|Ef3vWatQr)g*4t$WxG!$tYj8Cc_ z2%p0FM_SMu=7%s98u4pu5{oZnkX`TP5K{kZ?~byR{A z#VfH*r$u~%2+uo(ys;C~Vj#8>fYk8g#@e9Y^Yf?tz^Aj=mBHqAS9E4}ZtiXCj|~ld z$8%E+PPR5b@x^FZiOu2Xs}!Ner$hoQCcIU@{PmB|M>EaE-of!<}}lk z&#tPxnX-!R%giR!DPP^S%W?Lw>& z!NEbCv2l!8--n$*)fa~6VZT%HQs5KjK~SO(F5hkqGbO-wAKh1PIQesQ@3Eh+zyHlU z2$&)Go)94Lsee~>Xt$t1P~dv9YtVn7()U5CFREj~Nlq^rLK;ytp<|&=W1y{)sh)_G ziFM1XZmw-?tgThfwYRr(NGn9ADFK~_f4{ha~vUH1m zzvMl6ox_x6YIP|HUG(gdfa=1h5C2rnaJ3)C1_@vt+@W>20Jb!1#yCEfF>~>26S-D3 zTg?JuaV}5I59HS@mvbev(iZR{h~Y+3n0m)38_y$$al;b|N!>obH6uK!mXe?&1nI8Z z(p~UrCVUzaF$0MJw_F1ofj$_4sK8Y!;0PEx0!Ik)|4q}GfGJT4eJEy&mzH_LmeIYJ zm6ToT(OOc#V>gY=atjz->8WNjC(N6zt*xy@q7qPL1uAw}7M*@RJWm%%eSKRU zjf-!bShM>K8GUua-rl~ZX74TQtkqu9%$O^U#b|Dp=oyWkWGuNsNWDgDp`oFykm&gM z1O8dMl6%x1y4dB+aOnk#hVR+_ehYChMw#>E~}9*x%J{I4=-Xt7U2q!QIvXV1&QbJ{$M3oB#HZGT=`g znHNFGoXY`luL!F>mBw)C*QOYVr5R)MVb%fQCSe8YGCeIxcFbXeAT+QZ4pGs|mhYj5g}-q|>R;y#_t?ekk*SV$k=;iBBtwzzip=y_WA zkq|_;<0F5Hs<&yW4WKl;xH8Xl3cuCn#D#9&=jF4%A1A+7-c}^7h^7T}OzA%Zb zT;#;?H-u@p?|_V$tg&?{X~*#UrI?C03o&goqU9^R1w_a-bsrc9FeBHTE^V*;UYBA8 z`7EIq=Xt-}()oRTDFGg1%E`vagDw5N8|J6Bf@WTGJ}-|w2$woB7!Wh-+p9VBtfj@4 zmst5nSI(wWDYGDL=ALXEEn$UV(hC0OnI34 zNR^X^(9tj3k^85RM~jPUuW|bZ-6TJ9`kVs=2s1`i!I!+fcanaWNlK1-YLvRC5*I#x zI&)fe%z)n08UOS9!-xIQ%@Whi%u&x8D_BugoaoR7xkt*Ka4q(_;DNI{CDo+jN!dQn zPbT~d*}NaLo;+XVrKKl&VH}1;&&q|5)tcAe(DN3MfZ}A760G}cThCO&O;^FqpD&xW z$8YsH^8lsx)>d8do~bMYLh<0Zcjji|SEOP_qq53Y(5|AaXx^<~B{SHEPx*d633{Ff z6-jJ1yDC_)8F{9?STZ6jb7gH7hB4zzO930amF4;=VQ5@y!V{lOfxS*J7fo9E_H9t# zq5s;JdLa6kJWV}apyR)2EZjHs=BM_Z=ZUStE!_J2l=$omg>m^*-9l9Vg}&kXNL{R@ zzWI2bNz75H@aNw%<)pFj3^%fe_5$N94c_+RuE04-W zr^a?uoVx44?kbATLd$XnQmO;@u0xW)Wp4v->BALY(i}*>n?X$~^fF5FMPtETAMmz; zeUMUb>l7*Qc{pHEm^h;Wla^LU0?H!9Q>J4PqhWHF(PsCYrB{$C5ylUyuNEMc?B3N+gjyK%twdb~6TJ8DvColxU3IItYa#&boOEZlm@vF8%lU z)PHtYH5j1+-}Ky_QU0q4@>t8DU$eVGGBdm&XeS8M(k`=at}owCS6XLo!bYqtmVeMX zZ{LHRNK2qB&{8?wsvoN2mHPCViuze^52`bNbnvnYYYsUn16c-{icht$2`lIHXeVx) z@V4A+kglmO8+K0~7+1ak2)D^))tz>ymegS;Pbv+z46+&7;r6Y4a+&uUok_yHMRhKK zOJ$RMr)$h>sm|+TS71yP4LdrNu#imLH<3YNhB~TQYQRf}(CHF)dJm_C$U;7We@NuU zeLfvLO&BCYeo49kZFio{2bmf+B7T5C;k1ccbo zbZ$V#aC1pA0OV8KK^}^~;r*GdYT;MTZ~gNvu@YKYJrM zE8`ci|IC!RYd9wcEO(W8)AkM`=e3q;^?WmJ;Vh1lIz5K?rr(w9SZ1ZO%PzXEel*;;DrNtEo8hK&Z)05qvDs#RvLxN*b^Pk&;A;~Qj(+;fn?QzFlxJ8J ze!MwMhkv${HfXRownPCwtN~vc6v7!;Rznv=PpK z=`vwT?C70=`L9sVjuK*+?zOKWR`maG!Z!b+%2^x}J@5W@W@u%bK zOClg&j!ASn6(T_0Uf7pd*)ylk2!3@KgHR5qgOkimA>z1*Oe*&1a;tAia4&xuPvl%c zVr0G>%tK5*ya*#dB_Hc=8vR=2vwo_M!(AuIP*f_%mQS|0bVf!`T)p@1QXx1+Uw+l( oT0qeXH{|~?uKz{MG~aInH=8b0!)N_Iiufa7XYFWJhq+Ar4|jSJasU7T literal 0 HcmV?d00001 diff --git a/resources/images/cylinder-button.png b/resources/images/cylinder-button.png new file mode 100644 index 0000000000000000000000000000000000000000..07d5494cd7dde9803c4ca5a6c00bab39616482a0 GIT binary patch literal 3858 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 UQq)TA+W-In07*qoM6N<$feI1o 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 literal 0 HcmV?d00001 diff --git a/shared/dali-demo-strings.h b/shared/dali-demo-strings.h index cca2f01..a11b53c 100644 --- a/shared/dali-demo-strings.h +++ b/shared/dali-demo-strings.h @@ -108,6 +108,7 @@ extern "C" #define DALI_DEMO_STR_TITLE_EFFECTS_VIEW "Effects View" #define DALI_DEMO_STR_TITLE_NATIVE_IMAGE_SOURCE "Native Image Source" #define DALI_DEMO_STR_TITLE_MESH_RENDERER "Mesh Renderer" +#define DALI_DEMO_STR_TITLE_PRIMITIVE_SHAPES "Primitive Shapes" #endif -- 2.7.4