Shader compilation tool for dali-toolkit
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / bubble-effect / bubble-emitter-impl.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include "bubble-emitter-impl.h"
20
21 // EXTERNAL INCLUDES
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>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/internal/controls/bubble-effect/bubble-effect.h>
29 #include <dali-toolkit/internal/controls/bubble-effect/bubble-renderer.h>
30 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
31
32 namespace
33 {
34 struct Vertex
35 {
36   Vertex()
37   : index( 0.0f ), position(), textureCoord()
38   {
39   }
40
41   Vertex( float index, const Dali::Vector2& position, const Dali::Vector2& textureCoord )
42   : index( index ), position( position ), textureCoord( textureCoord )
43   {
44   }
45
46   float index;
47   Dali::Vector2 position;
48   Dali::Vector2 textureCoord;
49 };
50
51 /**
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
57  */
58 float RandomRange(float f0, float f1, unsigned int& seed)
59 {
60   return f0 + (rand_r( &seed ) & 0xfff) * (f1-f0) * (1.0f/4095.0f);
61 }
62
63 Dali::Geometry CreateTexturedQuad()
64 {
65   struct Vertex
66   {
67     Dali::Vector2 position;
68     Dali::Vector2 texCoord;
69   };
70
71   static const Vertex data[] = {{ Dali::Vector2( -0.5f, -0.5f ), Dali::Vector2( 0.0f, 0.0f ) },
72                                 { Dali::Vector2(  0.5f, -0.5f ), Dali::Vector2( 1.0f, 0.0f ) },
73                                 { Dali::Vector2( -0.5f,  0.5f ), Dali::Vector2( 0.0f, 1.0f ) },
74                                 { Dali::Vector2(  0.5f,  0.5f ), Dali::Vector2( 1.0f, 1.0f ) }};
75
76   // Create a vertex buffer for vertex positions and texture coordinates
77   Dali::VertexBuffer vertexBuffer = Dali::VertexBuffer::New( Dali::Property::Map()
78                                               .Add( "aPosition", Dali::Property::VECTOR2 )
79                                               .Add( "aTexCoord", Dali::Property::VECTOR2 ) );
80   vertexBuffer.SetData( data, 4u );
81
82   //Create the geometry
83   Dali::Geometry geometry = Dali::Geometry::New();
84   geometry.AddVertexBuffer( vertexBuffer );
85   geometry.SetType(Dali::Geometry::TRIANGLE_STRIP );
86
87   return geometry;
88 }
89
90 }
91
92 namespace Dali
93 {
94
95 namespace Toolkit
96 {
97
98 namespace Internal
99 {
100 BubbleEmitter::BubbleEmitter( const Vector2& movementArea,
101                               Texture shapeTexture,
102                               unsigned int maximumNumberOfBubble,
103                               const Vector2& bubbleSizeRange )
104 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
105   mShapeTexture( shapeTexture ),
106   mMovementArea( movementArea ),
107   mBubbleSizeRange( bubbleSizeRange ),
108   mDensity( 5 ),
109   mTotalNumOfBubble( maximumNumberOfBubble ),
110   mCurrentBubble( 0 ),
111   mRandomSeed( 0 ),
112   mRenderTaskRunning(false)
113 {
114   // Calculate how many shaders are required
115   if( mTotalNumOfBubble>100 )
116   {
117     mNumBubblePerRenderer = 100;
118     mNumRenderer = mTotalNumOfBubble / 100;
119     if( mNumRenderer*mNumBubblePerRenderer < mTotalNumOfBubble )
120     {
121       mNumRenderer++;
122       mNumBubblePerRenderer =  mTotalNumOfBubble / mNumRenderer+1;
123       mTotalNumOfBubble = mNumRenderer * mNumBubblePerRenderer;
124     }
125   }
126   else
127   {
128     mNumBubblePerRenderer = mTotalNumOfBubble;
129     mNumRenderer = 1;
130   }
131
132   mRandomSeed = time( NULL );
133 }
134
135 BubbleEmitter::~BubbleEmitter()
136 {
137 }
138
139 Toolkit::BubbleEmitter BubbleEmitter::New( const Vector2& winSize,
140                                            Texture shapeTexture,
141                                            unsigned int maximumNumberOfBubble,
142                                            const Vector2& bubbleSizeRange )
143 {
144   // Create the implementation
145    IntrusivePtr<BubbleEmitter> internalBubbleEmitter ( new BubbleEmitter( winSize, shapeTexture,
146                                                             maximumNumberOfBubble,bubbleSizeRange ) );
147
148   // Pass ownership to Toolkit::BubbleEmitter handle
149   Toolkit::BubbleEmitter bubbleEmitter( *internalBubbleEmitter );
150
151   //Second phase of implementeation : Initialization
152   internalBubbleEmitter->OnInitialize();
153
154   return bubbleEmitter;
155 }
156
157 void BubbleEmitter::OnInitialize()
158 {
159   // Create the root actor, all the meshActor should be its children
160   mBubbleRoot = Actor::New();
161   mBubbleRoot.SetProperty( Actor::Property::SIZE, mMovementArea );
162
163   // Prepare the frame buffer to store the color adjusted background texture
164   Vector2 imageSize = Vector2( mMovementArea.width/4.f, mMovementArea.height/4.f );
165   mFrameBuffer = FrameBuffer::New( imageSize.x, imageSize.y, FrameBuffer::Attachment::NONE );
166   mEffectTexture = Texture::New( TextureType::TEXTURE_2D, Pixel::RGBA8888, imageSize.x, imageSize.y );
167   mFrameBuffer.AttachColorTexture( mEffectTexture );
168
169   // Generate the geometry, which is used by all bubbleActors
170   mMeshGeometry =  CreateGeometry( mNumBubblePerRenderer*mDensity );
171
172   Shader bubbleShader = CreateBubbleShader( mNumBubblePerRenderer );
173
174   mTextureSet = TextureSet::New();
175   mTextureSet.SetTexture( 0u, mEffectTexture );
176   mTextureSet.SetTexture( 1u, mShapeTexture );
177
178   // Create the renderers to render the bubbles
179   mBubbleRenderers.resize( mNumRenderer );
180   for(unsigned int i=0; i < mNumRenderer; i++ )
181   {
182     mBubbleRenderers[i].Initialize( mNumBubblePerRenderer, mMovementArea, mMeshGeometry, mTextureSet, bubbleShader );
183     mBubbleRoot.AddRenderer( mBubbleRenderers[i].GetRenderer() );
184   }
185
186   // Create a cameraActor for the off screen render task.
187   mCameraActor = CameraActor::New(mMovementArea);
188   mCameraActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
189
190   Stage stage = Stage::GetCurrent();
191
192   stage.Add(mCameraActor);
193   stage.ContextRegainedSignal().Connect(this, &BubbleEmitter::OnContextRegained);
194 }
195
196 Actor BubbleEmitter::GetRootActor()
197 {
198   return mBubbleRoot;
199 }
200
201 void BubbleEmitter::SetBackground( Texture bgTexture, const Vector3& hsvDelta )
202 {
203   mBackgroundTexture = bgTexture;
204   mHSVDelta = hsvDelta;
205
206   //Create RenderTask source actor
207   Actor sourceActor = Actor::New();
208   sourceActor.SetProperty( Actor::Property::SIZE, mMovementArea );
209   sourceActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
210   sourceActor.RegisterProperty( "uHSVDelta", hsvDelta );
211   Stage::GetCurrent().Add( sourceActor );
212
213   //Create renderer
214   Dali::Geometry geometry = CreateTexturedQuad();
215   Shader shader = Shader::New( SHADER_BUBBLE_EMITTER_VERT, SHADER_BUBBLE_EMITTER_FRAG );
216   Renderer renderer = Renderer::New( geometry, shader );
217   TextureSet textureSet = TextureSet::New();
218   textureSet.SetTexture(0u, bgTexture );
219   renderer.SetTextures( textureSet );
220   sourceActor.AddRenderer( renderer );
221
222   RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
223   RenderTask task = taskList.CreateTask();
224   task.SetRefreshRate( RenderTask::REFRESH_ONCE );
225   task.SetSourceActor( sourceActor );
226   task.SetExclusive(true);
227   task.SetCameraActor(mCameraActor);
228   task.GetCameraActor().SetInvertYAxis(true);
229   task.SetFrameBuffer( mFrameBuffer );
230   task.FinishedSignal().Connect(this, &BubbleEmitter::OnRenderFinished);
231   mRenderTaskRunning = true;
232 }
233
234 void BubbleEmitter::SetBubbleShape( Texture shapeTexture )
235 {
236   mTextureSet.SetTexture( 1, shapeTexture );
237 }
238
239 void BubbleEmitter::SetBubbleScale( float scale )
240 {
241   for(unsigned int i=0; i < mNumRenderer; i++ )
242   {
243     mBubbleRenderers[i].SetDynamicScale( scale );
244   }
245 }
246
247 void BubbleEmitter::SetBubbleDensity( unsigned int density )
248 {
249   DALI_ASSERT_ALWAYS( density>0 && density<=9 && " Only densities between 1 to 9 are valid " );
250
251   if( density == mDensity )
252   {
253     return;
254   }
255   else
256   {
257     mDensity = density;
258     mMeshGeometry =  CreateGeometry( mNumBubblePerRenderer*mDensity );
259     for(unsigned int i=0; i < mNumRenderer; i++ )
260     {
261       mBubbleRenderers[i].SetGeometry( mMeshGeometry );
262     }
263   }
264 }
265
266 // clear the resources created for the off screen rendering
267 void BubbleEmitter::OnRenderFinished(RenderTask& source)
268 {
269   mRenderTaskRunning = false;
270   Actor sourceActor = source.GetSourceActor();
271   Stage stage = Stage::GetCurrent();
272   stage.Remove(sourceActor);
273   stage.GetRenderTaskList().RemoveTask(source);
274 }
275
276 void BubbleEmitter::OnContextRegained()
277 {
278   // Context was lost, so the framebuffer has been destroyed. Re-create render task
279   // and trigger re-draw if not already running
280   if( ! mRenderTaskRunning )
281   {
282     SetBackground( mBackgroundTexture, mHSVDelta );
283   }
284 }
285
286 void BubbleEmitter::EmitBubble( Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
287 {
288   unsigned int curUniform = mCurrentBubble  % mNumBubblePerRenderer;
289   unsigned int groupIdx = mCurrentBubble / mNumBubblePerRenderer;
290   SetBubbleParameter( mBubbleRenderers[groupIdx], curUniform, emitPosition - Vector2(mMovementArea.x*0.5f,mMovementArea.y*0.5f), direction, displacement);
291   animation.AnimateTo( mBubbleRenderers[groupIdx].GetPercentageProperty(curUniform),
292                        1.f, AlphaFunction::LINEAR );
293
294   mCurrentBubble = (mCurrentBubble + 1) % mTotalNumOfBubble;
295 }
296
297 void BubbleEmitter::Restore()
298 {
299   for(unsigned int i=0; i < mNumRenderer; i++ )
300   {
301     mBubbleRenderers[i].ResetProperties();
302   }
303 }
304
305 Geometry BubbleEmitter::CreateGeometry( unsigned int numOfPatch )
306 {
307   unsigned int numVertex = numOfPatch*4u;
308   Vector<Vertex> vertexData;
309   vertexData.Reserve( numVertex );
310
311   unsigned int numIndex = numOfPatch*6u;
312   Vector<unsigned short> indexData;
313   indexData.Reserve( numIndex );
314
315   for(unsigned int i = 0; i < numOfPatch; i++)
316   {
317     float halfSize = RandomRange(mBubbleSizeRange.x, mBubbleSizeRange.y, mRandomSeed) * 0.5f;
318
319     float index = static_cast<float>( i );
320     vertexData.PushBack( Vertex( index, Vector2(-halfSize,-halfSize),Vector2(0.f,0.f) ) );
321     vertexData.PushBack( Vertex( index, Vector2(-halfSize, halfSize), Vector2(0.f,1.f) ) );
322     vertexData.PushBack( Vertex( index, Vector2( halfSize, halfSize),  Vector2(1.f,1.f) ) );
323     vertexData.PushBack( Vertex( index, Vector2( halfSize,-halfSize), Vector2(1.f,0.f) ) );
324
325     unsigned short idx = index * 4;
326     indexData.PushBack( idx );
327     indexData.PushBack( idx+1 );
328     indexData.PushBack( idx+2 );
329     indexData.PushBack( idx );
330     indexData.PushBack( idx+2 );
331     indexData.PushBack( idx+3 );
332   }
333
334   Property::Map vertexFormat;
335   vertexFormat["aIndex"] = Property::FLOAT;
336   vertexFormat["aPosition"] = Property::VECTOR2;
337   vertexFormat["aTexCoord"] = Property::VECTOR2;
338   VertexBuffer vertices = VertexBuffer::New( vertexFormat );
339   vertices.SetData( &vertexData[0], numVertex );
340
341   Geometry geometry = Geometry::New();
342   geometry.AddVertexBuffer( vertices );
343   geometry.SetIndexBuffer( &indexData[0], numIndex );
344
345   return geometry;
346 }
347
348 void BubbleEmitter::SetBubbleParameter( BubbleRenderer& bubbleRenderer, unsigned int curUniform,
349                                         const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement )
350 {
351   Vector2 dir(direction);
352
353   int halfRange = displacement.x / 2;
354   // for the y coordinate, always negative, so bubbles always go upwards
355   Vector2 randomVec( rand_r( &mRandomSeed ) % static_cast<int>(displacement.x) - halfRange, -rand_r( &mRandomSeed ) % static_cast<int>(displacement.y) );
356   dir.Normalize();
357   randomVec.x -= dir.x*halfRange;
358   randomVec.y *= 1.0f - fabsf(dir.x)*0.33f;
359
360   if(randomVec.y > 0.0f)
361   {
362     randomVec.y *= 0.33f;
363   }
364   Vector4 startAndEndPos( emitPosition.x, emitPosition.y, emitPosition.x+randomVec.x, emitPosition.y+randomVec.y );
365   bubbleRenderer.SetStartAndEndPosition( curUniform, startAndEndPos );
366
367   bubbleRenderer.SetPercentage( curUniform, 0.f);
368 }
369
370 } // namespace Internal
371
372 } // namespace Toolkit
373
374 } // namespace Dali