2 * Copyright (c) 2021 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/shader.h>
25 #include <dali/public-api/rendering/texture.h>
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>
43 Vertex(float index, const Dali::Vector2& position, const Dali::Vector2& textureCoord)
46 textureCoord(textureCoord)
51 Dali::Vector2 position;
52 Dali::Vector2 textureCoord;
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
62 float RandomRange(float f0, float f1, unsigned int& seed)
64 return f0 + (rand_r(&seed) & 0xfff) * (f1 - f0) * (1.0f / 4095.0f);
67 Dali::Geometry CreateTexturedQuad()
71 Dali::Vector2 position;
72 Dali::Vector2 texCoord;
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)}};
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);
87 Dali::Geometry geometry = Dali::Geometry::New();
88 geometry.AddVertexBuffer(vertexBuffer);
89 geometry.SetType(Dali::Geometry::TRIANGLE_STRIP);
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),
111 mTotalNumOfBubble(maximumNumberOfBubble),
114 mRenderTaskRunning(false)
116 // Calculate how many shaders are required
117 if(mTotalNumOfBubble > 100)
119 mNumBubblePerRenderer = 100;
120 mNumRenderer = mTotalNumOfBubble / 100;
121 if(mNumRenderer * mNumBubblePerRenderer < mTotalNumOfBubble)
124 mNumBubblePerRenderer = mTotalNumOfBubble / mNumRenderer + 1;
125 mTotalNumOfBubble = mNumRenderer * mNumBubblePerRenderer;
130 mNumBubblePerRenderer = mTotalNumOfBubble;
134 mRandomSeed = time(NULL);
137 BubbleEmitter::~BubbleEmitter()
141 Toolkit::BubbleEmitter BubbleEmitter::New(const Vector2& winSize,
142 Texture shapeTexture,
143 unsigned int maximumNumberOfBubble,
144 const Vector2& bubbleSizeRange)
146 // Create the implementation
147 IntrusivePtr<BubbleEmitter> internalBubbleEmitter(new BubbleEmitter(winSize, shapeTexture, maximumNumberOfBubble, bubbleSizeRange));
149 // Pass ownership to Toolkit::BubbleEmitter handle
150 Toolkit::BubbleEmitter bubbleEmitter(*internalBubbleEmitter);
152 //Second phase of implementeation : Initialization
153 internalBubbleEmitter->OnInitialize();
155 return bubbleEmitter;
158 void BubbleEmitter::OnInitialize()
160 // Create the root actor, all the meshActor should be its children
161 mBubbleRoot = Actor::New();
162 mBubbleRoot.SetProperty(Actor::Property::SIZE, mMovementArea);
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);
170 // Generate the geometry, which is used by all bubbleActors
171 mMeshGeometry = CreateGeometry(mNumBubblePerRenderer * mDensity);
173 Shader bubbleShader = CreateBubbleShader(mNumBubblePerRenderer);
175 mTextureSet = TextureSet::New();
176 mTextureSet.SetTexture(0u, mEffectTexture);
177 mTextureSet.SetTexture(1u, mShapeTexture);
179 // Create the renderers to render the bubbles
180 mBubbleRenderers.resize(mNumRenderer);
181 for(unsigned int i = 0; i < mNumRenderer; i++)
183 mBubbleRenderers[i].Initialize(mNumBubblePerRenderer, mMovementArea, mMeshGeometry, mTextureSet, bubbleShader);
184 mBubbleRoot.AddRenderer(mBubbleRenderers[i].GetRenderer());
187 // Create a cameraActor for the off screen render task.
188 mCameraActor = CameraActor::New(mMovementArea);
189 mCameraActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
191 Stage stage = Stage::GetCurrent();
193 stage.Add(mCameraActor);
194 stage.ContextRegainedSignal().Connect(this, &BubbleEmitter::OnContextRegained);
197 Actor BubbleEmitter::GetRootActor()
202 void BubbleEmitter::SetBackground(Texture bgTexture, const Vector3& hsvDelta)
204 mBackgroundTexture = bgTexture;
205 mHSVDelta = hsvDelta;
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);
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);
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;
235 void BubbleEmitter::SetBubbleShape(Texture shapeTexture)
237 mTextureSet.SetTexture(1, shapeTexture);
240 void BubbleEmitter::SetBubbleScale(float scale)
242 for(unsigned int i = 0; i < mNumRenderer; i++)
244 mBubbleRenderers[i].SetDynamicScale(scale);
248 void BubbleEmitter::SetBubbleDensity(unsigned int density)
250 DALI_ASSERT_ALWAYS(density > 0 && density <= 9 && " Only densities between 1 to 9 are valid ");
252 if(density == mDensity)
259 mMeshGeometry = CreateGeometry(mNumBubblePerRenderer * mDensity);
260 for(unsigned int i = 0; i < mNumRenderer; i++)
262 mBubbleRenderers[i].SetGeometry(mMeshGeometry);
267 // clear the resources created for the off screen rendering
268 void BubbleEmitter::OnRenderFinished(RenderTask& source)
270 mRenderTaskRunning = false;
271 Actor sourceActor = source.GetSourceActor();
272 Stage stage = Stage::GetCurrent();
273 stage.Remove(sourceActor);
274 stage.GetRenderTaskList().RemoveTask(source);
277 void BubbleEmitter::OnContextRegained()
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)
283 SetBackground(mBackgroundTexture, mHSVDelta);
287 void BubbleEmitter::EmitBubble(Animation& animation, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement)
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),
294 AlphaFunction::LINEAR);
296 mCurrentBubble = (mCurrentBubble + 1) % mTotalNumOfBubble;
299 void BubbleEmitter::Restore()
301 for(unsigned int i = 0; i < mNumRenderer; i++)
303 mBubbleRenderers[i].ResetProperties();
307 Geometry BubbleEmitter::CreateGeometry(unsigned int numOfPatch)
309 unsigned int numVertex = numOfPatch * 4u;
310 Vector<Vertex> vertexData;
311 vertexData.Reserve(numVertex);
313 unsigned int numIndex = numOfPatch * 6u;
314 Vector<unsigned short> indexData;
315 indexData.Reserve(numIndex);
317 for(unsigned int i = 0; i < numOfPatch; i++)
319 float halfSize = RandomRange(mBubbleSizeRange.x, mBubbleSizeRange.y, mRandomSeed) * 0.5f;
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)));
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);
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);
343 Geometry geometry = Geometry::New();
344 geometry.AddVertexBuffer(vertices);
345 geometry.SetIndexBuffer(&indexData[0], numIndex);
350 void BubbleEmitter::SetBubbleParameter(BubbleRenderer& bubbleRenderer, unsigned int curUniform, const Vector2& emitPosition, const Vector2& direction, const Vector2& displacement)
352 Vector2 dir(direction);
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));
358 randomVec.x -= dir.x * halfRange;
359 randomVec.y *= 1.0f - fabsf(dir.x) * 0.33f;
361 if(randomVec.y > 0.0f)
363 randomVec.y *= 0.33f;
365 Vector4 startAndEndPos(emitPosition.x, emitPosition.y, emitPosition.x + randomVec.x, emitPosition.y + randomVec.y);
366 bubbleRenderer.SetStartAndEndPosition(curUniform, startAndEndPos);
368 bubbleRenderer.SetPercentage(curUniform, 0.f);
371 } // namespace Internal
373 } // namespace Toolkit