2 * Copyright (c) 2018 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.
21 #include <cstdint> // uint32_t, uint16_t etc
23 #include <dali/public-api/rendering/renderer.h>
24 #include <dali/public-api/rendering/texture.h>
25 #include <dali/public-api/rendering/texture-set.h>
26 #include <dali/public-api/rendering/frame-buffer.h>
27 #include <dali/public-api/math/random.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
45 const char* const METABALL_VERTEX_SHADER = DALI_COMPOSE_SHADER (
46 attribute mediump vec2 aPosition;\n
47 attribute mediump vec2 aTexture;\n
48 uniform mediump mat4 uMvpMatrix;\n
49 uniform mediump vec3 uSize;\n
50 uniform lowp vec4 uColor;\n
51 varying mediump vec2 vTexCoord;\n
55 vTexCoord = aTexture;\n
56 mediump vec4 vertexPosition = vec4(aPosition.x, aPosition.y, 0.0, 1.0);\n
57 gl_Position = uMvpMatrix * vertexPosition;\n
62 * Fragment shader code for metaball
64 const char* const METABALL_FRAG_SHADER = DALI_COMPOSE_SHADER (
65 precision mediump float;\n
66 varying vec2 vTexCoord;\n
67 uniform vec2 uPositionMetaball;\n
68 uniform vec2 uPositionVar;\n
69 uniform vec2 uGravityVector;\n
70 uniform float uRadius;\n
71 uniform float uRadiusVar;\n
74 vec2 adjustedCoords = vTexCoord * 2.0 - 1.0;\n
75 vec2 finalMetaballPosition = uPositionMetaball + uGravityVector + uPositionVar;\n
77 float finalRadius = uRadius + uRadiusVar;\n
78 vec2 distanceVec = adjustedCoords - finalMetaballPosition;\n
79 float result = dot(distanceVec, distanceVec);\n
80 float color = inversesqrt(result) * finalRadius;\n
82 gl_FragColor = vec4(color,color,color,1.0);\n
87 * Fragment shader code for metaball and background composition with refraction effect
89 const char* const REFRACTION_FRAG_SHADER = DALI_COMPOSE_SHADER (
90 precision highp float;\n
91 varying vec2 vTexCoord;\n
92 uniform sampler2D sTexture;\n
93 uniform sampler2D sEffect;\n
94 uniform vec2 uPositionMetaball;\n
98 vec3 normal = vec3(0.0,0.0,1.0);\n
99 vec2 fakePos = vec2(0.0,0.0);\n
100 vec3 color = vec3(1.0, 1.0, 1.0);
103 vec4 metaColor = texture2D(sEffect, vTexCoord);\n
105 vec2 adjustedCoords = vTexCoord.xy * vec2(2.0) - vec2(1.0);\n
106 fakePos = adjustedCoords.xy - vec2(uPositionMetaball.x, -uPositionMetaball.y);
107 float len = length(fakePos) + 0.01;\n
108 vec3 colorPos = vec3(0,0,1);
110 if (metaColor.r > 0.85)\n
112 zoomCoords = ((vTexCoord - 0.5) * 0.9);\n
113 zoomCoords = zoomCoords + 0.5;\n
115 float interpNormal = mix(0.7, 1.0, (metaColor.r - 0.85) * 4.);\n
116 normal.xyz = vec3(fakePos.x * (1.0 - interpNormal) / len, fakePos.y * (1.0 - interpNormal) / len, interpNormal);\n
117 normal.xyz = normalize(normal.xyz);\n
118 color = vec3(0.65, 1.0, 0);\n
119 colorPos = vec3(fakePos.x,fakePos.y,0);
121 else if (metaColor.r > 0.75)\n
123 float interpolation = mix(0.9, 1.15, (0.85 - metaColor.r) * 10.0);\n
124 zoomCoords = ((vTexCoord - 0.5) * interpolation);\n
125 zoomCoords = zoomCoords + 0.5;\n
127 float interpNormal = mix(0.7, 0.0, (0.85 - metaColor.r) * 10.0);\n
128 normal.xyz = vec3(fakePos.x * (1.0 - interpNormal) / len, fakePos.y * (1.0 - interpNormal) / len, interpNormal);\n
129 normal.xyz = normalize(normal.xyz);\n
130 color = vec3(0.65, 1.0, 0);\n
131 colorPos = vec3(fakePos.x,fakePos.y,0);
135 zoomCoords = vTexCoord;\n
136 normal = vec3(0,0,0);\n
140 vec3 lightPosition = vec3(-750.0,-1000.0,2000.0);\n
141 vec3 vertex = vec3(adjustedCoords.x,adjustedCoords.y,0.0);\n
143 vec3 vecToLight = normalize( lightPosition - vertex );\n
145 float lightDiffuse = dot( vecToLight, normal );\n
146 lightDiffuse = max(0.0,lightDiffuse);\n
147 lightDiffuse = lightDiffuse * 0.5 + 0.5;
149 vec3 vertexToEye = vec3(0,0,1) - vertex;\n
150 vertexToEye = normalize(vertexToEye);
151 vec3 lightReflect = normalize(reflect(-vecToLight, normal));\n
152 float specularFactor = max(0.0,dot(vertexToEye, lightReflect));\n
153 specularFactor = pow(specularFactor, 32.0) * 0.7;
155 vec4 texColor = texture2D(sTexture, zoomCoords);\n
156 gl_FragColor.rgb = texColor.rgb * ambient + color.rgb * texColor.rgb * lightDiffuse + vec3(specularFactor);\n
157 gl_FragColor.a = 1.0;
162 * Metadata for each ball
172 Property::Index positionIndex;
173 Property::Index positionVarIndex;
176 } // unnamed namespace
179 * Demo using Metaballs
181 * When the metaball is clicked it explodes to smaller balls
183 class MetaballExplosionController : public ConnectionTracker
191 MetaballExplosionController( Application& application );
196 virtual ~MetaballExplosionController();
199 * Creates the metaballs and initializes the scene
201 void Create( Application& app );
204 * Touch event handler to center metaballs at touch position
205 * and start explosion animation on release
207 bool OnTouch( Actor actor, const TouchData& touch );
210 * Key event handler to quit application on escape or back key
212 void OnKeyEvent(const KeyEvent& event);
216 Application& mApplication;
219 Texture mBackgroundTexture;
220 FrameBuffer mMetaballFBO;
221 Texture mMetaballFBOTexture;
224 MetaballInfo mMetaballs[METABALL_NUMBER];
226 Property::Index mPositionIndex;
227 Actor mCompositionActor;
230 Vector2 mCurrentTouchPosition;
231 Vector2 mMetaballPosVariation;
232 Vector2 mMetaballPosVariationFrom;
233 Vector2 mMetaballPosVariationTo;
234 Vector2 mMetaballCenter;
237 Animation mPositionVarAnimation[METABALL_NUMBER];
239 uint32_t mDispersion;
240 Animation mDispersionAnimation[METABALL_NUMBER];
242 Timer mTimerDispersion;
244 float mTimeMultiplier;
246 // Private helper functions
249 * Create a mesh data with the geometry for the metaball rendering
250 * @param aspectMappedTexture whether texture coords should be mapped based on aspect ratio
252 Geometry CreateGeometry( bool aspectMappedTexture = true );
255 * Create a actors and renderers for the metaballs
257 void CreateMetaballActors();
260 * Create the render task and FBO to render the metaballs into a texture
262 void CreateMetaballImage();
265 * Create the the final composition
267 void CreateComposition();
270 * Function to create animations for the small variations of position inside the metaball
272 void CreateAnimations();
275 * Function to reset metaball state
277 void ResetMetaballs( bool resetAnims );
280 * Function to create disperse each of the ball that compose the metaball when exploding
282 void DisperseBallAnimation( uint32_t ball );
285 * Function to make metaballs come back to reset position
287 void LaunchResetMetaballPosition( Animation& source );
290 * Function to set things at the end of the animation
292 void EndDisperseAnimation( Animation& source );
295 * Function to init dispersion of the metaballs one by one using a timer
296 * (so not all the balls begin moving at the same time)
298 bool OnTimerDispersionTick();
301 * Function to set the actual position of the metaballs when the user clicks the screen
303 void SetPositionToMetaballs( const Vector2& metaballCenter );
310 MetaballExplosionController::MetaballExplosionController( Application& application )
311 : mApplication( application ),
313 mBackgroundTexture(),
315 mMetaballFBOTexture(),
320 mCurrentTouchPosition(),
321 mMetaballPosVariation(),
322 mMetaballPosVariationFrom(),
323 mMetaballPosVariationTo(),
325 mPositionVarAnimation(),
327 mDispersionAnimation(),
329 mTimeMultiplier( 1.0f )
331 // Connect to the Application's Init signal
332 mApplication.InitSignal().Connect( this, &MetaballExplosionController::Create );
335 MetaballExplosionController::~MetaballExplosionController()
337 // Nothing to do here;
340 void MetaballExplosionController::Create( Application& app )
342 Stage stage = Stage::GetCurrent();
344 stage.KeyEventSignal().Connect( this, &MetaballExplosionController::OnKeyEvent );
346 mScreenSize = stage.GetSize();
348 mTimeMultiplier = 1.0f;
350 stage.SetBackgroundColor(Color::BLACK);
352 // Load background texture
353 mBackgroundTexture = DemoHelper::LoadTexture( BACKGROUND_IMAGE );
355 srand( static_cast<uint32_t>( time(0) ) );
357 //Create internal data
358 CreateMetaballActors();
359 CreateMetaballImage();
365 mTimerDispersion = Timer::New( 150 );
366 mTimerDispersion.TickSignal().Connect( this, &MetaballExplosionController::OnTimerDispersionTick );
368 // Connect the callback to the touch signal on the mesh actor
369 stage.GetRootLayer().TouchSignal().Connect( this, &MetaballExplosionController::OnTouch );
372 Geometry MetaballExplosionController::CreateGeometry( bool aspectMappedTexture )
374 const float aspect = mScreenSize.y / mScreenSize.x;
376 // Create vertices and specify their color
377 const float xsize = mScreenSize.x * 0.5;
379 // Create the meshdata for the metaballs
380 struct VertexPosition { Vector2 position; };
381 struct VertexTexture { Vector2 texture; };
383 VertexPosition vertices[] =
385 { Vector2( -xsize, -xsize * aspect ) },
386 { Vector2( xsize, -xsize * aspect ) },
387 { Vector2( -xsize, xsize * aspect ) },
388 { Vector2( xsize, xsize * aspect ) }
391 const float textureAspect = (aspectMappedTexture) ? aspect : 1.0f;
392 VertexTexture textures[] =
394 { Vector2( 0.0f, 0.0f ) },
395 { Vector2( 1.0f, 0.0f ) },
396 { Vector2( 0.0f, 1.0f * textureAspect ) },
397 { Vector2( 1.0f, 1.0f * textureAspect ) }
400 uint32_t numberOfVertices = sizeof(vertices)/sizeof(VertexPosition);
403 Property::Map positionVertexFormat;
404 positionVertexFormat["aPosition"] = Property::VECTOR2;
405 PropertyBuffer positionVertices = PropertyBuffer::New( positionVertexFormat );
406 positionVertices.SetData( vertices, numberOfVertices );
409 Property::Map textureVertexFormat;
410 textureVertexFormat["aTexture"] = Property::VECTOR2;
411 PropertyBuffer textureVertices = PropertyBuffer::New( textureVertexFormat );
412 textureVertices.SetData( textures, numberOfVertices );
415 const uint16_t indices[] = { 0, 3, 1, 0, 2, 3 };
417 // Create the geometry object
418 Geometry texturedQuadGeometry = Geometry::New();
419 texturedQuadGeometry.AddVertexBuffer( positionVertices );
420 texturedQuadGeometry.AddVertexBuffer( textureVertices );
422 texturedQuadGeometry.SetIndexBuffer ( &indices[0], sizeof( indices )/ sizeof( indices[0] ) );
424 return texturedQuadGeometry;
427 void MetaballExplosionController::CreateMetaballActors()
429 // Create the shader for the metaballs, tell DALi that shader modifies geometry so we dont need to set a meaningless size
430 Shader shader = Shader::New( METABALL_VERTEX_SHADER, METABALL_FRAG_SHADER, Shader::Hint::MODIFIES_GEOMETRY );
432 Geometry metaballGeom = CreateGeometry();
433 // Reuse same renderer for each actor
434 Renderer renderer = Renderer::New( metaballGeom, shader );
435 renderer.SetProperty( Renderer::Property::BLEND_MODE, BlendMode::ON );
436 renderer.SetProperty( Renderer::Property::BLEND_FACTOR_SRC_RGB, BlendFactor::ONE );
437 renderer.SetProperty( Renderer::Property::BLEND_FACTOR_DEST_RGB, BlendFactor::ONE );
438 renderer.SetProperty( Renderer::Property::BLEND_FACTOR_SRC_ALPHA, BlendFactor::ONE );
439 renderer.SetProperty( Renderer::Property::BLEND_FACTOR_DEST_ALPHA, BlendFactor::ONE );
441 //Initialization of each of the metaballs
442 for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
444 mMetaballs[i].position = Vector2(0.0f, 0.0f);
445 mMetaballs[i].radius = mMetaballs[i].initRadius = Random::Range(0.05f,0.07f);
447 mMetaballs[i].actor = Actor::New( );
448 mMetaballs[i].actor.SetName( "Metaball" );
449 mMetaballs[i].actor.SetScale( 1.0f );
450 mMetaballs[i].actor.SetParentOrigin( ParentOrigin::CENTER );
451 mMetaballs[i].actor.AddRenderer( renderer );
453 mMetaballs[i].positionIndex = mMetaballs[i].actor.RegisterProperty( "uPositionMetaball", mMetaballs[i].position );
455 mMetaballs[i].positionVarIndex = mMetaballs[i].actor.RegisterProperty( "uPositionVar", Vector2(0.f,0.f) );
457 mMetaballs[i].actor.RegisterProperty( "uGravityVector", Vector2(Random::Range(-0.2,0.2),Random::Range(-0.2,0.2)) );
458 mMetaballs[i].actor.RegisterProperty( "uRadius", mMetaballs[i].radius );
459 mMetaballs[i].actor.RegisterProperty( "uRadiusVar", 0.f );
463 mMetaballRoot = Actor::New();
464 mMetaballRoot.SetParentOrigin( ParentOrigin::CENTER );
465 for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
467 mMetaballRoot.Add( mMetaballs[i].actor );
472 void MetaballExplosionController::CreateMetaballImage()
474 // Create an FBO and a render task to create to render the metaballs with a fragment shader
475 Stage stage = Stage::GetCurrent();
477 mMetaballFBO = FrameBuffer::New( mScreenSize.x, mScreenSize.y, FrameBuffer::Attachment::NONE );
478 mMetaballFBOTexture = Texture::New( Dali::TextureType::TEXTURE_2D,
480 mScreenSize.x, mScreenSize.y );
481 mMetaballFBO.AttachColorTexture( mMetaballFBOTexture );
483 stage.Add(mMetaballRoot);
485 // Create the render task used to render the metaballs
486 RenderTaskList taskList = Stage::GetCurrent().GetRenderTaskList();
487 RenderTask task = taskList.CreateTask();
488 task.SetRefreshRate( RenderTask::REFRESH_ALWAYS );
489 task.SetSourceActor( mMetaballRoot );
490 task.SetExclusive( true );
491 task.SetClearColor( Color::BLACK );
492 task.SetClearEnabled( true );
493 task.SetFrameBuffer( mMetaballFBO );
496 void MetaballExplosionController::CreateComposition()
499 Shader shader = Shader::New( METABALL_VERTEX_SHADER, REFRACTION_FRAG_SHADER );
501 // Create new texture set
502 auto textureSet = TextureSet::New();
503 textureSet.SetTexture( 0u, mBackgroundTexture );
504 textureSet.SetTexture( 1u, mMetaballFBOTexture );
507 Geometry metaballGeom = CreateGeometry( false );
509 Renderer mRenderer = Renderer::New( metaballGeom, shader );
510 mRenderer.SetTextures( textureSet );
513 mCompositionActor = Actor::New( );
514 mCompositionActor.SetParentOrigin(ParentOrigin::CENTER);
515 mCompositionActor.SetPosition(Vector3(0.0f, 0.0f, 0.0f));
516 mCompositionActor.SetSize(mScreenSize.x, mScreenSize.y);
517 mCompositionActor.AddRenderer( mRenderer );
519 Vector2 metaballCenter(0.0,0);
520 metaballCenter.x = metaballCenter.x * 0.5;
521 metaballCenter.y = metaballCenter.y * 0.5;
522 mPositionIndex = mCompositionActor.RegisterProperty( "uPositionMetaball", metaballCenter );
524 SetPositionToMetaballs( metaballCenter );
526 mCompositionActor.SetSize(mScreenSize.x, mScreenSize.y);
528 Stage stage = Stage::GetCurrent();
529 stage.Add( mCompositionActor );
532 void MetaballExplosionController::CreateAnimations()
536 for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
538 KeyFrames keySinCosVariation = KeyFrames::New();
539 Vector2 sinCosVariation( 0,0 );
541 direction.x = Random::Range( -100.f,100.f );
542 direction.y = Random::Range( -100.f,100.f );
544 direction.Normalize();
547 for( uint32_t j = 0; j < 360; j++ )
549 sinCosVariation.x = sinf( j * Math::PI/180.f ) * direction.x;
550 sinCosVariation.y = cosf( j * Math::PI/180.f ) * direction.y;
552 keySinCosVariation.Add( key, sinCosVariation );
555 mPositionVarAnimation[i] = Animation::New( 3.f );
556 mPositionVarAnimation[i].AnimateBetween( Property( mMetaballs[i].actor, mMetaballs[i].positionVarIndex ), keySinCosVariation );
557 mPositionVarAnimation[i].SetLooping( true );
558 mPositionVarAnimation[i].Play();
562 void MetaballExplosionController::ResetMetaballs( bool resetAnims )
564 for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
566 if( mDispersionAnimation[i] )
568 mDispersionAnimation[i].Clear();
571 mMetaballs[i].position = Vector2( 0.0f, 0.0f );
572 mMetaballs[i].actor.SetProperty( mMetaballs[i].positionIndex, mMetaballs[i].position );
574 mTimerDispersion.Stop();
577 mCompositionActor.SetProperty( mPositionIndex, Vector2(0,0) );
580 void MetaballExplosionController::DisperseBallAnimation( uint32_t ball )
583 position.x = Random::Range(-1.5f,1.5f);
584 position.y = Random::Range(-1.5f,1.5f);
586 mDispersionAnimation[ball] = Animation::New(2.0f * mTimeMultiplier);
587 mDispersionAnimation[ball].AnimateTo( Property(mMetaballs[ball].actor, mMetaballs[ball].positionIndex), position);
588 mDispersionAnimation[ball].Play();
590 if( ball == METABALL_NUMBER - 1 )
592 mDispersionAnimation[ball].FinishedSignal().Connect( this, &MetaballExplosionController::LaunchResetMetaballPosition );
596 void MetaballExplosionController::LaunchResetMetaballPosition( Animation& source )
598 for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
600 mDispersionAnimation[i] = Animation::New( 1.5f + i * 0.25f * mTimeMultiplier );
601 mDispersionAnimation[i].AnimateTo(Property( mMetaballs[i].actor, mMetaballs[i].positionIndex), Vector2(0,0) );
602 mDispersionAnimation[i].Play();
604 if( i == METABALL_NUMBER - 1 )
606 mDispersionAnimation[i].FinishedSignal().Connect( this, &MetaballExplosionController::EndDisperseAnimation );
611 void MetaballExplosionController::EndDisperseAnimation( Animation& source )
613 mCompositionActor.SetProperty( mPositionIndex, Vector2(0,0) );
616 bool MetaballExplosionController::OnTimerDispersionTick()
618 if( mDispersion < METABALL_NUMBER )
620 DisperseBallAnimation( mDispersion );
626 void MetaballExplosionController::SetPositionToMetaballs( const Vector2& metaballCenter )
628 //We set the position for the metaballs based on click position
629 for( uint32_t i = 0; i < METABALL_NUMBER; i++ )
631 mMetaballs[i].position = metaballCenter;
632 mMetaballs[i].actor.SetProperty( mMetaballs[i].positionIndex, mMetaballs[i].position );
635 mCompositionActor.SetProperty( mPositionIndex, metaballCenter );
638 bool MetaballExplosionController::OnTouch( Actor actor, const TouchData& touch )
640 float aspectR = mScreenSize.y / mScreenSize.x;
642 switch( touch.GetState( 0 ) )
644 case PointState::DOWN:
646 ResetMetaballs(true);
648 const Vector2 screen = touch.GetScreenPosition( 0 );
649 Vector2 metaballCenter = Vector2( (screen.x / mScreenSize.x) - 0.5f, (aspectR * (mScreenSize.y - screen.y) / mScreenSize.y) - 0.5f ) * 2.0f;
650 SetPositionToMetaballs(metaballCenter);
654 case PointState::MOTION:
656 const Vector2 screen = touch.GetScreenPosition( 0 );
657 Vector2 metaballCenter = Vector2( (screen.x / mScreenSize.x) - 0.5f, (aspectR * (mScreenSize.y - screen.y) / mScreenSize.y) - 0.5f ) * 2.0f;
658 SetPositionToMetaballs(metaballCenter);
662 case PointState::LEAVE:
663 case PointState::INTERRUPTED:
665 mTimerDispersion.Start();
674 void MetaballExplosionController::OnKeyEvent(const KeyEvent& event)
676 if(event.state == KeyEvent::Down)
678 if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
688 int32_t DALI_EXPORT_API main( int argc, char **argv )
690 Application application = Application::New( &argc, &argv );
692 MetaballExplosionController test( application );
694 application.MainLoop();