SceneBuilderWorker.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Component/ComponentApplicationBus.h>
  9. #include <AzCore/IO/FileIO.h>
  10. #include <AzCore/IO/SystemFile.h>
  11. #include <AzCore/Serialization/Utils.h>
  12. #include <SceneAPI/SceneCore/DataTypes/IGraphObject.h>
  13. #include <AzCore/Serialization/SerializeContext.h>
  14. #include <AzCore/std/containers/set.h>
  15. #include <AzCore/Utils/Utils.h>
  16. #include <AzFramework/Application/Application.h>
  17. #include <AzFramework/StringFunc/StringFunc.h>
  18. #include <AzToolsFramework/Debug/TraceContext.h>
  19. #include <SceneAPI/SceneCore/Components/ExportingComponent.h>
  20. #include <SceneAPI/SceneCore/Components/GenerationComponent.h>
  21. #include <SceneAPI/SceneCore/Components/LoadingComponent.h>
  22. #include <SceneAPI/SceneCore/Components/Utilities/EntityConstructor.h>
  23. #include <SceneAPI/SceneCore/Containers/Scene.h>
  24. #include <SceneAPI/SceneCore/Containers/SceneManifest.h>
  25. #include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
  26. #include <SceneAPI/SceneCore/Events/GenerateEventContext.h>
  27. #include <SceneAPI/SceneCore/Events/ExportProductList.h>
  28. #include <SceneAPI/SceneCore/Events/ExportEventContext.h>
  29. #include <SceneAPI/SceneCore/Events/SceneSerializationBus.h>
  30. #include <SceneAPI/SceneCore/Utilities/Reporting.h>
  31. #include <SceneAPI/SceneCore/SceneBuilderDependencyBus.h>
  32. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
  33. #include <SceneAPI/SceneCore/DataTypes/GraphData/IAnimationData.h>
  34. #include <AssetBuilderSDK/AssetBuilderSDK.h>
  35. #include <AzCore/Serialization/Json/JsonUtils.h>
  36. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  37. #include <rapidjson/pointer.h>
  38. #include <SceneBuilder/SceneBuilderWorker.h>
  39. #include <SceneBuilder/TraceMessageHook.h>
  40. namespace SceneBuilder
  41. {
  42. struct DebugOutputScope final
  43. {
  44. DebugOutputScope() = delete;
  45. DebugOutputScope(bool isDebug)
  46. : m_inDebug(isDebug)
  47. {
  48. if (AZ::SettingsRegistry::Get())
  49. {
  50. AZ::SettingsRegistry::Get()->Set(AZ::SceneAPI::Utilities::Key_AssetProcessorInDebugOutput, m_inDebug);
  51. }
  52. }
  53. ~DebugOutputScope()
  54. {
  55. if (AZ::SettingsRegistry::Get())
  56. {
  57. AZ::SettingsRegistry::Get()->Set(AZ::SceneAPI::Utilities::Key_AssetProcessorInDebugOutput, false);
  58. }
  59. }
  60. bool m_inDebug;
  61. };
  62. void SceneBuilderWorker::ShutDown()
  63. {
  64. m_isShuttingDown = true;
  65. }
  66. const char* SceneBuilderWorker::GetFingerprint() const
  67. {
  68. if (m_cachedFingerprint.empty())
  69. {
  70. // put them in an ORDERED set so that changing the reflection
  71. // or the gems loaded does not invalidate scene files due to order of reflection changing.
  72. AZStd::set<AZStd::string> fragments;
  73. AZ::SerializeContext* context = nullptr;
  74. AZ::ComponentApplicationBus::BroadcastResult(context, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
  75. if (context)
  76. {
  77. auto callback = [&fragments](const AZ::SerializeContext::ClassData* data, const AZ::Uuid& typeId)
  78. {
  79. AZ_UNUSED(typeId);
  80. fragments.insert(AZStd::string::format("[%s:v%i]", data->m_name, data->m_version));
  81. return true;
  82. };
  83. context->EnumerateDerived(callback, azrtti_typeid<AZ::SceneAPI::SceneCore::ExportingComponent>(), azrtti_typeid<AZ::SceneAPI::SceneCore::ExportingComponent>());
  84. context->EnumerateDerived(callback, azrtti_typeid<AZ::SceneAPI::SceneCore::GenerationComponent>(), azrtti_typeid<AZ::SceneAPI::SceneCore::GenerationComponent>());
  85. context->EnumerateDerived(callback, azrtti_typeid<AZ::SceneAPI::SceneCore::LoadingComponent>(), azrtti_typeid<AZ::SceneAPI::SceneCore::LoadingComponent>());
  86. }
  87. AZ::SceneAPI::SceneBuilderDependencyBus::Broadcast(&AZ::SceneAPI::SceneBuilderDependencyRequests::AddFingerprintInfo, fragments);
  88. for (const AZStd::string& element : fragments)
  89. {
  90. m_cachedFingerprint.append(element);
  91. }
  92. // A general catch all version fingerprint. Update this to force all FBX files to recompile.
  93. m_cachedFingerprint.append("Version 4");
  94. }
  95. return m_cachedFingerprint.c_str();
  96. }
  97. void SceneBuilderWorker::PopulateSourceDependencies(const AZStd::string& manifestJson, AZStd::vector<AssetBuilderSDK::SourceFileDependency>& sourceFileDependencies)
  98. {
  99. auto readJsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(manifestJson);
  100. AZStd::string errorMsg;
  101. if (!readJsonOutcome.IsSuccess())
  102. {
  103. // This may be an old format xml file. We don't have any dependencies in the old format so there's no point trying to parse an xml
  104. return;
  105. }
  106. rapidjson::Document document = readJsonOutcome.TakeValue();
  107. auto manifestObject = document.GetObject();
  108. auto valuesIterator = manifestObject.FindMember("values");
  109. if (valuesIterator == manifestObject.MemberEnd())
  110. {
  111. // a blank or unexpected JSON formated .assetinfo file
  112. return;
  113. }
  114. auto valuesArray = valuesIterator->value.GetArray();
  115. AZStd::vector<AZStd::string> paths;
  116. AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast(
  117. &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetManifestDependencyPaths, paths);
  118. for (const auto& value : valuesArray)
  119. {
  120. auto object = value.GetObject();
  121. for (const auto& path : paths)
  122. {
  123. rapidjson::Pointer pointer(path.c_str());
  124. auto dependencyValue = pointer.Get(object);
  125. if (dependencyValue && dependencyValue->IsString())
  126. {
  127. AZStd::string dependency = dependencyValue->GetString();
  128. sourceFileDependencies.emplace_back(AssetBuilderSDK::SourceFileDependency(
  129. dependency, AZ::Uuid::CreateNull(), AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Absolute));
  130. }
  131. }
  132. }
  133. }
  134. bool SceneBuilderWorker::ManifestDependencyCheck(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
  135. {
  136. AZStd::string manifestExtension;
  137. AZStd::string generatedManifestExtension;
  138. AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast(
  139. &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetManifestExtension, manifestExtension);
  140. AZ::SceneAPI::Events::AssetImportRequestBus::Broadcast(
  141. &AZ::SceneAPI::Events::AssetImportRequestBus::Events::GetGeneratedManifestExtension, generatedManifestExtension);
  142. if (manifestExtension.empty() || generatedManifestExtension.empty())
  143. {
  144. AZ_Error("SceneBuilderWorker", false, "Failed to get scene manifest extension");
  145. return false;
  146. }
  147. AZ::SettingsRegistryInterface::FixedValueString assetCacheRoot;
  148. AZ::SettingsRegistry::Get()->Get(assetCacheRoot, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
  149. auto manifestPath = (AZ::IO::Path(request.m_watchFolder) / (request.m_sourceFile + manifestExtension));
  150. auto generatedManifestPath = (AZ::IO::Path(assetCacheRoot) / (request.m_sourceFile + generatedManifestExtension));
  151. auto populateDependenciesFunc = [&response](const AZStd::string& path)
  152. {
  153. auto readFileOutcome = AZ::Utils::ReadFile(path, AZ::SceneAPI::Containers::SceneManifest::MaxSceneManifestFileSizeInBytes);
  154. if (!readFileOutcome.IsSuccess())
  155. {
  156. AZ_Error("SceneBuilderWorker", false, "%s", readFileOutcome.GetError().c_str());
  157. return;
  158. }
  159. PopulateSourceDependencies(readFileOutcome.TakeValue(), response.m_sourceFileDependencyList);
  160. };
  161. if (AZ::IO::FileIOBase::GetInstance()->Exists(manifestPath.Native().c_str()))
  162. {
  163. populateDependenciesFunc(manifestPath.Native());
  164. }
  165. else if (AZ::IO::FileIOBase::GetInstance()->Exists(generatedManifestPath.Native().c_str()))
  166. {
  167. populateDependenciesFunc(generatedManifestPath.Native());
  168. }
  169. return true;
  170. }
  171. void SceneBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
  172. {
  173. // Check for shutdown
  174. if (m_isShuttingDown)
  175. {
  176. response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
  177. return;
  178. }
  179. for (auto& enabledPlatform : request.m_enabledPlatforms)
  180. {
  181. AssetBuilderSDK::JobDescriptor descriptor;
  182. descriptor.m_jobKey = "Scene compilation";
  183. descriptor.SetPlatformIdentifier(enabledPlatform.m_identifier.c_str());
  184. descriptor.m_failOnError = true;
  185. descriptor.m_priority = 11; // these may control logic (actors and motions specifically)
  186. descriptor.m_additionalFingerprintInfo = GetFingerprint();
  187. AZ::SceneAPI::SceneBuilderDependencyBus::Broadcast(&AZ::SceneAPI::SceneBuilderDependencyRequests::ReportJobDependencies,
  188. descriptor.m_jobDependencyList, enabledPlatform.m_identifier.c_str());
  189. response.m_createJobOutputs.push_back(descriptor);
  190. }
  191. // Adding corresponding _wrinklemask folder as a source file dependency
  192. // This enables morph target assets to get references to the wrinkle masks
  193. // in the MorphTargetExporter, so they can be automatically applied at runtime
  194. AssetBuilderSDK::SourceFileDependency sourceFileDependencyInfo;
  195. AZStd::string relPath = request.m_sourceFile.c_str();
  196. AzFramework::StringFunc::Path::StripExtension(relPath);
  197. relPath += "_wrinklemasks";
  198. AzFramework::StringFunc::Path::AppendSeparator(relPath);
  199. relPath += "*_wrinklemask.*";
  200. sourceFileDependencyInfo.m_sourceFileDependencyPath = relPath;
  201. sourceFileDependencyInfo.m_sourceDependencyType = AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Wildcards;
  202. response.m_sourceFileDependencyList.push_back(sourceFileDependencyInfo);
  203. if (!ManifestDependencyCheck(request, response))
  204. {
  205. return;
  206. }
  207. response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
  208. }
  209. void SceneBuilderWorker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
  210. {
  211. using namespace AZ::SceneAPI::Containers;
  212. // Only used during processing to redirect trace printfs with an warning or error window to the appropriate reporting function.
  213. TraceMessageHook messageHook;
  214. // Load Scene graph and manifest from the provided path and then initialize them.
  215. if (m_isShuttingDown)
  216. {
  217. AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Loading scene was canceled.\n");
  218. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
  219. return;
  220. }
  221. auto debugFlagItr = request.m_jobDescription.m_jobParameters.find(AZ_CRC_CE("DebugFlag"));
  222. DebugOutputScope theDebugOutputScope(debugFlagItr != request.m_jobDescription.m_jobParameters.end() && debugFlagItr->second == "true");
  223. AZStd::shared_ptr<Scene> scene;
  224. if (!LoadScene(scene, request, response))
  225. {
  226. return;
  227. }
  228. // Run scene generation step to allow for runtime generation of SceneGraph objects
  229. if (m_isShuttingDown)
  230. {
  231. AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Generation of dynamic scene objects was canceled.\n");
  232. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
  233. return;
  234. }
  235. if (!GenerateScene(scene.get(), request, response))
  236. {
  237. return;
  238. }
  239. // Process the scene.
  240. if (m_isShuttingDown)
  241. {
  242. AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Processing scene was canceled.\n");
  243. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
  244. return;
  245. }
  246. if (!ExportScene(scene, request, response))
  247. {
  248. return;
  249. }
  250. AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Finalizing scene processing.\n");
  251. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
  252. }
  253. AZ::Uuid SceneBuilderWorker::GetUUID()
  254. {
  255. return AZ::Uuid::CreateString("{BD8BF658-9485-4FE3-830E-8EC3A23C35F3}");
  256. }
  257. void SceneBuilderWorker::PopulateProductDependencies(const AZ::SceneAPI::Events::ExportProduct& exportProduct, const char* watchFolder, AssetBuilderSDK::JobProduct& jobProduct) const
  258. {
  259. // Register the product dependencies and path dependencies from the export product to the job product.
  260. for (const AZ::SceneAPI::Events::ExportProduct& dependency : exportProduct.m_productDependencies)
  261. {
  262. jobProduct.m_dependencies.emplace_back(
  263. AZ::Data::AssetId(dependency.m_id, dependency.m_subId.value_or(0)),
  264. dependency.m_dependencyFlags);
  265. }
  266. for (const AZStd::string& pathDependency : exportProduct.m_legacyPathDependencies)
  267. {
  268. // SceneCore doesn't have access to AssetBuilderSDK, so it doesn't have access to the
  269. // ProductPathDependency type or the ProductPathDependencyType enum. Exporters registered with the
  270. // Scene Builder should report path dependencies on source files as absolute paths, while dependencies
  271. // on product files should be reported as relative paths.
  272. if (AzFramework::StringFunc::Path::IsRelative(pathDependency.c_str()))
  273. {
  274. // Make sure the path is relative to the watch folder. Paths passed in might be using asset database separators.
  275. // Convert to system separators for path manipulation.
  276. AZStd::string normalizedPathDependency = pathDependency;
  277. AZStd::string normalizedWatchFolder(watchFolder);
  278. AZStd::string assetRootRelativePath;
  279. AzFramework::StringFunc::Path::Normalize(normalizedWatchFolder);
  280. AzFramework::StringFunc::Path::Normalize(normalizedPathDependency);
  281. AzFramework::StringFunc::Path::Join(normalizedWatchFolder.c_str(), normalizedPathDependency .c_str(), assetRootRelativePath);
  282. AzFramework::ApplicationRequests::Bus::Broadcast(&AzFramework::ApplicationRequests::Bus::Events::MakePathRelative, assetRootRelativePath, watchFolder);
  283. jobProduct.m_pathDependencies.emplace(assetRootRelativePath, AssetBuilderSDK::ProductPathDependencyType::ProductFile);
  284. }
  285. else
  286. {
  287. jobProduct.m_pathDependencies.emplace(pathDependency, AssetBuilderSDK::ProductPathDependencyType::SourceFile);
  288. }
  289. }
  290. jobProduct.m_dependenciesHandled = true; // We've populated the dependencies immediately above so it's OK to tell the AP we've handled dependencies
  291. }
  292. bool SceneBuilderWorker::LoadScene(AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& result,
  293. const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
  294. {
  295. using namespace AZ::SceneAPI;
  296. using namespace AZ::SceneAPI::Containers;
  297. using namespace AZ::SceneAPI::Events;
  298. AZ_TracePrintf(Utilities::LogWindow, "Loading scene.\n");
  299. SceneSerializationBus::BroadcastResult(result, &SceneSerializationBus::Events::LoadScene, request.m_fullPath, request.m_sourceFileUUID);
  300. if (!result)
  301. {
  302. AZ_TracePrintf(Utilities::ErrorWindow, "Failed to load scene file.\n");
  303. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
  304. return false;
  305. }
  306. AZ_TraceContext("Manifest", result->GetManifestFilename());
  307. if (result->GetManifest().IsEmpty())
  308. {
  309. AZ_TracePrintf(Utilities::WarningWindow, "No manifest loaded and not enough information to create a default manifest.\n");
  310. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
  311. return false; // Still return false as there's no work so should exit.
  312. }
  313. return true;
  314. }
  315. bool SceneBuilderWorker::GenerateScene(AZ::SceneAPI::Containers::Scene* scene,
  316. const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
  317. {
  318. using namespace AZ::SceneAPI;
  319. using namespace AZ::SceneAPI::Events;
  320. using namespace AZ::SceneAPI::SceneCore;
  321. const char* platformIdentifier = request.m_jobDescription.GetPlatformIdentifier().c_str();
  322. AZ_TracePrintf(Utilities::LogWindow, "Creating generate entities.\n");
  323. EntityConstructor::EntityPointer exporter = EntityConstructor::BuildEntity("Scene Generation", azrtti_typeid<GenerationComponent>());
  324. ProcessingResultCombiner result;
  325. AZ_TracePrintf(Utilities::LogWindow, "Preparing for generation.\n");
  326. result += Process<PreGenerateEventContext>(*scene, platformIdentifier);
  327. AZ_TracePrintf(Utilities::LogWindow, "Generating...\n");
  328. result += Process<GenerateEventContext>(*scene, platformIdentifier);
  329. AZ_TracePrintf(Utilities::LogWindow, "Generating LODs...\n");
  330. result += Process<GenerateLODEventContext>(*scene, platformIdentifier);
  331. AZ_TracePrintf(Utilities::LogWindow, "Generating additions...\n");
  332. result += Process<GenerateAdditionEventContext>(*scene, platformIdentifier);
  333. AZ_TracePrintf(Utilities::LogWindow, "Simplifing scene...\n");
  334. result += Process<GenerateSimplificationEventContext>(*scene, platformIdentifier);
  335. AZ_TracePrintf(Utilities::LogWindow, "Finalizing generation process.\n");
  336. result += Process<PostGenerateEventContext>(*scene, platformIdentifier);
  337. if (result.GetResult() == ProcessingResult::Failure)
  338. {
  339. AZ_TracePrintf(Utilities::ErrorWindow, "Failure during scene generation.\n");
  340. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
  341. return false;
  342. }
  343. return true;
  344. }
  345. bool SceneBuilderWorker::ExportScene(const AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene>& scene,
  346. const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
  347. {
  348. using namespace AZ::SceneAPI;
  349. using namespace AZ::SceneAPI::Events;
  350. using namespace AZ::SceneAPI::SceneCore;
  351. AZ_Assert(scene, "Invalid scene passed for exporting.");
  352. const AZStd::string& outputFolder = request.m_tempDirPath;
  353. const char* platformIdentifier = request.m_jobDescription.GetPlatformIdentifier().c_str();
  354. AZ_TraceContext("Output folder", outputFolder.c_str());
  355. AZ_TraceContext("Platform", platformIdentifier);
  356. AZ_TracePrintf(Utilities::LogWindow, "Processing scene.\n");
  357. AZ_TracePrintf(Utilities::LogWindow, "Creating export entities.\n");
  358. EntityConstructor::EntityPointer exporter = EntityConstructor::BuildEntity("Scene Exporters", ExportingComponent::TYPEINFO_Uuid());
  359. auto itr = request.m_jobDescription.m_jobParameters.find(AZ_CRC_CE("DebugFlag"));
  360. const bool isDebug = (itr != request.m_jobDescription.m_jobParameters.end() && itr->second == "true");
  361. ExportProductList productList;
  362. ProcessingResultCombiner result;
  363. AZ_TracePrintf(Utilities::LogWindow, "Preparing for export.\n");
  364. result += Process<PreExportEventContext>(productList, outputFolder, *scene, platformIdentifier, isDebug);
  365. AZ_TracePrintf(Utilities::LogWindow, "Exporting...\n");
  366. result += Process<ExportEventContext>(productList, outputFolder, *scene, platformIdentifier);
  367. AZ_TracePrintf(Utilities::LogWindow, "Finalizing export process.\n");
  368. result += Process<PostExportEventContext>(productList, outputFolder, platformIdentifier);
  369. if (isDebug)
  370. {
  371. AZStd::string productName;
  372. AzFramework::StringFunc::Path::GetFullFileName(scene->GetSourceFilename().c_str(), productName);
  373. AzFramework::StringFunc::Path::ReplaceExtension(productName, "dbgsg");
  374. AZ::SceneAPI::Utilities::DebugOutput::BuildDebugSceneGraph(outputFolder.c_str(), productList, scene, productName);
  375. }
  376. AZ_TracePrintf(Utilities::LogWindow, "Collecting and registering products.\n");
  377. for (const ExportProduct& product : productList.GetProducts())
  378. {
  379. const AZ::u32 subId = product.m_subId.has_value() ? product.m_subId.value() : BuildSubId(product);
  380. AZ_TracePrintf(Utilities::LogWindow, "Listed product: %s+0x%08x - %s (type %s)\n", product.m_id.ToString<AZStd::string>().c_str(),
  381. subId, product.m_filename.c_str(), product.m_assetType.ToString<AZStd::string>().c_str());
  382. AssetBuilderSDK::JobProduct jobProduct(product.m_filename, product.m_assetType, subId);
  383. PopulateProductDependencies(product, request.m_watchFolder.c_str(), jobProduct);
  384. response.m_outputProducts.emplace_back(jobProduct);
  385. // Unlike the version in ResourceCompilerScene/SceneCompiler.cpp, this version doesn't need to deal with sub ids that were
  386. // created before explicit sub ids were added to the SceneAPI.
  387. }
  388. switch (result.GetResult())
  389. {
  390. case ProcessingResult::Success:
  391. return true;
  392. case ProcessingResult::Ignored:
  393. // While ResourceCompilerScene is still around there's situations where either this builder or RCScene does work but the other not.
  394. // That used to be a cause for a warning and will be again once RCScene has been removed. It's not possible to detect if either
  395. // did any work so the warning is disabled for now.
  396. // AZ_TracePrintf(Utilities::WarningWindow, "Nothing found to convert and export.\n");
  397. return true;
  398. case ProcessingResult::Failure:
  399. AZ_TracePrintf(Utilities::ErrorWindow, "Failure during conversion and exporting.\n");
  400. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
  401. return false;
  402. default:
  403. AZ_TracePrintf(Utilities::ErrorWindow,
  404. "Unexpected result from conversion and exporting (%i).\n", result.GetResult());
  405. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
  406. return false;
  407. }
  408. return true;
  409. }
  410. // BuildSubId has an equivalent counterpart in ResourceCompilerScene. Both need to remain the same to avoid problems with sub ids.
  411. AZ::u32 SceneBuilderWorker::BuildSubId(const AZ::SceneAPI::Events::ExportProduct& product) const
  412. {
  413. // Instead of the just the lower 16-bits, use the full 32-bits that are available. There are production examples of
  414. // uber-scene files that contain hundreds of meshes that need to be split into individual mesh objects as an example.
  415. AZ::u32 id = static_cast<AZ::u32>(product.m_id.GetHash());
  416. if (product.m_lod.has_value())
  417. {
  418. AZ::u8 lod = product.m_lod.value();
  419. if (lod > 0xF)
  420. {
  421. AZ_TracePrintf(AZ::SceneAPI::Utilities::WarningWindow, "%i is too large to fit in the allotted bits for LOD.\n", static_cast<AZ::u32>(lod));
  422. lod = 0xF;
  423. }
  424. // The product uses lods so mask out the lod bits and set them appropriately.
  425. id &= ~AssetBuilderSDK::SUBID_MASK_LOD_LEVEL;
  426. id |= lod << AssetBuilderSDK::SUBID_LOD_LEVEL_SHIFT;
  427. }
  428. return id;
  429. }
  430. } // namespace SceneBuilder