2 * Copyright (c) 2020 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;
51 * Return a random value between the given interval.
52 * @param[in] f0 The low bound
53 * @param[in] f1 The up bound
54 * @param[in] seed The seed to genergate random number
55 * @return A random value between the given interval
57 float RandomRange(float f0, float f1, unsigned int& seed)
59 return f0 + (rand_r( &seed ) & 0xfff) * (f1-f0) * (1.0f/4095.0f);
62 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
63 attribute mediump vec2 aPosition;\n
64 attribute mediump vec2 aTexCoord;\n
65 uniform mediump vec3 uSize;\n
66 uniform mediump mat4 uMvpMatrix;\n
67 varying mediump vec2 vTexCoord;\n
72 gl_Position = uMvpMatrix * vec4(aPosition*uSize.xy,0.0,1.0);
73 vTexCoord = aTexCoord;\n
77 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
78 precision highp float;\n
79 uniform vec3 uHSVDelta;\n
80 varying mediump vec2 vTexCoord;\n
81 uniform sampler2D sTexture;\n
82 float rand(vec2 co) \n
84 return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); \n}
86 vec3 rgb2hsv(vec3 c)\n
88 vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n
89 vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n
90 vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n
92 float d = q.x - min(q.w, q.y);\n
94 return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n
96 vec3 hsv2rgb(vec3 c)\n
98 vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n
99 vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n
100 return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n
103 vec4 color = texture2D(sTexture, vTexCoord); \n
104 vec3 hsvColor = rgb2hsv( color.rgb );\n
105 // modify the hsv Value
106 hsvColor += uHSVDelta * rand(vTexCoord); \n
107 // if the new vale exceeds one, then decrease it
108 hsvColor -= max(hsvColor*2.0 - vec3(2.0), 0.0);\n
109 // if the new vale drops below zero, then increase it
110 hsvColor -= min(hsvColor*2.0, 0.0);\n
111 color = vec4( hsv2rgb( hsvColor ), 1.0 ); \n
112 gl_FragColor = color; \n
116 Dali::Geometry CreateTexturedQuad()
120 Dali::Vector2 position;
121 Dali::Vector2 texCoord;
124 static const Vertex data[] = {{ Dali::Vector2( -0.5f, -0.5f ), Dali::Vector2( 0.0f, 0.0f ) },
125 { Dali::Vector2( 0.5f, -0.5f ), Dali::Vector2( 1.0f, 0.0f ) },
126 { Dali::Vector2( -0.5f, 0.5f ), Dali::Vector2( 0.0f, 1.0f ) },
127 { Dali::Vector2( 0.5f, 0.5f ), Dali::Vector2( 1.0f, 1.0f ) }};
129 //Create a vertex buffer for vertex positions and texture coordinates
130 Dali::PropertyBuffer vertexBuffer = Dali::PropertyBuffer::New( Dali::Property::Map()
131 .Add( "aPosition", Dali::Property::VECTOR2 )
132 .Add( "aTexCoord", Dali::Property::VECTOR2 ) );
133 vertexBuffer.SetData( data, 4u );
135 //Create the geometry
136 Dali::Geometry geometry = Dali::Geometry::New();
137 geometry.AddVertexBuffer( vertexBuffer );
138 geometry.SetType(Dali::Geometry::TRIANGLE_STRIP );
153 BubbleEmitter::BubbleEmitter( const Vector2& movementArea,
154 Texture shapeTexture,
155 unsigned int maximumNumberOfBubble,
156 const Vector2& bubbleSizeRange )
157 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
158 mShapeTexture( shapeTexture ),
159 mMovementArea( movementArea ),
160 mBubbleSizeRange( bubbleSizeRange ),
162 mTotalNumOfBubble( maximumNumberOfBubble ),
165 mRenderTaskRunning(false)
167 // Calculate how many shaders are required
168 if( mTotalNumOfBubble>100 )
170 mNumBubblePerRenderer = 100;
171 mNumRenderer = mTotalNumOfBubble / 100;
172 if( mNumRenderer*mNumBubblePerRenderer < mTotalNumOfBubble )
175 mNumBubblePerRenderer = mTotalNumOfBubble / mNumRenderer+1;
176 mTotalNumOfBubble = mNumRenderer * mNumBubblePerRenderer;
181 mNumBubblePerRenderer = mTotalNumOfBubble;
185 mRandomSeed = time( NULL );
188 BubbleEmitter::~BubbleEmitter()
192 Toolkit::BubbleEmitter BubbleEmitter::New( const Vector2& winSize,
193 Texture shapeTexture,
194 unsigned int maximumNumberOfBubble,
195 const Vector2& bubbleSizeRange )
197 // Create the implementation
198 IntrusivePtr<BubbleEmitter> internalBubbleEmitter ( new BubbleEmitter( winSize, shapeTexture,
199 maximumNumberOfBubble,bubbleSizeRange ) );
201 // Pass ownership to Toolkit::BubbleEmitter handle
202 Toolkit::BubbleEmitter bubbleEmitter( *internalBubbleEmitter );
204 //Second phase of implementeation : Initialization
205 internalBubbleEmitter->OnInitialize();
207 return bubbleEmitter;
210 void BubbleEmitter::OnInitialize()
212 // Create the root actor, all the meshActor should be its children
213 mBubbleRoot = Actor::New();
214 mBubbleRoot.SetProperty( Actor::Property::SIZE, mMovementArea );
216 // Prepare the frame buffer to store the color adjusted background texture
217 Vector2 imageSize = Vector2( mMovementArea.width/4.f, mMovementArea.height/4.f );
218 mFrameBuffer = FrameBuffer::New( imageSize.x, imageSize.y, FrameBuffer::Attachment::NONE );
219 mEffectTexture = Texture::New( TextureType::TEXTURE_2D, Pixel::RGBA8888, imageSize.x, imageSize.y );
220 mFrameBuffer.AttachColorTexture( mEffectTexture );
222 // Generate the geometry, which is used by all bubbleActors
223 mMeshGeometry = CreateGeometry( mNumBubblePerRenderer*mDensity );
225 Shader bubbleShader = CreateBubbleShader( mNumBubblePerRenderer );
227 mTextureSet = TextureSet::New();
228 mTextureSet.SetTexture( 0u, mEffectTexture );
229 mTextureSet.SetTexture( 1u, mShapeTexture );
231 // Create the renderers to render the bubbles
232 mBubbleRenderers.resize( mNumRenderer );
233 for(unsigned int i=0; i < mNumRenderer; i++ )
235 mBubbleRenderers[i].Initialize( mNumBubblePerRenderer, mMovementArea, mMeshGeometry, mTextureSet, bubbleShader );
236 mBubbleRoot.AddRenderer( mBubbleRenderers[i].GetRenderer() );
239 // Create a cameraActor for the off screen render task.
240 mCameraActor = CameraActor::New(mMovementArea);
241 mCameraActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
243 Stage stage = Stage::GetCurrent();
245 stage.Add(mCameraActor);
246 stage.ContextRegainedSignal().Connect(this, &BubbleEmitter::OnContextRegained);
249 Actor BubbleEmitter::GetRootActor()
254 void BubbleEmitter::SetBackground( Texture bgTexture, const Vector3& hsvDelta )
256 mBackgroundTexture = bgTexture;
257 mHSVDelta = hsvDelta;
259 //Create RenderTask source actor
260 Actor sourceActor = Actor::New();
261 sourceActor.SetProperty( Actor::Property::SIZE, mMovementArea );
262 sourceActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
263 sourceActor.RegisterProperty( "uHSVDelta", hsvDelta );
264 Stage::GetCurrent().Add( sourceActor );
267 Dali::Geometry geometry = CreateTexturedQuad();
268 Shader shader = Shader::New( VERTEX_SHADER, FRAGMENT_SHADER );
269 Renderer renderer = Renderer::New( geometry, shader );
270 TextureSet textureSet = TextureSet::New();
271 textureSet.SetTexture(0u, bgTexture );
272 renderer.SetTextures( textureSet );
273 sourceActor.AddRenderer( renderer );
275 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
276 RenderTask task = taskList.CreateTask();
277 task.SetRefreshRate( RenderTask::REFRESH_ONCE );
278 task.SetSourceActor( sourceActor );
279 task.SetExclusive(true);
280 task.SetCameraActor(mCameraActor);
281 task.GetCameraActor().SetInvertYAxis(true);
282 task.SetFrameBuffer( mFrameBuffer );
283 task.FinishedSignal().Connect(this, &BubbleEmitter::OnRenderFinished);
284 mRenderTaskRunning = true;
287 void BubbleEmitter::SetBubbleShape( Texture shapeTexture )
289 mTextureSet.SetTexture( 1, shapeTexture );
292 void BubbleEmitter::SetBubbleScale( float scale )
294 for(unsigned int i=0; i < mNumRenderer; i++ )
296 mBubbleRenderers[i].SetDynamicScale( scale );
300 void BubbleEmitter::SetBubbleDensity( unsigned int density )
302 DALI_ASSERT_ALWAYS( density>0 && density<=9 && " Only densities between 1 to 9 are valid " );
304 if( density == mDensity )
311 mMeshGeometry = CreateGeometry( mNumBubblePerRenderer*mDensity );
312 for(unsigned int i=0; i < mNumRenderer; i++ )
314 mBubbleRenderers[i].SetGeometry( mMeshGeometry );
319 // clear the resources created for the off screen rendering
320 void BubbleEmitter::OnRenderFinished(RenderTask& source)
322 mRenderTaskRunning = false;
323 Actor sourceActor = source.GetSourceActor();
324 Stage stage = Stage::GetCurrent();
325 stage.Remove(sourceActor);
326 stage.GetRenderTaskList().RemoveTask(source);
329 void BubbleEmitter::OnContextRegained()
331 // Context was lost, so the framebuffer has been destroyed. Re-create render task
332 // and trigger re-draw if not already running
333 if( ! mRenderTaskRunning )
335 SetBackground( mBackgroundTexture, mHSVDelta );
339 void BubbleEmitter::EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
341 unsigned int curUniform = mCurrentBubble % mNumBubblePerRenderer;
342 unsigned int groupIdx = mCurrentBubble / mNumBubblePerRenderer;
343 SetBubbleParameter( mBubbleRenderers[groupIdx], curUniform, emitPosition - Vector2(mMovementArea.x*0.5f,mMovementArea.y*0.5f), direction, displacement);
344 animation.AnimateTo( mBubbleRenderers[groupIdx].GetPercentageProperty(curUniform),
345 1.f, AlphaFunction::LINEAR );
347 mCurrentBubble = (mCurrentBubble + 1) % mTotalNumOfBubble;
350 void BubbleEmitter::Restore()
352 for(unsigned int i=0; i < mNumRenderer; i++ )
354 mBubbleRenderers[i].ResetProperties();
358 Geometry BubbleEmitter::CreateGeometry( unsigned int numOfPatch )
360 unsigned int numVertex = numOfPatch*4u;
361 Vector<Vertex> vertexData;
362 vertexData.Reserve( numVertex );
364 unsigned int numIndex = numOfPatch*6u;
365 Vector<unsigned short> indexData;
366 indexData.Reserve( numIndex );
368 for(unsigned int i = 0; i < numOfPatch; i++)
370 float halfSize = RandomRange(mBubbleSizeRange.x, mBubbleSizeRange.y, mRandomSeed) * 0.5f;
372 float index = static_cast<float>( i );
373 vertexData.PushBack( Vertex( index, Vector2(-halfSize,-halfSize),Vector2(0.f,0.f) ) );
374 vertexData.PushBack( Vertex( index, Vector2(-halfSize, halfSize), Vector2(0.f,1.f) ) );
375 vertexData.PushBack( Vertex( index, Vector2( halfSize, halfSize), Vector2(1.f,1.f) ) );
376 vertexData.PushBack( Vertex( index, Vector2( halfSize,-halfSize), Vector2(1.f,0.f) ) );
378 unsigned short idx = index * 4;
379 indexData.PushBack( idx );
380 indexData.PushBack( idx+1 );
381 indexData.PushBack( idx+2 );
382 indexData.PushBack( idx );
383 indexData.PushBack( idx+2 );
384 indexData.PushBack( idx+3 );
387 Property::Map vertexFormat;
388 vertexFormat["aIndex"] = Property::FLOAT;
389 vertexFormat["aPosition"] = Property::VECTOR2;
390 vertexFormat["aTexCoord"] = Property::VECTOR2;
391 PropertyBuffer vertices = PropertyBuffer::New( vertexFormat );
392 vertices.SetData( &vertexData[0], numVertex );
394 Geometry geometry = Geometry::New();
395 geometry.AddVertexBuffer( vertices );
396 geometry.SetIndexBuffer( &indexData[0], numIndex );
401 void BubbleEmitter::SetBubbleParameter( BubbleRenderer& bubbleRenderer, unsigned int curUniform,
402 const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
404 Vector2 dir(direction);
406 int halfRange = displacement.x / 2;
407 // for the y coordinate, always negative, so bubbles always go upwards
408 Vector2 randomVec( rand_r( &mRandomSeed ) % static_cast<int>(displacement.x) - halfRange, -rand_r( &mRandomSeed ) % static_cast<int>(displacement.y) );
410 randomVec.x -= dir.x*halfRange;
411 randomVec.y *= 1.0f - fabsf(dir.x)*0.33f;
413 if(randomVec.y > 0.0f)
415 randomVec.y *= 0.33f;
417 Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y );
418 bubbleRenderer.SetStartAndEndPosition( curUniform, startAndEndPos );
420 bubbleRenderer.SetPercentage( curUniform, 0.f);
423 } // namespace Internal
425 } // namespace Toolkit