2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
\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
8 * http://www.apache.org/licenses/LICENSE-2.0
\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
17 #include "scene-loader-example.h"
\r
18 #include "dali-scene-loader/public-api/gltf2-loader.h"
\r
19 #include "dali-scene-loader/public-api/shader-definition-factory.h"
\r
20 #include "dali-scene-loader/public-api/light-parameters.h"
\r
21 #include "dali-scene-loader/public-api/dli-loader.h"
\r
22 #include "dali-scene-loader/public-api/load-result.h"
\r
23 #include "dali/public-api/adaptor-framework/key.h"
\r
24 #include "dali/public-api/events/key-event.h"
\r
25 #include "dali/public-api/actors/layer.h"
\r
26 #include "dali/public-api/render-tasks/render-task-list.h"
\r
27 #include "dali/public-api/object/property-array.h"
\r
28 #include "dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h"
\r
29 #include "dali-toolkit/public-api/controls/scrollable/item-view/default-item-layout.h"
\r
30 #include "dali-toolkit/public-api/controls/text-controls/text-label.h"
\r
31 #include "dali-toolkit/public-api/visuals/gradient-visual-properties.h"
\r
32 #include <string_view>
\r
36 using namespace Dali;
\r
37 using namespace Dali::Toolkit;
\r
38 using namespace Dali::SceneLoader;
\r
43 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
45 const float ITEM_HEIGHT = 50.f;
\r
47 const Vector3 CAMERA_DEFAULT_POSITION(0.0f, 0.0f, 3.5f);
\r
49 const std::string_view DLI_EXTENSION = ".dli";
\r
50 const std::string_view GLTF_EXTENSION = ".gltf";
\r
52 const std::string RESOURCE_TYPE_DIRS[]{
\r
59 using StringVector = std::vector<std::string>;
\r
61 StringVector ListFiles(const std::string& path, bool(*predicate)(const char*) = [](const char*) { return true; })
\r
63 StringVector results;
\r
65 if (auto dirp = opendir(path.c_str()))
\r
67 std::unique_ptr<DIR, int(*)(DIR*)> dir(dirp, closedir);
\r
70 while ((ent = readdir(dir.get())) != nullptr)
\r
72 if (ent->d_type == DT_REG && predicate(ent->d_name))
\r
74 results.push_back(ent->d_name);
\r
81 TextLabel MakeLabel(std::string msg)
\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
91 struct ItemFactoryImpl : Dali::Toolkit::ItemFactory
\r
93 const std::vector<std::string>& mSceneNames;
\r
94 TapGestureDetector mTapDetector;
\r
96 ItemFactoryImpl(const std::vector<std::string>& sceneNames, TapGestureDetector tapDetector)
\r
97 : mSceneNames(sceneNames),
\r
98 mTapDetector(tapDetector)
\r
101 unsigned int GetNumberOfItems() override
\r
103 return mSceneNames.size();
\r
106 Actor NewItem(unsigned int itemId) override
\r
108 auto label = MakeLabel(mSceneNames[itemId]);
\r
109 mTapDetector.Attach(label);
\r
114 Actor CreateErrorMessage(std::string msg)
\r
116 auto label = MakeLabel(msg);
\r
117 label.SetProperty(TextLabel::Property::MULTI_LINE, true);
\r
118 label.SetProperty(TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::LEFT);
\r
119 label.SetProperty(TextLabel::Property::VERTICAL_ALIGNMENT, VerticalAlignment::TOP);
\r
123 void ConfigureCamera(const CameraParameters& params, CameraActor camera)
\r
125 if (params.isPerspective)
\r
127 camera.SetProjectionMode(Camera::PERSPECTIVE_PROJECTION);
\r
128 camera.SetNearClippingPlane(params.zNear);
\r
129 camera.SetFarClippingPlane(params.zFar);
\r
130 camera.SetFieldOfView(Radian(Degree(params.yFov)));
\r
134 camera.SetProjectionMode(Camera::ORTHOGRAPHIC_PROJECTION);
\r
135 camera.SetOrthographicProjection(params.orthographicSize.x,
\r
136 params.orthographicSize.y,
\r
137 params.orthographicSize.z,
\r
138 params.orthographicSize.w,
\r
144 Vector3 camTranslation;
\r
146 Quaternion camOrientation;
\r
147 params.CalculateTransformComponents(camTranslation, camOrientation, camScale);
\r
149 SetActorCentered(camera);
\r
150 camera.SetInvertYAxis(true);
\r
151 camera.SetProperty(Actor::Property::POSITION, camTranslation);
\r
152 camera.SetProperty(Actor::Property::ORIENTATION, camOrientation);
\r
153 camera.SetProperty(Actor::Property::SCALE, camScale);
\r
155 camOrientation.Conjugate();
\r
158 void ConfigureBlendShapeShaders(ResourceBundle& resources, const SceneDefinition& scene, Actor root,
\r
159 std::vector<BlendshapeShaderConfigurationRequest>&& requests)
\r
161 std::vector<std::string> errors;
\r
162 auto onError = [&errors](const std::string& msg) {
\r
163 errors.push_back(msg);
\r
165 if (!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
\r
167 ExceptionFlinger flinger(ASSERT_LOCATION);
\r
168 for (auto& msg : errors)
\r
170 flinger << msg << '\n';
\r
175 Actor LoadScene(std::string sceneName, CameraActor camera)
\r
177 ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {
\r
178 return Application::GetResourcePath() + RESOURCE_TYPE_DIRS[type];
\r
181 auto path = pathProvider(ResourceType::Mesh) + sceneName;
\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
199 if (sceneName.rfind(DLI_EXTENSION) == sceneName.size() - DLI_EXTENSION.size())
\r
202 DliLoader::InputParams input{
\r
203 pathProvider(ResourceType::Mesh),
\r
209 DliLoader::LoadParams loadParams{ input, output };
\r
210 if (!loader.LoadScene(path, loadParams))
\r
212 ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << path << "': " << loader.GetParseError();
\r
217 ShaderDefinitionFactory sdf;
\r
218 sdf.SetResources(resources);
\r
219 LoadGltfScene(path, sdf, output);
\r
221 resources.mEnvironmentMaps.push_back({});
\r
224 if (cameraParameters.empty())
\r
226 cameraParameters.push_back(CameraParameters());
\r
227 cameraParameters[0].matrix.SetTranslation(CAMERA_DEFAULT_POSITION);
\r
229 ConfigureCamera(cameraParameters[0], camera);
\r
231 ViewProjection viewProjection = cameraParameters[0].GetViewProjection();
\r
236 NodeDefinition::CreateParams nodeParams{
\r
240 Customization::Choices choices;
\r
242 Actor root = Actor::New();
\r
243 SetActorCentered(root);
\r
245 for (auto iRoot : scene.GetRoots())
\r
247 auto resourceRefs = resources.CreateRefCounter();
\r
248 scene.CountResourceRefs(iRoot, choices, resourceRefs);
\r
249 resources.CountEnvironmentReferences(resourceRefs);
\r
251 resources.LoadResources(resourceRefs, pathProvider);
\r
253 if (auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
\r
255 scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
\r
256 scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
\r
257 ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
\r
259 scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
\r
265 if (!animations.empty())
\r
267 auto getActor = [&root](const std::string& name) {
\r
268 return root.FindChildByName(name);
\r
271 animations[0].ReAnimate(getActor).Play();
\r
279 SceneLoaderExample::SceneLoaderExample(Dali::Application& app)
\r
282 if (!std::getenv("DALI_APPLICATION_PACKAGE"))
\r
284 if (auto desktopPrefix = std::getenv("DESKTOP_PREFIX"))
\r
286 std::stringstream sstr;
\r
287 sstr << desktopPrefix << "/share/com.samsung.dali-demo/res/";
\r
289 auto daliApplicationPackage = sstr.str();
\r
290 setenv("DALI_APPLICATION_PACKAGE", daliApplicationPackage.c_str(), 0);
\r
294 app.InitSignal().Connect(this, &SceneLoaderExample::OnInit);
\r
295 app.TerminateSignal().Connect(this, &SceneLoaderExample::OnTerminate);
\r
298 void SceneLoaderExample::OnInit(Application& app)
\r
301 auto resPath = Application::GetResourcePath();
\r
302 auto scenePath = resPath + RESOURCE_TYPE_DIRS[ResourceType::Mesh];
\r
303 auto sceneNames = ListFiles(scenePath, [](const char* name) {
\r
304 auto len = strlen(name);
\r
305 return (len > DLI_EXTENSION.size() && DLI_EXTENSION.compare(name + (len - DLI_EXTENSION.size())) == 0) ||
\r
306 (len > GLTF_EXTENSION.size() && GLTF_EXTENSION.compare(name + (len - GLTF_EXTENSION.size())) == 0);
\r
308 mSceneNames = sceneNames;
\r
310 // create Dali objects
\r
311 auto window = app.GetWindow();
\r
314 auto navigationView = NavigationView::New();
\r
315 navigationView.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
\r
316 SetActorCentered(navigationView);
\r
318 // Set up the background gradient.
\r
319 Property::Array stopOffsets;
\r
320 stopOffsets.PushBack(0.0f);
\r
321 stopOffsets.PushBack(1.0f);
\r
322 Property::Array stopColors;
\r
323 stopColors.PushBack(Color::BLACK);
\r
324 stopColors.PushBack(Vector4(0.45f, 0.7f, 0.8f, 1.f)); // Medium bright, pastel blue
\r
325 const float percentageWindowHeight = window.GetSize().GetHeight() * 0.6f;
\r
327 navigationView.SetProperty(Toolkit::Control::Property::BACKGROUND, Dali::Property::Map().
\r
328 Add(Toolkit::Visual::Property::TYPE, Dali::Toolkit::Visual::GRADIENT).
\r
329 Add(Toolkit::GradientVisual::Property::STOP_OFFSET, stopOffsets).
\r
330 Add(Toolkit::GradientVisual::Property::STOP_COLOR, stopColors).
\r
331 Add(Toolkit::GradientVisual::Property::START_POSITION, Vector2(0.f, -percentageWindowHeight)).
\r
332 Add(Toolkit::GradientVisual::Property::END_POSITION, Vector2(0.f, percentageWindowHeight)).
\r
333 Add(Toolkit::GradientVisual::Property::UNITS, Toolkit::GradientVisual::Units::USER_SPACE));
\r
334 window.Add(navigationView);
\r
335 mNavigationView = navigationView;
\r
338 auto tapDetector = TapGestureDetector::New();
\r
339 mItemFactory.reset(new ::ItemFactoryImpl(mSceneNames, tapDetector));
\r
341 auto items = ItemView::New(*mItemFactory);
\r
342 SetActorCentered(items);
\r
343 items.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
\r
344 items.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
\r
346 Vector3 windowSize(window.GetSize());
\r
347 auto itemLayout = DefaultItemLayout::New(DefaultItemLayout::LIST);
\r
348 itemLayout->SetItemSize(Vector3(windowSize.x, ITEM_HEIGHT, 1.f));
\r
349 items.AddLayout(*itemLayout);
\r
350 navigationView.Push(items);
\r
352 mItemLayout = itemLayout;
\r
356 auto camera = CameraActor::New();
\r
357 camera.SetInvertYAxis(true);
\r
358 window.Add(camera);
\r
359 mSceneCamera = camera;
\r
362 window.KeyEventSignal().Connect(this, &SceneLoaderExample::OnKey);
\r
364 tapDetector.DetectedSignal().Connect(this, &SceneLoaderExample::OnTap);
\r
365 mTapDetector = tapDetector;
\r
368 mItemView.ActivateLayout(0, windowSize, 1.f);
\r
371 void SceneLoaderExample::OnTerminate(Application& app)
\r
373 mTapDetector.Reset();
\r
374 mPanDetector.Reset();
\r
376 auto window = app.GetWindow();
\r
377 auto renderTasks = window.GetRenderTaskList();
\r
378 renderTasks.RemoveTask(mSceneRender);
\r
379 mSceneRender.Reset();
\r
381 UnparentAndReset(mNavigationView);
\r
382 UnparentAndReset(mSceneCamera);
\r
384 mItemFactory.reset();
\r
387 void SceneLoaderExample::OnKey(const KeyEvent& e)
\r
389 if (e.GetState() == KeyEvent::UP)
\r
391 if (IsKey(e, DALI_KEY_ESCAPE) || IsKey(e, DALI_KEY_BACK))
\r
395 mPanDetector.Reset();
\r
397 mNavigationView.Pop();
\r
408 void SceneLoaderExample::OnPan(Actor actor, const PanGesture& pan)
\r
410 auto windowSize = mApp.GetWindow().GetSize();
\r
411 Vector2 size{ float(windowSize.GetWidth()), float(windowSize.GetHeight()) };
\r
412 float aspect = size.y / size.x;
\r
414 size /= ROTATION_SCALE;
\r
416 Vector2 rotation{ pan.GetDisplacement().x / size.x, pan.GetDisplacement().y / size.y * aspect };
\r
418 Quaternion q = Quaternion(Radian(Degree(rotation.y)), Radian(Degree(rotation.x)), Radian(0.f));
\r
419 Quaternion q0 = mScene.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
\r
421 mScene.SetProperty(Actor::Property::ORIENTATION, q * q0);
\r
424 void SceneLoaderExample::OnTap(Dali::Actor actor, const Dali::TapGesture& tap)
\r
426 auto id = mItemView.GetItemId(actor);
\r
430 auto window = mApp.GetWindow();
\r
431 auto renderTasks = window.GetRenderTaskList();
\r
432 renderTasks.RemoveTask(mSceneRender);
\r
434 auto scene = LoadScene(mSceneNames[id], mSceneCamera);
\r
436 auto sceneRender = renderTasks.CreateTask();
\r
437 sceneRender.SetCameraActor(mSceneCamera);
\r
438 sceneRender.SetSourceActor(scene);
\r
439 sceneRender.SetExclusive(true);
\r
442 mSceneRender = sceneRender;
\r
444 mPanDetector = PanGestureDetector::New();
\r
445 mPanDetector.DetectedSignal().Connect(this, &SceneLoaderExample::OnPan);
\r
446 mPanDetector.Attach(mNavigationView);
\r
448 catch (const DaliException& e)
\r
450 mScene = CreateErrorMessage(e.condition);
\r
453 mNavigationView.Push(mScene);
\r