• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "scene_manager.h"
17 
18 #include <scene/ext/intf_ecs_context.h>
19 #include <scene/ext/intf_internal_scene.h>
20 #include <scene/ext/util.h>
21 #include <scene/interface/intf_application_context.h>
22 #include <scene/interface/intf_environment.h>
23 #include <scene/interface/intf_image.h>
24 #include <scene/interface/intf_material.h>
25 #include <scene/interface/intf_postprocess.h>
26 #include <scene/interface/intf_scene.h>
27 #include <scene/interface/intf_scene_manager.h>
28 #include <scene/interface/intf_shader.h>
29 #include <scene/interface/resource/types.h>
30 
31 #include <core/intf_engine.h>
32 #include <render/intf_render_context.h>
33 
34 #include <meta/api/task_queue.h>
35 #include <meta/interface/builtin_objects.h>
36 #include <meta/interface/intf_object_hierarchy_observer.h>
37 #include <meta/interface/resource/intf_object_resource.h>
38 
39 #include "asset/asset_object.h"
40 #include "resource/resource_types.h"
41 
SCENE_BEGIN_NAMESPACE()42 SCENE_BEGIN_NAMESPACE()
43 
44 bool SceneManager::Build(const META_NS::IMetadata::Ptr& d)
45 {
46     bool res = Super::Build(d);
47     if (res) {
48         if (d) {
49             context_ = GetInterfaceBuildArg<IRenderContext>(d, "RenderContext");
50             opts_ = GetBuildArg<SceneOptions>(d, "Options");
51         }
52         if (!context_) {
53             if (auto app = GetDefaultApplicationContext()) {
54                 CORE_LOG_D("Using default context for scene manager");
55                 context_ = GetDefaultApplicationContext()->GetRenderContext();
56                 if (!context_) {
57                     CORE_LOG_W("No default context set");
58                     return false;
59                 }
60                 // use opts from app context if we are using it
61                 opts_ = app->GetDefaultSceneOptions();
62             }
63         }
64         RegisterResourceTypes(context_, opts_);
65     }
66     return res;
67 }
68 
CreateContext(SceneOptions opts) const69 META_NS::IMetadata::Ptr SceneManager::CreateContext(SceneOptions opts) const
70 {
71     auto context = CreateRenderContextArg(context_);
72     if (context) {
73         if (auto op = META_NS::ConstructProperty<SceneOptions>("Options", opts)) {
74             context->AddProperty(op);
75         }
76     }
77     return context;
78 }
79 
CreateScene()80 Future<IScene::Ptr> SceneManager::CreateScene()
81 {
82     return CreateScene(opts_);
83 }
84 
CreateScene(SceneOptions opts)85 Future<IScene::Ptr> SceneManager::CreateScene(SceneOptions opts)
86 {
87     return context_->AddTask([context = CreateContext(BASE_NS::move(opts))] {
88         if (auto scene = META_NS::GetObjectRegistry().Create<IScene>(SCENE_NS::ClassId::Scene, context)) {
89             auto& ecs = scene->GetInternalScene()->GetEcsContext();
90             if (ecs.CreateUnnamedRootNode()) {
91                 return scene;
92             }
93             CORE_LOG_E("Failed to create root node");
94         }
95         return SCENE_NS::IScene::Ptr {};
96     });
97 }
98 
Load(const IScene::Ptr & scene,BASE_NS::string_view uri)99 static IScene::Ptr Load(const IScene::Ptr& scene, BASE_NS::string_view uri)
100 {
101     CORE_LOG_I("Loading scene: '%s'", BASE_NS::string(uri).c_str());
102     if (auto assets = META_NS::GetObjectRegistry().Create<IAssetObject>(ClassId::AssetObject)) {
103         if (assets->Load(scene, uri)) {
104             if (auto att = interface_cast<META_NS::IAttach>(scene)) {
105                 att->Attach(assets);
106             }
107             return scene;
108         }
109     }
110     return {};
111 }
112 
CreateScene(BASE_NS::string_view uri)113 Future<IScene::Ptr> SceneManager::CreateScene(BASE_NS::string_view uri)
114 {
115     return CreateScene(uri, opts_);
116 }
117 
CreateScene(BASE_NS::string_view uri,SceneOptions opts)118 Future<IScene::Ptr> SceneManager::CreateScene(BASE_NS::string_view uri, SceneOptions opts)
119 {
120     if (uri == "" || uri == "scene://empty") {
121         return CreateScene(BASE_NS::move(opts));
122     }
123     if (!context_) {
124         return {};
125     }
126     return context_->AddTask(
127         [path = BASE_NS::string(uri), renderContext = context_, args = CreateContext(BASE_NS::move(opts))] {
128             IScene::Ptr result;
129             if (path.ends_with(".scene") || path.ends_with(".scene2")) {
130                 // Try loading as an editor project
131                 if (SetProjectPath(renderContext, path, ProjectPathAction::REGISTER)) {
132                     // Rely on an implementation detail: If the path is already registered, it can be re-registered.
133                     // Unregistering will remove only the re-registered path and leave the original.
134                     result = LoadSceneWithIndex(renderContext, path);
135                     SetProjectPath(renderContext, path, ProjectPathAction::UNREGISTER);
136                 }
137             }
138             if (!result) {
139                 // Was not an editor project (so could be either a .gltf or old format .scene)
140                 result = META_NS::GetObjectRegistry().Create<IScene>(SCENE_NS::ClassId::Scene, args);
141                 Load(result, path);
142             }
143             return result;
144         });
145 }
146 
LoadDefaultResourcesIfNeeded(const CORE_NS::IResourceManager::Ptr & resources)147 void SceneManager::LoadDefaultResourcesIfNeeded(const CORE_NS::IResourceManager::Ptr& resources)
148 {
149     static constexpr auto DEFAULT_PROJECT_RESOURCE_GROUP_URI = "project://default_resources.res";
150     bool foundDefault = false;
151     for (auto&& group : resources->GetResourceGroups()) {
152         // The project default resource group has no name
153         if (group.empty()) {
154             foundDefault = true;
155             break;
156         }
157     }
158     if (!foundDefault) {
159         // Did not find unnamed (default) resource group, load default resource file
160         resources->Import(DEFAULT_PROJECT_RESOURCE_GROUP_URI);
161     }
162 }
163 
LoadSceneWithIndex(const IRenderContext::Ptr & context,BASE_NS::string_view uri)164 IScene::Ptr SceneManager::LoadSceneWithIndex(const IRenderContext::Ptr& context, BASE_NS::string_view uri)
165 {
166     if (!context) {
167         return {};
168     }
169     auto resources = context->GetResources();
170     if (!resources) {
171         return {};
172     }
173 
174     // Make sure that project default resource group has been loaded
175     LoadDefaultResourcesIfNeeded(resources);
176 
177     const auto index = GuessIndexFilePath(uri);
178     auto ires = resources->Import(index);
179     if (ires != CORE_NS::IResourceManager::Result::OK) {
180         CORE_LOG_E("Failed to load resource index: %s [%d]", index.c_str(), int(ires));
181         return {};
182     }
183     CORE_NS::ResourceId rid { BASE_NS::string(uri) };
184     // see if the scene is resource in the index, if not, add it
185     if (!resources->GetResourceInfo(rid).id.IsValid()) {
186         resources->AddResource(rid, ClassId::SceneResource.Id().ToUid(), uri);
187     }
188     return interface_pointer_cast<IScene>(resources->GetResource(rid));
189 }
190 
SetProjectPath(const IRenderContext::Ptr & renderContext,BASE_NS::string_view uri,ProjectPathAction action)191 bool SceneManager::SetProjectPath(
192     const IRenderContext::Ptr& renderContext, BASE_NS::string_view uri, ProjectPathAction action)
193 {
194     if (renderContext) {
195         if (const auto renderer = renderContext->GetRenderer()) {
196             auto& fileManager = renderer->GetEngine().GetFileManager();
197             const auto pathToProject = GuessProjectPath(uri);
198             if (action == ProjectPathAction::REGISTER) {
199                 return fileManager.RegisterPath("project", pathToProject, true);
200             } else {
201                 fileManager.UnregisterPath("project", pathToProject);
202             }
203             return true;
204         }
205     }
206     CORE_LOG_E("Unable to access file manager: render context missing");
207     return false;
208 }
209 
GuessIndexFilePath(BASE_NS::string_view uri)210 BASE_NS::string SceneManager::GuessIndexFilePath(BASE_NS::string_view uri)
211 {
212     auto pos = uri.find_last_of('.');
213     return uri.substr(0, pos) + ".res";
214 }
215 
GuessProjectPath(BASE_NS::string_view uri)216 BASE_NS::string SceneManager::GuessProjectPath(BASE_NS::string_view uri)
217 {
218     const auto secondToLastSlashPos = uri.find_last_of('/', uri.find_last_of('/') - 1);
219     return BASE_NS::string { uri.substr(0, secondToLastSlashPos) };
220 }
221 
222 SCENE_END_NAMESPACE()
223