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