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"
27 using Dali::Toolkit::TextLabel;
32 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
33 attribute mediump vec3 aPosition;\n
34 attribute mediump vec3 aNormal;\n
35 attribute mediump vec2 aTexCoord;\n
36 uniform mediump mat4 uMvpMatrix;\n
37 uniform mediump mat3 uNormalMatrix;\n
38 uniform mediump vec3 uSize;\n
40 varying mediump vec2 vTexCoord; \n
41 varying mediump vec3 vNormal; \n
42 varying mediump vec3 vPosition; \n
45 mediump vec4 vertexPosition = vec4(aPosition, 1.0);\n
46 vertexPosition.xyz *= uSize;\n
47 vTexCoord = aTexCoord;\n
48 vNormal = normalize(uNormalMatrix * aNormal);\n
49 vPosition = aPosition; \n
50 gl_Position = uMvpMatrix * vertexPosition;\n
54 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
55 uniform lowp vec4 uColor;\n
56 uniform sampler2D sTexture; \n
57 varying mediump vec3 vNormal;\n
58 varying mediump vec3 vPosition; \n
59 varying mediump vec2 vTexCoord; \n
63 gl_FragColor = texture2D(sTexture, vTexCoord) * 50.0;\n
67 const char* FRAGMENT_SIMPLE_SHADER = DALI_COMPOSE_SHADER(
68 uniform lowp vec4 uColor;\n
69 uniform sampler2D sTexture; \n
70 varying mediump vec3 vNormal;\n
71 varying mediump vec3 vPosition; \n
72 varying mediump vec2 vTexCoord; \n
76 gl_FragColor = texture2D(sTexture, vTexCoord) * 2.0;\n
80 const char* TEXTURED_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
81 uniform lowp vec4 uColor;\n
82 uniform sampler2D sTexture; \n
83 uniform mediump vec2 uScreenSize;\n
85 uniform mediump vec3 eyePos;\n
86 uniform mediump vec3 lightDir;\n
88 varying mediump vec3 vNormal;\n
89 varying mediump vec3 vPosition; \n
90 varying mediump vec2 vTexCoord; \n
94 mediump vec3 n = normalize( vNormal );\n
95 mediump vec3 l = normalize( lightDir );\n
96 mediump vec3 e = normalize( eyePos );\n
97 mediump float intensity = max(dot(n,l), 0.0);\n
98 gl_FragColor = texture2D(sTexture, vTexCoord) * intensity;\n
102 const char* PLASMA_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
103 precision mediump float;\n
104 uniform sampler2D sTexture; \n
107 uniform float uKFactor;
108 uniform mediump vec3 eyePos;\n
109 uniform mediump vec3 lightDir;\n
110 varying mediump vec3 vNormal;\n
111 varying mediump vec3 vPosition; \n
112 varying mediump vec2 vTexCoord; \n
116 mediump vec3 n = normalize( vNormal );\n
117 mediump vec3 l = normalize( lightDir );\n
118 mediump vec3 e = normalize( eyePos );\n
119 mediump float intensity = max(dot(n,l), 0.0);\n
121 const mediump float PI = 3.1415926535897932384626433832795;\n
122 mediump float v = 0.0;\n
123 mediump vec2 c = vTexCoord * uKFactor - uKFactor/2.0;\n
124 v += sin((c.x+uTime));\n
125 v += sin((c.y+uTime)/2.0);\n
126 v += sin((c.x+c.y+uTime)/2.0);\n
127 c += uKFactor/2.0 * vec2(sin(uTime/3.0), cos(uTime/2.0));\n
128 v += sin(sqrt(c.x*c.x+c.y*c.y+1.0)+uTime);\n
130 mediump vec3 col = vec3(1, sin(PI*v), cos(PI*v));\n
131 gl_FragColor = (texture2D(sTexture, vTexCoord)) * (((col.r+col.g+col.b)/3.0)+1.0+intensity);\n
135 const char* TEX_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
136 uniform lowp vec4 uColor;\n
137 uniform sampler2D sTexture0; \n
138 uniform sampler2D sTexture1; \n
139 uniform mediump vec3 eyePos;\n
140 uniform mediump vec3 lightDir;\n
141 uniform mediump vec2 uScreenSize;\n
142 varying mediump vec3 vNormal;\n
143 varying mediump vec3 vPosition; \n
144 varying mediump vec2 vTexCoord; \n
147 mediump float rand(mediump vec2 co){\n
148 return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n
153 mediump vec2 tx = (gl_FragCoord.xy / uScreenSize.xy);\n
154 mediump vec3 n = normalize( vNormal );\n
155 mediump vec3 l = normalize( lightDir );\n
156 mediump vec3 e = normalize( eyePos );\n
157 mediump float factor = gl_FragCoord.y / uScreenSize.y;\n
158 mediump float intensity = max(dot(n,l), 0.0);\n
159 mediump vec2 uv = tx;\n
160 gl_FragColor = ((texture2D(sTexture0, vTexCoord) * factor ) + \n
161 (texture2D(sTexture1, uv))) * intensity;\n
172 bool LoadFile( const std::string& filename, std::vector<T>& bytes )
174 Dali::FileStream fileStream( filename, Dali::FileStream::READ | Dali::FileStream::BINARY );
175 FILE* fin = fileStream.GetFile();
179 if( fseek( fin, 0, SEEK_END ) )
183 bytes.resize( uint32_t(ftell( fin )) );
184 std::fill( bytes.begin(), bytes.end(), 0 );
185 if( fseek( fin, 0, SEEK_SET ) )
189 size_t result = fread( bytes.data(), 1, bytes.size(), fin );
190 return ( result != 0 );
196 Shader CreateShader( const std::string& vsh, const std::string& fsh )
198 std::vector<char> vshShaderSource;
199 std::vector<char> fshShaderSource;
204 std::string vshPath( DEMO_GAME_DIR );
207 LoadFile( vshPath, vshShaderSource );
211 vshShaderSource.insert(vshShaderSource.end(), vsh.begin(), vsh.end());
217 std::string fshPath( DEMO_GAME_DIR );
220 LoadFile( fshPath, fshShaderSource );
224 fshShaderSource.insert(fshShaderSource.end(), fsh.begin(), fsh.end());
227 vshShaderSource.emplace_back(0);
228 fshShaderSource.emplace_back(0);
229 return Shader::New( std::string(vshShaderSource.data()), std::string(fshShaderSource.data()) );
232 std::unique_ptr<Model> CreateModel( glTF& gltf,
233 const glTF_Mesh* mesh,
234 const std::string& vertexShaderSource,
235 const std::string& fragmentShaderSource )
238 * Obtain interleaved buffer for first mesh with position and normal attributes
240 auto positionBuffer = gltf.GetMeshAttributeBuffer( *mesh,
242 glTFAttributeType::POSITION,
243 glTFAttributeType::NORMAL,
244 glTFAttributeType::TEXCOORD_0
247 auto attributeCount = gltf.GetMeshAttributeCount( mesh );
249 * Create matching property buffer
251 auto vertexBuffer = PropertyBuffer::New( Property::Map()
252 .Add("aPosition", Property::VECTOR3 )
253 .Add("aNormal", Property::VECTOR3)
254 .Add("aTexCoord", Property::VECTOR2)
258 vertexBuffer.SetData( positionBuffer.data(), attributeCount );
260 auto geometry = Geometry::New();
261 geometry.AddVertexBuffer( vertexBuffer );
262 auto indexBuffer = gltf.GetMeshIndexBuffer( mesh );
263 geometry.SetIndexBuffer( indexBuffer.data(), indexBuffer.size() );
264 geometry.SetType( Geometry::Type::TRIANGLES );
265 std::unique_ptr<Model> retval( new Model() );
266 retval->shader = CreateShader( vertexShaderSource, fragmentShaderSource );
267 retval->geometry = geometry;
271 void ReplaceShader( Actor& actor, const std::string& vsh, const std::string& fsh )
273 auto renderer = actor.GetRendererAt(0);
274 auto shader = CreateShader(vsh, fsh);
275 renderer.SetShader( shader );
278 } // unnamed namespace
280 // This example shows how to create and display mirrored reflection using CameraActor
282 class ReflectionExample : public ConnectionTracker
286 ReflectionExample( Application& application )
287 : mApplication( application )
289 // Connect to the Application's Init signal
290 mApplication.InitSignal().Connect( this, &ReflectionExample::Create );
293 ~ReflectionExample() = default;
297 // The Init signal is received once (only) during the Application lifetime
298 void Create( Application& application )
300 // Get a handle to the window
301 Window window = application.GetWindow();
302 uint32_t windowWidth = uint32_t(window.GetSize().GetWidth());
303 uint32_t windowHeight = uint32_t(window.GetSize().GetHeight());
305 window.GetRenderTaskList().GetTask(0).SetClearEnabled(false);
306 mLayer3D = Layer::New();
307 mLayer3D.SetProperty( Actor::Property::SIZE, Vector2( windowWidth, windowHeight ) );
308 window.Add(mLayer3D);
310 mLayer3D.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
311 mLayer3D.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
312 mLayer3D.SetProperty( Layer::Property::BEHAVIOR, Layer::LAYER_3D );
313 mLayer3D.SetProperty( Layer::Property::DEPTH_TEST, true );
316 auto gltf = glTF(DEMO_GAME_DIR "/reflection");
318 // Define direction of light
319 mLightDir = Vector3( 0.5, 0.5, -1 );
322 * Instantiate texture sets
324 CreateTextureSetsFromGLTF( &gltf, DEMO_GAME_DIR );
329 CreateModelsFromGLTF( &gltf );
334 CreateSceneFromGLTF( window, &gltf );
336 auto planeActor = mLayer3D.FindChildByName( "Plane" );
337 auto solarActor = mLayer3D.FindChildByName( "solar_root" );
338 auto backgroundActor = mLayer3D.FindChildByName( "background" );
339 ReplaceShader( backgroundActor, VERTEX_SHADER, FRAGMENT_SIMPLE_SHADER );
340 mCenterActor = mLayer3D.FindChildByName( "center" );
341 mCenterHorizActor = mLayer3D.FindChildByName( "center2" );
344 auto sun = mLayer3D.FindChildByName( "sun" );
345 ReplaceShader( sun, VERTEX_SHADER, PLASMA_FRAGMENT_SHADER );
346 mSunTimeUniformIndex = sun.RegisterProperty( "uTime", 0.0f );
347 mSunKFactorUniformIndex = sun.RegisterProperty( "uKFactor", 0.0f );
349 mTickTimer = Timer::New( 16 );
350 mTickTimer.TickSignal().Connect( this, &ReflectionExample::TickTimerSignal);
353 auto milkyway = mLayer3D.FindChildByName( "milkyway" );
354 ReplaceShader( milkyway, VERTEX_SHADER, FRAGMENT_SHADER );
356 auto renderTaskSourceActor = mLayer3D.FindChildByName( "RenderTaskSource" );
359 * Access camera (it's a child of "Camera" node)
361 auto camera = mLayer3D.FindChildByName( "Camera_Orientation" );
362 auto cameraRef = mLayer3D.FindChildByName( "CameraReflection_Orientation" );
364 auto cameraActor = CameraActor::DownCast( camera );
365 mCamera3D = cameraActor;
367 auto cameraRefActor = CameraActor::DownCast( cameraRef );
368 cameraRefActor.SetProperty( DevelCameraActor::Property::REFLECTION_PLANE, Vector4(0.0f, -1.0f, 0.0f, 0.0f));
369 mReflectionCamera3D = cameraRefActor;
371 auto task3D = window.GetRenderTaskList().CreateTask();
372 task3D.SetSourceActor( mLayer3D );
373 task3D.SetViewport( Rect<int>(0, 0, windowWidth, windowHeight ) );
374 task3D.SetCameraActor( cameraActor );
375 task3D.SetClearColor( Color::BLACK );
376 task3D.SetClearEnabled( true );
377 task3D.SetExclusive( false );
378 task3D.SetCameraActor( cameraActor );
381 * Change shader to textured
383 Shader texShader = CreateShader( VERTEX_SHADER, TEX_FRAGMENT_SHADER );
384 planeActor.RegisterProperty( "uScreenSize", Vector2(windowWidth, windowHeight) );
385 auto renderer = planeActor.GetRendererAt(0);
386 auto textureSet = renderer.GetTextures();
387 renderer.SetShader( texShader );
389 Texture fbTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888, windowWidth, windowHeight );
390 textureSet.SetTexture( 1u, fbTexture );
392 auto fb = FrameBuffer::New(windowWidth, windowHeight,
393 FrameBuffer::Attachment::DEPTH );
395 fb.AttachColorTexture( fbTexture );
397 auto renderTask = window.GetRenderTaskList().CreateTask();
398 renderTask.SetFrameBuffer( fb );
399 renderTask.SetSourceActor( renderTaskSourceActor );
400 renderTask.SetViewport( Rect<int>(0, 0, windowWidth, windowHeight ) );
401 renderTask.SetCameraActor( cameraRefActor );
402 renderTask.SetClearColor( Color::BLACK );
403 renderTask.SetClearEnabled( true );
404 renderTask.SetExclusive( false );
406 mAnimation = Animation::New(30.0f );
407 mAnimation.AnimateBy(Property(solarActor, Actor::Property::ORIENTATION ),
408 Quaternion( Degree(359), Vector3(0.0, 1.0, 0.0)));
409 mAnimation.AnimateBy(Property(milkyway, Actor::Property::ORIENTATION ),
410 Quaternion( Degree(-359), Vector3(0.0, 1.0, 0.0)));
411 mAnimation.SetLooping(true );
414 Actor panScreen = Actor::New();
415 Vector2 windowSize = window.GetSize();
416 panScreen.SetProperty( Actor::Property::SIZE, windowSize );
417 panScreen.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
418 panScreen.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
419 auto camera2d = window.GetRenderTaskList().GetTask(0).GetCameraActor();
420 panScreen.SetProperty( Actor::Property::POSITION, Vector3( 0, 0, camera2d.GetNearClippingPlane() ));
421 camera2d.Add(panScreen);
422 camera2d.RotateBy( Degree(180.0f), Vector3( 0.0, 1.0, 0.0 ) );
423 mPanGestureDetector = PanGestureDetector::New();
424 mPanGestureDetector.Attach( panScreen );
425 mPanGestureDetector.DetectedSignal().Connect( this, &ReflectionExample::OnPan );
427 // Respond to key events
428 window.KeyEventSignal().Connect( this, &ReflectionExample::OnKeyEvent );
433 void CreateSceneFromGLTF( Window window, glTF* gltf )
435 const auto& nodes = gltf->GetNodes();
437 // for each node create nodes and children
438 // resolve parents later
439 std::vector<Actor> actors;
440 actors.reserve( nodes.size() );
441 for( const auto& node : nodes )
443 auto actor = node.cameraId != 0xffffffff ? CameraActor::New( window.GetSize() ) : Actor::New();
445 actor.SetProperty( Actor::Property::SIZE, Vector3( 1, 1, 1 ) );
446 actor.SetProperty( Dali::Actor::Property::NAME, node.name );
447 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
448 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
449 actor.SetProperty( Actor::Property::POSITION, Vector3( node.translation[0], node.translation[1], node.translation[2] ));
450 actor.SetProperty( Actor::Property::SCALE, Vector3( node.scale[0], node.scale[1], node.scale[2] ) );
451 actor.SetProperty( Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3],
452 node.rotationQuaternion[0],
453 node.rotationQuaternion[1],
454 node.rotationQuaternion[2]));
456 actors.emplace_back( actor );
458 // Initially add each actor to the very first one
459 if(actors.size() > 1)
461 actors[0].Add(actor);
464 // If mesh, create and add renderer
465 if(node.meshId != 0xffffffff)
467 const auto& model = mModels[node.meshId].get();
468 auto renderer = Renderer::New( model->geometry, model->shader );
470 // if textured, add texture set
471 auto materialId = gltf->GetMeshes()[node.meshId]->material;
472 if( materialId != 0xffffffff )
474 if( gltf->GetMaterials()[materialId].pbrMetallicRoughness.enabled )
476 renderer.SetTextures( mTextureSets[materialId] );
480 actor.AddRenderer( renderer );
483 // Reset and attach main camera
484 if( node.cameraId != 0xffffffff )
486 mCameraPos = Vector3(node.translation[0], node.translation[1], node.translation[2]);
487 auto quatY = Quaternion( Degree(180.0f), Vector3( 0.0, 1.0, 0.0) );
488 auto cameraActor = CameraActor::DownCast( actor );
489 cameraActor.SetProperty( Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3],
490 node.rotationQuaternion[0],
491 node.rotationQuaternion[1],
492 node.rotationQuaternion[2] )
495 cameraActor.SetProjectionMode( Camera::PERSPECTIVE_PROJECTION );
497 const auto camera = gltf->GetCameras()[node.cameraId];
498 cameraActor.SetNearClippingPlane( camera->znear );
499 cameraActor.SetFarClippingPlane( camera->zfar );
500 cameraActor.SetFieldOfView( camera->yfov );
502 cameraActor.SetProperty( CameraActor::Property::INVERT_Y_AXIS, true);
503 cameraActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
504 cameraActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
506 mCameras.emplace_back( cameraActor );
510 // Resolve hierarchy dependencies
512 for( const auto& node : nodes )
514 if(!node.children.empty())
516 for(const auto& childId : node.children)
518 actors[i].Add( actors[childId+1] );
524 mActors = std::move(actors);
526 // Add root actor to the window
527 mLayer3D.Add( mActors[0] );
529 for( auto& actor : mActors )
531 actor.RegisterProperty( "lightDir", mLightDir );
532 actor.RegisterProperty( "eyePos", mCameraPos );
538 * Creates models from glTF
540 void CreateModelsFromGLTF( glTF* gltf )
542 const auto& meshes = gltf->GetMeshes();
543 for( const auto& mesh : meshes )
545 // change shader to use texture if material indicates that
546 if(mesh->material != 0xffffffff && gltf->GetMaterials()[mesh->material].pbrMetallicRoughness.enabled)
548 mModels.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, TEXTURED_FRAGMENT_SHADER ) );
552 mModels.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, FRAGMENT_SHADER ) );
557 void CreateTextureSetsFromGLTF( glTF* gltf, const std::string& basePath )
559 const auto& materials = gltf->GetMaterials();
560 const auto& textures = gltf->GetTextures();
562 std::map<std::string, Texture> textureCache{};
564 for(const auto& material : materials )
566 TextureSet textureSet;
567 if(material.pbrMetallicRoughness.enabled)
569 textureSet = TextureSet::New();
570 std::string filename( basePath );
572 filename += textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
573 Dali::PixelData pixelData = Dali::Toolkit::SyncImageLoader::Load( filename );
575 auto cacheKey = textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
576 auto iter = textureCache.find(cacheKey);
578 if(iter == textureCache.end())
580 texture = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(),
581 pixelData.GetHeight());
582 texture.Upload(pixelData);
583 texture.GenerateMipmaps();
584 textureCache[cacheKey] = texture;
588 texture = iter->second;
590 textureSet.SetTexture( 0, texture );
591 Dali::Sampler sampler = Dali::Sampler::New();
592 sampler.SetWrapMode( Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT );
593 sampler.SetFilterMode( Dali::FilterMode::LINEAR_MIPMAP_LINEAR, Dali::FilterMode::LINEAR );
594 textureSet.SetSampler( 0, sampler );
596 mTextureSets.emplace_back( textureSet );
600 void OnPan( Actor actor, const PanGesture& panGesture )
602 auto displacement = panGesture.screenDisplacement;
603 mCenterActor.RotateBy( Degree( displacement.y *0.1f ), Vector3( 0.0, 0.0, 1.0) );
604 mCenterActor.RotateBy( Degree( displacement.x *0.1f ), Vector3( 0.0, 1.0, 0.0) );
606 mCenterActor.GetProperty( Actor::Property::ORIENTATION ).Get(q);
607 Matrix m = Matrix::IDENTITY;
608 m.SetTransformComponents( Vector3::ONE, q, Vector3::ZERO );
609 auto yAxis = m.GetYAxis() * -1.0f;
612 mReflectionCamera3D.SetProperty( DevelCameraActor::Property::REFLECTION_PLANE, Vector4(yAxis.x, yAxis.y, yAxis.z, 0.0f));
615 void OnKeyEvent( const KeyEvent& event )
617 if( event.state == KeyEvent::Down )
619 if ( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
626 bool TickTimerSignal()
628 auto root = mLayer3D;
629 static float rotationAngle = 0.0f;
631 const auto ROTATION_ANGLE_STEP = 0.05f;
632 const auto FRAME_DELTA_TIME = 0.016f;
633 const auto PLASMA_K_FACTOR = 12.0f; // 'granularity' of plasma effect
635 rotationAngle += ROTATION_ANGLE_STEP;
636 mMockTime += FRAME_DELTA_TIME;
637 mKFactor = PLASMA_K_FACTOR;
639 auto sun = root.FindChildByName( "sun" );
640 sun.SetProperty( mSunTimeUniformIndex, mMockTime );
641 sun.SetProperty( mSunKFactorUniformIndex, mKFactor );
642 sun.SetProperty( Actor::Property::ORIENTATION, Quaternion( Radian(Degree(rotationAngle)), Vector3(0.0, 1.0, 0.0)));
648 Application& mApplication;
652 std::vector<Actor> mActors {};
653 std::vector<CameraActor> mCameras {};
654 std::vector<std::unique_ptr<Model>> mModels {};
655 std::vector<TextureSet> mTextureSets {};
657 Animation mAnimation {};
658 float mMockTime { 0.0f };
659 float mKFactor { 0.0f };
660 Property::Index mSunTimeUniformIndex {};
661 Property::Index mSunKFactorUniformIndex {};
662 PanGestureDetector mPanGestureDetector {};
664 Vector3 mCameraPos { Vector3::ZERO };
665 Vector3 mLightDir { Vector3::ZERO };
668 CameraActor mCamera3D {};
669 CameraActor mReflectionCamera3D {};
670 Actor mCenterActor {};
671 Actor mCenterHorizActor {};
674 int DALI_EXPORT_API main( int argc, char **argv )
676 Application application = Application::New( &argc, &argv );
677 ReflectionExample test( application );
678 application.MainLoop();