2 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "bubble-emitter-impl.h"
22 #include <dali/public-api/animation/animation.h>
23 #include <dali/public-api/render-tasks/render-task-list.h>
24 #include <dali/public-api/rendering/texture.h>
25 #include <dali/public-api/rendering/shader.h>
28 #include <dali-toolkit/internal/controls/bubble-effect/bubble-effect.h>
29 #include <dali-toolkit/internal/controls/bubble-effect/bubble-renderer.h>
36 : index( 0.0f ), position(), textureCoord()
40 Vertex( float index, const Dali::Vector2& position, const Dali::Vector2& textureCoord )
41 : index( index ), position( position ), textureCoord( textureCoord )
46 Dali::Vector2 position;
47 Dali::Vector2 textureCoord;
52 * Return a random value between the given interval.
53 * @param[in] f0 The low bound
54 * @param[in] f1 The up bound
55 * @param[in] seed The seed to genergate random number
56 * @return A random value between the given interval
58 float RandomRange(float f0, float f1, unsigned int& seed)
60 return f0 + (rand_r(&seed) & 0xfff) * (f1 - f0) * (1.0f / 4095.0f);
63 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
64 attribute mediump vec2 aPosition;\n
65 attribute mediump vec2 aTexCoord;\n
66 uniform mediump vec3 uSize;\n
67 uniform mediump mat4 uMvpMatrix;\n
68 varying mediump vec2 vTexCoord;\n
73 gl_Position = uMvpMatrix * vec4(aPosition*uSize.xy,0.0,1.0);
74 vTexCoord = aTexCoord;\n
78 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
79 precision highp float;\n
80 uniform vec3 uHSVDelta;\n
81 varying mediump vec2 vTexCoord;\n
82 uniform sampler2D sTexture;\n
83 float rand(vec2 co) \n
85 return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); \n}
87 vec3 rgb2hsv(vec3 c)\n
89 vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n
90 vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n
91 vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n
93 float d = q.x - min(q.w, q.y);\n
95 return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n
97 vec3 hsv2rgb(vec3 c)\n
99 vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n
100 vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n
101 return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n
104 vec4 color = texture2D(sTexture, vTexCoord); \n
105 vec3 hsvColor = rgb2hsv( color.rgb );\n
106 // modify the hsv Value
107 hsvColor += uHSVDelta * rand(vTexCoord); \n
108 // if the new vale exceeds one, then decrease it
109 hsvColor -= max(hsvColor*2.0 - vec3(2.0), 0.0);\n
110 // if the new vale drops below zero, then increase it
111 hsvColor -= min(hsvColor*2.0, 0.0);\n
112 color = vec4( hsv2rgb( hsvColor ), 1.0 ); \n
113 gl_FragColor = color; \n
117 Dali::Geometry CreateTexturedQuad()
121 Dali::Vector2 position;
122 Dali::Vector2 texCoord;
125 static const Vertex data[] = {{ Dali::Vector2( -0.5f, -0.5f ), Dali::Vector2( 0.0f, 0.0f ) },
126 { Dali::Vector2( 0.5f, -0.5f ), Dali::Vector2( 1.0f, 0.0f ) },
127 { Dali::Vector2( -0.5f, 0.5f ), Dali::Vector2( 0.0f, 1.0f ) },
128 { Dali::Vector2( 0.5f, 0.5f ), Dali::Vector2( 1.0f, 1.0f ) }};
130 //Create a vertex buffer for vertex positions and texture coordinates
131 Dali::PropertyBuffer vertexBuffer = Dali::PropertyBuffer::New( Dali::Property::Map()
132 .Add( "aPosition", Dali::Property::VECTOR2 )
133 .Add( "aTexCoord", Dali::Property::VECTOR2 ) );
134 vertexBuffer.SetData( data, 4u );
136 //Create the geometry
137 Dali::Geometry geometry = Dali::Geometry::New();
138 geometry.AddVertexBuffer( vertexBuffer );
139 geometry.SetType(Dali::Geometry::TRIANGLE_STRIP );
154 BubbleEmitter::BubbleEmitter( const Vector2& movementArea,
155 Texture shapeTexture,
156 unsigned int maximumNumberOfBubble,
157 const Vector2& bubbleSizeRange )
158 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
159 mShapeTexture( shapeTexture ),
160 mMovementArea( movementArea ),
161 mBubbleSizeRange( bubbleSizeRange ),
163 mTotalNumOfBubble( maximumNumberOfBubble ),
166 mRenderTaskRunning(false)
168 // Calculate how many shaders are required
169 if( mTotalNumOfBubble>100 )
171 mNumBubblePerRenderer = 100;
172 mNumRenderer = mTotalNumOfBubble / 100;
173 if( mNumRenderer*mNumBubblePerRenderer < mTotalNumOfBubble )
176 mNumBubblePerRenderer = mTotalNumOfBubble / mNumRenderer+1;
177 mTotalNumOfBubble = mNumRenderer * mNumBubblePerRenderer;
182 mNumBubblePerRenderer = mTotalNumOfBubble;
186 mRandomSeed = time(NULL);
189 BubbleEmitter::~BubbleEmitter()
193 Toolkit::BubbleEmitter BubbleEmitter::New( const Vector2& winSize,
194 Texture shapeTexture,
195 unsigned int maximumNumberOfBubble,
196 const Vector2& bubbleSizeRange )
198 // Create the implementation
199 IntrusivePtr<BubbleEmitter> internalBubbleEmitter ( new BubbleEmitter( winSize, shapeTexture,
200 maximumNumberOfBubble,bubbleSizeRange ) );
202 // Pass ownership to Toolkit::BubbleEmitter handle
203 Toolkit::BubbleEmitter bubbleEmitter( *internalBubbleEmitter );
205 //Second phase of implementeation : Initialization
206 internalBubbleEmitter->OnInitialize();
208 return bubbleEmitter;
211 void BubbleEmitter::OnInitialize()
213 // Create the root actor, all the meshActor should be its children
214 mBubbleRoot = Actor::New();
215 mBubbleRoot.SetSize(mMovementArea);
217 // Prepare the frame buffer to store the color adjusted background texture
218 Vector2 imageSize = Vector2( mMovementArea.width/4.f, mMovementArea.height/4.f );
219 mFrameBuffer = FrameBuffer::New( imageSize.x, imageSize.y, 0 );
220 mEffectTexture = Texture::New( TextureType::TEXTURE_2D, Pixel::RGBA8888, imageSize.x, imageSize.y );
221 mFrameBuffer.AttachColorTexture( mEffectTexture );
223 // Generate the geometry, which is used by all bubbleActors
224 mMeshGeometry = CreateGeometry( mNumBubblePerRenderer*mDensity );
226 Shader bubbleShader = CreateBubbleShader( mNumBubblePerRenderer );
228 mTextureSet = TextureSet::New();
229 mTextureSet.SetTexture( 0u, mEffectTexture );
230 mTextureSet.SetTexture( 1u, mShapeTexture );
232 // Create the renderers to render the bubbles
233 mBubbleRenderers.resize( mNumRenderer );
234 for(unsigned int i=0; i < mNumRenderer; i++ )
236 mBubbleRenderers[i].Initialize( mNumBubblePerRenderer, mMovementArea, mMeshGeometry, mTextureSet, bubbleShader );
237 mBubbleRoot.AddRenderer( mBubbleRenderers[i].GetRenderer() );
240 // Create a cameraActor for the off screen render task.
241 mCameraActor = CameraActor::New(mMovementArea);
242 mCameraActor.SetParentOrigin(ParentOrigin::CENTER);
244 Stage stage = Stage::GetCurrent();
246 stage.Add(mCameraActor);
247 stage.ContextRegainedSignal().Connect(this, &BubbleEmitter::OnContextRegained);
250 Actor BubbleEmitter::GetRootActor()
255 void BubbleEmitter::SetBackground( Texture bgTexture, const Vector3& hsvDelta )
257 mBackgroundTexture = bgTexture;
258 mHSVDelta = hsvDelta;
260 //Create RenderTask source actor
261 Actor sourceActor = Actor::New();
262 sourceActor.SetSize( mMovementArea );
263 sourceActor.SetParentOrigin(ParentOrigin::CENTER);
264 sourceActor.RegisterProperty( "uHSVDelta", hsvDelta );
265 Stage::GetCurrent().Add( sourceActor );
268 Dali::Geometry geometry = CreateTexturedQuad();
269 Shader shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
270 Renderer renderer = Renderer::New( geometry, shader );
271 TextureSet textureSet = TextureSet::New();
272 textureSet.SetTexture(0u, bgTexture );
273 renderer.SetTextures( textureSet );
274 sourceActor.AddRenderer( renderer );
276 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
277 RenderTask task = taskList.CreateTask();
278 task.SetRefreshRate( RenderTask::REFRESH_ONCE );
279 task.SetSourceActor( sourceActor );
280 task.SetExclusive(true);
281 task.SetCameraActor(mCameraActor);
282 task.GetCameraActor().SetInvertYAxis(true);
283 task.SetFrameBuffer( mFrameBuffer );
284 task.FinishedSignal().Connect(this, &BubbleEmitter::OnRenderFinished);
285 mRenderTaskRunning = true;
288 void BubbleEmitter::SetBubbleShape( Texture shapeTexture )
290 mTextureSet.SetTexture( 1, shapeTexture );
293 void BubbleEmitter::SetBubbleScale( float scale )
295 for(unsigned int i=0; i < mNumRenderer; i++ )
297 mBubbleRenderers[i].SetDynamicScale( scale );
301 void BubbleEmitter::SetBubbleDensity( unsigned int density )
303 DALI_ASSERT_ALWAYS( density>0 && density<=9 && " Only densities between 1 to 9 are valid " );
305 if( density == mDensity )
312 mMeshGeometry = CreateGeometry( mNumBubblePerRenderer*mDensity );
313 for(unsigned int i=0; i < mNumRenderer; i++ )
315 mBubbleRenderers[i].SetGeometry( mMeshGeometry );
320 // clear the resources created for the off screen rendering
321 void BubbleEmitter::OnRenderFinished(RenderTask& source)
323 mRenderTaskRunning = false;
324 Actor sourceActor = source.GetSourceActor();
325 Stage stage = Stage::GetCurrent();
326 stage.Remove(sourceActor);
327 stage.GetRenderTaskList().RemoveTask(source);
330 void BubbleEmitter::OnContextRegained()
332 // Context was lost, so the framebuffer has been destroyed. Re-create render task
333 // and trigger re-draw if not already running
334 if( ! mRenderTaskRunning )
336 SetBackground( mBackgroundTexture, mHSVDelta );
340 void BubbleEmitter::EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
342 unsigned int curUniform = mCurrentBubble % mNumBubblePerRenderer;
343 unsigned int groupIdx = mCurrentBubble / mNumBubblePerRenderer;
344 SetBubbleParameter( mBubbleRenderers[groupIdx], curUniform, emitPosition - Vector2(mMovementArea.x*0.5f,mMovementArea.y*0.5f), direction, displacement);
345 animation.AnimateTo( mBubbleRenderers[groupIdx].GetPercentageProperty(curUniform),
346 1.f, AlphaFunction::LINEAR );
348 mCurrentBubble = (mCurrentBubble + 1) % mTotalNumOfBubble;
351 void BubbleEmitter::Restore()
353 for(unsigned int i=0; i < mNumRenderer; i++ )
355 mBubbleRenderers[i].ResetProperties();
359 Geometry BubbleEmitter::CreateGeometry( unsigned int numOfPatch )
361 unsigned int numVertex = numOfPatch*4u;
362 Vector<Vertex> vertexData;
363 vertexData.Reserve( numVertex );
365 unsigned int numIndex = numOfPatch*6u;
366 Vector<unsigned short> indexData;
367 indexData.Reserve( numIndex );
369 for(unsigned int i = 0; i < numOfPatch; i++)
371 float halfSize = RandomRange(mBubbleSizeRange.x, mBubbleSizeRange.y, mRandomSeed) * 0.5f;
373 float index = static_cast<float>( i );
374 vertexData.PushBack( Vertex( index, Vector2(-halfSize,-halfSize),Vector2(0.f,0.f) ) );
375 vertexData.PushBack( Vertex( index, Vector2(-halfSize, halfSize), Vector2(0.f,1.f) ) );
376 vertexData.PushBack( Vertex( index, Vector2( halfSize, halfSize), Vector2(1.f,1.f) ) );
377 vertexData.PushBack( Vertex( index, Vector2( halfSize,-halfSize), Vector2(1.f,0.f) ) );
379 unsigned short idx = index * 4;
380 indexData.PushBack( idx );
381 indexData.PushBack( idx+1 );
382 indexData.PushBack( idx+2 );
383 indexData.PushBack( idx );
384 indexData.PushBack( idx+2 );
385 indexData.PushBack( idx+3 );
388 Property::Map vertexFormat;
389 vertexFormat["aIndex"] = Property::FLOAT;
390 vertexFormat["aPosition"] = Property::VECTOR2;
391 vertexFormat["aTexCoord"] = Property::VECTOR2;
392 PropertyBuffer vertices = PropertyBuffer::New( vertexFormat );
393 vertices.SetData( &vertexData[0], numVertex );
395 Geometry geometry = Geometry::New();
396 geometry.AddVertexBuffer( vertices );
397 geometry.SetIndexBuffer( &indexData[0], numIndex );
402 void BubbleEmitter::SetBubbleParameter( BubbleRenderer& bubbleRenderer, unsigned int curUniform,
403 const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
405 Vector2 dir(direction);
407 int halfRange = displacement.x / 2;
408 // for the y coordinate, always negative, so bubbles always go upwards
409 Vector2 randomVec(rand_r(&mRandomSeed) % static_cast<int>(displacement.x) - halfRange, -rand_r(&mRandomSeed) % static_cast<int>(displacement.y));
411 randomVec.x -= dir.x*halfRange;
412 randomVec.y *= 1.0f - fabsf(dir.x)*0.33f;
414 if(randomVec.y > 0.0f)
416 randomVec.y *= 0.33f;
418 Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y );
419 bubbleRenderer.SetStartAndEndPosition( curUniform, startAndEndPos );
421 bubbleRenderer.SetPercentage( curUniform, 0.f);
424 } // namespace Internal
426 } // namespace Toolkit