radial-progress.cpp 9.3 KB
/*
 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <dali/dali.h>
#include <dali-toolkit/dali-toolkit.h>

using namespace Dali;

namespace // unnamed namespace
{

const char* TEXTURE_URL = DEMO_IMAGE_DIR "RadialEffect-280x280.png";
const unsigned int TEXTURE_WIDTH = 280;
const unsigned int TEXTURE_HEIGHT = 280;

const int NUMBER_OF_SIDES( 64 ); // number of sides of the polygon used as a stencil
const float INITIAL_DELAY( 2.0f ); // initial delay before showing the circle
const float PROGRESS_DURATION( 0.5f ); // number of seconds to fully show the circle


/*
 * Vertex shader for textured quad
 */
const char* VERTEX_SHADER_TEXTURED = DALI_COMPOSE_SHADER(
attribute mediump vec2 aPosition;\n
uniform   mediump mat4 uMvpMatrix;\n // DALi shader builtin
uniform   mediump vec3 uSize;\n // DALi shader builtin
\n
varying mediump vec2 vTexCoord;\n
void main()\n
{\n
  mediump vec4 vertexPosition = vec4(aPosition, 0.0, 1.0);\n
  vertexPosition.xyz *= uSize;\n
  vTexCoord = vec2(1.0, 1.0)*(aPosition + vec2(0.5) );\n
  gl_Position = uMvpMatrix * vertexPosition;\n
}\n
);

/*
 * Fragment shaderfor textured quad
 */
const char* FRAGMENT_SHADER_TEXTURED = DALI_COMPOSE_SHADER(
uniform sampler2D uTexture;\n
\n
varying mediump vec2 vTexCoord;\n
void main()\n
{\n
  mediump vec4 texColor = texture2D( uTexture, vTexCoord );\n
  gl_FragColor = texColor;\n
}\n
);

/*
 * Vertex shader for polygon
 */
const char* VERTEX_SHADER_BASIC = DALI_COMPOSE_SHADER(
attribute mediump vec3 aPosition;\n
uniform   mediump mat4 uMvpMatrix;\n // DALi shader builtin
uniform   mediump vec3 uSize;\n // DALi shader builtin
uniform   mediump float uProgress;\n
\n
varying mediump vec2 vTexCoord;\n
void main()\n
{\n
  mediump vec4 vertexPosition = vec4(aPosition.x, aPosition.y, 0.0, 1.0);\n
\n
  float index = aPosition.z;\n
  if( uProgress < index )\n
  {\n
    vertexPosition = vec4(0.0, 0.0, 0.0, 1.0);\n
  }\n
\n
  vertexPosition.xyz *= uSize;\n
  gl_Position = uMvpMatrix * vertexPosition;\n
}\n
);

/*
 * Fragment shader for polygon
 */
const char* FRAGMENT_SHADER_BASIC = DALI_COMPOSE_SHADER(

void main()\n
{\n
  gl_FragColor = vec4( 1.0, 1.0, 1.0, 1.0 );\n
}\n
);

} // unnamed namespace


// This example shows how to render a radial progress indicator
//
class RadialProgressController : public ConnectionTracker
{
public:

  RadialProgressController( Application& application )
  : mApplication( application )
  {
    // Connect to the Application's Init signal
    mApplication.InitSignal().Connect( this, &RadialProgressController::Create );
  }

  ~RadialProgressController()
  {
    // Nothing to do here
  }

  // The Init signal is received once (only) during the Application lifetime
  void Create( Application& application )
  {
    Stage stage = Stage::GetCurrent();
    stage.SetBackgroundColor( Color::BLACK );

    // 1. Create actor to show the effect
    mActor = Actor::New();
    mActor.SetAnchorPoint( AnchorPoint::CENTER );
    mActor.SetParentOrigin( ParentOrigin::CENTER );
    mActor.SetSize( Vector2( TEXTURE_WIDTH, TEXTURE_HEIGHT ) );
    mActor.RegisterProperty("uProgress", float(1.0f) );
    stage.Add( mActor );

    // 1. Create stencil renderer i.e. a triangle fan in the shape of a circle
    Renderer stencilRenderer = CreatePolygon( NUMBER_OF_SIDES );
    mActor.AddRenderer( stencilRenderer );

    // 2. Create textured quad renderer
    Renderer texturedQuad = CreateTexturedQuad( TEXTURE_URL );
    mActor.AddRenderer( texturedQuad );

    // 5. Animate the progress uniform
    Animation animation = Animation::New( PROGRESS_DURATION + INITIAL_DELAY );
    animation.AnimateTo( Property(mActor,"uProgress"), float(NUMBER_OF_SIDES+1), TimePeriod(INITIAL_DELAY, PROGRESS_DURATION) );
    animation.Play();

    // 6. Exit the application when touched
    stage.GetRootLayer().TouchSignal().Connect( this, &RadialProgressController::OnTouch );
  }

  bool OnTouch( Actor actor, const TouchData& touch )
  {
    // quit the application
    mApplication.Quit();
    return true;
  }

