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;
29 const char* VERTEX_SHADER = DALI_COMPOSE_SHADER(
30 attribute mediump vec3 aPosition;\n
31 attribute mediump vec3 aNormal;\n
32 attribute mediump vec2 aTexCoord;\n
33 uniform mediump mat4 uMvpMatrix;\n
34 uniform mediump mat3 uNormalMatrix;\n
35 uniform mediump vec3 uSize;\n
37 varying mediump vec2 vTexCoord; \n
38 varying mediump vec3 vNormal; \n
39 varying mediump vec3 vPosition; \n
42 mediump vec4 vertexPosition = vec4(aPosition, 1.0);\n
43 vertexPosition.xyz *= uSize;\n
44 vTexCoord = aTexCoord;\n
45 vNormal = normalize(uNormalMatrix * aNormal);\n
46 vPosition = aPosition; \n
47 gl_Position = uMvpMatrix * vertexPosition;\n
51 const char* FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
52 uniform lowp vec4 uColor;\n
53 uniform sampler2D sTexture; \n
54 varying mediump vec3 vNormal;\n
55 varying mediump vec3 vPosition; \n
56 varying mediump vec2 vTexCoord; \n
60 gl_FragColor = texture2D(sTexture, vTexCoord) * 50.0;\n
64 const char* FRAGMENT_SIMPLE_SHADER = DALI_COMPOSE_SHADER(
65 uniform lowp vec4 uColor;\n
66 uniform sampler2D sTexture; \n
67 varying mediump vec3 vNormal;\n
68 varying mediump vec3 vPosition; \n
69 varying mediump vec2 vTexCoord; \n
73 gl_FragColor = texture2D(sTexture, vTexCoord) * 2.0;\n
77 const char* TEXTURED_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
78 uniform lowp vec4 uColor;\n
79 uniform sampler2D sTexture; \n
80 uniform mediump vec2 uScreenSize;\n
82 uniform mediump vec3 eyePos;\n
83 uniform mediump vec3 lightDir;\n
85 varying mediump vec3 vNormal;\n
86 varying mediump vec3 vPosition; \n
87 varying mediump vec2 vTexCoord; \n
91 mediump vec3 n = normalize( vNormal );\n
92 mediump vec3 l = normalize( lightDir );\n
93 mediump vec3 e = normalize( eyePos );\n
94 mediump float intensity = max(dot(n,l), 0.0);\n
95 gl_FragColor = texture2D(sTexture, vTexCoord) * intensity;\n
99 const char* PLASMA_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
100 precision mediump float;\n
101 uniform sampler2D sTexture; \n
104 uniform float uKFactor;
105 uniform mediump vec3 eyePos;\n
106 uniform mediump vec3 lightDir;\n
107 varying mediump vec3 vNormal;\n
108 varying mediump vec3 vPosition; \n
109 varying mediump vec2 vTexCoord; \n
113 mediump vec3 n = normalize( vNormal );\n
114 mediump vec3 l = normalize( lightDir );\n
115 mediump vec3 e = normalize( eyePos );\n
116 mediump float intensity = max(dot(n,l), 0.0);\n
118 const mediump float PI = 3.1415926535897932384626433832795;\n
119 mediump float v = 0.0;\n
120 mediump vec2 c = vTexCoord * uKFactor - uKFactor/2.0;\n
121 v += sin((c.x+uTime));\n
122 v += sin((c.y+uTime)/2.0);\n
123 v += sin((c.x+c.y+uTime)/2.0);\n
124 c += uKFactor/2.0 * vec2(sin(uTime/3.0), cos(uTime/2.0));\n
125 v += sin(sqrt(c.x*c.x+c.y*c.y+1.0)+uTime);\n
127 mediump vec3 col = vec3(1, sin(PI*v), cos(PI*v));\n
128 gl_FragColor = (texture2D(sTexture, vTexCoord)) * (((col.r+col.g+col.b)/3.0)+1.0+intensity);\n
132 const char* TEX_FRAGMENT_SHADER = DALI_COMPOSE_SHADER(
133 uniform lowp vec4 uColor;\n
134 uniform sampler2D sTexture0; \n
135 uniform sampler2D sTexture1; \n
136 uniform mediump vec3 eyePos;\n
137 uniform mediump vec3 lightDir;\n
138 uniform mediump vec2 uScreenSize;\n
139 varying mediump vec3 vNormal;\n
140 varying mediump vec3 vPosition; \n
141 varying mediump vec2 vTexCoord; \n
144 mediump float rand(mediump vec2 co){\n
145 return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);\n
150 mediump vec2 tx = (gl_FragCoord.xy / uScreenSize.xy);\n
151 mediump vec3 n = normalize( vNormal );\n
152 mediump vec3 l = normalize( lightDir );\n
153 mediump vec3 e = normalize( eyePos );\n
154 mediump float factor = gl_FragCoord.y / uScreenSize.y;\n
155 mediump float intensity = max(dot(n,l), 0.0);\n
156 mediump vec2 uv = tx;\n
157 gl_FragColor = ((texture2D(sTexture0, vTexCoord) * factor ) + \n
158 (texture2D(sTexture1, uv))) * intensity;\n
168 // This example shows how to create and display mirrored reflection using CameraActor
170 class ReflectionExample : public ConnectionTracker
174 ReflectionExample( Application& application )
175 : mApplication( application )
177 // Connect to the Application's Init signal
178 mApplication.InitSignal().Connect( this, &ReflectionExample::Create );
181 ~ReflectionExample() = default;
185 // The Init signal is received once (only) during the Application lifetime
186 void Create( Application& application )
188 // Get a handle to the stage
189 Stage stage = Stage::GetCurrent();
190 uint32_t stageWidth = uint32_t(stage.GetSize().x);
191 uint32_t stageHeight = uint32_t(stage.GetSize().y);
193 stage.GetRenderTaskList().GetTask(0).SetClearEnabled(false);
194 mLayer3D = Layer::New();
195 mLayer3D.SetProperty( Actor::Property::SIZE, Vector2( stageWidth, stageHeight ) );
198 mLayer3D.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
199 mLayer3D.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
200 mLayer3D.SetBehavior( Layer::LAYER_3D );
201 mLayer3D.SetDepthTestDisabled( false );
204 auto gltf = glTF(DEMO_GAME_DIR "/reflection");
206 // Define direction of light
207 mLightDir = Vector3( 0.5, 0.5, -1 );
210 * Instantiate texture sets
212 CreateTextureSetsFromGLTF( &gltf, DEMO_GAME_DIR );
217 CreateModelsFromGLTF( &gltf );
222 CreateSceneFromGLTF( stage, &gltf );
224 auto planeActor = mLayer3D.FindChildByName( "Plane" );
225 auto solarActor = mLayer3D.FindChildByName( "solar_root" );
226 auto backgroundActor = mLayer3D.FindChildByName( "background" );
227 ReplaceShader( backgroundActor, VERTEX_SHADER, FRAGMENT_SIMPLE_SHADER );
228 mCenterActor = mLayer3D.FindChildByName( "center" );
229 mCenterHorizActor = mLayer3D.FindChildByName( "center2" );
232 auto sun = mLayer3D.FindChildByName( "sun" );
233 ReplaceShader( sun, VERTEX_SHADER, PLASMA_FRAGMENT_SHADER );
234 mSunTimeUniformIndex = sun.RegisterProperty( "uTime", 0.0f );
235 mSunKFactorUniformIndex = sun.RegisterProperty( "uKFactor", 0.0f );
237 mTickTimer = Timer::New( 16 );
238 mTickTimer.TickSignal().Connect( this, &ReflectionExample::TickTimerSignal);
241 auto milkyway = mLayer3D.FindChildByName( "milkyway" );
242 ReplaceShader( milkyway, VERTEX_SHADER, FRAGMENT_SHADER );
244 auto renderTaskSourceActor = mLayer3D.FindChildByName( "RenderTaskSource" );
247 * Access camera (it's a child of "Camera" node)
249 auto camera = mLayer3D.FindChildByName( "Camera_Orientation" );
250 auto cameraRef = mLayer3D.FindChildByName( "CameraReflection_Orientation" );
252 auto cameraActor = CameraActor::DownCast( camera );
253 mCamera3D = cameraActor;
255 auto cameraRefActor = CameraActor::DownCast( cameraRef );
256 cameraRefActor.SetProperty( DevelCameraActor::Property::REFLECTION_PLANE, Vector4(0.0f, -1.0f, 0.0f, 0.0f));
257 mReflectionCamera3D = cameraRefActor;
259 auto task3D = stage.GetRenderTaskList().CreateTask();
260 task3D.SetSourceActor( mLayer3D );
261 task3D.SetViewport( Rect<int>(0, 0, stageWidth, stageHeight ) );
262 task3D.SetCameraActor( cameraActor );
263 task3D.SetClearColor( Color::BLACK );
264 task3D.SetClearEnabled( true );
265 task3D.SetExclusive( false );
266 task3D.SetCameraActor( cameraActor );
269 * Change shader to textured
271 Shader texShader = CreateShader( VERTEX_SHADER, TEX_FRAGMENT_SHADER );
272 planeActor.RegisterProperty( "uScreenSize", Vector2(stageWidth, stageHeight) );
273 auto renderer = planeActor.GetRendererAt(0);
274 auto textureSet = renderer.GetTextures();
275 renderer.SetShader( texShader );
277 Texture fbTexture = Texture::New(TextureType::TEXTURE_2D, Pixel::Format::RGBA8888, stageWidth, stageHeight );
278 textureSet.SetTexture( 1u, fbTexture );
280 auto fb = FrameBuffer::New(stageWidth, stageHeight,
281 FrameBuffer::Attachment::DEPTH );
283 fb.AttachColorTexture( fbTexture );
285 auto renderTask = stage.GetRenderTaskList().CreateTask();
286 renderTask.SetFrameBuffer( fb );
287 renderTask.SetSourceActor( renderTaskSourceActor );
288 renderTask.SetViewport( Rect<int>(0, 0, stageWidth, stageHeight ) );
289 renderTask.SetCameraActor( cameraRefActor );
290 renderTask.SetClearColor( Color::BLACK );
291 renderTask.SetClearEnabled( true );
292 renderTask.SetExclusive( false );
294 mAnimation = Animation::New(30.0f );
295 mAnimation.AnimateBy(Property(solarActor, Actor::Property::ORIENTATION ),
296 Quaternion( Degree(359), Vector3(0.0, 1.0, 0.0)));
297 mAnimation.AnimateBy(Property(milkyway, Actor::Property::ORIENTATION ),
298 Quaternion( Degree(-359), Vector3(0.0, 1.0, 0.0)));
299 mAnimation.SetLooping(true );
302 Actor panScreen = Actor::New();
303 auto stageSize = stage.GetSize();
304 panScreen.SetProperty( Actor::Property::SIZE, Vector2( stageSize.width, stageSize.height ) );
305 panScreen.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
306 panScreen.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
307 auto camera2d = stage.GetRenderTaskList().GetTask(0).GetCameraActor();
308 panScreen.SetProperty( Actor::Property::POSITION, Vector3( 0, 0, camera2d.GetNearClippingPlane() ));
309 camera2d.Add(panScreen);
310 camera2d.RotateBy( Degree(180.0f), Vector3( 0.0, 1.0, 0.0 ) );
311 mPanGestureDetector = PanGestureDetector::New();
312 mPanGestureDetector.Attach( panScreen );
313 mPanGestureDetector.DetectedSignal().Connect( this, &ReflectionExample::OnPan );
315 // Respond to key events
316 stage.KeyEventSignal().Connect( this, &ReflectionExample::OnKeyEvent );
321 void CreateSceneFromGLTF( Stage stage, glTF* gltf )
323 const auto& nodes = gltf->GetNodes();
325 // for each node create nodes and children
326 // resolve parents later
327 std::vector<Actor> actors;
328 actors.reserve( nodes.size() );
329 for( const auto& node : nodes )
331 auto actor = node.cameraId != 0xffffffff ? CameraActor::New( stage.GetSize() ) : Actor::New();
333 actor.SetProperty( Actor::Property::SIZE, Vector3( 1, 1, 1 ) );
334 actor.SetProperty( Dali::Actor::Property::NAME, node.name );
335 actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
336 actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
337 actor.SetProperty( Actor::Property::POSITION, Vector3( node.translation[0], node.translation[1], node.translation[2] ));
338 actor.SetProperty( Actor::Property::SCALE, Vector3( node.scale[0], node.scale[1], node.scale[2] ) );
339 actor.SetProperty( Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3],
340 node.rotationQuaternion[0],
341 node.rotationQuaternion[1],
342 node.rotationQuaternion[2]));
344 actors.emplace_back( actor );
346 // Initially add each actor to the very first one
347 if(actors.size() > 1)
349 actors[0].Add(actor);
352 // If mesh, create and add renderer
353 if(node.meshId != 0xffffffff)
355 const auto& model = mModels[node.meshId].get();
356 auto renderer = Renderer::New( model->geometry, model->shader );
358 // if textured, add texture set
359 auto materialId = gltf->GetMeshes()[node.meshId]->material;
360 if( materialId != 0xffffffff )
362 if( gltf->GetMaterials()[materialId].pbrMetallicRoughness.enabled )
364 renderer.SetTextures( mTextureSets[materialId] );
368 actor.AddRenderer( renderer );
371 // Reset and attach main camera
372 if( node.cameraId != 0xffffffff )
374 mCameraPos = Vector3(node.translation[0], node.translation[1], node.translation[2]);
375 auto quatY = Quaternion( Degree(180.0f), Vector3( 0.0, 1.0, 0.0) );
376 auto cameraActor = CameraActor::DownCast( actor );
377 cameraActor.SetProperty( Actor::Property::ORIENTATION, Quaternion(node.rotationQuaternion[3],
378 node.rotationQuaternion[0],
379 node.rotationQuaternion[1],
380 node.rotationQuaternion[2] )
383 cameraActor.SetProjectionMode( Camera::PERSPECTIVE_PROJECTION );
385 const auto camera = gltf->GetCameras()[node.cameraId];
386 cameraActor.SetNearClippingPlane( camera->znear );
387 cameraActor.SetFarClippingPlane( camera->zfar );
388 cameraActor.SetFieldOfView( camera->yfov );
390 cameraActor.SetProperty( CameraActor::Property::INVERT_Y_AXIS, true);
391 cameraActor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
392 cameraActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
394 mCameras.emplace_back( cameraActor );
398 // Resolve hierarchy dependencies
400 for( const auto& node : nodes )
402 if(!node.children.empty())
404 for(const auto& childId : node.children)
406 actors[i].Add( actors[childId+1] );
412 mActors = std::move(actors);
414 // Add root actor to the stage
415 mLayer3D.Add( mActors[0] );
417 for( auto& actor : mActors )
419 actor.RegisterProperty( "lightDir", mLightDir );
420 actor.RegisterProperty( "eyePos", mCameraPos );
426 * Creates models from glTF
428 void CreateModelsFromGLTF( glTF* gltf )
430 const auto& meshes = gltf->GetMeshes();
431 for( const auto& mesh : meshes )
433 // change shader to use texture if material indicates that
434 if(mesh->material != 0xffffffff && gltf->GetMaterials()[mesh->material].pbrMetallicRoughness.enabled)
436 mModels.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, TEXTURED_FRAGMENT_SHADER ) );
440 mModels.emplace_back( CreateModel( *gltf, mesh, VERTEX_SHADER, FRAGMENT_SHADER ) );
445 void CreateTextureSetsFromGLTF( glTF* gltf, const std::string& basePath )
447 const auto& materials = gltf->GetMaterials();
448 const auto& textures = gltf->GetTextures();
450 std::map<std::string, Texture> textureCache{};
452 for(const auto& material : materials )
454 TextureSet textureSet;
455 if(material.pbrMetallicRoughness.enabled)
457 textureSet = TextureSet::New();
458 std::string filename( basePath );
460 filename += textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
461 Dali::PixelData pixelData = Dali::Toolkit::SyncImageLoader::Load( filename );
463 auto cacheKey = textures[material.pbrMetallicRoughness.baseTextureColor.index].uri;
464 auto iter = textureCache.find(cacheKey);
466 if(iter == textureCache.end())
468 texture = Texture::New(TextureType::TEXTURE_2D, pixelData.GetPixelFormat(), pixelData.GetWidth(),
469 pixelData.GetHeight());
470 texture.Upload(pixelData);
471 texture.GenerateMipmaps();
472 textureCache[cacheKey] = texture;
476 texture = iter->second;
478 textureSet.SetTexture( 0, texture );
479 Dali::Sampler sampler = Dali::Sampler::New();
480 sampler.SetWrapMode( Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT, Dali::WrapMode::REPEAT );
481 sampler.SetFilterMode( Dali::FilterMode::LINEAR_MIPMAP_LINEAR, Dali::FilterMode::LINEAR );
482 textureSet.SetSampler( 0, sampler );
484 mTextureSets.emplace_back( textureSet );
489 bool LoadFile( const std::string& filename, std::vector<T>& bytes )
491 Dali::FileStream fileStream( filename, Dali::FileStream::READ | Dali::FileStream::BINARY );
492 FILE* fin = fileStream.GetFile();
496 if( fseek( fin, 0, SEEK_END ) )
500 bytes.resize( uint32_t(ftell( fin )) );
501 std::fill( bytes.begin(), bytes.end(), 0 );
502 if( fseek( fin, 0, SEEK_SET ) )
506 size_t result = fread( bytes.data(), 1, bytes.size(), fin );
507 return ( result != 0 );
513 Shader CreateShader( const std::string& vsh, const std::string& fsh )
515 std::vector<char> vshShaderSource;
516 std::vector<char> fshShaderSource;
521 std::string vshPath( DEMO_GAME_DIR );
524 LoadFile( vshPath, vshShaderSource );
528 vshShaderSource.insert(vshShaderSource.end(), vsh.begin(), vsh.end());
534 std::string fshPath( DEMO_GAME_DIR );
537 LoadFile( fshPath, fshShaderSource );
541 fshShaderSource.insert(fshShaderSource.end(), fsh.begin(), fsh.end());
544 vshShaderSource.emplace_back(0);
545 fshShaderSource.emplace_back(0);
546 return Shader::New( std::string(vshShaderSource.data()), std::string(fshShaderSource.data()) );
549 std::unique_ptr<Model> CreateModel( glTF& gltf,
550 const glTF_Mesh* mesh,
551 const std::string& vertexShaderSource,
552 const std::string& fragmentShaderSource )
555 * Obtain interleaved buffer for first mesh with position and normal attributes
557 auto positionBuffer = gltf.GetMeshAttributeBuffer( *mesh,
559 glTFAttributeType::POSITION,
560 glTFAttributeType::NORMAL,
561 glTFAttributeType::TEXCOORD_0
564 auto attributeCount = gltf.GetMeshAttributeCount( mesh );
566 * Create matching property buffer
568 auto vertexBuffer = PropertyBuffer::New( Property::Map()
569 .Add("aPosition", Property::VECTOR3 )
570 .Add("aNormal", Property::VECTOR3)
571 .Add("aTexCoord", Property::VECTOR2)
575 vertexBuffer.SetData( positionBuffer.data(), attributeCount );
577 auto geometry = Geometry::New();
578 geometry.AddVertexBuffer( vertexBuffer );
579 auto indexBuffer = gltf.GetMeshIndexBuffer( mesh );
580 geometry.SetIndexBuffer( indexBuffer.data(), indexBuffer.size() );
581 geometry.SetType( Geometry::Type::TRIANGLES );
582 std::unique_ptr<Model> retval( new Model() );
583 retval->shader = CreateShader( vertexShaderSource, fragmentShaderSource );
584 retval->geometry = geometry;
588 void ReplaceShader( Actor& actor, const std::string& vsh, const std::string& fsh )
590 auto renderer = actor.GetRendererAt(0);
591 auto shader = CreateShader(vsh, fsh);
592 renderer.SetShader( shader );
595 void OnPan( Actor actor, const PanGesture& panGesture )
597 auto displacement = panGesture.screenDisplacement;
598 mCenterActor.RotateBy( Degree( displacement.y *0.1f ), Vector3( 0.0, 0.0, 1.0) );
599 mCenterActor.RotateBy( Degree( displacement.x *0.1f ), Vector3( 0.0, 1.0, 0.0) );
601 mCenterActor.GetProperty( Actor::Property::ORIENTATION ).Get(q);
602 Matrix m = Matrix::IDENTITY;
603 m.SetTransformComponents( Vector3::ONE, q, Vector3::ZERO );
604 auto yAxis = m.GetYAxis() * -1.0f;
607 mReflectionCamera3D.SetProperty( DevelCameraActor::Property::REFLECTION_PLANE, Vector4(yAxis.x, yAxis.y, yAxis.z, 0.0f));
610 void OnKeyEvent( const KeyEvent& event )
612 if( event.state == KeyEvent::Down )
614 if ( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
621 bool TickTimerSignal()
623 auto root = mLayer3D;
624 static float rotationAngle = 0.0f;
626 const auto ROTATION_ANGLE_STEP = 0.05f;
627 const auto FRAME_DELTA_TIME = 0.016f;
628 const auto PLASMA_K_FACTOR = 12.0f; // 'granularity' of plasma effect
630 rotationAngle += ROTATION_ANGLE_STEP;
631 mMockTime += FRAME_DELTA_TIME;
632 mKFactor = PLASMA_K_FACTOR;
634 auto sun = root.FindChildByName( "sun" );
635 sun.SetProperty( mSunTimeUniformIndex, mMockTime );
636 sun.SetProperty( mSunKFactorUniformIndex, mKFactor );
637 sun.SetProperty( Actor::Property::ORIENTATION, Quaternion( Radian(Degree(rotationAngle)), Vector3(0.0, 1.0, 0.0)));
643 Application& mApplication;
647 std::vector<Actor> mActors {};
648 std::vector<CameraActor> mCameras {};
649 std::vector<std::unique_ptr<Model>> mModels {};
650 std::vector<TextureSet> mTextureSets {};
652 Animation mAnimation {};
653 float mMockTime { 0.0f };
654 float mKFactor { 0.0f };
655 Property::Index mSunTimeUniformIndex {};
656 Property::Index mSunKFactorUniformIndex {};
657 PanGestureDetector mPanGestureDetector {};
659 Vector3 mCameraPos { Vector3::ZERO };
660 Vector3 mLightDir { Vector3::ZERO };
663 CameraActor mCamera3D {};
664 CameraActor mReflectionCamera3D {};
665 Actor mCenterActor {};
666 Actor mCenterHorizActor {};
669 int DALI_EXPORT_API main( int argc, char **argv )
671 Application application = Application::New( &argc, &argv );
672 ReflectionExample test( application );
673 application.MainLoop();