/*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2016 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.
// EXTERNAL INCLUDES
#include <dali/public-api/animation/animation.h>
#include <dali/public-api/render-tasks/render-task-list.h>
-#include <dali/public-api/images/resource-image.h>
+#include <dali/public-api/rendering/texture.h>
+#include <dali/public-api/rendering/shader.h>
// INTERNAL INCLUDES
-#include <dali-toolkit/internal/controls/bubble-effect/bubble-actor.h>
-#include <dali-toolkit/internal/controls/bubble-effect/color-adjuster.h>
#include <dali-toolkit/internal/controls/bubble-effect/bubble-effect.h>
+#include <dali-toolkit/internal/controls/bubble-effect/bubble-renderer.h>
namespace
{
struct Vertex
{
- float index;
- Dali::Vector2 position;
- Dali::Vector2 textureCoord;
-
Vertex()
- {}
+ : index( 0.0f ), position(), textureCoord()
+ {
+ }
Vertex( float index, const Dali::Vector2& position, const Dali::Vector2& textureCoord )
: index( index ), position( position ), textureCoord( textureCoord )
- {}
+ {
+ }
+
+ float index;
+ Dali::Vector2 position;
+ Dali::Vector2 textureCoord;
};
/**
* Return a random value between the given interval.
* @param[in] f0 The low bound
* @param[in] f1 The up bound
+ * @param[in] seed The seed to genergate random number
* @return A random value between the given interval
*/
-float RandomRange(float f0, float f1)
+float RandomRange(float f0, float f1, unsigned int& seed)
+{
+ return f0 + (rand_r( &seed ) & 0xfff) * (f1-f0) * (1.0f/4095.0f);
+}
+
+const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
+ attribute mediump vec2 aPosition;\n
+ attribute mediump vec2 aTexCoord;\n
+ uniform mediump vec3 uSize;\n
+ uniform mediump mat4 uMvpMatrix;\n
+ varying mediump vec2 vTexCoord;\n
+ \n
+
+ void main()\n
+ {\n
+ gl_Position = uMvpMatrix * vec4(aPosition*uSize.xy,0.0,1.0);
+ vTexCoord = aTexCoord;\n
+ }\n
+);
+
+const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
+ precision highp float;\n
+ uniform vec3 uHSVDelta;\n
+ varying mediump vec2 vTexCoord;\n
+ uniform sampler2D sTexture;\n
+ float rand(vec2 co) \n
+ {\n
+ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); \n}
+ \n
+ vec3 rgb2hsv(vec3 c)\n
+ {\n
+ vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n
+ vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n
+ vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n
+ \n
+ float d = q.x - min(q.w, q.y);\n
+ float e = 1.0e-10;\n
+ return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n
+ }\n
+ vec3 hsv2rgb(vec3 c)\n
+ {\n
+ vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n
+ vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n
+ return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n
+ }\n
+ void main() {\n
+ vec4 color = texture2D(sTexture, vTexCoord); \n
+ vec3 hsvColor = rgb2hsv( color.rgb );\n
+ // modify the hsv Value
+ hsvColor += uHSVDelta * rand(vTexCoord); \n
+ // if the new vale exceeds one, then decrease it
+ hsvColor -= max(hsvColor*2.0 - vec3(2.0), 0.0);\n
+ // if the new vale drops below zero, then increase it
+ hsvColor -= min(hsvColor*2.0, 0.0);\n
+ color = vec4( hsv2rgb( hsvColor ), 1.0 ); \n
+ gl_FragColor = color; \n
+ }\n
+ );
+
+Dali::Geometry CreateTexturedQuad()
{
- return f0 + (rand() & 0xfff) * (f1-f0) * (1.0f/4095.0f);
+ struct Vertex
+ {
+ Dali::Vector2 position;
+ Dali::Vector2 texCoord;
+ };
+
+ static const Vertex data[] = {{ Dali::Vector2( -0.5f, -0.5f ), Dali::Vector2( 0.0f, 0.0f ) },
+ { Dali::Vector2( 0.5f, -0.5f ), Dali::Vector2( 1.0f, 0.0f ) },
+ { Dali::Vector2( -0.5f, 0.5f ), Dali::Vector2( 0.0f, 1.0f ) },
+ { Dali::Vector2( 0.5f, 0.5f ), Dali::Vector2( 1.0f, 1.0f ) }};
+
+ //Create a vertex buffer for vertex positions and texture coordinates
+ Dali::PropertyBuffer vertexBuffer = Dali::PropertyBuffer::New( Dali::Property::Map()
+ .Add( "aPosition", Dali::Property::VECTOR2 )
+ .Add( "aTexCoord", Dali::Property::VECTOR2 ) );
+ vertexBuffer.SetData( data, 4u );
+
+ //Create the geometry
+ Dali::Geometry geometry = Dali::Geometry::New();
+ geometry.AddVertexBuffer( vertexBuffer );
+ geometry.SetType(Dali::Geometry::TRIANGLE_STRIP );
+
+ return geometry;
}
}
namespace Internal
{
BubbleEmitter::BubbleEmitter( const Vector2& movementArea,
- Image shapeImage,
+ Texture shapeTexture,
unsigned int maximumNumberOfBubble,
const Vector2& bubbleSizeRange )
-: Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS ) ),
- mShapeImage( shapeImage ),
+: Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
+ mShapeTexture( shapeTexture ),
mMovementArea( movementArea ),
mBubbleSizeRange( bubbleSizeRange ),
mDensity( 5 ),
mTotalNumOfBubble( maximumNumberOfBubble ),
mCurrentBubble( 0 ),
+ mRandomSeed( 0 ),
mRenderTaskRunning(false)
{
// Calculate how many shaders are required
if( mTotalNumOfBubble>100 )
{
- mNumBubblePerActor = 100;
- mNumActor = mTotalNumOfBubble / 100;
- if( mNumActor*mNumBubblePerActor < mTotalNumOfBubble )
+ mNumBubblePerRenderer = 100;
+ mNumRenderer = mTotalNumOfBubble / 100;
+ if( mNumRenderer*mNumBubblePerRenderer < mTotalNumOfBubble )
{
- mNumActor++;
- mNumBubblePerActor = mTotalNumOfBubble / mNumActor+1;
- mTotalNumOfBubble = mNumActor * mNumBubblePerActor;
+ mNumRenderer++;
+ mNumBubblePerRenderer = mTotalNumOfBubble / mNumRenderer+1;
+ mTotalNumOfBubble = mNumRenderer * mNumBubblePerRenderer;
}
}
else
{
- mNumBubblePerActor = mTotalNumOfBubble;
- mNumActor = 1;
+ mNumBubblePerRenderer = mTotalNumOfBubble;
+ mNumRenderer = 1;
}
+
+ mRandomSeed = time( NULL );
}
BubbleEmitter::~BubbleEmitter()
}
Toolkit::BubbleEmitter BubbleEmitter::New( const Vector2& winSize,
- Image shapeImage,
+ Texture shapeTexture,
unsigned int maximumNumberOfBubble,
const Vector2& bubbleSizeRange )
{
// Create the implementation
- IntrusivePtr<BubbleEmitter> internalBubbleEmitter ( new BubbleEmitter( winSize, shapeImage,
+ IntrusivePtr<BubbleEmitter> internalBubbleEmitter ( new BubbleEmitter( winSize, shapeTexture,
maximumNumberOfBubble,bubbleSizeRange ) );
// Pass ownership to Toolkit::BubbleEmitter handle
mBubbleRoot = Actor::New();
mBubbleRoot.SetSize(mMovementArea);
- // Prepare the frame buffer to store the color adjusted background image
- mEffectImage = FrameBufferImage::New( mMovementArea.width/4.f, mMovementArea.height/4.f, Pixel::RGBA8888, Dali::Image::UNUSED );
-
- // Generate the samplers and geometry, which is used by all bubbleActors
- mSamplerBackground = Sampler::New( mEffectImage, "sBackground" );
- mSamplerBubbleShape = Sampler::New( mShapeImage, "sBubbleShape" );
- mMeshGeometry = CreateGeometry( mNumBubblePerActor*mDensity );
+ // Prepare the frame buffer to store the color adjusted background texture
+ Vector2 imageSize = Vector2( mMovementArea.width/4.f, mMovementArea.height/4.f );
+ mFrameBuffer = FrameBuffer::New( imageSize.x, imageSize.y, 0 );
+ mEffectTexture = Texture::New( TextureType::TEXTURE_2D, Pixel::RGBA8888, imageSize.x, imageSize.y );
+ mFrameBuffer.AttachColorTexture( mEffectTexture );
- Shader bubbleShader = CreateBubbleShader (mNumBubblePerActor );
+ // Generate the geometry, which is used by all bubbleActors
+ mMeshGeometry = CreateGeometry( mNumBubblePerRenderer*mDensity );
- mMaterial = Material::New( bubbleShader );
- mMaterial.AddSampler( mSamplerBackground );
- mMaterial.AddSampler( mSamplerBubbleShape );
+ Shader bubbleShader = CreateBubbleShader( mNumBubblePerRenderer );
- mBubbleActors.resize( mNumActor );
+ mTextureSet = TextureSet::New();
+ mTextureSet.SetTexture( 0u, mEffectTexture );
+ mTextureSet.SetTexture( 1u, mShapeTexture );
- // Create the meshActor group and bubbleEffect group to emit bubbles following the given track, such as finger touch track.
- for(unsigned int i=0; i < mNumActor; i++ )
+ // Create the renderers to render the bubbles
+ mBubbleRenderers.resize( mNumRenderer );
+ for(unsigned int i=0; i < mNumRenderer; i++ )
{
- mBubbleActors[i] = new BubbleActor( mNumBubblePerActor, mMovementArea );
- (mBubbleActors[i])->MakeRenderable( mMeshGeometry, mMaterial );
- mBubbleRoot.Add( (mBubbleActors[i])->GetMeshActor() );
+ mBubbleRenderers[i].Initialize( mNumBubblePerRenderer, mMovementArea, mMeshGeometry, mTextureSet, bubbleShader );
+ mBubbleRoot.AddRenderer( mBubbleRenderers[i].GetRenderer() );
}
// Create a cameraActor for the off screen render task.
return mBubbleRoot;
}
-void BubbleEmitter::SetBackground( Image bgImage, const Vector3& hsvDelta )
+void BubbleEmitter::SetBackground( Texture bgTexture, const Vector3& hsvDelta )
{
- mBackgroundImage = bgImage;
+ mBackgroundTexture = bgTexture;
mHSVDelta = hsvDelta;
- ImageActor sourceActor = ImageActor::New( bgImage );
+ //Create RenderTask source actor
+ Actor sourceActor = Actor::New();
sourceActor.SetSize( mMovementArea );
sourceActor.SetParentOrigin(ParentOrigin::CENTER);
+ sourceActor.RegisterProperty( "uHSVDelta", hsvDelta );
Stage::GetCurrent().Add( sourceActor );
- ShaderEffect colorAdjuster = CreateColorAdjuster( hsvDelta, true /*ignore alpha to make bubble color always*/ );
- sourceActor.SetShaderEffect( colorAdjuster );
+ //Create renderer
+ Dali::Geometry geometry = CreateTexturedQuad();
+ Shader shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
+ Renderer renderer = Renderer::New( geometry, shader );
+ TextureSet textureSet = TextureSet::New();
+ textureSet.SetTexture(0u, bgTexture );
+ renderer.SetTextures( textureSet );
+ sourceActor.AddRenderer( renderer );
RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
RenderTask task = taskList.CreateTask();
task.SetExclusive(true);
task.SetCameraActor(mCameraActor);
task.GetCameraActor().SetInvertYAxis(true);
- task.SetTargetFrameBuffer( mEffectImage );
+ task.SetFrameBuffer( mFrameBuffer );
task.FinishedSignal().Connect(this, &BubbleEmitter::OnRenderFinished);
mRenderTaskRunning = true;
}
-void BubbleEmitter::SetShapeImage( Image shapeImage )
+void BubbleEmitter::SetBubbleShape( Texture shapeTexture )
{
- mSamplerBubbleShape.SetImage( shapeImage );
+ mTextureSet.SetTexture( 1, shapeTexture );
}
void BubbleEmitter::SetBubbleScale( float scale )
{
- for(unsigned int i=0; i < mNumActor; i++ )
+ for(unsigned int i=0; i < mNumRenderer; i++ )
{
- (mBubbleActors[i])->SetDynamicScale( scale );
+ mBubbleRenderers[i].SetDynamicScale( scale );
}
}
else
{
mDensity = density;
- mMeshGeometry = CreateGeometry( mNumBubblePerActor*mDensity );
- for(unsigned int i=0; i < mNumActor; i++ )
+ mMeshGeometry = CreateGeometry( mNumBubblePerRenderer*mDensity );
+ for(unsigned int i=0; i < mNumRenderer; i++ )
{
- (mBubbleActors[i])->SetGeometry( mMeshGeometry );
+ mBubbleRenderers[i].SetGeometry( mMeshGeometry );
}
}
}
-void BubbleEmitter::SetBlendMode( bool enable )
-{
- if(enable)
- {
- // linear overlay
- mMaterial.SetBlendFunc(BlendingFactor::SRC_ALPHA, BlendingFactor::ONE,
- BlendingFactor::ZERO, BlendingFactor::ONE);
- }
- else
- {
- // using default blend func
- mMaterial.SetBlendFunc( BlendingFactor::SRC_ALPHA, BlendingFactor::ONE_MINUS_SRC_ALPHA,
- BlendingFactor::ONE, BlendingFactor::ONE_MINUS_SRC_ALPHA );
- }
-}
-
// clear the resources created for the off screen rendering
void BubbleEmitter::OnRenderFinished(RenderTask& source)
{
mRenderTaskRunning = false;
Actor sourceActor = source.GetSourceActor();
- if( sourceActor )
- {
- ImageActor renderable = ImageActor::DownCast( sourceActor );
- if( renderable )
- {
- renderable.RemoveShaderEffect();
- }
- }
-
Stage stage = Stage::GetCurrent();
stage.Remove(sourceActor);
stage.GetRenderTaskList().RemoveTask(source);
// and trigger re-draw if not already running
if( ! mRenderTaskRunning )
{
- SetBackground( mBackgroundImage, mHSVDelta );
+ SetBackground( mBackgroundTexture, mHSVDelta );
}
}
void BubbleEmitter::EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
{
- unsigned int curUniform = mCurrentBubble % mNumBubblePerActor;
- unsigned int groupIdx = mCurrentBubble / mNumBubblePerActor;
- SetBubbleParameter( mBubbleActors[groupIdx], curUniform, emitPosition, direction, displacement);
- animation.AnimateTo( (mBubbleActors[groupIdx])->GetPercentageProperty(curUniform),
+ unsigned int curUniform = mCurrentBubble % mNumBubblePerRenderer;
+ unsigned int groupIdx = mCurrentBubble / mNumBubblePerRenderer;
+ SetBubbleParameter( mBubbleRenderers[groupIdx], curUniform, emitPosition - Vector2(mMovementArea.x*0.5f,mMovementArea.y*0.5f), direction, displacement);
+ animation.AnimateTo( mBubbleRenderers[groupIdx].GetPercentageProperty(curUniform),
1.f, AlphaFunction::LINEAR );
mCurrentBubble = (mCurrentBubble + 1) % mTotalNumOfBubble;
void BubbleEmitter::Restore()
{
- for(unsigned int i=0; i < mNumActor; i++ )
+ for(unsigned int i=0; i < mNumRenderer; i++ )
{
- (mBubbleActors[i])->ResetProperties();
+ mBubbleRenderers[i].ResetProperties();
}
}
Geometry BubbleEmitter::CreateGeometry( unsigned int numOfPatch )
{
unsigned int numVertex = numOfPatch*4u;
- std::vector<Vertex> vertexData;
- vertexData.reserve( numVertex );
+ Vector<Vertex> vertexData;
+ vertexData.Reserve( numVertex );
unsigned int numIndex = numOfPatch*6u;
- Vector<unsigned int> indexData;
+ Vector<unsigned short> indexData;
indexData.Reserve( numIndex );
for(unsigned int i = 0; i < numOfPatch; i++)
{
- float curSize = RandomRange(mBubbleSizeRange.x, mBubbleSizeRange.y);
+ float halfSize = RandomRange(mBubbleSizeRange.x, mBubbleSizeRange.y, mRandomSeed) * 0.5f;
float index = static_cast<float>( i );
- vertexData.push_back( Vertex( index, Vector2(0.f,0.f), Vector2(0.f,0.f) ) );
- vertexData.push_back( Vertex( index, Vector2(0.f,curSize), Vector2(0.f,1.f) ) );
- vertexData.push_back( Vertex( index, Vector2(curSize,curSize), Vector2(1.f,1.f) ) );
- vertexData.push_back( Vertex( index, Vector2(curSize,0.f), Vector2(1.f,0.f) ) );
+ vertexData.PushBack( Vertex( index, Vector2(-halfSize,-halfSize),Vector2(0.f,0.f) ) );
+ vertexData.PushBack( Vertex( index, Vector2(-halfSize, halfSize), Vector2(0.f,1.f) ) );
+ vertexData.PushBack( Vertex( index, Vector2( halfSize, halfSize), Vector2(1.f,1.f) ) );
+ vertexData.PushBack( Vertex( index, Vector2( halfSize,-halfSize), Vector2(1.f,0.f) ) );
- unsigned int idx = index * 4;
+ unsigned short idx = index * 4;
indexData.PushBack( idx );
indexData.PushBack( idx+1 );
indexData.PushBack( idx+2 );
vertexFormat["aIndex"] = Property::FLOAT;
vertexFormat["aPosition"] = Property::VECTOR2;
vertexFormat["aTexCoord"] = Property::VECTOR2;
- PropertyBuffer vertices = PropertyBuffer::New( vertexFormat, numVertex );
- vertices.SetData( &vertexData[0] );
-
- Property::Map indexFormat;
- indexFormat["indices"] = Property::INTEGER;
- PropertyBuffer indices = PropertyBuffer::New( indexFormat, numIndex );
- indices.SetData( &indexData[0] );
+ PropertyBuffer vertices = PropertyBuffer::New( vertexFormat );
+ vertices.SetData( &vertexData[0], numVertex );
Geometry geometry = Geometry::New();
geometry.AddVertexBuffer( vertices );
- geometry.SetIndexBuffer( indices );
+ geometry.SetIndexBuffer( &indexData[0], numIndex );
return geometry;
}
-void BubbleEmitter::SetBubbleParameter( BubbleActorPtr bubbleActor, unsigned int curUniform,
+void BubbleEmitter::SetBubbleParameter( BubbleRenderer& bubbleRenderer, unsigned int curUniform,
const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
{
Vector2 dir(direction);
int halfRange = displacement.x / 2;
// for the y coordinate, always negative, so bubbles always go upwards
- Vector2 randomVec(rand()%static_cast<int>(displacement.x) - halfRange, -rand()%static_cast<int>(displacement.y));
+ Vector2 randomVec( rand_r( &mRandomSeed ) % static_cast<int>(displacement.x) - halfRange, -rand_r( &mRandomSeed ) % static_cast<int>(displacement.y) );
dir.Normalize();
randomVec.x -= dir.x*halfRange;
randomVec.y *= 1.0f - fabsf(dir.x)*0.33f;
randomVec.y *= 0.33f;
}
Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y );
- bubbleActor->SetStartAndEndPosition( curUniform, startAndEndPos );
+ bubbleRenderer.SetStartAndEndPosition( curUniform, startAndEndPos );
- bubbleActor->SetPercentage( curUniform, 0.f);
+ bubbleRenderer.SetPercentage( curUniform, 0.f);
}
} // namespace Internal