6accbf1b81ffa3b7b376a00fc25f2c9a82f0b79d
[platform/core/uifw/dali-demo.git] / examples / scene3d-view / scene3d-view-example.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 #include <dali-toolkit/dali-toolkit.h>
19 #include <dali-toolkit/devel-api/controls/scene3d-view/scene3d-view.h>
20 #include <dali/dali.h>
21 #include <dali/devel-api/adaptor-framework/file-loader.h>
22 #include <dali/devel-api/adaptor-framework/file-stream.h>
23 #include <dali/devel-api/adaptor-framework/image-loading.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/actors/camera-actor.h>
26 #include <cstring>
27
28 using namespace Dali;
29 using namespace Dali::Toolkit;
30
31 /*
32  * This example shows how to create and display a Scene3dView control.
33  * The application can load 5 different glTF model to Scene3dView control.
34  * Each model has diffirent material. BoomBox shows glossy or matt plastic material.
35  * DamagedHelmet shows a kind of reflective glass and metallic object.
36  * Microphone shows a roughness of metallic objects.
37  * and Lantern shows a realistic difference between wood object and metallic object.
38  * Rotate the camera by swiping.
39  * A double tap changes the model.
40  */
41
42 namespace
43 {
44 enum GLTF_MODEL_LIST
45 {
46   GLTF_ANIMATED_BOX = 0,
47   GLTF_LANTERN,
48   GLTF_BOOMBOX,
49   GLTF_DAMAGED_HELMET,
50   GLTF_MICROPHONE,
51   NUM_OF_GLTF_MODELS
52 };
53
54 const char* gltf_list[6] =
55   {
56     /**
57      * For the BoxAnimated.gltf and its Assets
58      * Donated by Cesium for glTF testing.
59      * Take from https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated
60      */
61     "BoxAnimated.gltf",
62     /**
63      * For the Lantern.gltf and its Assets
64      * Donated by Microsoft for glTF testing
65      * Created by Ryan Martin
66      * Take from https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Lantern
67      */
68     "Lantern.gltf",
69     /**
70      * For the BoomBox.gltf and its Assets
71      * Donated by Microsoft for glTF testing
72      * Created by Ryan Martin
73      * Take from https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoomBox
74      */
75     "BoomBox.gltf",
76     /**
77      * For the DamagedHelmet.gltf and its Assets
78      * Battle Damaged Sci-fi Helmet - PBR by theblueturtle_, published under a
79      * Creative Commons Attribution-NonCommercial license
80      * https://sketchfab.com/models/b81008d513954189a063ff901f7abfe4
81      */
82     "DamagedHelmet.gltf",
83     /**
84      * For the microphone.gltf and its Assets
85      * Microphone GXL 066 Bafhcteks by Gistold, published under a
86      * Creative Commons Attribution-NonCommercial license
87      * https://sketchfab.com/models/5172dbe9281a45f48cee8c15bdfa1831
88      */
89     "microphone.gltf"};
90
91 Vector3 camera_position_list[6] =
92   {
93     Vector3(-6.00, -8.00, 12.00),
94     Vector3(-30.0, -40.0, 60.0),
95     Vector3(-0.03, -0.04, 0.06),
96     Vector3(-3.00, -4.00, 6.00),
97     Vector3(-0.00, -3.00, 4.00)};
98
99 /**
100  * For the diffuse and specular cube map texture.
101  * These textures are based off version of Wave engine sample
102  * Take from https://github.com/WaveEngine/Samples
103  *
104  * Copyright (c) 2016 Wave Coorporation
105  *
106  * Permission is hereby granted, free of charge, to any person obtaining a copy
107  * of this software and associated documentation files (the "Software"), to
108  * deal in the Software without restriction, including without limitation the
109  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
110  * sell copies of the Software, and to permit persons to whom the Software is
111  * furnished to do so, subject to the following conditions:
112  *
113  * The above copyright notice and this permission notice shall be included in
114  * all copies or substantial portions of the Software.
115  *
116  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
117  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
118  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
119  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
120  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
121  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
122  * THE SOFTWARE.
123  */
124
125 const std::string modeldir = DEMO_MODEL_DIR;
126 const std::string imagedir = DEMO_IMAGE_DIR;
127 const std::string uri_diffuse_texture(imagedir + "forest_diffuse_cubemap.png");
128 const std::string uri_specular_texture(imagedir + "forest_specular_cubemap.png");
129
130 const int32_t cubeMap_index_x[6] = {2, 0, 1, 1, 1, 3};
131 const int32_t cubeMap_index_y[6] = {1, 1, 0, 2, 1, 1};
132
133 const char* VERTEX_SHADER_URL   = DEMO_SHADER_DIR "cube_shader.vsh";
134 const char* FRAGMENT_SHADER_URL = DEMO_SHADER_DIR "cube_shader.fsh";
135
136 /**
137  * @brief Load a shader source file
138  * @param[in] The path of the source file
139  * @param[out] The contents of file
140  * @return True if the source was read successfully
141  */
142 bool LoadShaderCode(const std::string& fullpath, std::vector<char>& output)
143 {
144   Dali::FileStream fileStream(fullpath, FileStream::READ | FileStream::BINARY);
145   FILE*            file = fileStream.GetFile();
146   if(nullptr == file)
147   {
148     return false;
149   }
150
151   bool retValue = false;
152   if(!fseek(file, 0, SEEK_END))
153   {
154     long int size = ftell(file);
155
156     if((size != -1L) &&
157        (!fseek(file, 0, SEEK_SET)))
158     {
159       output.resize(size + 1);
160       std::fill(output.begin(), output.end(), 0);
161       ssize_t result = fread(output.data(), size, 1, file);
162
163       retValue = (result >= 0);
164     }
165   }
166
167   return retValue;
168 }
169
170 /**
171  * @brief Load vertex and fragment shader source
172  * @param[in] shaderVertexFileName is the filepath of Vertex shader
173  * @param[in] shaderFragFileName is the filepath of Fragment shader
174  * @return the Dali::Shader object
175  */
176 Shader LoadShaders(const std::string& shaderVertexFileName, const std::string& shaderFragFileName)
177 {
178   Shader            shader;
179   std::vector<char> bufV, bufF;
180
181   if(LoadShaderCode(shaderVertexFileName.c_str(), bufV))
182   {
183     if(LoadShaderCode(shaderFragFileName.c_str(), bufF))
184     {
185       shader = Shader::New(bufV.data(), bufF.data());
186     }
187   }
188   return shader;
189 }
190
191 } // namespace
192
193 /**
194  * This example shows how to render glTF model with Scene3dView
195  * How to test
196  *  - Input UP or DOWN key to make the model rotate or stop.
197  *  - Input LEFT or RIGHT key to change glTF model
198  *  - Double Touch also changes glTF model.
199  */
200 class Scene3dViewController : public ConnectionTracker
201 {
202 public:
203   Scene3dViewController(Application& application)
204   : mApplication(application),
205     mModelOrientation(),
206     mAnimationStop(false)
207   {
208     // Connect to the Application's Init signal
209     mApplication.InitSignal().Connect(this, &Scene3dViewController::Create);
210   }
211
212   ~Scene3dViewController()
213   {
214     mAnimation.Stop();
215   }
216
217   // The Init signal is received once (only) during the Application lifetime
218   void Create(Application& application)
219   {
220     mWindow = application.GetWindow();
221
222     // Get a handle to the mWindow
223     mWindow.SetBackgroundColor(Color::WHITE);
224
225     RenderTask renderTask = mWindow.GetRenderTaskList().GetTask(0);
226     renderTask.SetCullMode(false);
227
228     mCurrentGlTF = GLTF_ANIMATED_BOX;
229     CreateSceneFromGLTF(gltf_list[mCurrentGlTF]);
230     SetCameraActor();
231     CreateSkybox();
232     SetAnimation();
233
234     // Respond to a click anywhere on the mWindow
235     mWindow.GetRootLayer().TouchedSignal().Connect(this, &Scene3dViewController::OnTouch);
236     mWindow.KeyEventSignal().Connect(this, &Scene3dViewController::OnKeyEvent);
237     mWindow.GetRootLayer().WheelEventSignal().Connect(this, &Scene3dViewController::OnWheel);
238
239     mDoubleTap     = false;
240     mDoubleTapTime = Timer::New(150);
241     mDoubleTapTime.TickSignal().Connect(this, &Scene3dViewController::OnDoubleTapTime);
242   }
243
244   bool OnWheel(Actor actor, const WheelEvent& wheelEvent)
245   {
246     mWheelDelta -= wheelEvent.GetDelta() * 0.025f;
247     mWheelDelta = std::max(0.5f, mWheelDelta);
248     mWheelDelta = std::min(2.0f, mWheelDelta);
249
250     if(mScene3dView)
251     {
252       mScene3dView.SetProperty(Actor::Property::SCALE, mWheelDelta);
253     }
254
255     return true;
256   }
257
258   bool OnDoubleTapTime()
259   {
260     mDoubleTap = false;
261     return true;
262   }
263
264   void CreateSceneFromGLTF(std::string modelName)
265   {
266     if(mScene3dView)
267     {
268       mWindow.GetRootLayer().Remove(mScene3dView);
269     }
270
271     std::string gltfUrl = modeldir;
272     gltfUrl += modelName;
273     mScene3dView = Scene3dView::New(gltfUrl, uri_diffuse_texture, uri_specular_texture, Vector4::ONE);
274
275     mScene3dView.SetLight(Scene3dView::LightType::POINT_LIGHT, Vector3(-5, -5, 5), Vector3(1, 1, 1));
276
277     mWindow.Add(mScene3dView);
278     mScene3dView.PlayAnimations();
279   }
280
281   void SetCameraActor()
282   {
283     mCameraPosition = camera_position_list[mCurrentGlTF];
284     mCameraActor    = mWindow.GetRenderTaskList().GetTask(0).GetCameraActor();
285     mCameraActor.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
286     mCameraActor.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
287     mCameraActor.SetProperty(Dali::Actor::Property::POSITION, mCameraPosition);
288     mCameraActor.SetType(Dali::Camera::LOOK_AT_TARGET);
289     mCameraActor.SetNearClippingPlane(0.01);
290   }
291
292   void CreateSkybox()
293   {
294     struct Vertex
295     {
296       Vector3 aPosition;
297     };
298
299     Vertex skyboxVertices[] = {
300       // back
301       {Vector3(-1.0f, 1.0f, -1.0f)},
302       {Vector3(-1.0f, -1.0f, -1.0f)},
303       {Vector3(1.0f, -1.0f, -1.0f)},
304       {Vector3(1.0f, -1.0f, -1.0f)},
305       {Vector3(1.0f, 1.0f, -1.0f)},
306       {Vector3(-1.0f, 1.0f, -1.0f)},
307
308       // left
309       {Vector3(-1.0f, -1.0f, 1.0f)},
310       {Vector3(-1.0f, -1.0f, -1.0f)},
311       {Vector3(-1.0f, 1.0f, -1.0f)},
312       {Vector3(-1.0f, 1.0f, -1.0f)},
313       {Vector3(-1.0f, 1.0f, 1.0f)},
314       {Vector3(-1.0f, -1.0f, 1.0f)},
315
316       // right
317       {Vector3(1.0f, -1.0f, -1.0f)},
318       {Vector3(1.0f, -1.0f, 1.0f)},
319       {Vector3(1.0f, 1.0f, 1.0f)},
320       {Vector3(1.0f, 1.0f, 1.0f)},
321       {Vector3(1.0f, 1.0f, -1.0f)},
322       {Vector3(1.0f, -1.0f, -1.0f)},
323
324       // front
325       {Vector3(-1.0f, -1.0f, 1.0f)},
326       {Vector3(-1.0f, 1.0f, 1.0f)},
327       {Vector3(1.0f, 1.0f, 1.0f)},
328       {Vector3(1.0f, 1.0f, 1.0f)},
329       {Vector3(1.0f, -1.0f, 1.0f)},
330       {Vector3(-1.0f, -1.0f, 1.0f)},
331
332       // botton
333       {Vector3(-1.0f, 1.0f, -1.0f)},
334       {Vector3(1.0f, 1.0f, -1.0f)},
335       {Vector3(1.0f, 1.0f, 1.0f)},
336       {Vector3(1.0f, 1.0f, 1.0f)},
337       {Vector3(-1.0f, 1.0f, 1.0f)},
338       {Vector3(-1.0f, 1.0f, -1.0f)},
339
340       // top
341       {Vector3(-1.0f, -1.0f, -1.0f)},
342       {Vector3(-1.0f, -1.0f, 1.0f)},
343       {Vector3(1.0f, -1.0f, -1.0f)},
344       {Vector3(1.0f, -1.0f, -1.0f)},
345       {Vector3(-1.0f, -1.0f, 1.0f)},
346       {Vector3(1.0f, -1.0f, 1.0f)}};
347
348     const std::string currentVShaderFile(VERTEX_SHADER_URL);
349     const std::string currentFShaderFile(FRAGMENT_SHADER_URL);
350
351     mShaderSkybox = LoadShaders(currentVShaderFile, currentFShaderFile);
352
353     Dali::VertexBuffer vertexBuffer = Dali::VertexBuffer::New(Property::Map()
354                                                                 .Add("aPosition", Property::VECTOR3));
355     vertexBuffer.SetData(skyboxVertices, sizeof(skyboxVertices) / sizeof(Vertex));
356
357     mSkyboxGeometry = Geometry::New();
358     mSkyboxGeometry.AddVertexBuffer(vertexBuffer);
359     mSkyboxGeometry.SetType(Geometry::TRIANGLES);
360
361     // Diffuse Cube Map
362     Devel::PixelBuffer diffusePixelBuffer = LoadImageFromFile(uri_diffuse_texture);
363     int32_t            diffuseFaceSize    = diffusePixelBuffer.GetWidth() / 4;
364     Texture            texture            = Texture::New(TextureType::TEXTURE_CUBE, diffusePixelBuffer.GetPixelFormat(), diffuseFaceSize, diffuseFaceSize);
365     for(int32_t i = 0; i < 6; ++i)
366     {
367       UploadTextureFace(texture, diffusePixelBuffer, i);
368     }
369     texture.GenerateMipmaps();
370
371     mSkyboxTextures = TextureSet::New();
372     mSkyboxTextures.SetTexture(0, texture);
373
374     mSkyboxRenderer = Renderer::New(mSkyboxGeometry, mShaderSkybox);
375     mSkyboxRenderer.SetTextures(mSkyboxTextures);
376     mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_INDEX, 2.0f);
377
378     // Enables the depth test.
379     mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::ON);
380
381     // The fragment shader will run only is those pixels that have the max depth value.
382     mSkyboxRenderer.SetProperty(Renderer::Property::DEPTH_FUNCTION, DepthFunction::LESS_EQUAL);
383
384     mSkyboxActor = Actor::New();
385     mSkyboxActor.SetProperty(Dali::Actor::Property::NAME, "SkyBox");
386     mSkyboxActor.SetProperty(Dali::Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
387     mSkyboxActor.SetProperty(Dali::Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
388     mSkyboxActor.SetProperty(Dali::Actor::Property::POSITION, mCameraPosition);
389     mSkyboxActor.AddRenderer(mSkyboxRenderer);
390     mWindow.Add(mSkyboxActor);
391   }
392
393   void SetAnimation()
394   {
395     /**
396      * Rotate object Actor along Y axis.
397      * Animation duration is 8 seconds.
398      * Five keyframes are set for each 90 degree. i.e., 0, 90, 180, 270, and 360.
399      * Each keyframes are interpolated by linear interpolator.
400      */
401     mAnimation                = Animation::New(8.0);
402     KeyFrames keyframes       = KeyFrames::New();
403     float     lengthAnimation = 0.25;
404     for(int32_t i = 0; i < 5; ++i)
405     {
406       keyframes.Add(i * lengthAnimation, Quaternion(Degree(i * 90.0), Vector3::YAXIS));
407     }
408     mAnimation.AnimateBetween(Property(mScene3dView, Dali::Actor::Property::ORIENTATION), keyframes, Animation::Interpolation::LINEAR);
409     mAnimation.SetLooping(true);
410     mAnimation.Play();
411   }
412
413   uint8_t* CropBuffer(uint8_t* sourceBuffer, uint32_t bytesPerPixel, uint32_t width, uint32_t height, uint32_t xOffset, uint32_t yOffset, uint32_t xFaceSize, uint32_t yFaceSize)
414   {
415     if(xOffset + xFaceSize > width || yOffset + yFaceSize > height)
416     {
417       DALI_LOG_ERROR("Can not crop outside of texture area.\n");
418       return nullptr;
419     }
420     uint32_t byteSize   = bytesPerPixel * xFaceSize * yFaceSize;
421     uint8_t* destBuffer = reinterpret_cast<uint8_t*>(malloc(byteSize + 4u));
422
423     int32_t srcStride  = width * bytesPerPixel;
424     int32_t destStride = xFaceSize * bytesPerPixel;
425     int32_t srcOffset  = xOffset * bytesPerPixel + yOffset * srcStride;
426     int32_t destOffset = 0;
427     for(uint16_t row = yOffset; row < yOffset + yFaceSize; ++row)
428     {
429       memcpy(destBuffer + destOffset, sourceBuffer + srcOffset, destStride);
430       srcOffset += srcStride;
431       destOffset += destStride;
432     }
433
434     return destBuffer;
435   }
436
437   void UploadTextureFace(Texture& texture, Devel::PixelBuffer pixelBuffer, int32_t faceIndex)
438   {
439     uint8_t* imageBuffer   = pixelBuffer.GetBuffer();
440     uint32_t bytesPerPixel = Pixel::GetBytesPerPixel(pixelBuffer.GetPixelFormat());
441     uint32_t imageWidth    = pixelBuffer.GetWidth();
442     uint32_t imageHeight   = pixelBuffer.GetHeight();
443
444     int32_t faceSize = imageWidth / 4;
445
446     uint32_t xOffset = cubeMap_index_x[faceIndex] * faceSize;
447     uint32_t yOffset = cubeMap_index_y[faceIndex] * faceSize;
448
449     uint8_t* tempImageBuffer = CropBuffer(imageBuffer, bytesPerPixel, imageWidth, imageHeight, xOffset, yOffset, faceSize, faceSize);
450     if(tempImageBuffer)
451     {
452       PixelData pixelData = PixelData::New(tempImageBuffer, faceSize * faceSize * bytesPerPixel, faceSize, faceSize, pixelBuffer.GetPixelFormat(), PixelData::FREE);
453       texture.Upload(pixelData, CubeMapLayer::POSITIVE_X + faceIndex, 0, 0, 0, faceSize, faceSize);
454     }
455   }
456
457   void ChangeModel(int32_t direction)
458   {
459     mCurrentGlTF += direction;
460     if(mCurrentGlTF >= NUM_OF_GLTF_MODELS)
461     {
462       mCurrentGlTF = 0;
463     }
464     if(mCurrentGlTF < 0)
465     {
466       mCurrentGlTF = NUM_OF_GLTF_MODELS - 1;
467     }
468     CreateSceneFromGLTF(gltf_list[mCurrentGlTF]);
469     mCameraPosition = camera_position_list[mCurrentGlTF];
470     mCameraActor.SetProperty(Dali::Actor::Property::POSITION, mCameraPosition);
471     if(mCurrentGlTF == GLTF_LANTERN)
472     {
473       mCameraActor.SetTargetPosition(Vector3(0.0, -15.0, 0.0));
474     }
475     else
476     {
477       mCameraActor.SetTargetPosition(Vector3::ZERO);
478     }
479     mSkyboxActor.SetProperty(Dali::Actor::Property::POSITION, mCameraPosition);
480     SetAnimation();
481     mAnimationStop = false;
482     mWheelDelta    = 1.0f;
483   }
484
485   /**
486    * This function will change the material Roughness, Metalness or the model orientation when touched
487    */
488   bool OnTouch(Actor actor, const TouchEvent& touch)
489   {
490     const PointState::Type state = touch.GetState(0);
491
492     switch(state)
493     {
494       case PointState::DOWN:
495       {
496         if(mDoubleTap)
497         {
498           ChangeModel(1);
499         }
500         mDoubleTapTime.Stop();
501
502         mPointZ = touch.GetScreenPosition(0);
503         if(!mAnimationStop)
504         {
505           mAnimation.Pause();
506         }
507         break;
508       }
509
510       case PointState::MOTION:
511       {
512         const Size    size   = mWindow.GetSize();
513         const float   scaleX = size.width;
514         const float   scaleY = size.height;
515         const Vector2 point  = touch.GetScreenPosition(0);
516         mProcess             = false;
517         // If the touch is not processed above, then change the model orientation
518         if(!mProcess)
519         {
520           /**
521            * This is the motion for the swipe to rotate camera that targeting Vector3::ZERO.
522            * Each quaternion is used to rotate camera by Pitch and Yaw respectively.
523            * Final Orientation of camera is combination of Pitch and Yaw quaternion.
524            * For the natural rendering, Skybox also relocated at the new camera position.
525            */
526           mProcess           = true;
527           const float angle1 = ((mPointZ.y - point.y) / scaleY);
528           const float angle2 = ((mPointZ.x - point.x) / scaleX);
529
530           Quaternion pitch(Radian(Degree(angle1 * -200.0f)), (mCameraPosition.z >= 0) ? Vector3::XAXIS : -Vector3::XAXIS);
531           Quaternion yaw(Radian(Degree(angle2 * -200.0f)), -Vector3::YAXIS);
532
533           Quaternion newModelOrientation = yaw * pitch;
534           mCameraPosition                = newModelOrientation.Rotate(mCameraPosition);
535           mCameraActor.SetProperty(Dali::Actor::Property::POSITION, mCameraPosition);
536           mSkyboxActor.SetProperty(Dali::Actor::Property::POSITION, mCameraPosition);
537
538           mPointZ = point;
539         }
540         break;
541       }
542
543       case PointState::UP:
544       {
545         mDoubleTapTime.Start();
546         mDoubleTap = true;
547         if(!mAnimationStop)
548         {
549           mAnimation.Play();
550         }
551         mProcess = false;
552         break;
553       }
554
555       default:
556       {
557         break;
558       }
559     }
560     return true;
561   }
562
563   void OnKeyEvent(const KeyEvent& event)
564   {
565     if(event.GetState() == KeyEvent::DOWN)
566     {
567       if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
568       {
569         mApplication.Quit();
570       }
571       if(event.GetKeyName() == "Down" ||
572          event.GetKeyName() == "Up")
573       {
574         if(!mProcess)
575         {
576           if(mAnimationStop)
577           {
578             mAnimation.Play();
579           }
580           else
581           {
582             mAnimation.Pause();
583           }
584           mAnimationStop = !mAnimationStop;
585         }
586       }
587
588       if(IsKey(event, Dali::DALI_KEY_CURSOR_RIGHT))
589       {
590         ChangeModel(1);
591       }
592
593       if(IsKey(event, Dali::DALI_KEY_CURSOR_LEFT))
594       {
595         ChangeModel(-1);
596       }
597     }
598   }
599
600 private:
601   Window       mWindow;
602   Layer        mLayer3D;
603   Application& mApplication;
604   CameraActor  mCameraActor;
605   Dali::Timer  mTimer;
606
607   Vector3     mCameraPosition;
608   Scene3dView mScene3dView;
609
610   Vector2    mPointZ;
611   Quaternion mModelOrientation;
612
613   Shader     mShaderSkybox;
614   Geometry   mSkyboxGeometry;
615   TextureSet mSkyboxTextures;
616   Renderer   mSkyboxRenderer;
617   Actor      mSkyboxActor;
618
619   Animation mAnimation;
620   bool      mProcess{false};
621   bool      mAnimationStop;
622
623   Timer mDoubleTapTime;
624   bool  mDoubleTap{false};
625
626   float mWheelDelta{1.0f};
627
628   int32_t mCurrentGlTF{0};
629 };
630
631 int32_t DALI_EXPORT_API main(int32_t argc, char** argv)
632 {
633   Application           application = Application::New(&argc, &argv);
634   Scene3dViewController test(application);
635   application.MainLoop();
636   return 0;
637 }