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