SceneBuilderWorker.cpp 26 KB

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