  /**
   * Generates stencil mask geometry. Geometry is rendered as
   * a triangle fan and occupies square 2.0x2.0.
   * @param[in] numberOfSides The more subdivisions the more smooth mask animation.
   */
  Renderer CreatePolygon( unsigned int numberOfSides )
  {
    float count( numberOfSides );

    // compute radial step in radians
    const float STEP( (2.0f * M_PI) / count );
    float angle( 0.0f );

    std::vector< Vector3 > vertices;
    vertices.push_back( Vector3::ZERO );

    for( size_t i = 0; i <= numberOfSides; ++i )
    {
      vertices.push_back( Vector3( -0.5f * cos( angle ), -0.5f * sin( angle ), i+1 ) );
      angle += STEP;
    }

    Property::Map vertexFormat;
    vertexFormat[ "aPosition" ] = Property::VECTOR3;

    // describe vertex format ( only 2-dimensional positions )
    PropertyBuffer vertexBuffer = PropertyBuffer::New( vertexFormat );
    vertexBuffer.SetData( vertices.data(), vertices.size() );

    // create geometry
    Geometry geometry = Geometry::New();
    geometry.AddVertexBuffer( vertexBuffer );
    geometry.SetType( Geometry::TRIANGLE_FAN );

    Shader shader = Shader::New( VERTEX_SHADER_BASIC, FRAGMENT_SHADER_BASIC );
    Renderer renderer = Renderer::New( geometry, shader );

    // Setting stencil data. We don't want to render to the color buffer so
    // with use of RenderMode property we specify that only stencil buffer will
    // be affected.
    renderer.SetProperty( Renderer::Property::RENDER_MODE, RenderMode::STENCIL );

    // Set stencil function
    renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION, StencilFunction::ALWAYS );

    // Stencil function reference
    renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION_REFERENCE, 1 );

    // Stencil function mask
    renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION_MASK, 0xFF );

    // Set stencil operations
    renderer.SetProperty( Renderer::Property::STENCIL_OPERATION_ON_FAIL, StencilOperation::KEEP );
    renderer.SetProperty( Renderer::Property::STENCIL_OPERATION_ON_Z_FAIL, StencilOperation::KEEP );
    renderer.SetProperty( Renderer::Property::STENCIL_OPERATION_ON_Z_PASS, StencilOperation::REPLACE );

    // Stencil mask to write
    renderer.SetProperty( Renderer::Property::STENCIL_MASK, 0xFF );

    // Set depth index lower than textured quad renderer, so stencil will render first
    renderer.SetProperty( Renderer::Property::DEPTH_INDEX, 1 );

    return renderer;
  }

  /**
   * Creates textured quad renderer
   */
  Renderer CreateTexturedQuad( const char* url )
  {
    // Create shader & geometry needed by Renderer

    Shader shader = Shader::New( VERTEX_SHADER_TEXTURED, FRAGMENT_SHADER_TEXTURED );

    Property::Map vertexFormat;
    vertexFormat["aPosition"] = Property::VECTOR2;
    PropertyBuffer vertexBuffer = PropertyBuffer::New( vertexFormat );

    const float P( 0.5f );
    const Vector2 vertices[] = {
      Vector2( -P, -P ),
      Vector2( +P, -P ),
      Vector2( -P, +P ),
      Vector2( +P, +P )
    };

    vertexBuffer.SetData( vertices, 4 );

    // Instantiate quad geometry
    Geometry geometry = Geometry::New();
    geometry.AddVertexBuffer( vertexBuffer );
    geometry.SetType( Geometry::TRIANGLE_STRIP );

    // Load texture
    PixelData pixelData = Toolkit::SyncImageLoader::Load( url );
    Texture texture = Texture::New( TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(), pixelData.GetHeight() );
    texture.Upload( pixelData );
    texture.GenerateMipmaps();

    // Create texture set
    TextureSet textureSet = TextureSet::New();
    textureSet.SetTexture( 0, texture  );

    // Create renderer
    Renderer renderer = Renderer::New( geometry, shader );
    renderer.SetTextures( textureSet );

    // Set mode indicating we will use both stencil and color buffers
    renderer.SetProperty( Renderer::Property::RENDER_MODE, RenderMode::COLOR_STENCIL );

    // Stencil function - expecing drawing only when function mask matches exactly
    renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION, StencilFunction::EQUAL );
    renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION_REFERENCE, 1 );
    renderer.SetProperty( Renderer::Property::STENCIL_FUNCTION_MASK, 0xFF );

    // We don't want to draw to the stencil, so setting stencil draw mask to 0
    renderer.SetProperty( Renderer::Property::STENCIL_MASK, 0x00 );

    // Make sure the quad will render after drawing to stencil buffer
    renderer.SetProperty( Renderer::Property::DEPTH_INDEX, 2 );

    return renderer;
  }

private:

  Application&  mApplication;

  Actor mActor;
};

void RunTest( Application& application )
{
  RadialProgressController test( application );

  application.MainLoop();
}

// Entry point for Linux & Tizen applications
//
int DALI_EXPORT_API main( int argc, char **argv )
{
  Application application = Application::New( &argc, &argv );

  RunTest( application );

  return 0;
}