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 "asset_loader.h"
17
18 #include <algorithm>
19
20 #include <3d/ecs/components/animation_component.h>
21 #include <3d/ecs/components/animation_track_component.h>
22 #include <3d/ecs/components/name_component.h>
23 #include <3d/ecs/components/node_component.h>
24 #include <3d/ecs/components/render_handle_component.h>
25 #include <3d/ecs/components/uri_component.h>
26 #include <3d/ecs/systems/intf_animation_system.h>
27 #include <3d/ecs/systems/intf_node_system.h>
28 #include <base/containers/fixed_string.h>
29 #include <base/containers/vector.h>
30 #include <core/image/intf_image_loader_manager.h>
31 #include <core/intf_engine.h>
32 #include <core/io/intf_file_manager.h>
33 #include <render/device/intf_gpu_resource_manager.h>
34 #include <render/intf_render_context.h>
35
36 #include "asset_manager.h"
37 #include "asset_migration.h"
38
39 using namespace BASE_NS;
40 using namespace CORE_NS;
41 using namespace RENDER_NS;
42 using namespace CORE3D_NS;
43
44 SCENE_BEGIN_NAMESPACE()
45
46 namespace {
47
48 struct IoUtil {
LoadTextFile__anon5335af650110::IoUtil49 static bool LoadTextFile(string_view fileUri, string& fileContentsOut, IFileManager& fileManager)
50 {
51 auto file = fileManager.OpenFile(fileUri);
52 if (file) {
53 const size_t length = file->GetLength();
54 fileContentsOut.resize(length);
55 return file->Read(fileContentsOut.data(), length) == length;
56 }
57 return false;
58 }
59 };
60
61 // Add a node and all of its children recursively to a collection
AddNodeToCollectionRecursive(IEntityCollection & ec,ISceneNode & node,string_view path)62 void AddNodeToCollectionRecursive(IEntityCollection& ec, ISceneNode& node, string_view path)
63 {
64 const auto entity = node.GetEntity();
65 const auto currentPath = path + node.GetName();
66
67 EntityReference ref = ec.GetEcs().GetEntityManager().GetReferenceCounted(entity);
68 ec.AddEntity(ref);
69 ec.SetId(currentPath, ref);
70
71 const auto childBasePath = currentPath + "/";
72 for (auto* child : node.GetChildren()) {
73 AddNodeToCollectionRecursive(ec, *child, childBasePath);
74 }
75 }
76 } // namespace
77
78 // A class that executes asset load operation over multiple frames.
79 class AssetLoader final : public IAssetLoader, private IAssetLoader::IListener, private IGLTF2Importer::Listener {
80 public:
AssetLoader(AssetManager & assetManager,IRenderContext & renderContext,IGraphicsContext & graphicsContext,IEntityCollection & ec,string_view src,string_view contextUri)81 AssetLoader(AssetManager& assetManager, IRenderContext& renderContext, IGraphicsContext& graphicsContext,
82 IEntityCollection& ec, string_view src, string_view contextUri)
83 : assetManager_(assetManager), renderContext_(renderContext), graphicsContext_(graphicsContext),
84 ecs_(ec.GetEcs()), ec_(ec), src_(src), contextUri_(contextUri),
85 fileManager_(renderContext_.GetEngine().GetFileManager())
86 {}
87
~AssetLoader()88 ~AssetLoader() override
89 {
90 Cancel();
91 }
92
GetEntityCollection() const93 IEntityCollection& GetEntityCollection() const override
94 {
95 return ec_;
96 }
97
GetSrc() const98 string GetSrc() const override
99 {
100 return src_;
101 }
102
GetContextUri() const103 string GetContextUri() const override
104 {
105 return contextUri_;
106 }
107
GetUri() const108 string GetUri() const override
109 {
110 return PathUtil::ResolveUri(contextUri_, src_, true);
111 }
112
AddListener(IAssetLoader::IListener & listener)113 void AddListener(IAssetLoader::IListener& listener) override
114 {
115 CORE_ASSERT(&listener);
116 listeners_.emplace_back(&listener);
117 }
118
RemoveListener(IAssetLoader::IListener & listener)119 void RemoveListener(IAssetLoader::IListener& listener) override
120 {
121 CORE_ASSERT(&listener);
122 for (size_t i = 0; i < listeners_.size(); ++i) {
123 if (&listener == listeners_[i]) {
124 listeners_.erase(listeners_.begin() + i);
125 return;
126 }
127 }
128
129 // trying to remove a non-existent listener.
130 CORE_ASSERT(true);
131 }
132
LoadAsset()133 void LoadAsset() override
134 {
135 async_ = false;
136 StartLoading();
137 }
138
LoadAssetAsync()139 void LoadAssetAsync() override
140 {
141 async_ = true;
142 StartLoading();
143 }
144
GetNextDependency() const145 IAssetLoader* GetNextDependency() const
146 {
147 if (dependencies_.empty()) {
148 return nullptr;
149 }
150 for (const auto& dep : dependencies_) {
151 if (dep && !dep->IsCompleted()) {
152 return dep.get();
153 }
154 }
155 return nullptr;
156 }
157
158 //
159 // From IAssetLoader::Listener
160 //
OnLoadFinished(const IAssetLoader & loader)161 void OnLoadFinished(const IAssetLoader& loader) override
162 {
163 // This will be called when a dependency has finished loading.
164
165 // Hide cached data.
166 loader.GetEntityCollection().SetActive(false);
167
168 auto* dep = GetNextDependency();
169 if (dep) {
170 // Load next dependency.
171 ContinueLoading();
172 } else {
173 // There are no more dependencies. Try loading the main asset again but now with the dependencies loaded.
174 StartLoading();
175 }
176 }
177
178 //
179 // From CORE_NS::IGLTF2Importer::Listener
180 //
OnImportStarted()181 void OnImportStarted() override {}
OnImportFinished()182 void OnImportFinished() override
183 {
184 GltfImportFinished();
185 if (async_) {
186 ContinueLoading();
187 }
188 }
OnImportProgressed(size_t taskIndex,size_t taskCount)189 void OnImportProgressed(size_t taskIndex, size_t taskCount) override {}
190
Execute(uint32_t timeBudget)191 bool Execute(uint32_t timeBudget) override
192 {
193 // NOTE: Currently actually only one import will be active at a time so we don't need to worry about the time
194 // budget.
195 bool done = true;
196
197 for (auto& dep : dependencies_) {
198 if (dep) {
199 done &= dep->Execute(timeBudget);
200 }
201 }
202 if (importer_) {
203 done &= importer_->Execute(timeBudget);
204 }
205 return done;
206 }
207
Cancel()208 void Cancel() override
209 {
210 cancelled_ = true;
211 for (auto& dep : dependencies_) {
212 if (dep) {
213 dep->Cancel();
214 }
215 }
216
217 if (importer_) {
218 importer_->Cancel();
219 importer_.reset();
220 }
221 }
222
IsCompleted() const223 bool IsCompleted() const override
224 {
225 return done_;
226 }
227
GetResult() const228 Result GetResult() const override
229 {
230 return result_;
231 }
232
Destroy()233 void Destroy() override
234 {
235 delete this;
236 }
237
238 private:
StartLoading()239 void StartLoading()
240 {
241 if (src_.empty()) {
242 result_ = { false, "Ivalid uri" };
243 done_ = true;
244 return;
245 }
246
247 const auto resolvedUri = GetUri();
248
249 const auto resolvedFile = PathUtil::ResolveUri(contextUri_, src_, false);
250 const auto ext = PathUtil::GetExtension(resolvedFile);
251 const auto type = assetManager_.GetExtensionType(ext);
252 // TODO: Separate different loaders and make it possible to register more.
253 switch (type) {
254 case IAssetManager::ExtensionType::COLLECTION: {
255 ec_.SetType("entitycollection");
256 if (LoadJsonEntityCollection()) {
257 MigrateAnimation(ec_);
258 }
259 break;
260 }
261
262 case IAssetManager::ExtensionType::SCENE: {
263 ec_.SetType("scene");
264 if (LoadJsonEntityCollection()) {
265 MigrateAnimation(ec_);
266 }
267 break;
268 }
269
270 case IAssetManager::ExtensionType::ANIMATION: {
271 ec_.SetType("animation");
272 if (LoadJsonEntityCollection()) {
273 MigrateAnimation(ec_);
274 }
275 break;
276 }
277
278 case IAssetManager::ExtensionType::MATERIAL: {
279 ec_.SetType("material");
280 LoadJsonEntityCollection();
281 break;
282 }
283
284 case IAssetManager::ExtensionType::GLTF:
285 case IAssetManager::ExtensionType::GLB: {
286 ec_.SetType("gltf");
287 LoadGltfEntityCollection();
288 break;
289 }
290
291 case IAssetManager::ExtensionType::JPG:
292 case IAssetManager::ExtensionType::PNG:
293 case IAssetManager::ExtensionType::KTX: {
294 ec_.SetType("image");
295 LoadImageEntityCollection();
296 break;
297 }
298
299 case IAssetManager::ExtensionType::NOT_SUPPORTED:
300 default: {
301 CORE_LOG_E("Unsupported asset format: '%s'", src_.c_str());
302 result_ = { false, "Unsupported asset format" };
303 done_ = true;
304 break;
305 }
306 }
307
308 ContinueLoading();
309 }
310
ContinueLoading()311 void ContinueLoading()
312 {
313 if (done_) {
314 ec_.MarkModified(false, true);
315
316 for (auto* listener : listeners_) {
317 listener->OnLoadFinished(*this);
318 }
319 return;
320 }
321
322 auto* dep = GetNextDependency();
323 if (dep) {
324 if (async_) {
325 dep->LoadAssetAsync();
326 } else {
327 dep->LoadAsset();
328 }
329 return;
330 }
331 }
332
CreateDummyEntity()333 void CreateDummyEntity()
334 {
335 // Create a dummy root entity as a placeholder for a missing asset.
336 auto dummyEntity = ecs_.GetEntityManager().CreateReferenceCounted();
337 // Note: adding a node component for now so it will be displayed in the hierarchy pane.
338 // This is wrong as the failed asset might not be a 3D node in reality. this could be removed after combining
339 // the Entity pane functionlity to the hierarchy pane and when there is better handling for missing references.
340 auto* nodeComponentManager = GetManager<INodeComponentManager>(ecs_);
341 CORE_ASSERT(nodeComponentManager);
342 if (nodeComponentManager) {
343 nodeComponentManager->Create(dummyEntity);
344 }
345 if (!GetUri().empty()) {
346 auto* nameComponentManager = GetManager<INameComponentManager>(ecs_);
347 CORE_ASSERT(nameComponentManager);
348 if (nameComponentManager) {
349 nameComponentManager->Create(dummyEntity);
350 nameComponentManager->Write(dummyEntity)->name = GetUri();
351 }
352 }
353 ec_.AddEntity(dummyEntity);
354 ec_.SetId("/", dummyEntity);
355 }
356
LoadJsonEntityCollection()357 bool LoadJsonEntityCollection()
358 {
359 const auto resolvedUri = GetUri();
360 const auto resolvedFile = PathUtil::ResolveUri(contextUri_, src_, false);
361
362 const auto params = PathUtil::GetUriParameters(src_);
363 const auto targetParam = params.find("target");
364 string entityTarget = (targetParam != params.cend()) ? targetParam->second : "";
365 auto targetEntity = ec_.GetEntity(entityTarget);
366 size_t subcollectionCount = ec_.GetSubCollectionCount();
367 while (!targetEntity && subcollectionCount) {
368 targetEntity = ec_.GetSubCollection(--subcollectionCount)->GetEntity(entityTarget);
369 }
370
371 string textIn;
372 if (IoUtil::LoadTextFile(resolvedFile, textIn, fileManager_)) {
373 // TODO: Check file version here.
374
375 auto json = json::parse(textIn.data());
376 if (!json) {
377 CORE_LOG_E("Parsing json failed: '%s':\n%s", resolvedUri.c_str(), textIn.c_str());
378 } else {
379 if (!dependencies_.empty()) {
380 // There were dependencies loaded earlier and now we want to load the actual asset.
381 // No dependencies to load. Just load the entity collection itself and this loading is done.
382 auto result = assetManager_.GetEcsSerializer().ReadEntityCollection(ec_, json, resolvedUri);
383 done_ = true;
384 return result;
385 }
386
387 vector<IEcsSerializer::ExternalCollection> dependencies;
388 assetManager_.GetEcsSerializer().GatherExternalCollections(json, resolvedUri, dependencies);
389
390 for (const auto& dep : dependencies) {
391 BASE_NS::string patchedDepUri = dep.src;
392 if (!entityTarget.empty())
393 patchedDepUri.append("?target=").append(entityTarget);
394 if (!assetManager_.IsCachedCollection(patchedDepUri, dep.contextUri)) {
395 auto* cacheEc =
396 assetManager_.CreateCachedCollection(ec_.GetEcs(), patchedDepUri, dep.contextUri);
397 dependencies_.emplace_back(
398 assetManager_.CreateAssetLoader(*cacheEc, patchedDepUri, dep.contextUri));
399 auto& dep = *dependencies_.back();
400 dep.AddListener(*this);
401 }
402 }
403
404 if (GetNextDependency() == nullptr) {
405 // No dependencies to load. Just load the entity collection itself and this loading is done.
406 auto result = assetManager_.GetEcsSerializer().ReadEntityCollection(ec_, json, resolvedUri);
407 done_ = true;
408 return result;
409 }
410
411 // There are dependencies that need to be parsed in a next step.
412 return true;
413 }
414 }
415
416 CreateDummyEntity();
417 result_ = { false, "collection loading failed." };
418 done_ = true;
419 return false;
420 }
421
LoadGltfEntityCollection()422 void LoadGltfEntityCollection()
423 {
424 const auto resolvedFile = PathUtil::ResolveUri(contextUri_, src_, false);
425
426 auto& gltf = graphicsContext_.GetGltf();
427
428 loadResult_ = gltf.LoadGLTF(resolvedFile);
429 if (!loadResult_.success) {
430 CORE_LOG_E("Loaded '%s' with errors:\n%s", resolvedFile.c_str(), loadResult_.error.c_str());
431 }
432 if (!loadResult_.data) {
433 CreateDummyEntity();
434 result_ = { false, "glTF load failed." };
435 done_ = true;
436 return;
437 }
438
439 importer_ = gltf.CreateGLTF2Importer(ec_.GetEcs());
440
441 if (async_) {
442 importer_->ImportGLTFAsync(*loadResult_.data, CORE_GLTF_IMPORT_RESOURCE_FLAG_BITS_ALL, this);
443 } else {
444 importer_->ImportGLTF(*loadResult_.data, CORE_GLTF_IMPORT_RESOURCE_FLAG_BITS_ALL);
445 OnImportFinished();
446 }
447 }
448
ImportSceneFromGltf(EntityReference root)449 Entity ImportSceneFromGltf(EntityReference root)
450 {
451 auto& gltf = graphicsContext_.GetGltf();
452
453 // Import the default scene, or first scene if there is no default scene set.
454 size_t sceneIndex = loadResult_.data->GetDefaultSceneIndex();
455 if (sceneIndex == CORE_GLTF_INVALID_INDEX && loadResult_.data->GetSceneCount() > 0) {
456 // Use first scene.
457 sceneIndex = 0;
458 }
459
460 const CORE3D_NS::GLTFResourceData& resourceData = importer_->GetResult().data;
461 Entity importedSceneEntity {};
462 if (sceneIndex != CORE_GLTF_INVALID_INDEX) {
463 GltfSceneImportFlags importFlags = CORE_GLTF_IMPORT_COMPONENT_FLAG_BITS_ALL;
464 importedSceneEntity =
465 gltf.ImportGltfScene(sceneIndex, *loadResult_.data, resourceData, ecs_, root, importFlags);
466 }
467 if (!EntityUtil::IsValid(importedSceneEntity)) {
468 return {};
469 }
470
471 // make sure everyone has a name (rest of the system fails if there are unnamed nodes)
472 auto* nodeComponentManager = GetManager<INodeComponentManager>(ecs_);
473 for (auto i = 0; i < nodeComponentManager->GetComponentCount(); i++) {
474 auto ent = nodeComponentManager->GetEntity(i);
475 auto* ncm = GetManager<INameComponentManager>(ecs_);
476 bool setName = true;
477 if (ncm->HasComponent(ent)) {
478 // check if empty
479 auto name = ncm->Get(ent).name;
480 if (!name.empty()) {
481 // it has a non empty name..
482 setName = false;
483 }
484 }
485 if (setName) {
486 // no name component, so create one create one.
487 char buf[256];
488 // possibly not unique enough. user created names could conflict.
489 sprintf(buf, "Unnamed Node %d", i);
490 ncm->Set(ent, { buf });
491 }
492 }
493
494 // Link animation tracks to targets
495 if (!resourceData.animations.empty()) {
496 INodeSystem* nodeSystem = GetSystem<INodeSystem>(ecs_);
497 if (auto animationRootNode = nodeSystem->GetNode(importedSceneEntity); animationRootNode) {
498 for (const auto& animationEntity : resourceData.animations) {
499 UpdateTrackTargets(animationEntity, animationRootNode);
500 }
501 }
502 }
503
504 return importedSceneEntity;
505 }
506
UpdateTrackTargets(Entity animationEntity,ISceneNode * node)507 void UpdateTrackTargets(Entity animationEntity, ISceneNode* node)
508 {
509 auto& nameManager_ = *GetManager<INameComponentManager>(ecs_);
510 auto animationManager = GetManager<IAnimationComponentManager>(ecs_);
511 auto animationTrackManager = GetManager<IAnimationTrackComponentManager>(ecs_);
512 auto& entityManager = ecs_.GetEntityManager();
513
514 if (const ScopedHandle<const AnimationComponent> animationData = animationManager->Read(animationEntity);
515 animationData) {
516 vector<Entity> targetEntities;
517 targetEntities.reserve(animationData->tracks.size());
518 std::transform(animationData->tracks.begin(), animationData->tracks.end(),
519 std::back_inserter(targetEntities), [&nameManager = nameManager_, &node](const Entity& trackEntity) {
520 if (auto nameHandle = nameManager.Read(trackEntity); nameHandle) {
521 if (auto targetNode = node->LookupNodeByPath(nameHandle->name); targetNode) {
522 return targetNode->GetEntity();
523 }
524 }
525 return Entity {};
526 });
527 if (animationData->tracks.size() == targetEntities.size()) {
528 auto targetIt = targetEntities.begin();
529 for (const auto& trackEntity : animationData->tracks) {
530 if (auto track = animationTrackManager->Write(trackEntity); track) {
531 if (track->target) {
532 SCENE_PLUGIN_VERBOSE_LOG("AnimationTrack %s already targetted",
533 to_hex(static_cast<const Entity&>(track->target).id).data());
534 }
535 track->target = entityManager.GetReferenceCounted(*targetIt);
536 }
537 ++targetIt;
538 }
539 }
540 }
541 }
542
GltfImportFinished()543 void GltfImportFinished()
544 {
545 auto* nodeSystem = GetSystem<INodeSystem>(ecs_);
546 CORE_ASSERT(nodeSystem);
547 auto* nodeComponentManager = GetManager<INodeComponentManager>(ecs_);
548 CORE_ASSERT(nodeComponentManager);
549 if (!nodeSystem || !nodeComponentManager) {
550 result_ = { false, {} };
551 done_ = true;
552 return;
553 }
554
555 const auto params = PathUtil::GetUriParameters(src_);
556 const auto rootParam = params.find("root");
557 string entityPath = (rootParam != params.cend()) ? rootParam->second : "";
558
559 const auto targetParam = params.find("target");
560 string entityTarget = (targetParam != params.cend()) ? targetParam->second : "";
561 auto targetEntity = ec_.GetEntity(entityTarget);
562 size_t subcollectionCount = ec_.GetSubCollectionCount();
563 while (!targetEntity && subcollectionCount) {
564 targetEntity = ec_.GetSubCollection(--subcollectionCount)->GetEntity(entityTarget);
565 }
566
567 if (importer_) {
568 auto const gltfImportResult = importer_->GetResult();
569 if (!gltfImportResult.success) {
570 CORE_LOG_E("Importing of '%s' failed: %s", GetUri().c_str(), gltfImportResult.error.c_str());
571 CreateDummyEntity();
572 result_ = { false, "glTF import failed." };
573 done_ = true;
574 return;
575
576 } else if (cancelled_) {
577 CORE_LOG_V("Importing of '%s' cancelled", GetUri().c_str());
578 CreateDummyEntity();
579 result_ = { false, "glTF import cancelled." };
580 done_ = true;
581 return;
582 }
583
584 // Loading and importing of glTF was done successfully. Fill the collection with all the gltf entities.
585 const auto originalRootEntity = ImportSceneFromGltf(targetEntity);
586 auto* originalRootNode = nodeSystem->GetNode(originalRootEntity);
587
588 // It's possible to only add some specific node from the gltf.
589 auto* loadNode = originalRootNode;
590 if (!entityPath.empty()) {
591 loadNode = originalRootNode->LookupNodeByPath(entityPath);
592 if (!loadNode || loadNode->GetEntity() == Entity {}) {
593 CORE_LOG_E("Entity '%s' not found from '%s'", entityPath.c_str(), GetUri().c_str());
594 }
595 }
596
597 if (!loadNode || loadNode->GetEntity() == Entity {}) {
598 CreateDummyEntity();
599 result_ = { false, "Ivalid uri" };
600 done_ = true;
601 return;
602 }
603
604 Entity entity = loadNode->GetEntity();
605 if (entity != Entity {}) {
606 EntityReference ref = ecs_.GetEntityManager().GetReferenceCounted(entity);
607 ec_.AddEntity(ref);
608 ec_.SetId("/", ref);
609
610 if (!targetEntity) {
611 loadNode->SetParent(nodeSystem->GetRootNode());
612 }
613 for (auto* child : loadNode->GetChildren()) {
614 AddNodeToCollectionRecursive(ec_, *child, "/");
615 }
616 }
617
618 // TODO: a little backwards to first create everything and then delete the extra.
619 if (entity != originalRootEntity) {
620 auto* oldRoot = nodeSystem->GetNode(originalRootEntity);
621 CORE_ASSERT(oldRoot);
622 if (oldRoot) {
623 nodeSystem->DestroyNode(*oldRoot);
624 }
625 }
626
627 // Add all resources in separate sub-collections. Not just 3D nodes.
628 {
629 const auto& importResult = importer_->GetResult();
630 ec_.AddSubCollection("images", {}).AddEntities(importResult.data.images);
631 ec_.AddSubCollection("materials", {}).AddEntities(importResult.data.materials);
632 ec_.AddSubCollection("meshes", {}).AddEntities(importResult.data.meshes);
633 ec_.AddSubCollection("skins", {}).AddEntities(importResult.data.skins);
634 ec_.AddSubCollection("animations", {}).AddEntities(importResult.data.animations);
635
636 // TODO: don't list duplicates
637 vector<EntityReference> animationTracks;
638 auto* acm = GetManager<IAnimationComponentManager>(ecs_);
639 if (acm) {
640 for (auto& entity : importResult.data.animations) {
641 if (auto handle = acm->Read(entity); handle) {
642 const auto& tracks = handle->tracks;
643 for (auto& entityRef : tracks) {
644 animationTracks.emplace_back(entityRef);
645 }
646 }
647 }
648 }
649 ec_.AddSubCollection("animationTracks", {}).AddEntities(animationTracks);
650 }
651
652 // Load finished successfully.
653 done_ = true;
654 }
655 }
656
LoadImageEntityCollection()657 bool LoadImageEntityCollection()
658 {
659 auto uri = GetUri();
660 auto imageHandle = assetManager_.GetEcsSerializer().LoadImageResource(uri);
661
662 // NOTE: Creating the entity even when the image load failed to load so we can detect a missing resource.
663 EntityReference entity = ecs_.GetEntityManager().CreateReferenceCounted();
664 ec_.AddEntity(entity);
665
666 auto* ncm = GetManager<INameComponentManager>(ecs_);
667 auto* ucm = GetManager<IUriComponentManager>(ecs_);
668 auto* gcm = GetManager<IRenderHandleComponentManager>(ecs_);
669 if (ncm && ucm && gcm) {
670 {
671 ncm->Create(entity);
672 auto nc = ncm->Get(entity);
673 nc.name = PathUtil::GetFilename(uri);
674 ec_.SetUniqueIdentifier(nc.name, entity);
675 ncm->Set(entity, nc);
676 }
677
678 {
679 // TODO: maybe need to save uri + contextUri
680 ucm->Create(entity);
681 auto uc = ucm->Get(entity);
682 uc.uri = uri;
683 ucm->Set(entity, uc);
684 }
685
686 gcm->Create(entity);
687 if (imageHandle) {
688 auto ic = gcm->Get(entity);
689 ic.reference = imageHandle;
690 gcm->Set(entity, ic);
691
692 done_ = true;
693 return true;
694 }
695 }
696
697 // NOTE: Always returning true as even when this fails a placeholder entity is created.
698 CORE_LOG_E("Error creating image '%s'", uri.c_str());
699 CreateDummyEntity();
700 result_ = { false, "Error creating image" };
701 done_ = true;
702 return true;
703 }
704
705 private:
706 AssetManager& assetManager_;
707 RENDER_NS::IRenderContext& renderContext_;
708 CORE3D_NS::IGraphicsContext& graphicsContext_;
709
710 CORE_NS::IEcs& ecs_;
711 IEntityCollection& ec_;
712 BASE_NS::string src_;
713 BASE_NS::string contextUri_;
714
715 IAssetLoader::Result result_ {};
716
717 vector<AssetLoader::Ptr> dependencies_;
718
719 bool done_ { false };
720 bool cancelled_ { false };
721 bool async_ { false };
722
723 GLTFLoadResult loadResult_ {};
724 IGLTF2Importer::Ptr importer_ {};
725
726 vector<IAssetLoader::IListener*> listeners_;
727 IFileManager& fileManager_;
728 };
729
CreateAssetLoader(AssetManager & assetManager,IRenderContext & renderContext,IGraphicsContext & graphicsContext,IEntityCollection & ec,string_view src,string_view contextUri)730 IAssetLoader::Ptr CreateAssetLoader(AssetManager& assetManager, IRenderContext& renderContext,
731 IGraphicsContext& graphicsContext, IEntityCollection& ec, string_view src, string_view contextUri)
732 {
733 return IAssetLoader::Ptr { new AssetLoader(assetManager, renderContext, graphicsContext, ec, src, contextUri) };
734 }
735
736 SCENE_END_NAMESPACE()
737