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/math/random.h>
24 #include <dali/public-api/rendering/frame-buffer.h>
25 #include <dali/public-api/rendering/renderer.h>
26 #include <dali/public-api/rendering/texture-set.h>
27 #include <dali/public-api/rendering/texture.h>
30 #include "shared/utility.h" // DemoHelper::LoadTexture
34 namespace // unnamed namespace for constants
37 const char* const BACKGROUND_IMAGE(DEMO_IMAGE_DIR "background-2.jpg");
39 // number of metaballs
40 constexpr uint32_t METABALL_NUMBER = 6;
43 * Vertex shader code for metaball
46 const char* const METABALL_VERTEX_SHADER = DALI_COMPOSE_SHADER (
47 attribute mediump vec2 aPosition;\n
48 attribute mediump vec2 aTexture;\n
49 uniform mediump mat4 uMvpMatrix;\n
50 uniform mediump vec3 uSize;\n
51 uniform lowp vec4 uColor;\n
52 varying mediump vec2 vTexCoord;\n
56 vTexCoord = aTexture;\n
57 mediump vec4 vertexPosition = vec4(aPosition.x, aPosition.y, 0.0, 1.0);\n
58 gl_Position = uMvpMatrix * vertexPosition;\n
63 * Fragment shader code for metaball
65 const char* const METABALL_FRAG_SHADER = DALI_COMPOSE_SHADER (
66 precision mediump float;\n
67 varying vec2 vTexCoord;\n
68 uniform vec2 uPositionMetaball;\n
69 uniform vec2 uPositionVar;\n
70 uniform vec2 uGravityVector;\n
71 uniform float uRadius;\n
72 uniform float uRadiusVar;\n
75 vec2 adjustedCoords = vTexCoord * 2.0 - 1.0;\n
76 vec2 finalMetaballPosition = uPositionMetaball + uGravityVector + uPositionVar;\n
78 float finalRadius = uRadius + uRadiusVar;\n
79 vec2 distanceVec = adjustedCoords - finalMetaballPosition;\n
80 float result = dot(distanceVec, distanceVec);\n
81 float color = inversesqrt(result) * finalRadius;\n
83 gl_FragColor = vec4(color,color,color,1.0);\n
88 * Fragment shader code for metaball and background composition with refraction effect
90 const char* const REFRACTION_FRAG_SHADER = DALI_COMPOSE_SHADER (
91 precision highp float;\n
92 varying vec2 vTexCoord;\n
93 uniform sampler2D sTexture;\n
94 uniform sampler2D sEffect;\n
95 uniform vec2 uPositionMetaball;\n
99 vec3 normal = vec3(0.0,0.0,1.0);\n
100 vec2 fakePos = vec2(0.0,0.0);\n
101 vec3 color = vec3(1.0, 1.0, 1.0);
104 vec4 metaColor = texture2D(sEffect, vTexCoord);\n
106 vec2 adjustedCoords = vTexCoord.xy * vec2(2.0) - vec2(1.0);\n
107 fakePos = adjustedCoords.xy - vec2(uPositionMetaball.x, -uPositionMetaball.y);
108 float len = length(fakePos) + 0.01;\n
109 vec3 colorPos = vec3(0,0,1);
111 if (metaColor.r > 0.85)\n
113 zoomCoords = ((vTexCoord - 0.5) * 0.9);\n
114 zoomCoords = zoomCoords + 0.5;\n
116 float interpNormal = mix(0.7, 1.0, (metaColor.r - 0.85) * 4.);\n
117 normal.xyz = vec3(fakePos.x * (1.0 - interpNormal) / len, fakePos.y * (1.0 - interpNormal) / len, interpNormal);\n
118 normal.xyz = normalize(normal.xyz);\n
119 color = vec3(0.65, 1.0, 0);\n
120 colorPos = vec3(fakePos.x,fakePos.y,0);
122 else if (metaColor.r > 0.75)\n
124 float interpolation = mix(0.9, 1.15, (0.85 - metaColor.r) * 10.0);\n
125 zoomCoords = ((vTexCoord - 0.5) * interpolation);\n
126 zoomCoords = zoomCoords + 0.5;\n
128 float interpNormal = mix(0.7, 0.0, (0.85 - metaColor.r) * 10.0);\n
129 normal.xyz = vec3(fakePos.x * (1.0 - interpNormal) / len, fakePos.y * (1.0 - interpNormal) / len, interpNormal);\n
130 normal.xyz = normalize(normal.xyz);\n
131 color = vec3(0.65, 1.0, 0);\n
132 colorPos = vec3(fakePos.x,fakePos.y,0);
136 zoomCoords = vTexCoord;\n
137 normal = vec3(0,0,0);\n
141 vec3 lightPosition = vec3(-750.0,-1000.0,2000.0);\n
142 vec3 vertex = vec3(adjustedCoords.x,adjustedCoords.y,0.0);\n
144 vec3 vecToLight = normalize( lightPosition - vertex );\n
146 float lightDiffuse = dot( vecToLight, normal );\n
147 lightDiffuse = max(0.0,lightDiffuse);\n
148 lightDiffuse = lightDiffuse * 0.5 + 0.5;
150 vec3 vertexToEye = vec3(0,0,1) - vertex;\n
151 vertexToEye = normalize(vertexToEye);
152 vec3 lightReflect = normalize(reflect(-vecToLight, normal));\n
153 float specularFactor = max(0.0,dot(vertexToEye, lightReflect));\n
154 specularFactor = pow(specularFactor, 32.0) * 0.7;
156 vec4 texColor = texture2D(sTexture, zoomCoords);\n
157 gl_FragColor.rgb = texColor.rgb * ambient + color.rgb * texColor.rgb * lightDiffuse + vec3(specularFactor);\n
158 gl_FragColor.a = 1.0;
164 * Metadata for each ball
174 Property::Index positionIndex;
175 Property::Index positionVarIndex;
178 } // unnamed namespace
181 * Demo using Metaballs
183 * When the metaball is clicked it explodes to smaller balls
185 class MetaballExplosionController : public ConnectionTracker
192 MetaballExplosionController(Application& application);
197 virtual ~MetaballExplosionController();
200 * Creates the metaballs and initializes the scene
202 void Create(Application& app);
205 * Touch event handler to center metaballs at touch position
206 * and start explosion animation on release
208 bool OnTouch(Actor actor, const TouchEvent& touch);
211 * Key event handler to quit application on escape or back key
213 void OnKeyEvent(const KeyEvent& event);
216 Application& mApplication;
219 Texture mBackgroundTexture;
220 FrameBuffer mMetaballFBO;
223 MetaballInfo mMetaballs[METABALL_NUMBER];
225 Property::Index mPositionIndex;
226 Actor mCompositionActor;
229 Vector2 mCurrentTouchPosition;
230 Vector2 mMetaballPosVariation;
231 Vector2 mMetaballPosVariationFrom;
232 Vector2 mMetaballPosVariationTo;
233 Vector2 mMetaballCenter;
236 Animation mPositionVarAnimation[METABALL_NUMBER];
238 uint32_t mDispersion;
239 Animation mDispersionAnimation[METABALL_NUMBER];
241 Timer mTimerDispersion;
243 float mTimeMultiplier;
245 // Private helper functions
248 * Create a mesh data with the geometry for the metaball rendering
249 * @param aspectMappedTexture whether texture coords should be mapped based on aspect ratio
251 Geometry CreateGeometry(bool aspectMappedTexture = true);
254 * Create a actors and renderers for the metaballs
256 void CreateMetaballActors();
259 * Create the render task and FBO to render the metaballs into a texture
261 void CreateMetaballImage();
264 * Create the the final composition
266 void CreateComposition();
269 * Function to create animations for the small variations of position inside the metaball
271 void CreateAnimations();
274 * Function to reset metaball state
276 void ResetMetaballs(bool resetAnims);
279 * Function to create disperse each of the ball that compose the metaball when exploding
281 void DisperseBallAnimation(uint32_t ball);
284 * Function to make metaballs come back to reset position
286 void LaunchResetMetaballPosition(Animation& source);
289 * Function to set things at the end of the animation
291 void EndDisperseAnimation(Animation& source);
294 * Function to init dispersion of the metaballs one by one using a timer
295 * (so not all the balls begin moving at the same time)
297 bool OnTimerDispersionTick();
300 * Function to set the actual position of the metaballs when the user clicks the screen
302 void SetPositionToMetaballs(const Vector2& metaballCenter);
309 MetaballExplosionController::MetaballExplosionController(Application& application)
310 : mApplication(application),
312 mBackgroundTexture(),
318 mCurrentTouchPosition(),
319 mMetaballPosVariation(),
320 mMetaballPosVariationFrom(),
321 mMetaballPosVariationTo(),
323 mPositionVarAnimation(),
325 mDispersionAnimation(),
327 mTimeMultiplier(1.0f)
329 // Connect to the Application's Init signal
330 mApplication.InitSignal().Connect(this, &MetaballExplosionController::Create);
333 MetaballExplosionController::~MetaballExplosionController()
335 // Nothing to do here;
338 void MetaballExplosionController::Create(Application& app)
340 Window window = app.GetWindow();
342 window.KeyEventSignal().Connect(this, &MetaballExplosionController::OnKeyEvent);
344 mScreenSize = window.GetSize();
346 mTimeMultiplier = 1.0f;
348 window.SetBackgroundColor(Color::BLACK);
350 // Load background texture
351 mBackgroundTexture = DemoHelper::LoadTexture(BACKGROUND_IMAGE);
353 srand(static_cast<uint32_t>(time(0)));
355 //Create internal data
356 CreateMetaballActors();
357 CreateMetaballImage();
363 mTimerDispersion = Timer::New(150);
364 mTimerDispersion.TickSignal().Connect(this, &MetaballExplosionController::OnTimerDispersionTick);
366 // Connect the callback to the touch signal on the mesh actor
367 window.GetRootLayer().TouchedSignal().Connect(this, &MetaballExplosionController::OnTouch);
370 Geometry MetaballExplosionController::CreateGeometry(bool aspectMappedTexture)
372 const float aspect = mScreenSize.y / mScreenSize.x;
374 // Create vertices and specify their color
375 const float xsize = mScreenSize.x * 0.5;
377 // Create the meshdata for the metaballs
378 struct VertexPosition
387 VertexPosition vertices[] =
389 {Vector2(-xsize, -xsize * aspect)},
390 {Vector2(xsize, -xsize * aspect)},
391 {Vector2(-xsize, xsize * aspect)},
392 {Vector2(xsize, xsize * aspect)}};
394 const float textureAspect = (aspectMappedTexture) ? aspect : 1.0f;
395 VertexTexture textures[] =
397 {Vector2(0.0f, 0.0f)},
398 {Vector2(1.0f, 0.0f)},
399 {Vector2(0.0f, 1.0f * textureAspect)},
400 {Vector2(1.0f, 1.0f * textureAspect)}};
402 uint32_t numberOfVertices = sizeof(vertices) / sizeof(VertexPosition);
405 Property::Map positionVertexFormat;
406 positionVertexFormat["aPosition"] = Property::VECTOR2;
407 VertexBuffer positionVertices = VertexBuffer::New(positionVertexFormat);
408 positionVertices.SetData(vertices, numberOfVertices);
411 Property::Map textureVertexFormat;
412 textureVertexFormat["aTexture"] = Property::VECTOR2;
413 VertexBuffer textureVertices = VertexBuffer::New(textureVertexFormat);
414 textureVertices.SetData(textures, numberOfVertices);
417 const uint16_t indices[] = {0, 3, 1, 0, 2, 3};
419 // Create the geometry object
420 Geometry texturedQuadGeometry = Geometry::New();
421 texturedQuadGeometry.AddVertexBuffer(positionVertices);
422 texturedQuadGeometry.AddVertexBuffer(textureVertices);
424 texturedQuadGeometry.SetIndexBuffer(&indices[0], sizeof(indices) / sizeof(indices[0]));
426 return texturedQuadGeometry;
429 void MetaballExplosionController::CreateMetaballActors()
431 // Create the shader for the metaballs, tell DALi that shader modifies geometry so we dont need to set a meaningless size
432 Shader shader = Shader::New(METABALL_VERTEX_SHADER, METABALL_FRAG_SHADER, Shader::Hint::MODIFIES_GEOMETRY);
434 Geometry metaballGeom = CreateGeometry();
435 // Reuse same renderer for each actor
436 Renderer renderer = Renderer::New(metaballGeom, shader);
437 renderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
438 renderer.SetProperty(Renderer::Property::BLEND_FACTOR_SRC_RGB, BlendFactor::ONE);
439 renderer.SetProperty(Renderer::Property::BLEND_FACTOR_DEST_RGB, BlendFactor::ONE);
440 renderer.SetProperty(Renderer::Property::BLEND_FACTOR_SRC_ALPHA, BlendFactor::ONE);
441 renderer.SetProperty(Renderer::Property::BLEND_FACTOR_DEST_ALPHA, BlendFactor::ONE);
443 //Initialization of each of the metaballs
444 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
446 mMetaballs[i].position = Vector2(0.0f, 0.0f);
447 mMetaballs[i].radius = mMetaballs[i].initRadius = Random::Range(0.05f, 0.07f);
449 mMetaballs[i].actor = Actor::New();
450 mMetaballs[i].actor.SetProperty(Dali::Actor::Property::NAME, "Metaball");
451 mMetaballs[i].actor.SetProperty(Actor::Property::SCALE, 1.0f);
452 mMetaballs[i].actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
453 mMetaballs[i].actor.AddRenderer(renderer);
455 mMetaballs[i].positionIndex = mMetaballs[i].actor.RegisterProperty("uPositionMetaball", mMetaballs[i].position);
457 mMetaballs[i].positionVarIndex = mMetaballs[i].actor.RegisterProperty("uPositionVar", Vector2(0.f, 0.f));
459 mMetaballs[i].actor.RegisterProperty("uGravityVector", Vector2(Random::Range(-0.2, 0.2), Random::Range(-0.2, 0.2)));
460 mMetaballs[i].actor.RegisterProperty("uRadius", mMetaballs[i].radius);
461 mMetaballs[i].actor.RegisterProperty("uRadiusVar", 0.f);
465 mMetaballRoot = Actor::New();
466 mMetaballRoot.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
467 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
469 mMetaballRoot.Add(mMetaballs[i].actor);
473 void MetaballExplosionController::CreateMetaballImage()
475 // Create an FBO and a render task to create to render the metaballs with a fragment shader
476 Window window = mApplication.GetWindow();
478 mMetaballFBO = FrameBuffer::New(mScreenSize.x, mScreenSize.y);
480 window.Add(mMetaballRoot);
482 // Create the render task used to render the metaballs
483 RenderTaskList taskList = window.GetRenderTaskList();
484 RenderTask task = taskList.CreateTask();
485 task.SetRefreshRate(RenderTask::REFRESH_ALWAYS);
486 task.SetSourceActor(mMetaballRoot);
487 task.SetExclusive(true);
488 task.SetClearColor(Color::BLACK);
489 task.SetClearEnabled(true);
490 task.SetFrameBuffer(mMetaballFBO);
493 void MetaballExplosionController::CreateComposition()
496 Shader shader = Shader::New(METABALL_VERTEX_SHADER, REFRACTION_FRAG_SHADER);
498 // Create new texture set
499 auto textureSet = TextureSet::New();
500 textureSet.SetTexture(0u, mBackgroundTexture);
501 textureSet.SetTexture(1u, mMetaballFBO.GetColorTexture());
504 Geometry metaballGeom = CreateGeometry(false);
506 Renderer mRenderer = Renderer::New(metaballGeom, shader);
507 mRenderer.SetTextures(textureSet);
510 mCompositionActor = Actor::New();
511 mCompositionActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
512 mCompositionActor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, 0.0f));
513 mCompositionActor.SetProperty(Actor::Property::SIZE, Vector2(mScreenSize.x, mScreenSize.y));
514 mCompositionActor.AddRenderer(mRenderer);
516 Vector2 metaballCenter(0.0, 0);
517 metaballCenter.x = metaballCenter.x * 0.5;
518 metaballCenter.y = metaballCenter.y * 0.5;
519 mPositionIndex = mCompositionActor.RegisterProperty("uPositionMetaball", metaballCenter);
521 SetPositionToMetaballs(metaballCenter);
523 mCompositionActor.SetProperty(Actor::Property::SIZE, Vector2(mScreenSize.x, mScreenSize.y));
525 Window window = mApplication.GetWindow();
526 window.Add(mCompositionActor);
529 void MetaballExplosionController::CreateAnimations()
533 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
535 KeyFrames keySinCosVariation = KeyFrames::New();
536 Vector2 sinCosVariation(0, 0);
538 direction.x = Random::Range(-100.f, 100.f);
539 direction.y = Random::Range(-100.f, 100.f);
541 direction.Normalize();
544 for(uint32_t j = 0; j < 360; j++)
546 sinCosVariation.x = sinf(j * Math::PI / 180.f) * direction.x;
547 sinCosVariation.y = cosf(j * Math::PI / 180.f) * direction.y;
548 float key = j / 360.f;
549 keySinCosVariation.Add(key, sinCosVariation);
552 mPositionVarAnimation[i] = Animation::New(3.f);
553 mPositionVarAnimation[i].AnimateBetween(Property(mMetaballs[i].actor, mMetaballs[i].positionVarIndex), keySinCosVariation);
554 mPositionVarAnimation[i].SetLooping(true);
555 mPositionVarAnimation[i].Play();
559 void MetaballExplosionController::ResetMetaballs(bool resetAnims)
561 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
563 if(mDispersionAnimation[i])
565 mDispersionAnimation[i].Clear();
568 mMetaballs[i].position = Vector2(0.0f, 0.0f);
569 mMetaballs[i].actor.SetProperty(mMetaballs[i].positionIndex, mMetaballs[i].position);
571 mTimerDispersion.Stop();
574 mCompositionActor.SetProperty(mPositionIndex, Vector2(0, 0));
577 void MetaballExplosionController::DisperseBallAnimation(uint32_t ball)
580 position.x = Random::Range(-1.5f, 1.5f);
581 position.y = Random::Range(-1.5f, 1.5f);
583 mDispersionAnimation[ball] = Animation::New(2.0f * mTimeMultiplier);
584 mDispersionAnimation[ball].AnimateTo(Property(mMetaballs[ball].actor, mMetaballs[ball].positionIndex), position);
585 mDispersionAnimation[ball].Play();
587 if(ball == METABALL_NUMBER - 1)
589 mDispersionAnimation[ball].FinishedSignal().Connect(this, &MetaballExplosionController::LaunchResetMetaballPosition);
593 void MetaballExplosionController::LaunchResetMetaballPosition(Animation& source)
595 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
597 mDispersionAnimation[i] = Animation::New(1.5f + i * 0.25f * mTimeMultiplier);
598 mDispersionAnimation[i].AnimateTo(Property(mMetaballs[i].actor, mMetaballs[i].positionIndex), Vector2(0, 0));
599 mDispersionAnimation[i].Play();
601 if(i == METABALL_NUMBER - 1)
603 mDispersionAnimation[i].FinishedSignal().Connect(this, &MetaballExplosionController::EndDisperseAnimation);
608 void MetaballExplosionController::EndDisperseAnimation(Animation& source)
610 mCompositionActor.SetProperty(mPositionIndex, Vector2(0, 0));
613 bool MetaballExplosionController::OnTimerDispersionTick()
615 if(mDispersion < METABALL_NUMBER)
617 DisperseBallAnimation(mDispersion);
623 void MetaballExplosionController::SetPositionToMetaballs(const Vector2& metaballCenter)
625 //We set the position for the metaballs based on click position
626 for(uint32_t i = 0; i < METABALL_NUMBER; i++)
628 mMetaballs[i].position = metaballCenter;
629 mMetaballs[i].actor.SetProperty(mMetaballs[i].positionIndex, mMetaballs[i].position);
632 mCompositionActor.SetProperty(mPositionIndex, metaballCenter);
635 bool MetaballExplosionController::OnTouch(Actor actor, const TouchEvent& touch)
637 float aspectR = mScreenSize.y / mScreenSize.x;
639 switch(touch.GetState(0))
641 case PointState::DOWN:
643 ResetMetaballs(true);
645 const Vector2 screen = touch.GetScreenPosition(0);
646 Vector2 metaballCenter = Vector2((screen.x / mScreenSize.x) - 0.5f, (aspectR * (mScreenSize.y - screen.y) / mScreenSize.y) - 0.5f) * 2.0f;
647 SetPositionToMetaballs(metaballCenter);
651 case PointState::MOTION:
653 const Vector2 screen = touch.GetScreenPosition(0);
654 Vector2 metaballCenter = Vector2((screen.x / mScreenSize.x) - 0.5f, (aspectR * (mScreenSize.y - screen.y) / mScreenSize.y) - 0.5f) * 2.0f;
655 SetPositionToMetaballs(metaballCenter);
659 case PointState::LEAVE:
660 case PointState::INTERRUPTED:
662 mTimerDispersion.Start();
671 void MetaballExplosionController::OnKeyEvent(const KeyEvent& event)
673 if(event.GetState() == KeyEvent::DOWN)
675 if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
685 int32_t DALI_EXPORT_API main(int argc, char** argv)
687 Application application = Application::New(&argc, &argv);
689 MetaballExplosionController test(application);
691 application.MainLoop();