Updated all files to new format
[platform/core/uifw/dali-demo.git] / examples / scene-loader / scene-loader-example.cpp
1 /*\r
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.\r
3  *\r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  * http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  *\r
16  */\r
17 #include "scene-loader-example.h"\r
18 #include <dirent.h>\r
19 #include <cstring>\r
20 #include <string_view>\r
21 #include "dali-scene-loader/public-api/dli-loader.h"\r
22 #include "dali-scene-loader/public-api/gltf2-loader.h"\r
23 #include "dali-scene-loader/public-api/light-parameters.h"\r
24 #include "dali-scene-loader/public-api/load-result.h"\r
25 #include "dali-scene-loader/public-api/shader-definition-factory.h"\r
26 #include "dali-toolkit/public-api/controls/scrollable/item-view/default-item-layout.h"\r
27 #include "dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h"\r
28 #include "dali-toolkit/public-api/controls/text-controls/text-label.h"\r
29 #include "dali-toolkit/public-api/visuals/gradient-visual-properties.h"\r
30 #include "dali/public-api/actors/layer.h"\r
31 #include "dali/public-api/adaptor-framework/key.h"\r
32 #include "dali/public-api/events/key-event.h"\r
33 #include "dali/public-api/object/property-array.h"\r
34 #include "dali/public-api/render-tasks/render-task-list.h"\r
35 \r
36 using namespace Dali;\r
37 using namespace Dali::Toolkit;\r
38 using namespace Dali::SceneLoader;\r
39 \r
40 namespace\r
41 {\r
42 const float ROTATION_SCALE = 180.f; // the amount of rotation that a swipe whose length is the width of the screen, causes, in degrees.\r
43 \r
44 const float ITEM_HEIGHT = 50.f;\r
45 \r
46 const Vector3 CAMERA_DEFAULT_POSITION(0.0f, 0.0f, 3.5f);\r
47 \r
48 const std::string_view DLI_EXTENSION  = ".dli";\r
49 const std::string_view GLTF_EXTENSION = ".gltf";\r
50 \r
51 const std::string RESOURCE_TYPE_DIRS[]{\r
52   "images/",\r
53   "shaders/",\r
54   "models/",\r
55   "images/",\r
56 };\r
57 \r
58 using StringVector = std::vector<std::string>;\r
59 \r
60 StringVector ListFiles(\r
61   const std::string& path, bool (*predicate)(const char*) = [](const char*) { return true; })\r
62 {\r
63   StringVector results;\r
64 \r
65   if(auto dirp = opendir(path.c_str()))\r
66   {\r
67     std::unique_ptr<DIR, int (*)(DIR*)> dir(dirp, closedir);\r
68 \r
69     struct dirent* ent;\r
70     while((ent = readdir(dir.get())) != nullptr)\r
71     {\r
72       if(ent->d_type == DT_REG && predicate(ent->d_name))\r
73       {\r
74         results.push_back(ent->d_name);\r
75       }\r
76     }\r
77   }\r
78   return results;\r
79 }\r
80 \r
81 TextLabel MakeLabel(std::string msg)\r
82 {\r
83   TextLabel label = TextLabel::New(msg);\r
84   label.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);\r
85   label.SetProperty(TextLabel::Property::TEXT_COLOR, Color::WHITE);\r
86   label.SetProperty(TextLabel::Property::PIXEL_SIZE, ITEM_HEIGHT * 4 / 7);\r
87   SetActorCentered(label);\r
88   return label;\r
89 }\r
90 \r
91 struct ItemFactoryImpl : Dali::Toolkit::ItemFactory\r
92 {\r
93   const std::vector<std::string>& mSceneNames;\r
94   TapGestureDetector              mTapDetector;\r
95 \r
96   ItemFactoryImpl(const std::vector<std::string>& sceneNames, TapGestureDetector tapDetector)\r
97   : mSceneNames(sceneNames),\r
98     mTapDetector(tapDetector)\r
99   {\r
100   }\r
101 \r
102   unsigned int GetNumberOfItems() override\r
103   {\r
104     return mSceneNames.size();\r
105   }\r
106 \r
107   Actor NewItem(unsigned int itemId) override\r
108   {\r
109     auto label = MakeLabel(mSceneNames[itemId]);\r
110     mTapDetector.Attach(label);\r
111     return label;\r
112   }\r
113 };\r
114 \r
115 Actor CreateErrorMessage(std::string msg)\r
116 {\r
117   auto label = MakeLabel(msg);\r
118   label.SetProperty(TextLabel::Property::MULTI_LINE, true);\r
119   label.SetProperty(TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::LEFT);\r
120   label.SetProperty(TextLabel::Property::VERTICAL_ALIGNMENT, VerticalAlignment::TOP);\r
121   return label;\r
122 }\r
123 \r
124 void ConfigureCamera(const CameraParameters& params, CameraActor camera)\r
125 {\r
126   if(params.isPerspective)\r
127   {\r
128     camera.SetProjectionMode(Camera::PERSPECTIVE_PROJECTION);\r
129     camera.SetNearClippingPlane(params.zNear);\r
130     camera.SetFarClippingPlane(params.zFar);\r
131     camera.SetFieldOfView(Radian(Degree(params.yFov)));\r
132   }\r
133   else\r
134   {\r
135     camera.SetProjectionMode(Camera::ORTHOGRAPHIC_PROJECTION);\r
136     camera.SetOrthographicProjection(params.orthographicSize.x,\r
137                                      params.orthographicSize.y,\r
138                                      params.orthographicSize.z,\r
139                                      params.orthographicSize.w,\r
140                                      params.zNear,\r
141                                      params.zFar);\r
142   }\r
143 \r
144   // model\r
145   Vector3    camTranslation;\r
146   Vector3    camScale;\r
147   Quaternion camOrientation;\r
148   params.CalculateTransformComponents(camTranslation, camOrientation, camScale);\r
149 \r
150   SetActorCentered(camera);\r
151   camera.SetInvertYAxis(true);\r
152   camera.SetProperty(Actor::Property::POSITION, camTranslation);\r
153   camera.SetProperty(Actor::Property::ORIENTATION, camOrientation);\r
154   camera.SetProperty(Actor::Property::SCALE, camScale);\r
155 \r
156   camOrientation.Conjugate();\r
157 }\r
158 \r
159 void ConfigureBlendShapeShaders(ResourceBundle& resources, const SceneDefinition& scene, Actor root, std::vector<BlendshapeShaderConfigurationRequest>&& requests)\r
160 {\r
161   std::vector<std::string> errors;\r
162   auto                     onError = [&errors](const std::string& msg) {\r
163     errors.push_back(msg);\r
164   };\r
165   if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))\r
166   {\r
167     ExceptionFlinger flinger(ASSERT_LOCATION);\r
168     for(auto& msg : errors)\r
169     {\r
170       flinger << msg << '\n';\r
171     }\r
172   }\r
173 }\r
174 \r
175 Actor LoadScene(std::string sceneName, CameraActor camera)\r
176 {\r
177   ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {\r
178     return Application::GetResourcePath() + RESOURCE_TYPE_DIRS[type];\r
179   };\r
180 \r
181   auto path = pathProvider(ResourceType::Mesh) + sceneName;\r
182 \r
183   ResourceBundle                        resources;\r
184   SceneDefinition                       scene;\r
185   std::vector<AnimationGroupDefinition> animGroups;\r
186   std::vector<CameraParameters>         cameraParameters;\r
187   std::vector<LightParameters>          lights;\r
188   std::vector<AnimationDefinition>      animations;\r
189 \r
190   LoadResult output{\r
191     resources,\r
192     scene,\r
193     animations,\r
194     animGroups,\r
195     cameraParameters,\r
196     lights};\r
197 \r
198   if(sceneName.rfind(DLI_EXTENSION) == sceneName.size() - DLI_EXTENSION.size())\r
199   {\r
200     DliLoader              loader;\r
201     DliLoader::InputParams input{\r
202       pathProvider(ResourceType::Mesh),\r
203       nullptr,\r
204       {},\r
205       {},\r
206       nullptr,\r
207     };\r
208     DliLoader::LoadParams loadParams{input, output};\r
209     if(!loader.LoadScene(path, loadParams))\r
210     {\r
211       ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << path << "': " << loader.GetParseError();\r
212     }\r
213   }\r
214   else\r
215   {\r
216     ShaderDefinitionFactory sdf;\r
217     sdf.SetResources(resources);\r
218     LoadGltfScene(path, sdf, output);\r
219 \r
220     resources.mEnvironmentMaps.push_back({});\r
221   }\r
222 \r
223   if(cameraParameters.empty())\r
224   {\r
225     cameraParameters.push_back(CameraParameters());\r
226     cameraParameters[0].matrix.SetTranslation(CAMERA_DEFAULT_POSITION);\r
227   }\r
228   ConfigureCamera(cameraParameters[0], camera);\r
229 \r
230   ViewProjection viewProjection = cameraParameters[0].GetViewProjection();\r
231   Transforms     xforms{\r
232     MatrixStack{},\r
233     viewProjection};\r
234   NodeDefinition::CreateParams nodeParams{\r
235     resources,\r
236     xforms};\r
237   Customization::Choices choices;\r
238 \r
239   Actor root = Actor::New();\r
240   SetActorCentered(root);\r
241 \r
242   for(auto iRoot : scene.GetRoots())\r
243   {\r
244     auto resourceRefs = resources.CreateRefCounter();\r
245     scene.CountResourceRefs(iRoot, choices, resourceRefs);\r
246     resources.CountEnvironmentReferences(resourceRefs);\r
247 \r
248     resources.LoadResources(resourceRefs, pathProvider);\r
249 \r
250     if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))\r
251     {\r
252       scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);\r
253       scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));\r
254       ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));\r
255 \r
256       scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));\r
257 \r
258       root.Add(actor);\r
259     }\r
260   }\r
261 \r
262   if(!animations.empty())\r
263   {\r
264     auto getActor = [&root](const std::string& name) {\r
265       return root.FindChildByName(name);\r
266     };\r
267 \r
268     animations[0].ReAnimate(getActor).Play();\r
269   }\r
270 \r
271   return root;\r
272 }\r
273 \r
274 } // namespace\r
275 \r
276 SceneLoaderExample::SceneLoaderExample(Dali::Application& app)\r
277 : mApp(app)\r
278 {\r
279   if(!std::getenv("DALI_APPLICATION_PACKAGE"))\r
280   {\r
281     if(auto desktopPrefix = std::getenv("DESKTOP_PREFIX"))\r
282     {\r
283       std::stringstream sstr;\r
284       sstr << desktopPrefix << "/share/com.samsung.dali-demo/res/";\r
285 \r
286       auto daliApplicationPackage = sstr.str();\r
287       setenv("DALI_APPLICATION_PACKAGE", daliApplicationPackage.c_str(), 0);\r
288     }\r
289   }\r
290 \r
291   app.InitSignal().Connect(this, &SceneLoaderExample::OnInit);\r
292   app.TerminateSignal().Connect(this, &SceneLoaderExample::OnTerminate);\r
293 }\r
294 \r
295 void SceneLoaderExample::OnInit(Application& app)\r
296 {\r
297   // get scenes\r
298   auto resPath    = Application::GetResourcePath();\r
299   auto scenePath  = resPath + RESOURCE_TYPE_DIRS[ResourceType::Mesh];\r
300   auto sceneNames = ListFiles(scenePath, [](const char* name) {\r
301     auto len = strlen(name);\r
302     return (len > DLI_EXTENSION.size() && DLI_EXTENSION.compare(name + (len - DLI_EXTENSION.size())) == 0) ||\r
303            (len > GLTF_EXTENSION.size() && GLTF_EXTENSION.compare(name + (len - GLTF_EXTENSION.size())) == 0);\r
304   });\r
305   mSceneNames     = sceneNames;\r
306 \r
307   // create Dali objects\r
308   auto window = app.GetWindow();\r
309 \r
310   // navigation view\r
311   auto navigationView = NavigationView::New();\r
312   navigationView.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);\r
313   SetActorCentered(navigationView);\r
314 \r
315   // Set up the background gradient.\r
316   Property::Array stopOffsets;\r
317   stopOffsets.PushBack(0.0f);\r
318   stopOffsets.PushBack(1.0f);\r
319   Property::Array stopColors;\r
320   stopColors.PushBack(Color::BLACK);\r
321   stopColors.PushBack(Vector4(0.45f, 0.7f, 0.8f, 1.f)); // Medium bright, pastel blue\r
322   const float percentageWindowHeight = window.GetSize().GetHeight() * 0.6f;\r
323 \r
324   navigationView.SetProperty(Toolkit::Control::Property::BACKGROUND,\r
325                              Dali::Property::Map()\r
326                                .Add(Toolkit::Visual::Property::TYPE, Dali::Toolkit::Visual::GRADIENT)\r
327                                .Add(Toolkit::GradientVisual::Property::STOP_OFFSET, stopOffsets)\r
328                                .Add(Toolkit::GradientVisual::Property::STOP_COLOR, stopColors)\r
329                                .Add(Toolkit::GradientVisual::Property::START_POSITION, Vector2(0.f, -percentageWindowHeight))\r
330                                .Add(Toolkit::GradientVisual::Property::END_POSITION, Vector2(0.f, percentageWindowHeight))\r
331                                .Add(Toolkit::GradientVisual::Property::UNITS, Toolkit::GradientVisual::Units::USER_SPACE));\r
332   window.Add(navigationView);\r
333   mNavigationView = navigationView;\r
334 \r
335   // item view\r
336   auto tapDetector = TapGestureDetector::New();\r
337   mItemFactory.reset(new ::ItemFactoryImpl(mSceneNames, tapDetector));\r
338 \r
339   auto items = ItemView::New(*mItemFactory);\r
340   SetActorCentered(items);\r
341   items.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);\r
342   items.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);\r
343 \r
344   Vector3 windowSize(window.GetSize());\r
345   auto    itemLayout = DefaultItemLayout::New(DefaultItemLayout::LIST);\r
346   itemLayout->SetItemSize(Vector3(windowSize.x, ITEM_HEIGHT, 1.f));\r
347   items.AddLayout(*itemLayout);\r
348   navigationView.Push(items);\r
349 \r
350   mItemLayout = itemLayout;\r
351   mItemView   = items;\r
352 \r
353   // camera\r
354   auto camera = CameraActor::New();\r
355   camera.SetInvertYAxis(true);\r
356   window.Add(camera);\r
357   mSceneCamera = camera;\r
358 \r
359   // event handling\r
360   window.KeyEventSignal().Connect(this, &SceneLoaderExample::OnKey);\r
361 \r
362   tapDetector.DetectedSignal().Connect(this, &SceneLoaderExample::OnTap);\r
363   mTapDetector = tapDetector;\r
364 \r
365   // activate layout\r
366   mItemView.ActivateLayout(0, windowSize, 1.f);\r
367 }\r
368 \r
369 void SceneLoaderExample::OnTerminate(Application& app)\r
370 {\r
371   mTapDetector.Reset();\r
372   mPanDetector.Reset();\r
373 \r
374   auto window      = app.GetWindow();\r
375   auto renderTasks = window.GetRenderTaskList();\r
376   renderTasks.RemoveTask(mSceneRender);\r
377   mSceneRender.Reset();\r
378 \r
379   UnparentAndReset(mNavigationView);\r
380   UnparentAndReset(mSceneCamera);\r
381 \r
382   mItemFactory.reset();\r
383 }\r
384 \r
385 void SceneLoaderExample::OnKey(const KeyEvent& e)\r
386 {\r
387   if(e.GetState() == KeyEvent::UP)\r
388   {\r
389     if(IsKey(e, DALI_KEY_ESCAPE) || IsKey(e, DALI_KEY_BACK))\r
390     {\r
391       if(mScene)\r
392       {\r
393         mPanDetector.Reset();\r
394 \r
395         mNavigationView.Pop();\r
396         mScene.Reset();\r
397       }\r
398       else\r
399       {\r
400         mApp.Quit();\r
401       }\r
402     }\r
403   }\r
404 }\r
405 \r
406 void SceneLoaderExample::OnPan(Actor actor, const PanGesture& pan)\r
407 {\r
408   auto    windowSize = mApp.GetWindow().GetSize();\r
409   Vector2 size{float(windowSize.GetWidth()), float(windowSize.GetHeight())};\r
410   float   aspect = size.y / size.x;\r
411 \r
412   size /= ROTATION_SCALE;\r
413 \r
414   Vector2 rotation{pan.GetDisplacement().x / size.x, pan.GetDisplacement().y / size.y * aspect};\r
415 \r
416   Quaternion q  = Quaternion(Radian(Degree(rotation.y)), Radian(Degree(rotation.x)), Radian(0.f));\r
417   Quaternion q0 = mScene.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();\r
418 \r
419   mScene.SetProperty(Actor::Property::ORIENTATION, q * q0);\r
420 }\r
421 \r
422 void SceneLoaderExample::OnTap(Dali::Actor actor, const Dali::TapGesture& tap)\r
423 {\r
424   auto id = mItemView.GetItemId(actor);\r
425 \r
426   try\r
427   {\r
428     auto window      = mApp.GetWindow();\r
429     auto renderTasks = window.GetRenderTaskList();\r
430     renderTasks.RemoveTask(mSceneRender);\r
431 \r
432     auto scene = LoadScene(mSceneNames[id], mSceneCamera);\r
433 \r
434     auto sceneRender = renderTasks.CreateTask();\r
435     sceneRender.SetCameraActor(mSceneCamera);\r
436     sceneRender.SetSourceActor(scene);\r
437     sceneRender.SetExclusive(true);\r
438 \r
439     mScene       = scene;\r
440     mSceneRender = sceneRender;\r
441 \r
442     mPanDetector = PanGestureDetector::New();\r
443     mPanDetector.DetectedSignal().Connect(this, &SceneLoaderExample::OnPan);\r
444     mPanDetector.Attach(mNavigationView);\r
445   }\r
446   catch(const DaliException& e)\r
447   {\r
448     mScene = CreateErrorMessage(e.condition);\r
449   }\r
450 \r
451   mNavigationView.Push(mScene);\r
452 }\r