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 <cstdint> // uint32_t, uint16_t etc
23 #include <dali/public-api/rendering/frame-buffer.h>
24 #include <dali/public-api/rendering/renderer.h>
25 #include <dali/public-api/rendering/texture-set.h>
26 #include <dali/public-api/rendering/texture.h>
29 #include "shared/utility.h" // DemoHelper::LoadTexture
30 #include "generated/metaball-vert.h"
31 #include "generated/metaball-frag.h"
32 #include "generated/metaball-refraction-frag.h"
33 #include "generated/fragment-frag.h"
37 namespace // unnamed namespace for constants
39 const char* const BACKGROUND_IMAGE(DEMO_IMAGE_DIR "background-2.jpg");
40 const float GRAVITY_X(0);
41 const float GRAVITY_Y(-0.09);
43 // number of metaballs
44 constexpr uint32_t METABALL_NUMBER = 6;
47 * Metadata for each ball
56 //Properties needed for animations
57 Property::Index positionIndex;
58 Property::Index positionVarIndex;
59 Property::Index gravityIndex;
60 Property::Index radiusIndex;
61 Property::Index radiusVarIndex;
62 Property::Index aspectIndex;
65 } // unnamed namespace
68 * Demo using Metaballs
70 * When the metaball is clicked it starts to grow and fuses into the closest edge of screen
72 class MetaballRefracController : public ConnectionTracker
79 MetaballRefracController(Application& application);
84 virtual ~MetaballRefracController();
87 * Creates the metaballs and initializes the scene
89 void Create(Application& app);
92 * Touch handler, start the grow animation and creates additional metaballs
94 bool OnTouch(Actor actor, const TouchEvent& touch);
97 * Key event callback to quit the application on escape or back key
99 void OnKeyEvent(const KeyEvent& event);
102 Application& mApplication;
105 Texture mBackgroundTexture;
106 FrameBuffer mMetaballFBO;
109 MetaballInfo mMetaballs[METABALL_NUMBER];
111 Actor mCompositionActor;
114 Vector2 mCurrentTouchPosition;
115 Vector2 mMetaballPosVariation;
116 Vector2 mMetaballPosVariationFrom;
117 Vector2 mMetaballPosVariationTo;
118 Vector2 mMetaballCenter;
123 Renderer mRendererRefraction;
124 TextureSet mTextureSetRefraction;
125 Shader mShaderRefraction;
126 TextureSet mTextureSetNormal;
127 Shader mShaderNormal;
130 Animation mGravityAnimation[METABALL_NUMBER];
131 Animation mRadiusDecAnimation[METABALL_NUMBER];
132 Animation mRadiusIncFastAnimation[METABALL_NUMBER];
133 Animation mRadiusIncSlowAnimation[METABALL_NUMBER];
134 Animation mRadiusVarAnimation[METABALL_NUMBER];
135 Animation mPositionVarAnimation[METABALL_NUMBER];
137 // Private Helper functions
140 * Create a mesh data with the geometry for the metaball rendering
141 * @param aspectMappedTexture whether texture coords should be mapped based on aspect ratio
143 Geometry CreateGeometry(bool aspectMappedTexture = true);
146 * Create a actor for the metaballs
148 void CreateMetaballActors();
151 * Create the render task and FBO to render the metaballs into a texture
153 void CreateMetaballImage();
156 * Create the the final composition
158 void CreateComposition();
161 * Create all the metaballs animations (gravity, movement, size, etc.)
163 void CreateAnimations();
166 * Function to launch the grow slow radius for the metaballs, and also the small variations for metaball[2] and [3]
168 void LaunchRadiusIncSlowAnimations(Animation& source);
171 * Function to launch the animation to get the metaball[1] back to the center
173 void LaunchGetBackToPositionAnimation(Animation& source);
176 * Function to stop all animations related to the click of the user in the screen
178 void StopClickAnimations();
181 * Function to stop all animations related to the after click of the user in the screen
183 void StopAfterClickAnimations();
186 * Function that resets the sate of the different Metaballs
188 void ResetMetaballsState();
191 * Function to set the actual position of the metaballs when the user clicks the screen
193 void SetPositionToMetaballs(const Vector2& metaballCenter);
200 MetaballRefracController::MetaballRefracController(Application& application)
201 : mApplication(application)
203 // Connect to the Application's Init signal
204 mApplication.InitSignal().Connect(this, &MetaballRefracController::Create);
207 MetaballRefracController::~MetaballRefracController()
209 // Nothing to do here;
212 void MetaballRefracController::Create(Application& app)
214 Window window = app.GetWindow();
216 window.KeyEventSignal().Connect(this, &MetaballRefracController::OnKeyEvent);
218 mScreenSize = window.GetSize();
220 window.SetBackgroundColor(Color::BLACK);
222 // Load background texture
223 mBackgroundTexture = DemoHelper::LoadTexture(BACKGROUND_IMAGE);
225 mGravity = Vector2(GRAVITY_X, GRAVITY_Y);
226 mGravityVar = Vector2(0, 0);
228 CreateMetaballActors();
229 CreateMetaballImage();
233 // Connect the callback to the touch signal on the mesh actor
234 window.GetRootLayer().TouchedSignal().Connect(this, &MetaballRefracController::OnTouch);
237 Geometry MetaballRefracController::CreateGeometry(bool aspectMappedTexture)
239 const float aspect = mScreenSize.y / mScreenSize.x;
241 // Create vertices and specify their color
242 const float xsize = mScreenSize.x * 0.5;
244 // Create the meshdata for the metaballs
245 struct VertexPosition
254 VertexPosition vertices[] =
256 {Vector2(-xsize, -xsize * aspect)},
257 {Vector2(xsize, -xsize * aspect)},
258 {Vector2(-xsize, xsize * aspect)},
259 {Vector2(xsize, xsize * aspect)}};
261 const float textureAspect = (aspectMappedTexture) ? aspect : 1.0f;
262 VertexTexture textures[] =
264 {Vector2(0.0f, 0.0f)},
265 {Vector2(1.0f, 0.0f)},
266 {Vector2(0.0f, 1.0f * textureAspect)},
267 {Vector2(1.0f, 1.0f * textureAspect)}};
269 uint32_t numberOfVertices = sizeof(vertices) / sizeof(VertexPosition);
272 Property::Map positionVertexFormat;
273 positionVertexFormat["aPosition"] = Property::VECTOR2;
274 VertexBuffer positionVertices = VertexBuffer::New(positionVertexFormat);
275 positionVertices.SetData(vertices, numberOfVertices);
278 Property::Map textureVertexFormat;
279 textureVertexFormat["aTexture"] = Property::VECTOR2;
280 VertexBuffer textureVertices = VertexBuffer::New(textureVertexFormat);
281 textureVertices.SetData(textures, numberOfVertices);
284 const uint16_t indices[] = {0, 3, 1, 0, 2, 3};
286 // Create the geometry object
287 Geometry texturedQuadGeometry = Geometry::New();
288 texturedQuadGeometry.AddVertexBuffer(positionVertices);
289 texturedQuadGeometry.AddVertexBuffer(textureVertices);
291 texturedQuadGeometry.SetIndexBuffer(&indices[0], sizeof(indices) / sizeof(indices[0]));
293 return texturedQuadGeometry;
296 void MetaballRefracController::CreateMetaballActors()
298 const float aspect = mScreenSize.y / mScreenSize.x;
300 // Create the renderer for the metaballs
301 Shader shader = Shader::New(SHADER_METABALL_VERT, SHADER_METABALL_FRAG, Shader::Hint::MODIFIES_GEOMETRY);
302 Geometry metaballGeometry = CreateGeometry();
303 Renderer renderer = Renderer::New(metaballGeometry, shader);
304 renderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
305 renderer.SetProperty(Renderer::Property::BLEND_FACTOR_SRC_RGB, BlendFactor::ONE);
306 renderer.SetProperty(Renderer::Property::BLEND_FACTOR_DEST_RGB, BlendFactor::ONE);
307 renderer.SetProperty(Renderer::Property::BLEND_FACTOR_SRC_ALPHA, BlendFactor::ONE);
308 renderer.SetProperty(Renderer::Property::BLEND_FACTOR_DEST_ALPHA, BlendFactor::ONE);
310 // Each metaball has a different radius
311 mMetaballs[0].radius = mMetaballs[0].initRadius = 0.0145f;
312 mMetaballs[1].radius = mMetaballs[1].initRadius = 0.012f;
313 mMetaballs[2].radius = mMetaballs[2].initRadius = 0.0135f;
314 mMetaballs[3].radius = mMetaballs[3].initRadius = 0.0135f;
316 // Initialization of each of the metaballs
317 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
319 mMetaballs[i].position = Vector2(0.0f, 0.0f);
321 mMetaballs[i].actor = Actor::New();
322 mMetaballs[i].actor.SetProperty(Dali::Actor::Property::NAME, "Metaball");
323 mMetaballs[i].actor.SetProperty(Actor::Property::SCALE, 1.0f);
324 mMetaballs[i].actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
326 mMetaballs[i].actor.AddRenderer(renderer);
328 mMetaballs[i].positionIndex = mMetaballs[i].actor.RegisterProperty("uPositionMetaball", mMetaballs[i].position);
329 mMetaballs[i].positionVarIndex = mMetaballs[i].actor.RegisterProperty("uPositionVar", Vector2(0.f, 0.f));
330 mMetaballs[i].gravityIndex = mMetaballs[i].actor.RegisterProperty("uGravityVector", Vector2(0.f, 0.f));
331 mMetaballs[i].radiusIndex = mMetaballs[i].actor.RegisterProperty("uRadius", mMetaballs[i].radius);
332 mMetaballs[i].radiusVarIndex = mMetaballs[i].actor.RegisterProperty("uRadiusVar", 0.f);
333 mMetaballs[i].aspectIndex = mMetaballs[i].actor.RegisterProperty("uAspect", aspect);
337 mMetaballRoot = Actor::New();
338 mMetaballRoot.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
339 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
341 mMetaballRoot.Add(mMetaballs[i].actor);
345 void MetaballRefracController::CreateMetaballImage()
347 // Create an FBO and a render task to create to render the metaballs with a fragment shader
348 Window window = mApplication.GetWindow();
349 mMetaballFBO = FrameBuffer::New(mScreenSize.x, mScreenSize.y);
351 window.Add(mMetaballRoot);
353 //Creation of the render task used to render the metaballs
354 RenderTaskList taskList = window.GetRenderTaskList();
355 RenderTask task = taskList.CreateTask();
356 task.SetRefreshRate(RenderTask::REFRESH_ALWAYS);
357 task.SetSourceActor(mMetaballRoot);
358 task.SetExclusive(true);
359 task.SetClearColor(Color::BLACK);
360 task.SetClearEnabled(true);
361 task.SetFrameBuffer(mMetaballFBO);
364 void MetaballRefracController::CreateComposition()
366 // Create Refraction shader and renderer
367 mShaderRefraction = Shader::New(SHADER_METABALL_VERT, SHADER_METABALL_REFRACTION_FRAG);
369 // Create new texture set
370 mTextureSetRefraction = TextureSet::New();
371 mTextureSetRefraction.SetTexture(0u, mBackgroundTexture);
372 mTextureSetRefraction.SetTexture(1u, mMetaballFBO.GetColorTexture());
374 // Create normal shader
375 mShaderNormal = Shader::New(SHADER_METABALL_VERT, SHADER_FRAGMENT_FRAG);
377 // Create new texture set
378 mTextureSetNormal = TextureSet::New();
379 mTextureSetNormal.SetTexture(0u, mBackgroundTexture);
382 mCompositionActor = Actor::New();
383 mCompositionActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
384 mCompositionActor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, 0.0f));
385 mCompositionActor.SetProperty(Actor::Property::SIZE, Vector2(mScreenSize.x, mScreenSize.y));
388 Geometry metaballGeometry = CreateGeometry(false);
389 mRendererRefraction = Renderer::New(metaballGeometry, mShaderNormal);
390 mRendererRefraction.SetTextures(mTextureSetNormal);
391 mCompositionActor.AddRenderer(mRendererRefraction);
393 Window window = mApplication.GetWindow();
394 window.Add(mCompositionActor);
397 void MetaballRefracController::CreateAnimations()
402 mPositionVarAnimation[1] = Animation::New(2.f);
403 mPositionVarAnimation[1].SetLooping(false);
404 mPositionVarAnimation[1].Pause();
405 mPositionVarAnimation[1].FinishedSignal().Connect(this, &MetaballRefracController::LaunchGetBackToPositionAnimation);
407 KeyFrames keySinCosVariation = KeyFrames::New();
408 Vector2 sinCosVariation(0, 0);
409 for(i = 0; i < 360; i++)
411 sinCosVariation.x = 0.05f * (-sinf(i * Math::PI_OVER_180) + cosf(i * Math::PI_OVER_180));
412 sinCosVariation.y = 0.05f * (sinf(i * Math::PI_OVER_180) - cosf(i * Math::PI_OVER_180));
414 keySinCosVariation.Add(key, sinCosVariation);
417 mPositionVarAnimation[2] = Animation::New(6.f);
418 mPositionVarAnimation[2].AnimateBetween(Property(mMetaballs[2].actor, mMetaballs[2].positionVarIndex), keySinCosVariation);
419 mPositionVarAnimation[2].SetLooping(true);
420 mPositionVarAnimation[2].Pause();
422 KeyFrames keyCosSinVariation = KeyFrames::New();
423 Vector2 cosSinVariation(0, 0);
424 for(i = 0; i < 360; i++)
426 cosSinVariation.x = 0.05f * (-sinf(i * Math::PI_OVER_180) - cosf(i * Math::PI_OVER_180));
427 cosSinVariation.y = 0.05f * (sinf(i * Math::PI_OVER_180) + cosf(i * Math::PI_OVER_180));
429 keyCosSinVariation.Add(key, cosSinVariation);
432 mPositionVarAnimation[3] = Animation::New(6.f);
433 mPositionVarAnimation[3].AnimateBetween(Property(mMetaballs[3].actor, mMetaballs[3].positionVarIndex), keyCosSinVariation);
434 mPositionVarAnimation[3].SetLooping(true);
435 mPositionVarAnimation[3].Pause();
437 //Animations for gravity
438 for(i = 0; i < METABALL_NUMBER; i++)
440 mGravityAnimation[i] = Animation::New(25.f);
441 mGravityAnimation[i].AnimateBy(Property(mMetaballs[i].actor, mMetaballs[i].gravityIndex), mGravity * 25.f * 3.f);
442 mGravityAnimation[i].SetLooping(false);
443 mGravityAnimation[i].Pause();
446 //Animation to decrease size of metaballs when there is no click
447 for(i = 0; i < METABALL_NUMBER; i++)
449 mRadiusDecAnimation[i] = Animation::New(25.f);
450 mRadiusDecAnimation[i].AnimateBy(Property(mMetaballs[i].actor, mMetaballs[i].radiusIndex), -0.004f * 25.f * 3.f);
451 mRadiusDecAnimation[i].SetLooping(false);
452 mRadiusDecAnimation[i].Pause();
455 // Animation to grow the size of the metaballs the first second of the click
456 for(i = 0; i < METABALL_NUMBER; i++)
458 mRadiusIncFastAnimation[i] = Animation::New(0.3f);
459 mRadiusIncFastAnimation[i].AnimateBy(Property(mMetaballs[i].actor, mMetaballs[i].radiusIndex), 0.06f);
460 mRadiusIncFastAnimation[i].SetLooping(false);
461 mRadiusIncFastAnimation[i].Pause();
463 mRadiusIncFastAnimation[0].FinishedSignal().Connect(this, &MetaballRefracController::LaunchRadiusIncSlowAnimations);
465 // Animation to grow the size of the metaballs afterwards
466 for(i = 0; i < METABALL_NUMBER; i++)
468 mRadiusIncSlowAnimation[i] = Animation::New(20.f);
469 mRadiusIncSlowAnimation[i].AnimateBy(Property(mMetaballs[i].actor, mMetaballs[i].radiusIndex), 0.04f);
470 mRadiusIncSlowAnimation[i].SetLooping(false);
471 mRadiusIncSlowAnimation[i].Pause();
474 // Keyframes of a sin function
475 KeyFrames keySin = KeyFrames::New();
477 for(i = 0; i < 360; i++)
479 val = 0.01f * sin(i * Math::PI / 180.f);
481 keySin.Add(key, val);
484 //Animation to change the size of the metaball
485 mRadiusVarAnimation[2] = Animation::New(8.f);
486 mRadiusVarAnimation[2].AnimateBetween(Property(mMetaballs[2].actor, mMetaballs[2].radiusVarIndex), keySin);
487 mRadiusVarAnimation[2].SetLooping(true);
489 // Keyframes of a cos function
490 KeyFrames keyCos = KeyFrames::New();
491 for(i = 0; i < 360; i++)
493 val = 0.01f * cos(i * Math::PI / 180.f);
495 keyCos.Add(key, val);
498 //Animation to change the size of the metaball
499 mRadiusVarAnimation[3] = Animation::New(8.f);
500 mRadiusVarAnimation[3].AnimateBetween(Property(mMetaballs[3].actor, mMetaballs[3].radiusVarIndex), keyCos);
501 mRadiusVarAnimation[3].SetLooping(true);
504 void MetaballRefracController::LaunchGetBackToPositionAnimation(Animation& source)
506 mMetaballPosVariationTo = Vector2(0, 0);
508 mPositionVarAnimation[1] = Animation::New(1.f);
509 mPositionVarAnimation[1].SetLooping(false);
510 mPositionVarAnimation[1].AnimateTo(Property(mMetaballs[1].actor, mMetaballs[1].positionVarIndex), Vector2(0, 0));
511 mPositionVarAnimation[1].Play();
514 void MetaballRefracController::LaunchRadiusIncSlowAnimations(Animation& source)
516 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
518 mRadiusIncSlowAnimation[i].Play();
520 mPositionVarAnimation[2].Play();
521 mPositionVarAnimation[3].Play();
524 void MetaballRefracController::StopClickAnimations()
526 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
528 mRadiusIncSlowAnimation[i].Stop();
529 mRadiusIncFastAnimation[i].Stop();
531 mPositionVarAnimation[1].Stop();
532 mPositionVarAnimation[2].Stop();
533 mPositionVarAnimation[3].Stop();
536 void MetaballRefracController::StopAfterClickAnimations()
538 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
540 mGravityAnimation[i].Stop();
541 mRadiusDecAnimation[i].Stop();
543 mMetaballs[i].radius = mMetaballs[i].initRadius;
545 mMetaballs[i].actor.SetProperty(mMetaballs[i].gravityIndex, Vector2(0, 0));
546 mMetaballs[i].actor.SetProperty(mMetaballs[i].radiusIndex, mMetaballs[i].radius);
547 mMetaballs[i].actor.SetProperty(mMetaballs[i].radiusVarIndex, 0.f);
549 mRadiusVarAnimation[2].Stop();
550 mRadiusVarAnimation[3].Stop();
553 void MetaballRefracController::ResetMetaballsState()
555 mRendererRefraction.SetTextures(mTextureSetNormal);
556 mRendererRefraction.SetShader(mShaderNormal);
558 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
560 mMetaballs[i].radius = mMetaballs[i].initRadius;
563 mMetaballPosVariationTo = Vector2(0, 0);
564 mMetaballPosVariationFrom = Vector2(0, 0);
565 mMetaballPosVariation = Vector2(0, 0);
566 mGravityVar = Vector2(0, 0);
569 void MetaballRefracController::SetPositionToMetaballs(const Vector2& metaballCenter)
571 //We set the position for the metaballs based on click position
572 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
574 mMetaballs[i].position = metaballCenter;
575 mMetaballs[i].actor.SetProperty(mMetaballs[i].positionIndex, mMetaballs[i].position);
579 bool MetaballRefracController::OnTouch(Actor actor, const TouchEvent& touch)
581 const float aspect = mScreenSize.y / mScreenSize.x;
582 switch(touch.GetState(0))
584 case PointState::DOWN:
586 StopAfterClickAnimations();
587 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
589 mRadiusIncFastAnimation[i].Play();
591 mRadiusVarAnimation[2].Play();
592 mRadiusVarAnimation[3].Play();
594 //We draw with the refraction-composition shader
595 mRendererRefraction.SetTextures(mTextureSetRefraction);
596 mRendererRefraction.SetShader(mShaderRefraction);
597 mCurrentTouchPosition = touch.GetScreenPosition(0);
599 //we use the click position for the metaballs
600 Vector2 metaballCenter = Vector2((mCurrentTouchPosition.x / mScreenSize.x) - 0.5f,
601 (aspect * (mScreenSize.y - mCurrentTouchPosition.y) / mScreenSize.y) - 0.5f) *
603 SetPositionToMetaballs(metaballCenter);
606 case PointState::MOTION:
608 Vector2 screen = touch.GetScreenPosition(0);
609 Vector2 displacement = screen - mCurrentTouchPosition;
610 mCurrentTouchPosition = screen;
612 mMetaballPosVariationTo.x += (displacement.x / mScreenSize.x) * 2.2f;
613 mMetaballPosVariationTo.y += (-displacement.y / mScreenSize.y) * 2.2f;
615 if(mPositionVarAnimation[1])
617 mPositionVarAnimation[1].FinishedSignal().Disconnect(this, &MetaballRefracController::LaunchGetBackToPositionAnimation);
618 mPositionVarAnimation[1].Stop();
620 mPositionVarAnimation[1] = Animation::New(1.f);
621 mPositionVarAnimation[1].SetLooping(false);
622 mPositionVarAnimation[1].AnimateTo(Property(mMetaballs[1].actor, mMetaballs[1].positionVarIndex), mMetaballPosVariationTo);
623 mPositionVarAnimation[1].FinishedSignal().Connect(this, &MetaballRefracController::LaunchGetBackToPositionAnimation);
624 mPositionVarAnimation[1].Play();
626 //we use the click position for the metaballs
627 Vector2 metaballCenter = Vector2((screen.x / mScreenSize.x) - 0.5f,
628 (aspect * (mScreenSize.y - screen.y) / mScreenSize.y) - 0.5f) *
630 SetPositionToMetaballs(metaballCenter);
634 case PointState::LEAVE:
635 case PointState::INTERRUPTED:
637 //Stop click animations
638 StopClickAnimations();
640 //Launch out of screen animations
641 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
643 mGravityAnimation[i].Play();
646 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
648 mRadiusDecAnimation[i].Play();
658 void MetaballRefracController::OnKeyEvent(const KeyEvent& event)
660 if(event.GetState() == KeyEvent::DOWN)
662 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
672 int32_t DALI_EXPORT_API main(int argc, char** argv)
674 Application application = Application::New(&argc, &argv);
676 MetaballRefracController test(application);
677 application.MainLoop();