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 39using namespace BASE_NS; 40using namespace CORE_NS; 41using namespace RENDER_NS; 42using namespace CORE3D_NS; 43 44SCENE_BEGIN_NAMESPACE() 45 46namespace { 47 48struct IoUtil { 49 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 62void 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. 79class AssetLoader final : public IAssetLoader, private IAssetLoader::IListener, private IGLTF2Importer::Listener { 80public: 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 88 ~AssetLoader() override 89 { 90 Cancel(); 91 } 92 93 IEntityCollection& GetEntityCollection() const override 94 { 95 return ec_; 96 } 97 98 string GetSrc() const override 99 { 100 return src_; 101 } 102 103 string GetContextUri() const override 104 { 105 return contextUri_; 106 } 107 108 string GetUri() const override 109 { 110 return PathUtil::ResolveUri(contextUri_, src_, true); 111 } 112 113 void AddListener(IAssetLoader::IListener& listener) override 114 { 115 CORE_ASSERT(&listener); 116 listeners_.emplace_back(&listener); 117 } 118 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 133 void LoadAsset() override 134 { 135 async_ = false; 136 StartLoading(); 137 } 138 139 void LoadAssetAsync() override 140 { 141 async_ = true; 142 StartLoading(); 143 } 144 145 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 // 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 // 181 void OnImportStarted() override {} 182 void OnImportFinished() override 183 { 184 GltfImportFinished(); 185 if (async_) { 186 ContinueLoading(); 187 } 188 } 189 void OnImportProgressed(size_t taskIndex, size_t taskCount) override {} 190 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 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 223 bool IsCompleted() const override 224 { 225 return done_; 226 } 227 228 Result GetResult() const override 229 { 230 return result_; 231 } 232 233 void Destroy() override 234 { 235 delete this; 236 } 237 238private: 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 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 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 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 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 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 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 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 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 705private: 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 730IAssetLoader::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 736SCENE_END_NAMESPACE() 737