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 ConfigureBlendShapeShaders(ResourceBundle& resources, const SceneDefinition& scene, Actor root, std::vector<BlendshapeShaderConfigurationRequest>&& requests)
\r
130 std::vector<std::string> errors;
\r
131 auto onError = [&errors](const std::string& msg) {
\r
132 errors.push_back(msg);
\r
134 if(!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
\r
136 ExceptionFlinger flinger(ASSERT_LOCATION);
\r
137 for(auto& msg : errors)
\r
139 flinger << msg << '\n';
\r
144 Actor LoadScene(std::string sceneName, CameraActor camera, std::vector<AnimationDefinition>* animations, Animation& animation)
\r
146 ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {
\r
147 return Application::GetResourcePath() + RESOURCE_TYPE_DIRS[type];
\r
150 auto path = pathProvider(ResourceType::Mesh) + sceneName;
\r
152 ResourceBundle resources;
\r
153 SceneDefinition scene;
\r
154 SceneMetadata metaData;
\r
155 std::vector<AnimationGroupDefinition> animGroups;
\r
156 std::vector<CameraParameters> cameraParameters;
\r
157 std::vector<LightParameters> lights;
\r
159 animations->clear();
\r
170 if(sceneName.rfind(DLI_EXTENSION) == sceneName.size() - DLI_EXTENSION.size())
\r
173 DliLoader::InputParams input{
\r
174 pathProvider(ResourceType::Mesh),
\r
180 DliLoader::LoadParams loadParams{input, output};
\r
181 if(!loader.LoadScene(path, loadParams))
\r
183 ExceptionFlinger(ASSERT_LOCATION) << "Failed to load scene from '" << path << "': " << loader.GetParseError();
\r
188 ShaderDefinitionFactory sdf;
\r
189 sdf.SetResources(resources);
\r
190 LoadGltfScene(path, sdf, output);
\r
192 resources.mEnvironmentMaps.push_back({});
\r
195 if(cameraParameters.empty())
\r
197 cameraParameters.push_back(CameraParameters());
\r
198 cameraParameters[0].matrix.SetTranslation(CAMERA_DEFAULT_POSITION);
\r
200 cameraParameters[0].ConfigureCamera(camera);
\r
201 SetActorCentered(camera);
\r
203 ViewProjection viewProjection = cameraParameters[0].GetViewProjection();
\r
207 NodeDefinition::CreateParams nodeParams{
\r
213 Customization::Choices choices;
\r
215 Actor root = Actor::New();
\r
216 SetActorCentered(root);
\r
218 for(auto iRoot : scene.GetRoots())
\r
220 auto resourceRefs = resources.CreateRefCounter();
\r
221 scene.CountResourceRefs(iRoot, choices, resourceRefs);
\r
222 resources.CountEnvironmentReferences(resourceRefs);
\r
224 resources.LoadResources(resourceRefs, pathProvider);
\r
226 if(auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
\r
228 scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
\r
229 scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
\r
230 ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
\r
232 scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
\r
238 if(!animations->empty())
\r
240 auto getActor = [&](const Scene3D::Loader::AnimatedProperty& property)
\r
243 if(property.mNodeIndex != Scene3D::Loader::INVALID_INDEX)
\r
245 auto* node = scene.GetNode(property.mNodeIndex);
\r
246 if(node != nullptr)
\r
248 actor = root.FindChildById(node->mNodeId);
\r
253 actor = root.FindChildByName(property.mNodeName);
\r
258 animation = (*animations)[0].ReAnimate(getActor);
\r
267 Scene3DExample::Scene3DExample(Dali::Application& app)
\r
269 mScene3DExtension(new Scene3DExtension())
\r
271 if(!std::getenv("DALI_APPLICATION_PACKAGE"))
\r
273 if(auto desktopPrefix = std::getenv("DESKTOP_PREFIX"))
\r
275 std::stringstream sstr;
\r
276 sstr << desktopPrefix << "/share/com.samsung.dali-demo/res/";
\r
278 auto daliApplicationPackage = sstr.str();
\r
279 setenv("DALI_APPLICATION_PACKAGE", daliApplicationPackage.c_str(), 0);
\r
283 app.InitSignal().Connect(this, &Scene3DExample::OnInit);
\r
284 app.TerminateSignal().Connect(this, &Scene3DExample::OnTerminate);
\r
287 Scene3DExample::~Scene3DExample() = default;
\r
289 void Scene3DExample::OnInit(Application& app)
\r
292 auto resPath = Application::GetResourcePath();
\r
293 auto scenePath = resPath + RESOURCE_TYPE_DIRS[ResourceType::Mesh];
\r
294 auto sceneNames = ListFiles(scenePath, [](const char* name) {
\r
295 auto len = strlen(name);
\r
296 return (len > DLI_EXTENSION.size() && DLI_EXTENSION.compare(name + (len - DLI_EXTENSION.size())) == 0) ||
\r
297 (len > GLTF_EXTENSION.size() && GLTF_EXTENSION.compare(name + (len - GLTF_EXTENSION.size())) == 0);
\r
299 mSceneNames = sceneNames;
\r
301 // create Dali objects
\r
302 auto window = app.GetWindow();
\r
305 auto navigationView = NavigationView::New();
\r
306 navigationView.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
\r
307 SetActorCentered(navigationView);
\r
309 // Set up the background gradient.
\r
310 Property::Array stopOffsets;
\r
311 stopOffsets.PushBack(0.0f);
\r
312 stopOffsets.PushBack(1.0f);
\r
313 Property::Array stopColors;
\r
314 stopColors.PushBack(Color::BLACK);
\r
315 stopColors.PushBack(Vector4(0.45f, 0.7f, 0.8f, 1.f)); // Medium bright, pastel blue
\r
316 const float percentageWindowHeight = window.GetSize().GetHeight() * 0.6f;
\r
318 navigationView.SetProperty(Toolkit::Control::Property::BACKGROUND,
\r
319 Dali::Property::Map()
\r
320 .Add(Toolkit::Visual::Property::TYPE, Dali::Toolkit::Visual::GRADIENT)
\r
321 .Add(Toolkit::GradientVisual::Property::STOP_OFFSET, stopOffsets)
\r
322 .Add(Toolkit::GradientVisual::Property::STOP_COLOR, stopColors)
\r
323 .Add(Toolkit::GradientVisual::Property::START_POSITION, Vector2(0.f, -percentageWindowHeight))
\r
324 .Add(Toolkit::GradientVisual::Property::END_POSITION, Vector2(0.f, percentageWindowHeight))
\r
325 .Add(Toolkit::GradientVisual::Property::UNITS, Toolkit::GradientVisual::Units::USER_SPACE));
\r
326 window.Add(navigationView);
\r
327 mNavigationView = navigationView;
\r
330 auto tapDetector = TapGestureDetector::New();
\r
331 mItemFactory.reset(new ::ItemFactoryImpl(mSceneNames, tapDetector));
\r
333 auto items = ItemView::New(*mItemFactory);
\r
334 SetActorCentered(items);
\r
335 items.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
\r
336 items.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
\r
338 Vector3 windowSize(window.GetSize());
\r
339 auto itemLayout = DefaultItemLayout::New(DefaultItemLayout::LIST);
\r
340 itemLayout->SetItemSize(Vector3(windowSize.x * 0.9f, ITEM_HEIGHT, 1.f));
\r
341 items.AddLayout(*itemLayout);
\r
342 navigationView.Push(items);
\r
344 mItemLayout = itemLayout;
\r
347 mItemView.SetProperty(Actor::Property::KEYBOARD_FOCUSABLE, true);
\r
348 KeyboardFocusManager::Get().PreFocusChangeSignal().Connect(this, &Scene3DExample::OnKeyboardPreFocusChange);
\r
349 KeyboardFocusManager::Get().FocusedActorEnterKeySignal().Connect(this, &Scene3DExample::OnKeyboardFocusedActorActivated);
\r
350 KeyboardFocusManager::Get().FocusChangedSignal().Connect(this, &Scene3DExample::OnKeyboardFocusChanged);
\r
352 SetActorCentered(KeyboardFocusManager::Get().GetFocusIndicatorActor());
\r
355 auto camera = CameraActor::New();
\r
356 camera.SetInvertYAxis(true);
\r
357 window.Add(camera);
\r
358 mSceneCamera = camera;
\r
361 window.KeyEventSignal().Connect(this, &Scene3DExample::OnKey);
\r
363 tapDetector.DetectedSignal().Connect(this, &Scene3DExample::OnTap);
\r
364 mTapDetector = tapDetector;
\r
367 mItemView.ActivateLayout(0, windowSize, 0.f);
\r
369 mScene3DExtension->SetSceneLoader(this);
\r
372 Actor Scene3DExample::OnKeyboardPreFocusChange(Actor current, Actor proposed, Control::KeyboardFocus::Direction direction)
\r
374 if(!current && !proposed)
\r
382 void Scene3DExample::OnKeyboardFocusedActorActivated(Actor activatedActor)
\r
386 OnTap(activatedActor, Dali::TapGesture());
\r
390 void Scene3DExample::OnKeyboardFocusChanged(Actor originalFocusedActor, Actor currentFocusedActor)
\r
392 if(currentFocusedActor)
\r
394 auto itemId = mItemView.GetItemId(currentFocusedActor);
\r
395 mItemView.ScrollToItem(itemId, 0.1f);
\r
399 void Scene3DExample::OnTerminate(Application& app)
\r
401 mTapDetector.Reset();
\r
402 mPanDetector.Reset();
\r
404 auto window = app.GetWindow();
\r
405 auto renderTasks = window.GetRenderTaskList();
\r
406 renderTasks.RemoveTask(mSceneRender);
\r
407 mSceneRender.Reset();
\r
409 UnparentAndReset(mNavigationView);
\r
410 UnparentAndReset(mSceneCamera);
\r
412 mItemFactory.reset();
\r
415 void Scene3DExample::OnKey(const KeyEvent& e)
\r
417 if(e.GetState() == KeyEvent::UP)
\r
419 if(IsKey(e, DALI_KEY_ESCAPE) || IsKey(e, DALI_KEY_BACK))
\r
423 mPanDetector.Reset();
\r
425 mNavigationView.Pop();
\r
428 if(mActivatedActor)
\r
430 KeyboardFocusManager::Get().SetCurrentFocusActor(mActivatedActor);
\r
432 auto window = mApp.GetWindow();
\r
433 window.GetRootLayer().SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_UI);
\r
442 mScene3DExtension->OnKey(e);
\r
447 void Scene3DExample::OnPan(Actor actor, const PanGesture& pan)
\r
449 auto windowSize = mApp.GetWindow().GetSize();
\r
450 Vector2 size{float(windowSize.GetWidth()), float(windowSize.GetHeight())};
\r
451 float aspect = size.y / size.x;
\r
453 size /= ROTATION_SCALE;
\r
455 Vector2 rotation{pan.GetDisplacement().x / size.x, pan.GetDisplacement().y / size.y * aspect};
\r
457 Quaternion q = Quaternion(Radian(Degree(rotation.y)), Radian(Degree(rotation.x)), Radian(0.f));
\r
458 Quaternion q0 = mScene.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>();
\r
460 mScene.SetProperty(Actor::Property::ORIENTATION, q * q0);
\r
463 void Scene3DExample::OnTap(Dali::Actor actor, const Dali::TapGesture& tap)
\r
465 mActivatedActor = actor;
\r
467 auto id = mItemView.GetItemId(actor);
\r
469 Scene3D::Loader::InitializeGltfLoader();
\r
472 auto window = mApp.GetWindow();
\r
473 window.GetRootLayer().SetProperty(Layer::Property::BEHAVIOR, Layer::LAYER_3D);
\r
474 auto renderTasks = window.GetRenderTaskList();
\r
475 renderTasks.RemoveTask(mSceneRender);
\r
477 auto scene = LoadScene(mSceneNames[id], mSceneCamera, &mSceneAnimations, mCurrentAnimation);
\r
479 auto sceneRender = renderTasks.CreateTask();
\r
480 sceneRender.SetCameraActor(mSceneCamera);
\r
481 sceneRender.SetSourceActor(scene);
\r
482 sceneRender.SetExclusive(true);
\r
485 mSceneRender = sceneRender;
\r
487 mPanDetector = PanGestureDetector::New();
\r
488 mPanDetector.DetectedSignal().Connect(this, &Scene3DExample::OnPan);
\r
489 mPanDetector.Attach(mNavigationView);
\r
491 catch(const DaliException& e)
\r
493 mScene = CreateErrorMessage(e.condition);
\r
496 mNavigationView.Push(mScene);
\r
498 mScene3DExtension->ConnectTouchSignals();
\r