2 * Copyright (c) 2021 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
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
36 using namespace Dali;
\r
37 using namespace Dali::Toolkit;
\r
38 using namespace Dali::SceneLoader;
\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
44 const float ITEM_HEIGHT = 50.f;
\r
46 const Vector3 CAMERA_DEFAULT_POSITION(0.0f, 0.0f, 3.5f);
\r
48 const std::string_view DLI_EXTENSION = ".dli";
\r
49 const std::string_view GLTF_EXTENSION = ".gltf";
\r
51 const std::string RESOURCE_TYPE_DIRS[]{
\r
58 using StringVector = std::vector<std::string>;
\r
60 StringVector ListFiles(
\r
61 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
102 unsigned int GetNumberOfItems() override
\r
104 return mSceneNames.size();
\r
107 Actor NewItem(unsigned int itemId) override
\r
109 auto label = MakeLabel(mSceneNames[itemId]);
\r
110 mTapDetector.Attach(label);
\r
115 Actor CreateErrorMessage(std::string msg)
\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
124 void ConfigureCamera(const CameraParameters& params, CameraActor camera)
\r
126 if(params.isPerspective)
\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
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
145 Vector3 camTranslation;
\r
147 Quaternion camOrientation;
\r
148 params.CalculateTransformComponents(camTranslation, camOrientation, camScale);
\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
156 camOrientation.Conjugate();
\r
159 void ConfigureBlendShapeShaders(ResourceBundle& resources, const SceneDefinition& scene, Actor root, 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
198 if(sceneName.rfind(DLI_EXTENSION) == sceneName.size() - DLI_EXTENSION.size())
\r
201 DliLoader::InputParams input{
\r
202 pathProvider(ResourceType::Mesh),
\r
208 DliLoader::LoadParams loadParams{input, output};
\r
209 if(!loader.LoadScene(path, loadParams))
\r
211 ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << path << "': " << loader.GetParseError();
\r
216 ShaderDefinitionFactory sdf;
\r
217 sdf.SetResources(resources);
\r
218 LoadGltfScene(path, sdf, output);
\r
220 resources.mEnvironmentMaps.push_back({});
\r
223 if(cameraParameters.empty())
\r
225 cameraParameters.push_back(CameraParameters());
\r
226 cameraParameters[0].matrix.SetTranslation(CAMERA_DEFAULT_POSITION);
\r
228 ConfigureCamera(cameraParameters[0], camera);
\r
230 ViewProjection viewProjection = cameraParameters[0].GetViewProjection();
\r
234 NodeDefinition::CreateParams nodeParams{
\r
237 Customization::Choices choices;
\r
239 Actor root = Actor::New();
\r
240 SetActorCentered(root);
\r
242 for(auto iRoot : scene.GetRoots())
\r
244 auto resourceRefs = resources.CreateRefCounter();
\r
245 scene.CountResourceRefs(iRoot, choices, resourceRefs);
\r
246 resources.CountEnvironmentReferences(resourceRefs);
\r
248 resources.LoadResources(resourceRefs, pathProvider);
\r
250 if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
\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
256 scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
\r
262 if(!animations.empty())
\r
264 auto getActor = [&root](const std::string& name) {
\r
265 return root.FindChildByName(name);
\r
268 animations[0].ReAnimate(getActor).Play();
\r
276 SceneLoaderExample::SceneLoaderExample(Dali::Application& app)
\r
279 if(!std::getenv("DALI_APPLICATION_PACKAGE"))
\r
281 if(auto desktopPrefix = std::getenv("DESKTOP_PREFIX"))
\r
283 std::stringstream sstr;
\r
284 sstr << desktopPrefix << "/share/com.samsung.dali-demo/res/";
\r
286 auto daliApplicationPackage = sstr.str();
\r
287 setenv("DALI_APPLICATION_PACKAGE", daliApplicationPackage.c_str(), 0);
\r
291 app.InitSignal().Connect(this, &SceneLoaderExample::OnInit);
\r
292 app.TerminateSignal().Connect(this, &SceneLoaderExample::OnTerminate);
\r
295 void SceneLoaderExample::OnInit(Application& app)
\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
305 mSceneNames = sceneNames;
\r
307 // create Dali objects
\r
308 auto window = app.GetWindow();
\r
311 auto navigationView = NavigationView::New();
\r
312 navigationView.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
\r
313 SetActorCentered(navigationView);
\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
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
336 auto tapDetector = TapGestureDetector::New();
\r
337 mItemFactory.reset(new ::ItemFactoryImpl(mSceneNames, tapDetector));
\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
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
350 mItemLayout = itemLayout;
\r
354 auto camera = CameraActor::New();
\r
355 camera.SetInvertYAxis(true);
\r
356 window.Add(camera);
\r
357 mSceneCamera = camera;
\r
360 window.KeyEventSignal().Connect(this, &SceneLoaderExample::OnKey);
\r
362 tapDetector.DetectedSignal().Connect(this, &SceneLoaderExample::OnTap);
\r
363 mTapDetector = tapDetector;
\r
366 mItemView.ActivateLayout(0, windowSize, 1.f);
\r
369 void SceneLoaderExample::OnTerminate(Application& app)
\r
371 mTapDetector.Reset();
\r
372 mPanDetector.Reset();
\r
374 auto window = app.GetWindow();
\r
375 auto renderTasks = window.GetRenderTaskList();
\r
376 renderTasks.RemoveTask(mSceneRender);
\r
377 mSceneRender.Reset();
\r
379 UnparentAndReset(mNavigationView);
\r
380 UnparentAndReset(mSceneCamera);
\r
382 mItemFactory.reset();
\r
385 void SceneLoaderExample::OnKey(const KeyEvent& e)
\r
387 if(e.GetState() == KeyEvent::UP)
\r
389 if(IsKey(e, DALI_KEY_ESCAPE) || IsKey(e, DALI_KEY_BACK))
\r
393 mPanDetector.Reset();
\r
395 mNavigationView.Pop();
\r
406 void SceneLoaderExample::OnPan(Actor actor, const PanGesture& pan)
\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
412 size /= ROTATION_SCALE;
\r
414 Vector2 rotation{pan.GetDisplacement().x / size.x, pan.GetDisplacement().y / size.y * aspect};
\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
419 mScene.SetProperty(Actor::Property::ORIENTATION, q * q0);
\r
422 void SceneLoaderExample::OnTap(Dali::Actor actor, const Dali::TapGesture& tap)
\r
424 auto id = mItemView.GetItemId(actor);
\r
428 auto window = mApp.GetWindow();
\r
429 auto renderTasks = window.GetRenderTaskList();
\r
430 renderTasks.RemoveTask(mSceneRender);
\r
432 auto scene = LoadScene(mSceneNames[id], mSceneCamera);
\r
434 auto sceneRender = renderTasks.CreateTask();
\r
435 sceneRender.SetCameraActor(mSceneCamera);
\r
436 sceneRender.SetSourceActor(scene);
\r
437 sceneRender.SetExclusive(true);
\r
440 mSceneRender = sceneRender;
\r
442 mPanDetector = PanGestureDetector::New();
\r
443 mPanDetector.DetectedSignal().Connect(this, &SceneLoaderExample::OnPan);
\r
444 mPanDetector.Attach(mNavigationView);
\r
446 catch(const DaliException& e)
\r
448 mScene = CreateErrorMessage(e.condition);
\r
451 mNavigationView.Push(mScene);
\r