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.
18 #include <dali-toolkit/dali-toolkit.h>
19 #include <dali/devel-api/actors/camera-actor-devel.h>
20 #include <dali/devel-api/adaptor-framework/file-stream.h>
24 #include "gltf-scene.h"
31 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
32 attribute mediump vec3 aPosition;\n
33 attribute mediump vec3 aNormal;\n
34 attribute mediump vec2 aTexCoord;\n
35 uniform mediump mat4 uMvpMatrix;\n
36 uniform mediump mat3 uNormalMatrix;\n
37 uniform mediump vec3 uSize;\n
39 varying mediump vec2 vTexCoord; \n
40 varying mediump vec3 vNormal; \n
41 varying mediump vec3 vPosition; \n
44 mediump vec4 vertexPosition = vec4(aPosition, 1.0);\n
45 vertexPosition.xyz *= uSize;\n
46 vTexCoord = aTexCoord;\n
47 vNormal = normalize(uNormalMatrix * aNormal);\n
48 vPosition = aPosition; \n
49 gl_Position = uMvpMatrix * vertexPosition;\n
53 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
54 uniform lowp vec4 uColor;\n
55 uniform sampler2D sTexture; \n
56 varying mediump vec3 vNormal;\n
57 varying mediump vec3 vPosition; \n
58 varying mediump vec2 vTexCoord; \n
62 gl_FragColor = texture2D(sTexture, vTexCoord) * 50.0;\n
66 const char* FRAGMENT_SIMPLE_SHADER = DALI_COMPOSE_SHADER(
67 uniform lowp vec4 uColor;\n
68 uniform sampler2D sTexture; \n
69 varying mediump vec3 vNormal;\n
70 varying mediump vec3 vPosition; \n
71 varying mediump vec2 vTexCoord; \n
75 gl_FragColor = texture2D(sTexture, vTexCoord) * 2.0;\n
79 const char* TEXTURED_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
80 uniform lowp vec4 uColor;\n
81 uniform sampler2D sTexture; \n
82 uniform mediump vec2 uScreenSize;\n
84 uniform mediump vec3 eyePos;\n
85 uniform mediump vec3 lightDir;\n
87 varying mediump vec3 vNormal;\n
88 varying mediump vec3 vPosition; \n
89 varying mediump vec2 vTexCoord; \n
93 mediump vec3 n = normalize( vNormal );\n
94 mediump vec3 l = normalize( lightDir );\n
95 mediump vec3 e = normalize( eyePos );\n
96 mediump float intensity = max(dot(n,l), 0.0);\n
97 gl_FragColor = texture2D(sTexture, vTexCoord) * intensity;\n
101 const char* PLASMA_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
102 precision mediump float;\n
103 uniform sampler2D sTexture; \n
106 uniform float uKFactor;
107 uniform mediump vec3 eyePos;\n
108 uniform mediump vec3 lightDir;\n
109 varying mediump vec3 vNormal;\n
110 varying mediump vec3 vPosition; \n
111 varying mediump vec2 vTexCoord; \n
115 mediump vec3 n = normalize( vNormal );\n
116 mediump vec3 l = normalize( lightDir );\n
117 mediump vec3 e = normalize( eyePos );\n
118 mediump float intensity = max(dot(n,l), 0.0);\n
120 const mediump float PI = 3.1415926535897932384626433832795;\n
121 mediump float v = 0.0;\n
122 mediump vec2 c = vTexCoord * uKFactor - uKFactor/2.0;\n
123 v += sin((c.x+uTime));\n
124 v += sin((c.y+uTime)/2.0);\n
125 v += sin((c.x+c.y+uTime)/2.0);\n
126 c += uKFactor/2.0 * vec2(sin(uTime/3.0), cos(uTime/2.0));\n
127 v += sin(sqrt(c.x*c.x+c.y*c.y+1.0)+uTime);\n
129 mediump vec3 col = vec3(1, sin(PI*v), cos(PI*v));\n
130 gl_FragColor = (texture2D(sTexture, vTexCoord)) * (((col.r+col.g+col.b)/3.0)+1.0+intensity);\n
134 const char* TEX_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
135 uniform lowp vec4 uColor;\n
136 uniform sampler2D sTexture0; \n
137 uniform sampler2D sTexture1; \n
138 uniform mediump vec3 eyePos;\n
139 uniform mediump vec3 lightDir;\n
140 uniform mediump vec2 uScreenSize;\n
141 varying mediump vec3 vNormal;\n
142 varying mediump vec3 vPosition; \n
143 varying mediump vec2 vTexCoord; \n
146 mediump float rand(mediump vec2 co){\n
147 return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n
152 mediump vec2 tx = (gl_FragCoord.xy / uScreenSize.xy);\n
153 mediump vec3 n = normalize( vNormal );\n
154 mediump vec3 l = normalize( lightDir );\n
155 mediump vec3 e = normalize( eyePos );\n
156 mediump float factor = gl_FragCoord.y / uScreenSize.y;\n
157 mediump float intensity = max(dot(n,l), 0.0);\n
158 mediump vec2 uv = tx;\n
159 gl_FragColor = ((texture2D(sTexture0, vTexCoord) * factor ) + \n
160 (texture2D(sTexture1, uv))) * intensity;\n
169 using ModelPtr = std::unique_ptr<Model>;
171 using ActorContainer = std::vector<Actor>;
172 using CameraContainer = std::vector<CameraActor>;
173 using ModelContainer = std::vector<ModelPtr>;
174 using TextureSetContainer = std::vector<TextureSet>;
176 const Vector3 DEFAULT_LIGHT_DIRECTION( 0.5, 0.5, -1 );
179 bool LoadFile( const std::string& filename, std::vector<T>& bytes )
181 Dali::FileStream fileStream( filename, Dali::FileStream::READ | Dali::FileStream::BINARY );
182 FILE* fin = fileStream.GetFile();
186 if( fseek( fin, 0, SEEK_END ) )
190 bytes.resize( uint32_t(ftell( fin )) );
191 std::fill( bytes.begin(), bytes.end(), 0 );
192 if( fseek( fin, 0, SEEK_SET ) )
196 size_t result = fread( bytes.data(), 1, bytes.size(), fin );
197 return ( result != 0 );
203 Shader CreateShader( const std::string& vsh, const std::string& fsh )
205 std::vector<char> vshShaderSource;
206 std::vector<char> fshShaderSource;
211 std::string vshPath( DEMO_GAME_DIR );
214 LoadFile( vshPath, vshShaderSource );
218 vshShaderSource.insert(vshShaderSource.end(), vsh.begin(), vsh.end());
224 std::string fshPath( DEMO_GAME_DIR );
227 LoadFile( fshPath, fshShaderSource );
231 fshShaderSource.insert(fshShaderSource.end(), fsh.begin(), fsh.end());
234 vshShaderSource.emplace_back(0);
235 fshShaderSource.emplace_back(0);
236 return Shader::New( std::string(vshShaderSource.data()), std::string(fshShaderSource.data()) );
239 ModelPtr CreateModel(
241 const glTF_Mesh* mesh,
242 const std::string& vertexShaderSource,
243 const std::string& fragmentShaderSource )
246 * Obtain interleaved buffer for first mesh with position and normal attributes
248 auto positionBuffer = gltf.GetMeshAttributeBuffer( *mesh,
250 glTFAttributeType::POSITION,
251 glTFAttributeType::NORMAL,
252 glTFAttributeType::TEXCOORD_0
255 auto attributeCount = gltf.GetMeshAttributeCount( mesh );
257 * Create matching property buffer
259 auto vertexBuffer = VertexBuffer::New( Property::Map()
260 .Add("aPosition", Property::VECTOR3 )
261 .Add("aNormal", Property::VECTOR3)
262 .Add("aTexCoord", Property::VECTOR2)
266 vertexBuffer.SetData( positionBuffer.data(), attributeCount );
268 auto geometry = Geometry::New();
269 geometry.AddVertexBuffer( vertexBuffer );
270 auto indexBuffer = gltf.GetMeshIndexBuffer( mesh );
271 geometry.SetIndexBuffer( indexBuffer.data(), indexBuffer.size() );
272 geometry.SetType( Geometry::Type::TRIANGLES );
273 ModelPtr retval( new Model() );
274 retval->shader = CreateShader( vertexShaderSource, fragmentShaderSource );
275 retval->geometry = geometry;
279 void ReplaceShader( Actor& actor, const std::string& vsh, const std::string& fsh )
281 auto renderer = actor.GetRendererAt(0);
282 auto shader = CreateShader(vsh, fsh);
283 renderer.SetShader( shader );
286 void CreateTextureSetsFromGLTF( glTF* gltf, const std::string& basePath, TextureSetContainer& textureSets )
288 const auto& materials = gltf->GetMaterials();
289 const auto& textures = gltf->GetTextures();
291 std::map<std::string, Texture> textureCache{};
293 for(const auto& material : materials )
295 TextureSet textureSet;
296 if(material.pbrMetallicRoughness.enabled)
298 textureSet = TextureSet::New();
299 std::string filename( basePath );
301 filename += textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
302 Dali::PixelData pixelData = Dali::Toolkit::SyncImageLoader::Load( filename );
304 auto cacheKey = textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
305 auto iter = textureCache.find(cacheKey);
307 if(iter == textureCache.end())
309 texture = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(),
310 pixelData.GetHeight());
311 texture.Upload(pixelData);
312 texture.GenerateMipmaps();
313 textureCache[cacheKey] = texture;
317 texture = iter->second;
319 textureSet.SetTexture( 0, texture );
320 Dali::Sampler sampler = Dali::Sampler::New();
321 sampler.SetWrapMode( Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT );
322 sampler.SetFilterMode( Dali::FilterMode::LINEAR_MIPMAP_LINEAR, Dali::FilterMode::LINEAR );
323 textureSet.SetSampler( 0, sampler );
325 textureSets.emplace_back( textureSet );
331 * Creates models from glTF
333 void CreateModelsFromGLTF( glTF* gltf, ModelContainer& models )
335 const auto& meshes = gltf->GetMeshes();
336 for( const auto& mesh : meshes )
338 // change shader to use texture if material indicates that
339 if(mesh->material != 0xffffffff && gltf->GetMaterials()[mesh->material].pbrMetallicRoughness.enabled)
341 models.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, TEXTURED_FRAGMENT_SHADER ) );
345 models.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, FRAGMENT_SHADER ) );
350 Actor CreateSceneFromGLTF(
353 ModelContainer& models,
354 ActorContainer& actors,
355 CameraContainer& cameras,
356 TextureSetContainer& textureSets )
358 const auto& nodes = gltf->GetNodes();
360 Vector3 cameraPosition;
362 // for each node create nodes and children
363 // resolve parents later
364 actors.reserve( nodes.size() );
365 for( const auto& node : nodes )
367 auto actor = node.cameraId != 0xffffffff ? CameraActor::New( window.GetSize() ) : Actor::New();
369 actor.SetProperty( Actor::Property::SIZE, Vector3( 1, 1, 1 ) );
370 actor.SetProperty( Dali::Actor::Property::NAME, node.name );
371 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
372 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
373 actor.SetProperty( Actor::Property::POSITION, Vector3( node.translation[0], node.translation[1], node.translation[2] ));
374 actor.SetProperty( Actor::Property::SCALE, Vector3( node.scale[0], node.scale[1], node.scale[2] ) );
375 actor.SetProperty( Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3],
376 node.rotationQuaternion[0],
377 node.rotationQuaternion[1],
378 node.rotationQuaternion[2]));
380 actors.emplace_back( actor );
382 // Initially add each actor to the very first one
383 if(actors.size() > 1)
385 actors[0].Add(actor);
388 // If mesh, create and add renderer
389 if(node.meshId != 0xffffffff)
391 const auto& model = models[node.meshId].get();
392 auto renderer = Renderer::New( model->geometry, model->shader );
394 // if textured, add texture set
395 auto materialId = gltf->GetMeshes()[node.meshId]->material;
396 if( materialId != 0xffffffff )
398 if( gltf->GetMaterials()[materialId].pbrMetallicRoughness.enabled )
400 renderer.SetTextures( textureSets[materialId] );
404 actor.AddRenderer( renderer );
407 // Reset and attach main camera
408 if( node.cameraId != 0xffffffff )
410 cameraPosition = Vector3(node.translation[0], node.translation[1], node.translation[2]);
411 auto quatY = Quaternion( Degree(180.0f), Vector3( 0.0, 1.0, 0.0) );
412 auto cameraActor = CameraActor::DownCast( actor );
413 cameraActor.SetProperty( Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3],
414 node.rotationQuaternion[0],
415 node.rotationQuaternion[1],
416 node.rotationQuaternion[2] )
419 cameraActor.SetProjectionMode( Camera::PERSPECTIVE_PROJECTION );
421 const auto camera = gltf->GetCameras()[node.cameraId];
422 cameraActor.SetNearClippingPlane( camera->znear );
423 cameraActor.SetFarClippingPlane( camera->zfar );
424 cameraActor.SetFieldOfView( camera->yfov );
426 cameraActor.SetProperty( CameraActor::Property::INVERT_Y_AXIS, true);
427 cameraActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
428 cameraActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
430 cameras.emplace_back( cameraActor );
434 // Resolve hierarchy dependencies
436 for( const auto& node : nodes )
438 if(!node.children.empty())
440 for(const auto& childId : node.children)
442 actors[i].Add( actors[childId+1] );
448 for( auto& actor : actors )
450 actor.RegisterProperty( "lightDir", DEFAULT_LIGHT_DIRECTION );
451 actor.RegisterProperty( "eyePos", cameraPosition );
457 } // unnamed namespace
459 // This example shows how to create and display mirrored reflection using CameraActor
461 class ReflectionExample : public ConnectionTracker
465 ReflectionExample( Application& application )
466 : mApplication( application )
468 // Connect to the Application's Init signal
469 mApplication.InitSignal().Connect( this, &ReflectionExample::Create );
472 ~ReflectionExample() = default;
476 // The Init signal is received once (only) during the Application lifetime
477 void Create( Application& application )
479 // Get a handle to the window
480 Window window = application.GetWindow();
481 uint32_t windowWidth = uint32_t(window.GetSize().GetWidth());
482 uint32_t windowHeight = uint32_t(window.GetSize().GetHeight());
484 window.GetRenderTaskList().GetTask(0).SetClearEnabled(false);
485 mLayer3D = Layer::New();
486 mLayer3D.SetProperty( Actor::Property::SIZE, Vector2( windowWidth, windowHeight ) );
487 window.Add(mLayer3D);
489 mLayer3D.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
490 mLayer3D.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
491 mLayer3D.SetProperty( Layer::Property::BEHAVIOR, Layer::LAYER_3D );
492 mLayer3D.SetProperty( Layer::Property::DEPTH_TEST, true );
495 auto gltf = glTF(DEMO_GAME_DIR "/reflection");
497 // Define direction of light
500 * Instantiate texture sets
502 CreateTextureSetsFromGLTF( &gltf, DEMO_GAME_DIR, mTextureSets );
507 CreateModelsFromGLTF( &gltf, mModels );
510 * Create scene nodes & add to 3D Layer
512 mLayer3D.Add( CreateSceneFromGLTF( window, &gltf, mModels, mActors, mCameras, mTextureSets ) );
514 auto planeActor = mLayer3D.FindChildByName( "Plane" );
515 auto solarActor = mLayer3D.FindChildByName( "solar_root" );
516 auto backgroundActor = mLayer3D.FindChildByName( "background" );
517 ReplaceShader( backgroundActor, VERTEX_SHADER, FRAGMENT_SIMPLE_SHADER );
518 mCenterActor = mLayer3D.FindChildByName( "center" );
519 mCenterHorizActor = mLayer3D.FindChildByName( "center2" );
522 auto sun = mLayer3D.FindChildByName( "sun" );
523 ReplaceShader( sun, VERTEX_SHADER, PLASMA_FRAGMENT_SHADER );
524 mSunTimeUniformIndex = sun.RegisterProperty( "uTime", 0.0f );
525 mSunKFactorUniformIndex = sun.RegisterProperty( "uKFactor", 0.0f );
527 mTickTimer = Timer::New( 16 );
528 mTickTimer.TickSignal().Connect( this, &ReflectionExample::TickTimerSignal);
531 auto milkyway = mLayer3D.FindChildByName( "milkyway" );
532 ReplaceShader( milkyway, VERTEX_SHADER, FRAGMENT_SHADER );
534 auto renderTaskSourceActor = mLayer3D.FindChildByName( "RenderTaskSource" );
537 * Access camera (it's a child of "Camera" node)
539 auto camera = mLayer3D.FindChildByName( "Camera_Orientation" );
540 auto cameraRef = mLayer3D.FindChildByName( "CameraReflection_Orientation" );
542 auto cameraActor = CameraActor::DownCast( camera );
543 mCamera3D = cameraActor;
545 auto cameraRefActor = CameraActor::DownCast( cameraRef );
546 cameraRefActor.SetProperty( DevelCameraActor::Property::REFLECTION_PLANE, Vector4(0.0f, -1.0f, 0.0f, 0.0f));
547 mReflectionCamera3D = cameraRefActor;
549 auto task3D = window.GetRenderTaskList().CreateTask();
550 task3D.SetSourceActor( mLayer3D );
551 task3D.SetViewport( Rect<int>(0, 0, windowWidth, windowHeight ) );
552 task3D.SetCameraActor( cameraActor );
553 task3D.SetClearColor( Color::BLACK );
554 task3D.SetClearEnabled( true );
555 task3D.SetExclusive( false );
556 task3D.SetCameraActor( cameraActor );
559 * Change shader to textured
561 Shader texShader = CreateShader( VERTEX_SHADER, TEX_FRAGMENT_SHADER );
562 planeActor.RegisterProperty( "uScreenSize", Vector2(windowWidth, windowHeight) );
563 auto renderer = planeActor.GetRendererAt(0);
564 auto textureSet = renderer.GetTextures();
565 renderer.SetShader( texShader );
567 Texture fbTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888, windowWidth, windowHeight );
568 textureSet.SetTexture( 1u, fbTexture );
570 auto fb = FrameBuffer::New(windowWidth, windowHeight,
571 FrameBuffer::Attachment::DEPTH );
573 fb.AttachColorTexture( fbTexture );
575 auto renderTask = window.GetRenderTaskList().CreateTask();
576 renderTask.SetFrameBuffer( fb );
577 renderTask.SetSourceActor( renderTaskSourceActor );
578 renderTask.SetViewport( Rect<int>(0, 0, windowWidth, windowHeight ) );
579 renderTask.SetCameraActor( cameraRefActor );
580 renderTask.SetClearColor( Color::BLACK );
581 renderTask.SetClearEnabled( true );
582 renderTask.SetExclusive( false );
584 mAnimation = Animation::New(30.0f );
585 mAnimation.AnimateBy(Property(solarActor, Actor::Property::ORIENTATION ),
586 Quaternion( Degree(359), Vector3(0.0, 1.0, 0.0)));
587 mAnimation.AnimateBy(Property(milkyway, Actor::Property::ORIENTATION ),
588 Quaternion( Degree(-359), Vector3(0.0, 1.0, 0.0)));
589 mAnimation.SetLooping(true );
592 Actor panScreen = Actor::New();
593 Vector2 windowSize = window.GetSize();
594 panScreen.SetProperty( Actor::Property::SIZE, windowSize );
595 panScreen.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
596 panScreen.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
597 auto camera2d = window.GetRenderTaskList().GetTask(0).GetCameraActor();
598 panScreen.SetProperty( Actor::Property::POSITION, Vector3( 0, 0, camera2d.GetNearClippingPlane() ));
599 camera2d.Add(panScreen);
600 camera2d.RotateBy( Degree(180.0f), Vector3( 0.0, 1.0, 0.0 ) );
601 mPanGestureDetector = PanGestureDetector::New();
602 mPanGestureDetector.Attach( panScreen );
603 mPanGestureDetector.DetectedSignal().Connect( this, &ReflectionExample::OnPan );
605 // Respond to key events
606 window.KeyEventSignal().Connect( this, &ReflectionExample::OnKeyEvent );
611 void OnPan( Actor actor, const PanGesture& panGesture )
613 auto displacement = panGesture.screenDisplacement;
614 mCenterActor.RotateBy( Degree( displacement.y *0.1f ), Vector3( 0.0, 0.0, 1.0) );
615 mCenterActor.RotateBy( Degree( displacement.x *0.1f ), Vector3( 0.0, 1.0, 0.0) );
617 mCenterActor.GetProperty( Actor::Property::ORIENTATION ).Get(q);
618 Matrix m = Matrix::IDENTITY;
619 m.SetTransformComponents( Vector3::ONE, q, Vector3::ZERO );
620 auto yAxis = m.GetYAxis() * -1.0f;
623 mReflectionCamera3D.SetProperty( DevelCameraActor::Property::REFLECTION_PLANE, Vector4(yAxis.x, yAxis.y, yAxis.z, 0.0f));
626 void OnKeyEvent( const KeyEvent& event )
628 if( event.GetState() == KeyEvent::Down )
630 if ( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
637 bool TickTimerSignal()
639 auto root = mLayer3D;
640 static float rotationAngle = 0.0f;
642 const auto ROTATION_ANGLE_STEP = 0.05f;
643 const auto FRAME_DELTA_TIME = 0.016f;
644 const auto PLASMA_K_FACTOR = 12.0f; // 'granularity' of plasma effect
646 rotationAngle += ROTATION_ANGLE_STEP;
647 mMockTime += FRAME_DELTA_TIME;
648 mKFactor = PLASMA_K_FACTOR;
650 auto sun = root.FindChildByName( "sun" );
651 sun.SetProperty( mSunTimeUniformIndex, mMockTime );
652 sun.SetProperty( mSunKFactorUniformIndex, mKFactor );
653 sun.SetProperty( Actor::Property::ORIENTATION, Quaternion( Radian(Degree(rotationAngle)), Vector3(0.0, 1.0, 0.0)));
659 Application& mApplication;
663 ActorContainer mActors {};
664 CameraContainer mCameras {};
665 ModelContainer mModels {};
666 TextureSetContainer mTextureSets {};
668 Animation mAnimation {};
669 float mMockTime { 0.0f };
670 float mKFactor { 0.0f };
671 Property::Index mSunTimeUniformIndex {};
672 Property::Index mSunKFactorUniformIndex {};
673 PanGestureDetector mPanGestureDetector {};
677 CameraActor mCamera3D {};
678 CameraActor mReflectionCamera3D {};
679 Actor mCenterActor {};
680 Actor mCenterHorizActor {};
683 int DALI_EXPORT_API main( int argc, char **argv )
685 Application application = Application::New( &argc, &argv );
686 ReflectionExample test( application );
687 application.MainLoop();