2 * Copyright (c) 2022 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 "scene3d-example.h"
\r
18 #include <dali-toolkit/dali-toolkit.h>
\r
21 #include <string_view>
\r
22 #include "dali-scene3d/public-api/loader/dli-loader.h"
\r
23 #include "dali-scene3d/public-api/loader/gltf2-loader.h"
\r
24 #include "dali-scene3d/public-api/loader/light-parameters.h"
\r
25 #include "dali-scene3d/public-api/loader/load-result.h"
\r
26 #include "dali-scene3d/public-api/loader/shader-definition-factory.h"
\r
27 #include "dali-toolkit/public-api/controls/scrollable/item-view/default-item-layout.h"
\r
28 #include "dali-toolkit/public-api/controls/scrollable/item-view/item-factory.h"
\r
29 #include "dali-toolkit/public-api/controls/text-controls/text-label.h"
\r
30 #include "dali-toolkit/public-api/visuals/gradient-visual-properties.h"
\r
31 #include "dali/public-api/actors/layer.h"
\r
32 #include "dali/public-api/adaptor-framework/key.h"
\r
33 #include "dali/public-api/events/key-event.h"
\r
34 #include "dali/public-api/object/property-array.h"
\r
35 #include "dali/public-api/render-tasks/render-task-list.h"
\r
36 #include "scene3d-extension.h"
\r
38 using namespace Dali;
\r
39 using namespace Dali::Toolkit;
\r
40 using namespace Dali::Scene3D::Loader;
\r
44 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
46 const float ITEM_HEIGHT = 50.f;
\r
48 const Vector3 CAMERA_DEFAULT_POSITION(0.0f, 0.0f, 3.5f);
\r
50 const std::string_view DLI_EXTENSION = ".dli";
\r
51 const std::string_view GLTF_EXTENSION = ".gltf";
\r
53 const std::string RESOURCE_TYPE_DIRS[]{
\r
60 using StringVector = std::vector<std::string>;
\r
62 StringVector ListFiles(
\r
63 const std::string& path, bool (*predicate)(const char*) = [](const char*) { return true; })
\r
65 StringVector results;
\r
67 if(auto dirp = opendir(path.c_str()))
\r
69 std::unique_ptr<DIR, int (*)(DIR*)> dir(dirp, closedir);
\r
72 while((ent = readdir(dir.get())) != nullptr)
\r
74 if(ent->d_type == DT_REG && predicate(ent->d_name))
\r
76 results.push_back(ent->d_name);
\r
83 TextLabel MakeLabel(std::string msg)
\r
85 TextLabel label = TextLabel::New(" " + msg);
\r
86 label.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
\r
87 label.SetProperty(TextLabel::Property::TEXT_COLOR, Color::WHITE);
\r
88 label.SetProperty(TextLabel::Property::PIXEL_SIZE, ITEM_HEIGHT * 4 / 7);
\r
89 label.SetProperty(TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER");
\r
90 SetActorCentered(label);
\r
94 struct ItemFactoryImpl : Dali::Toolkit::ItemFactory
\r
96 const std::vector<std::string>& mSceneNames;
\r
97 TapGestureDetector mTapDetector;
\r
99 ItemFactoryImpl(const std::vector<std::string>& sceneNames, TapGestureDetector tapDetector)
\r
100 : mSceneNames(sceneNames),
\r
101 mTapDetector(tapDetector)
\r
105 unsigned int GetNumberOfItems() override
\r
107 return mSceneNames.size();
\r
110 Actor NewItem(unsigned int itemId) override
\r
112 auto label = MakeLabel(mSceneNames[itemId]);
\r
113 mTapDetector.Attach(label);
\r
114 label.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
\r
119 Actor CreateErrorMessage(std::string msg)
\r
121 auto label = MakeLabel(msg);
\r
122 label.SetProperty(TextLabel::Property::MULTI_LINE, true);
\r
123 label.SetProperty(TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::LEFT);
\r
124 label.SetProperty(TextLabel::Property::VERTICAL_ALIGNMENT, VerticalAlignment::TOP);
\r
128 void ConfigureCamera(const CameraParameters& params, CameraActor camera)
\r
130 if(params.isPerspective)
\r
132 camera.SetProjectionMode(Camera::PERSPECTIVE_PROJECTION);
\r
133 camera.SetNearClippingPlane(params.zNear);
\r
134 camera.SetFarClippingPlane(params.zFar);
\r
135 camera.SetFieldOfView(Radian(Degree(params.yFov)));
\r
139 camera.SetProjectionMode(Camera::ORTHOGRAPHIC_PROJECTION);
\r
140 camera.SetOrthographicProjection(params.orthographicSize.x,
\r
141 params.orthographicSize.y,
\r
142 params.orthographicSize.z,
\r
143 params.orthographicSize.w,
\r
149 Vector3 camTranslation;
\r
151 Quaternion camOrientation;
\r
152 params.CalculateTransformComponents(camTranslation, camOrientation, camScale);
\r
154 SetActorCentered(camera);
\r
155 camera.SetInvertYAxis(true);
\r
156 camera.SetProperty(Actor::Property::POSITION, camTranslation);
\r
157 camera.SetProperty(Actor::Property::ORIENTATION, camOrientation);
\r
158 camera.SetProperty(Actor::Property::SCALE, camScale);
\r
160 camOrientation.Conjugate();
\r
163 void ConfigureBlendShapeShaders(ResourceBundle& resources, const SceneDefinition& scene, Actor root, std::vector<BlendshapeShaderConfigurationRequest>&& requests)
\r
165 std::vector<std::string> errors;
\r
166 auto onError = [&errors](const std::string& msg) {
\r
167 errors.push_back(msg);
\r
169 if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
\r
171 ExceptionFlinger flinger(ASSERT_LOCATION);
\r
172 for(auto& msg : errors)
\r
174 flinger << msg << '\n';
\r
179 Actor LoadScene(std::string sceneName, CameraActor camera, std::vector<AnimationDefinition>* animations, Animation& animation)
\r
181 ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {
\r
182 return Application::GetResourcePath() + RESOURCE_TYPE_DIRS[type];
\r
185 auto path = pathProvider(ResourceType::Mesh) + sceneName;
\r
187 ResourceBundle resources;
\r
188 SceneDefinition scene;
\r
189 SceneMetadata metaData;
\r
190 std::vector<AnimationGroupDefinition> animGroups;
\r
191 std::vector<CameraParameters> cameraParameters;
\r
192 std::vector<LightParameters> lights;
\r
194 animations->clear();
\r
205 if(sceneName.rfind(DLI_EXTENSION) == sceneName.size() - DLI_EXTENSION.size())
\r
208 DliLoader::InputParams input{
\r
209 pathProvider(ResourceType::Mesh),
\r
215 DliLoader::LoadParams loadParams{input, output};
\r
216 if(!loader.LoadScene(path, loadParams))
\r
218 ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << path << "': " << loader.GetParseError();
\r
223 ShaderDefinitionFactory sdf;
\r
224 sdf.SetResources(resources);
\r
225 LoadGltfScene(path, sdf, output);
\r
227 resources.mEnvironmentMaps.push_back({});
\r
230 if(cameraParameters.empty())
\r
232 cameraParameters.push_back(CameraParameters());
\r
233 cameraParameters[0].matrix.SetTranslation(CAMERA_DEFAULT_POSITION);
\r
235 ConfigureCamera(cameraParameters[0], camera);
\r
237 ViewProjection viewProjection = cameraParameters[0].GetViewProjection();
\r
241 NodeDefinition::CreateParams nodeParams{
\r
247 Customization::Choices choices;
\r
249 Actor root = Actor::New();
\r
250 SetActorCentered(root);
\r
252 for(auto iRoot : scene.GetRoots())
\r
254 auto resourceRefs = resources.CreateRefCounter();
\r
255 scene.CountResourceRefs(iRoot, choices, resourceRefs);
\r
256 resources.CountEnvironmentReferences(resourceRefs);
\r
258 resources.LoadResources(resourceRefs, pathProvider);
\r
260 if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
\r
262 scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
\r
263 scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
\r
264 ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
\r
266 scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
\r
272 if(!animations->empty())
\r
274 auto getActor = [&root](const std::string& name) {
\r
275 return root.FindChildByName(name);
\r
278 animation = (*animations)[0].ReAnimate(getActor);
\r
287 Scene3DExample::Scene3DExample(Dali::Application& app)
\r
289 mScene3DExtension(new Scene3DExtension())
\r
291 if(!std::getenv("DALI_APPLICATION_PACKAGE"))
\r
293 if(auto desktopPrefix = std::getenv("DESKTOP_PREFIX"))
\r
295 std::stringstream sstr;
\r
296 sstr << desktopPrefix << "/share/com.samsung.dali-demo/res/";
\r
298 auto daliApplicationPackage = sstr.str();
\r
299 setenv("DALI_APPLICATION_PACKAGE", daliApplicationPackage.c_str(), 0);
\r
303 app.InitSignal().Connect(this, &Scene3DExample::OnInit);
\r
304 app.TerminateSignal().Connect(this, &Scene3DExample::OnTerminate);
\r
307 Scene3DExample::~Scene3DExample() = default;
\r
309 void Scene3DExample::OnInit(Application& app)
\r
312 auto resPath = Application::GetResourcePath();
\r
313 auto scenePath = resPath + RESOURCE_TYPE_DIRS[ResourceType::Mesh];
\r
314 auto sceneNames = ListFiles(scenePath, [](const char* name) {
\r
315 auto len = strlen(name);
\r
316 return (len > DLI_EXTENSION.size() && DLI_EXTENSION.compare(name + (len - DLI_EXTENSION.size())) == 0) ||
\r
317 (len > GLTF_EXTENSION.size() && GLTF_EXTENSION.compare(name + (len - GLTF_EXTENSION.size())) == 0);
\r
319 mSceneNames = sceneNames;
\r
321 // create Dali objects
\r
322 auto window = app.GetWindow();
\r
325 auto navigationView = NavigationView::New();
\r
326 navigationView.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
\r
327 SetActorCentered(navigationView);
\r
329 // Set up the background gradient.
\r
330 Property::Array stopOffsets;
\r
331 stopOffsets.PushBack(0.0f);
\r
332 stopOffsets.PushBack(1.0f);
\r
333 Property::Array stopColors;
\r
334 stopColors.PushBack(Color::BLACK);
\r
335 stopColors.PushBack(Vector4(0.45f, 0.7f, 0.8f, 1.f)); // Medium bright, pastel blue
\r
336 const float percentageWindowHeight = window.GetSize().GetHeight() * 0.6f;
\r
338 navigationView.SetProperty(Toolkit::Control::Property::BACKGROUND,
\r
339 Dali::Property::Map()
\r
340 .Add(Toolkit::Visual::Property::TYPE, Dali::Toolkit::Visual::GRADIENT)
\r
341 .Add(Toolkit::GradientVisual::Property::STOP_OFFSET, stopOffsets)
\r
342 .Add(Toolkit::GradientVisual::Property::STOP_COLOR, stopColors)
\r
343 .Add(Toolkit::GradientVisual::Property::START_POSITION, Vector2(0.f, -percentageWindowHeight))
\r
344 .Add(Toolkit::GradientVisual::Property::END_POSITION, Vector2(0.f, percentageWindowHeight))
\r
345 .Add(Toolkit::GradientVisual::Property::UNITS, Toolkit::GradientVisual::Units::USER_SPACE));
\r
346 window.Add(navigationView);
\r
347 mNavigationView = navigationView;
\r
350 auto tapDetector = TapGestureDetector::New();
\r
351 mItemFactory.reset(new ::ItemFactoryImpl(mSceneNames, tapDetector));
\r
353 auto items = ItemView::New(*mItemFactory);
\r
354 SetActorCentered(items);
\r
355 items.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
\r
356 items.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
\r
358 Vector3 windowSize(window.GetSize());
\r
359 auto itemLayout = DefaultItemLayout::New(DefaultItemLayout::LIST);
\r
360 itemLayout->SetItemSize(Vector3(windowSize.x * 0.9f, ITEM_HEIGHT, 1.f));
\r
361 items.AddLayout(*itemLayout);
\r
362 navigationView.Push(items);
\r
364 mItemLayout = itemLayout;
\r
367 mItemView.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
\r
368 KeyboardFocusManager::Get().PreFocusChangeSignal().Connect(this, &Scene3DExample::OnKeyboardPreFocusChange);
\r
369 KeyboardFocusManager::Get().FocusedActorEnterKeySignal().Connect(this, &Scene3DExample::OnKeyboardFocusedActorActivated);
\r
370 KeyboardFocusManager::Get().FocusChangedSignal().Connect(this, &Scene3DExample::OnKeyboardFocusChanged);
\r
372 SetActorCentered(KeyboardFocusManager::Get().GetFocusIndicatorActor());
\r
375 auto camera = CameraActor::New();
\r
376 camera.SetInvertYAxis(true);
\r
377 window.Add(camera);
\r
378 mSceneCamera = camera;
\r
381 window.KeyEventSignal().Connect(this, &Scene3DExample::OnKey);
\r
383 tapDetector.DetectedSignal().Connect(this, &Scene3DExample::OnTap);
\r
384 mTapDetector = tapDetector;
\r
387 mItemView.ActivateLayout(0, windowSize, 0.f);
\r
389 mScene3DExtension->SetSceneLoader(this);
\r
392 Actor Scene3DExample::OnKeyboardPreFocusChange(Actor current, Actor proposed, Control::KeyboardFocus::Direction direction)
\r
394 if(!current && !proposed)
\r
402 void Scene3DExample::OnKeyboardFocusedActorActivated(Actor activatedActor)
\r
406 OnTap(activatedActor, Dali::TapGesture());
\r
410 void Scene3DExample::OnKeyboardFocusChanged(Actor originalFocusedActor, Actor currentFocusedActor)
\r
412 if(currentFocusedActor)
\r
414 auto itemId = mItemView.GetItemId(currentFocusedActor);
\r
415 mItemView.ScrollToItem(itemId, 0.1f);
\r
419 void Scene3DExample::OnTerminate(Application& app)
\r
421 mTapDetector.Reset();
\r
422 mPanDetector.Reset();
\r
424 auto window = app.GetWindow();
\r
425 auto renderTasks = window.GetRenderTaskList();
\r
426 renderTasks.RemoveTask(mSceneRender);
\r
427 mSceneRender.Reset();
\r
429 UnparentAndReset(mNavigationView);
\r
430 UnparentAndReset(mSceneCamera);
\r
432 mItemFactory.reset();
\r
435 void Scene3DExample::OnKey(const KeyEvent& e)
\r
437 if(e.GetState() == KeyEvent::UP)
\r
439 if(IsKey(e, DALI_KEY_ESCAPE) || IsKey(e, DALI_KEY_BACK))
\r
443 mPanDetector.Reset();
\r
445 mNavigationView.Pop();
\r
448 if(mActivatedActor)
\r
450 KeyboardFocusManager::Get().SetCurrentFocusActor(mActivatedActor);
\r
460 mScene3DExtension->OnKey(e);
\r
465 void Scene3DExample::OnPan(Actor actor, const PanGesture& pan)
\r
467 auto windowSize = mApp.GetWindow().GetSize();
\r
468 Vector2 size{float(windowSize.GetWidth()), float(windowSize.GetHeight())};
\r
469 float aspect = size.y / size.x;
\r
471 size /= ROTATION_SCALE;
\r
473 Vector2 rotation{pan.GetDisplacement().x / size.x, pan.GetDisplacement().y / size.y * aspect};
\r
475 Quaternion q = Quaternion(Radian(Degree(rotation.y)), Radian(Degree(rotation.x)), Radian(0.f));
\r
476 Quaternion q0 = mScene.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
\r
478 mScene.SetProperty(Actor::Property::ORIENTATION, q * q0);
\r
481 void Scene3DExample::OnTap(Dali::Actor actor, const Dali::TapGesture& tap)
\r
483 mActivatedActor = actor;
\r
485 auto id = mItemView.GetItemId(actor);
\r
489 auto window = mApp.GetWindow();
\r
490 auto renderTasks = window.GetRenderTaskList();
\r
491 renderTasks.RemoveTask(mSceneRender);
\r
493 auto scene = LoadScene(mSceneNames[id], mSceneCamera, &mSceneAnimations, mCurrentAnimation);
\r
495 auto sceneRender = renderTasks.CreateTask();
\r
496 sceneRender.SetCameraActor(mSceneCamera);
\r
497 sceneRender.SetSourceActor(scene);
\r
498 sceneRender.SetExclusive(true);
\r
501 mSceneRender = sceneRender;
\r
503 mPanDetector = PanGestureDetector::New();
\r
504 mPanDetector.DetectedSignal().Connect(this, &Scene3DExample::OnPan);
\r
505 mPanDetector.Attach(mNavigationView);
\r
507 catch(const DaliException& e)
\r
509 mScene = CreateErrorMessage(e.condition);
\r
512 mNavigationView.Push(mScene);
\r
514 mScene3DExtension->ConnectTouchSignals();
\r