• 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 #include "scene_importer.h"
16 
17 #include <charconv>
18 #include <scene/api/external_node.h>
19 #include <scene/ext/intf_component.h>
20 #include <scene/ext/intf_internal_scene.h>
21 #include <scene/ext/util.h>
22 #include <scene/interface/intf_application_context.h>
23 #include <scene/interface/intf_external_node.h>
24 #include <scene/interface/intf_mesh.h>
25 #include <scene/interface/intf_node_import.h>
26 #include <scene/interface/intf_scene_manager.h>
27 
28 #include <meta/api/metadata_util.h>
29 #include <meta/interface/resource/intf_resource.h>
30 #include <meta/interface/serialization/intf_importer.h>
31 
32 #include "scene_ser.h"
33 #include "util.h"
34 
SCENE_BEGIN_NAMESPACE()35 SCENE_BEGIN_NAMESPACE()
36 
37 static BASE_NS::string_view::size_type FindFirstSeparator(BASE_NS::string_view path)
38 {
39     BASE_NS::string_view::size_type res {};
40 
41     while (res != BASE_NS::string_view::npos) {
42         res = path.find_first_of(".!/", res);
43         if (res != BASE_NS::string_view::npos) {
44             if (res == 0 || path[res - 1] != ESCAPE_SER_CHAR) {
45                 return res;
46             }
47             ++res;
48         }
49     }
50     return res;
51 }
52 
GetNameAndSub(BASE_NS::string_view element,size_t & index)53 BASE_NS::string GetNameAndSub(BASE_NS::string_view element, size_t& index)
54 {
55     if (element.size() > 3 && element.back() == ']' && element[element.size() - 2] != ESCAPE_SER_CHAR) {
56         auto pos = element.find_last_of('[');
57         if (pos != BASE_NS::string_view::npos) {
58             std::from_chars_result res =
59                 std::from_chars(element.data() + pos + 1, element.data() + element.size() - 1, index);
60             if (res.ec != std::errc {} || res.ptr != element.data() + element.size() - 1) {
61                 CORE_LOG_W("Invalid index");
62             }
63             element = element.substr(0, pos);
64         }
65     }
66     return UnescapeSerName(element);
67 }
68 
GetIObjectFromProperty(const META_NS::IProperty::Ptr & p,size_t index)69 static META_NS::IObject::Ptr GetIObjectFromProperty(const META_NS::IProperty::Ptr& p, size_t index)
70 {
71     META_NS::IObject::Ptr res;
72     if (index != -1) {
73         if (META_NS::ArrayPropertyLock arr { p }) {
74             if (index < arr->GetSize()) {
75                 res = interface_pointer_cast<META_NS::IObject>(META_NS::GetPointer(arr->GetAnyAt(index)));
76             } else {
77                 CORE_LOG_W("Index out of bounds: %s [%zu]", p ? p->GetName().c_str() : "unknown", index);
78             }
79         } else {
80             CORE_LOG_W("Trying to index non-array property: %s", p ? p->GetName().c_str() : "unknown");
81         }
82     } else {
83         if (auto obj = META_NS::GetPointer(p)) {
84             res = interface_pointer_cast<META_NS::IObject>(obj);
85         }
86     }
87     return res;
88 }
89 
FindObject(META_NS::IObject & obj,BASE_NS::string_view path)90 static META_NS::IObject::Ptr FindObject(META_NS::IObject& obj, BASE_NS::string_view path)
91 {
92     if (path.empty()) {
93         return obj.GetSelf();
94     }
95     char pre = '/';
96     if (path[0] == '!' || path[0] == '.' || path[0] == '/') {
97         pre = path[0];
98         path.remove_prefix(1);
99     }
100     auto pos = FindFirstSeparator(path);
101     auto element = path.substr(0, pos);
102     std::size_t index = -1;
103     BASE_NS::string name = GetNameAndSub(element, index);
104     path.remove_prefix(element.size());
105     META_NS::IObject::Ptr child;
106     if (pre == '!') {
107         if (auto att = interface_cast<META_NS::IAttach>(&obj)) {
108             auto attcont = att->GetAttachmentContainer(true);
109             child = interface_pointer_cast<META_NS::IObject>(
110                 attcont->FindAny<IComponent>(name, META_NS::TraversalType::NO_HIERARCHY));
111             if (!child) {
112                 CORE_LOG_W("No such attachment for object [%s]", name.c_str());
113             }
114         }
115     } else if (pre == '.') {
116         if (auto prop = interface_cast<META_NS::IMetadata>(&obj)) {
117             auto p = prop->GetProperty(name);
118             child = interface_pointer_cast<META_NS::IObject>(p);
119             if (!p) {
120                 CORE_LOG_W("No such property for object [%s]", name.c_str());
121             }
122             if (!path.empty()) {
123                 // if the path is not empty we try to take the object in the property to continue
124                 child = GetIObjectFromProperty(p, index);
125             }
126         }
127 
128     } else if (pre == '/') {
129         if (auto cont = interface_cast<META_NS::IContainer>(&obj)) {
130             child = cont->FindByName(name);
131             if (!child) {
132                 CORE_LOG_W("No such child for object [%s]", name.c_str());
133             }
134         }
135     }
136     if (child && !path.empty()) {
137         child = FindObject(*child, path);
138     }
139     return child;
140 }
141 
AddFlatProperty(const META_NS::IProperty::ConstPtr & p,META_NS::IObject & parent)142 static void AddFlatProperty(const META_NS::IProperty::ConstPtr& p, META_NS::IObject& parent)
143 {
144     if (auto target = interface_pointer_cast<META_NS::IProperty>(FindObject(parent, p->GetName()))) {
145         META_NS::CopyValue(p, *target);
146     } else {
147         CORE_LOG_W("Failed to resolve property [%s]", p->GetName().c_str());
148     }
149 }
150 
AddFlatProperties(const META_NS::IMetadata & in,META_NS::IObject & parent)151 void AddFlatProperties(const META_NS::IMetadata& in, META_NS::IObject& parent)
152 {
153     for (auto&& p : in.GetProperties()) {
154         AddFlatProperty(p, parent);
155     }
156 }
157 
AddFlatAttachments(const ISceneExternalNodeSer & in,META_NS::IObject & parent)158 static void AddFlatAttachments(const ISceneExternalNodeSer& in, META_NS::IObject& parent)
159 {
160     for (auto&& p : in.GetAttachments()) {
161         if (auto target = interface_pointer_cast<META_NS::IAttach>(FindObject(parent, p->GetPath()))) {
162             if (!target->Attach(p->GetAttachment())) {
163                 CORE_LOG_W("Failed to attach");
164             }
165         } else {
166             CORE_LOG_W("Failed to find object to attach to [%s]", p->GetPath().c_str());
167         }
168     }
169 }
170 
SetAttachments(const ISceneNodeSer & in,META_NS::IAttach & out)171 static void SetAttachments(const ISceneNodeSer& in, META_NS::IAttach& out)
172 {
173     for (auto&& v : in.GetAttachments()) {
174         out.Attach(v);
175     }
176 }
177 
CopyObjectValues(const ISceneNodeSer & in,META_NS::IObject & out)178 static void CopyObjectValues(const ISceneNodeSer& in, META_NS::IObject& out)
179 {
180     if (auto meta = interface_cast<META_NS::IMetadata>(&in)) {
181         AddFlatProperties(*meta, out);
182     }
183     if (auto att = interface_cast<META_NS::IAttach>(&out)) {
184         SetAttachments(in, *att);
185     }
186 }
187 
ConstructNode(const ISceneNodeSer & ser,INode & parent)188 static INode::Ptr ConstructNode(const ISceneNodeSer& ser, INode& parent)
189 {
190     auto& scene = *parent.GetScene();
191     INode::Ptr res = scene.CreateNode(parent.GetPath().GetResult() + "/" + ser.GetName(), ser.GetId()).GetResult();
192     if (auto obj = interface_cast<META_NS::IObject>(res)) {
193         CopyObjectValues(ser, *obj);
194     }
195     return res;
196 }
197 
LoadExternalNode(const ISceneExternalNodeSer & in,INode & parent)198 static void LoadExternalNode(const ISceneExternalNodeSer& in, INode& parent)
199 {
200     if (auto context = parent.GetScene()->GetInternalScene()->GetContext()) {
201         if (!context->GetResources()) {
202             CORE_LOG_W("Failed to import scene resource to scene, no resource manager set");
203             return;
204         }
205         if (auto res = interface_pointer_cast<IScene>(context->GetResources()->GetResource(in.GetResourceId()))) {
206             if (auto node = interface_pointer_cast<META_NS::IObject>(AddExternalNode(parent, res, in.GetName()))) {
207                 if (auto m = interface_cast<META_NS::IMetadata>(&in)) {
208                     AddFlatProperties(*m, *node);
209                     AddFlatAttachments(in, *node);
210                 }
211             } else {
212                 CORE_LOG_W("Failed to import scene resource to scene");
213             }
214         } else {
215             CORE_LOG_W("No resource/invalid resource: %s", in.GetResourceId().ToString().c_str());
216         }
217     }
218 }
219 
BuildSceneNodeHierarchy(const ISceneNodeSer & data,INode & node)220 static void BuildSceneNodeHierarchy(const ISceneNodeSer& data, INode& node)
221 {
222     META_NS::Internal::ConstIterate(
223         interface_cast<META_NS::IIterable>(&data),
224         [&](META_NS::IObject::Ptr obj) {
225             if (auto ext = interface_cast<ISceneExternalNodeSer>(obj)) {
226                 LoadExternalNode(*ext, node);
227             } else if (auto ser = interface_cast<ISceneNodeSer>(obj)) {
228                 if (auto n = ConstructNode(*ser, node)) {
229                     BuildSceneNodeHierarchy(*ser, *n);
230                 }
231             }
232             return true;
233         },
234         META_NS::IterateStrategy { META_NS::TraversalType::NO_HIERARCHY });
235 }
236 
BuildSceneHierarchy(const IScene::Ptr & scene,const ISceneNodeSer & data)237 static IScene::Ptr BuildSceneHierarchy(const IScene::Ptr& scene, const ISceneNodeSer& data)
238 {
239     if (auto obj = interface_cast<META_NS::IObject>(scene)) {
240         CopyObjectValues(data, *obj);
241     }
242     if (auto i = interface_cast<META_NS::IContainer>(&data)) {
243         auto nodes = i->GetAll();
244         if (nodes.empty()) {
245             // should have root node at least
246             return nullptr;
247         }
248         if (nodes.size() != 1) {
249             CORE_LOG_W("multiple root nodes?!");
250         }
251         auto rootdata = interface_cast<ISceneNodeSer>(nodes.front());
252         if (!rootdata) {
253             CORE_LOG_W("invalid root node");
254             return nullptr;
255         }
256         auto root = scene->GetRootNode().GetResult();
257         if (auto obj = interface_cast<META_NS::IObject>(root)) {
258             CopyObjectValues(*rootdata, *obj);
259             BuildSceneNodeHierarchy(*rootdata, *root);
260         }
261         return scene;
262     }
263     return nullptr;
264 }
265 
BuildSceneHierarchy(const INode::Ptr & node,const ISceneNodeSer & data)266 static INode::Ptr BuildSceneHierarchy(const INode::Ptr& node, const ISceneNodeSer& data)
267 {
268     auto n = ConstructNode(data, *node);
269     if (n) {
270         BuildSceneNodeHierarchy(data, *n);
271     }
272     return n;
273 }
274 
Build(const META_NS::IMetadata::Ptr & d)275 bool SceneImporter::Build(const META_NS::IMetadata::Ptr& d)
276 {
277     bool res = Super::Build(d);
278     if (res) {
279         auto context = GetInterfaceBuildArg<IRenderContext>(d, "RenderContext");
280         if (!context) {
281             CORE_LOG_E("Invalid arguments to construct SceneImporter");
282             return false;
283         }
284         opts_ = GetBuildArg<SceneOptions>(d, "Options");
285         context_ = context;
286     }
287     return res;
288 }
289 
ImportScene(CORE_NS::IFile & in)290 IScene::Ptr SceneImporter::ImportScene(CORE_NS::IFile& in)
291 {
292     IScene::Ptr res;
293     auto importer = META_NS::GetObjectRegistry().Create<META_NS::IFileImporter>(META_NS::ClassId::JsonImporter);
294     auto context = context_.lock();
295     if (context) {
296         importer->SetResourceManager(context->GetResources());
297     }
298     auto md = CreateRenderContextArg(context);
299     if (md) {
300         md->AddProperty(META_NS::ConstructProperty<SceneOptions>("Options", opts_));
301     }
302     if (auto m = GetSceneManager(context)) {
303         if (auto scene = m->CreateScene().GetResult()) {
304             importer->SetUserContext(interface_pointer_cast<META_NS::IObject>(scene));
305             if (auto obj = importer->Import(in)) {
306                 if (auto i = interface_cast<ISceneNodeSer>(obj)) {
307                     res = BuildSceneHierarchy(scene, *i);
308                 }
309             }
310         }
311     }
312     return res;
313 }
ImportNode(CORE_NS::IFile & in,const INode::Ptr & parent)314 INode::Ptr SceneImporter::ImportNode(CORE_NS::IFile& in, const INode::Ptr& parent)
315 {
316     INode::Ptr res;
317     auto importer = META_NS::GetObjectRegistry().Create<META_NS::IFileImporter>(META_NS::ClassId::JsonImporter);
318     auto context = context_.lock();
319     if (context) {
320         importer->SetResourceManager(context->GetResources());
321     }
322     importer->SetUserContext(interface_pointer_cast<META_NS::IObject>(parent->GetScene()));
323     if (auto obj = importer->Import(in)) {
324         if (auto i = interface_cast<ISceneNodeSer>(obj)) {
325             res = BuildSceneHierarchy(parent, *i);
326         }
327     }
328     return res;
329 }
330 
331 SCENE_END_NAMESPACE()
332