SourceAssetsStorage.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  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 "SourceAssetsStorage.h"
  9. #include "RobotImporterUtils.h"
  10. #include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
  11. #include <Atom/RPI.Reflect/Model/ModelAsset.h>
  12. #include <AzCore/IO/FileIO.h>
  13. #include <AzCore/Serialization/Json/JsonImporter.h>
  14. #include <AzCore/Serialization/Json/JsonUtils.h>
  15. #include <AzCore/Utils/Utils.h>
  16. #include <AzCore/std/smart_ptr/make_shared.h>
  17. #include <AzCore/std/smart_ptr/shared_ptr.h>
  18. #include <AzFramework/Asset/AssetSystemBus.h>
  19. #include <AzToolsFramework/Asset/AssetUtils.h>
  20. #include <PhysX/MeshAsset.h>
  21. #include <SceneAPI/SceneCore/Containers/Scene.h>
  22. #include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
  23. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMaterialData.h>
  24. #include <SceneAPI/SceneCore/DataTypes/Groups/ISceneNodeGroup.h>
  25. #include <SceneAPI/SceneCore/Events/AssetImportRequest.h>
  26. #include <SceneAPI/SceneCore/Events/SceneSerializationBus.h>
  27. #include <SceneAPI/SceneCore/Import/SceneImportSettings.h>
  28. #include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
  29. #include <SceneAPI/SceneData/Groups/ImportGroup.h>
  30. #include <SceneAPI/SceneData/Groups/MeshGroup.h>
  31. #include <SceneAPI/SceneData/Rules/UVsRule.h>
  32. #include <Source/Pipeline/MeshGroup.h> // PhysX/Code/Source/Pipeline/MeshGroup.h
  33. namespace ROS2::Utils
  34. {
  35. //! A helper class that do no extend PhysX::Pipeline::MeshGroup functionality, but gives necessary setters.
  36. class UrdfPhysxMeshGroupHelper : public PhysX::Pipeline::MeshGroup
  37. {
  38. public:
  39. void SetIsDecomposeMeshes(bool decompose)
  40. {
  41. m_decomposeMeshes = decompose;
  42. }
  43. void SetMeshExportMethod(PhysX::Pipeline::MeshExportMethod method)
  44. {
  45. m_exportMethod = method;
  46. }
  47. };
  48. //! Returns supported filenames by Asset Processor
  49. AZStd::vector<AZStd::string> GetSupportedExtensions()
  50. {
  51. struct Visitor : AZ::SettingsRegistryInterface::Visitor
  52. {
  53. using AZ::SettingsRegistryInterface::Visitor::Visit;
  54. void Visit(const AZ::SettingsRegistryInterface::VisitArgs&, AZStd::string_view value) override
  55. {
  56. m_supportedFileExtensions.emplace_back(value);
  57. }
  58. AZStd::vector<AZStd::string> m_supportedFileExtensions;
  59. };
  60. auto settingsRegistry = AZ::SettingsRegistry::Get();
  61. if (settingsRegistry == nullptr)
  62. {
  63. AZ_Warning("GetInterestingSourceAssetsCRC", false, "Global Settings Registry is not set.");
  64. return AZStd::vector<AZStd::string>();
  65. }
  66. using namespace AzToolsFramework::AssetUtils;
  67. Visitor assetImporterVisitor;
  68. settingsRegistry->Visit(
  69. assetImporterVisitor,
  70. AZ::SettingsRegistryInterface::FixedValueString("/O3DE/SceneAPI/AssetImporter/SupportedFileTypeExtensions"));
  71. return assetImporterVisitor.m_supportedFileExtensions;
  72. }
  73. /// Function computes CRC32 on first kilobyte of file.
  74. AZ::Crc32 GetFileCRC(const AZ::IO::Path& filename)
  75. {
  76. auto fileSize = AZ::IO::SystemFile::Length(filename.c_str());
  77. fileSize = AZStd::min(fileSize, 1024ull); // limit crc computation to first kilobyte
  78. if (fileSize == 0)
  79. {
  80. return AZ::Crc32();
  81. }
  82. AZStd::vector<char> buffer;
  83. buffer.resize_no_construct(fileSize + 1);
  84. buffer[fileSize] = '\0';
  85. if (!AZ::IO::SystemFile::Read(filename.c_str(), buffer.data(), fileSize))
  86. {
  87. return AZ::Crc32();
  88. }
  89. AZ::Crc32 r;
  90. r.Add(buffer.data(), fileSize);
  91. return r;
  92. }
  93. AZStd::vector<AZStd::string> GetProductAssets(const AZ::Uuid& sourceAssetUUID)
  94. {
  95. AZStd::vector<AZStd::string> productPaths;
  96. const auto importantTypes = AZStd::to_array<const AZ::TypeId>(
  97. {
  98. azrtti_typeid<AZ::RPI::StreamingImageAsset>(),
  99. azrtti_typeid<AZ::RPI::ModelAsset>(),
  100. azrtti_typeid<PhysX::Pipeline::MeshAsset>()
  101. });
  102. AZStd::vector<AZ::Data::AssetInfo> productsAssetInfo;
  103. using AssetSysReqBus = AzToolsFramework::AssetSystemRequestBus;
  104. bool ok{ false };
  105. AssetSysReqBus::BroadcastResult(ok, &AssetSysReqBus::Events::GetAssetsProducedBySourceUUID, sourceAssetUUID, productsAssetInfo);
  106. if (ok)
  107. {
  108. for (auto& product : productsAssetInfo)
  109. {
  110. for (auto& typeId : importantTypes)
  111. {
  112. if (product.m_assetType == typeId)
  113. {
  114. AZStd::string assetPath;
  115. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  116. assetPath, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetPathById, product.m_assetId);
  117. if (!assetPath.empty())
  118. {
  119. productPaths.emplace_back(AZStd::move(assetPath));
  120. break;
  121. }
  122. }
  123. }
  124. }
  125. }
  126. return productPaths;
  127. }
  128. AZ::Data::AssetId GetProductAssetId(const AZ::Uuid& sourceAssetUUID, const AZ::TypeId typeId)
  129. {
  130. AZStd::vector<AZ::Data::AssetInfo> productsAssetInfo;
  131. using AssetSysReqBus = AzToolsFramework::AssetSystemRequestBus;
  132. bool ok{ false };
  133. AssetSysReqBus::BroadcastResult(ok, &AssetSysReqBus::Events::GetAssetsProducedBySourceUUID, sourceAssetUUID, productsAssetInfo);
  134. if (ok)
  135. {
  136. for (const auto& product : productsAssetInfo)
  137. {
  138. if (product.m_assetType == typeId)
  139. {
  140. return product.m_assetId;
  141. }
  142. }
  143. }
  144. return {};
  145. }
  146. AZ::Data::AssetId GetImageProductAssetId(const AZ::Uuid& sourceAssetUUID)
  147. {
  148. return GetProductAssetId(sourceAssetUUID, azrtti_typeid<AZ::RPI::StreamingImageAsset>());
  149. }
  150. AZ::Data::AssetId GetModelProductAssetId(const AZ::Uuid& sourceAssetUUID)
  151. {
  152. return GetProductAssetId(sourceAssetUUID, azrtti_typeid<AZ::RPI::ModelAsset>());
  153. }
  154. AZ::Data::AssetId GetPhysXMeshProductAssetId(const AZ::Uuid& sourceAssetUUID)
  155. {
  156. return GetProductAssetId(sourceAssetUUID, azrtti_typeid<PhysX::Pipeline::MeshAsset>());
  157. }
  158. AvailableAsset GetAvailableAssetInfo(const AZStd::string& globalSourceAssetPath)
  159. {
  160. using AssetSysReqBus = AzToolsFramework::AssetSystemRequestBus;
  161. AvailableAsset foundAsset;
  162. // get source asset info
  163. bool sourceAssetFound{ false };
  164. AZ::Data::AssetInfo assetInfo;
  165. AZStd::string watchFolder;
  166. AssetSysReqBus::BroadcastResult(
  167. sourceAssetFound, &AssetSysReqBus::Events::GetSourceInfoBySourcePath, globalSourceAssetPath.c_str(), assetInfo, watchFolder);
  168. if (!sourceAssetFound)
  169. {
  170. AZ_Trace("GetAvailableAssetInfo", "Source asset info not found for: %s", globalSourceAssetPath.c_str());
  171. return foundAsset;
  172. }
  173. foundAsset.m_sourceGuid = assetInfo.m_assetId.m_guid;
  174. foundAsset.m_sourceAssetRelativePath = assetInfo.m_relativePath;
  175. foundAsset.m_sourceAssetGlobalPath = globalSourceAssetPath;
  176. AZ::Crc32 crc = Utils::GetFileCRC(foundAsset.m_sourceAssetGlobalPath);
  177. if (crc == AZ::Crc32(0))
  178. {
  179. AZ_Warning("GetInterestingSourceAssetsCRC", false, "Zero CRC for source asset %s", foundAsset.m_sourceAssetGlobalPath.c_str());
  180. return foundAsset;
  181. }
  182. AZ_Printf("GetAvailableAssetInfo", "Found asset:");
  183. AZ_Printf("GetAvailableAssetInfo", "\tm_sourceAssetRelativePath : %s", foundAsset.m_sourceAssetRelativePath.c_str());
  184. AZ_Printf("GetAvailableAssetInfo", "\tm_sourceAssetGlobalPath : %s", foundAsset.m_sourceAssetGlobalPath.c_str());
  185. AZ_Printf("GetAvailableAssetInfo", "\tm_productAssetRelativePath : %s", foundAsset.m_productAssetRelativePath.c_str());
  186. AZ_Printf("GetAvailableAssetInfo", "\tm_sourceGuid : %s", foundAsset.m_sourceGuid.ToString<AZStd::string>().c_str());
  187. return foundAsset;
  188. }
  189. AZStd::unordered_map<AZ::Crc32, AvailableAsset> GetInterestingSourceAssetsCRC()
  190. {
  191. // connect to database API
  192. const AZStd::vector<AZStd::string> InterestingExtensions = GetSupportedExtensions();
  193. AZStd::unordered_map<AZ::Crc32, AvailableAsset> availableAssets;
  194. AzToolsFramework::AssetDatabase::AssetDatabaseConnection assetDatabaseConnection;
  195. if (!assetDatabaseConnection.OpenDatabase())
  196. {
  197. AZ_Warning("GetInterestingSourceAssetsCRC", false, "Cannot open database");
  198. return {};
  199. }
  200. auto callback = [&availableAssets](AzToolsFramework::AssetDatabase::SourceDatabaseEntry& entry)
  201. {
  202. using AssetSysReqBus = AzToolsFramework::AssetSystemRequestBus;
  203. AvailableAsset foundAsset;
  204. foundAsset.m_sourceGuid = entry.m_sourceGuid;
  205. using AssetSysReqBus = AzToolsFramework::AssetSystemRequestBus;
  206. // get source asset info
  207. bool sourceAssetFound{ false };
  208. AZ::Data::AssetInfo assetInfo;
  209. AZStd::string watchFolder;
  210. AssetSysReqBus::BroadcastResult(
  211. sourceAssetFound, &AssetSysReqBus::Events::GetSourceInfoBySourceUUID, entry.m_sourceGuid, assetInfo, watchFolder);
  212. if (!sourceAssetFound)
  213. {
  214. AZ_Warning("GetInterestingSourceAssetsCRC", false, "Cannot find source asset info for %s", entry.ToString().c_str());
  215. return true;
  216. }
  217. const auto fullSourcePath = AZ::IO::Path(watchFolder) / AZ::IO::Path(assetInfo.m_relativePath);
  218. foundAsset.m_sourceAssetRelativePath = assetInfo.m_relativePath;
  219. foundAsset.m_sourceAssetGlobalPath = fullSourcePath.String();
  220. AZ::Crc32 crc = Utils::GetFileCRC(foundAsset.m_sourceAssetGlobalPath);
  221. if (crc == AZ::Crc32(0))
  222. {
  223. AZ_Warning(
  224. "GetInterestingSourceAssetsCRC", false, "Zero CRC for source asset %s", foundAsset.m_sourceAssetGlobalPath.c_str());
  225. return true;
  226. }
  227. AZ_Printf("GetInterestingSourceAssetsCRC", "Found asset:");
  228. AZ_Printf("GetInterestingSourceAssetsCRC", "\tm_sourceAssetRelativePath : %s", foundAsset.m_sourceAssetRelativePath.c_str());
  229. AZ_Printf("GetInterestingSourceAssetsCRC", "\tm_sourceAssetGlobalPath : %s", foundAsset.m_sourceAssetGlobalPath.c_str());
  230. AZ_Printf("GetInterestingSourceAssetsCRC", "\tm_productAssetRelativePath : %s", foundAsset.m_productAssetRelativePath.c_str());
  231. AZ_Printf(
  232. "GetInterestingSourceAssetsCRC",
  233. "\tm_sourceGuid : %s",
  234. foundAsset.m_sourceGuid.ToString<AZStd::string>().c_str());
  235. AZ_Printf("GetInterestingSourceAssetsCRC", "\tcrc : %d", crc);
  236. auto availableAssetIt = availableAssets.find(crc);
  237. if (availableAssetIt != availableAssets.end())
  238. {
  239. AZ_Warning(
  240. "GetInterestingSourceAssetsCRC", false, "Asset already in database : %s ", foundAsset.m_sourceAssetGlobalPath.c_str());
  241. AZ_Warning(
  242. "GetInterestingSourceAssetsCRC", false, "Found asset : %s ", availableAssetIt->second.m_sourceAssetGlobalPath.c_str());
  243. }
  244. else
  245. {
  246. availableAssets.insert({ crc, foundAsset });
  247. }
  248. return true;
  249. };
  250. for (auto& extension : InterestingExtensions)
  251. {
  252. assetDatabaseConnection.QuerySourceLikeSourceName(
  253. extension.c_str(), AzToolsFramework::AssetDatabase::AssetDatabaseConnection::LikeType::EndsWith, callback);
  254. }
  255. return availableAssets;
  256. }
  257. UrdfAssetMap CopyReferencedAssetsAndCreateAssetMap(
  258. const AssetFilenameReferences& assetFilenames,
  259. const AZStd::string& urdfFilename,
  260. const SdfAssetBuilderSettings& sdfBuilderSettings,
  261. AZStd::string_view outputDirSuffix,
  262. AZ::IO::FileIOBase* fileIO)
  263. {
  264. UrdfAssetMap urdfAssetMap;
  265. if (assetFilenames.empty())
  266. {
  267. return urdfAssetMap;
  268. }
  269. //! Maps the unresolved urdf path to global path
  270. AZStd::unordered_map<AZStd::string, AZ::IO::Path> copiedFiles;
  271. AZ_Assert(fileIO, "No FileIO instance");
  272. AZ::Crc32 urdfFileCrc;
  273. urdfFileCrc.Add(urdfFilename);
  274. const AZ::IO::Path urdfPath(urdfFilename);
  275. // By naming the temp directory '$tmp_*', the default configuration in AssetProcessorPlatformConfig.setreg will
  276. // exclude these files from processing.
  277. const AZStd::string directoryNameTmp = AZStd::string::format("$tmp_%u.tmp", AZ::u32(urdfFileCrc));
  278. const auto directoryNameDst = AZ::IO::FixedMaxPathString::format(
  279. "%u_%.*s%.*s", AZ::u32(urdfFileCrc), AZ_PATH_ARG(urdfPath.Stem()), AZ_STRING_ARG(outputDirSuffix));
  280. const AZ::IO::Path importDirectoryTmp = AZ::IO::Path(AZ::Utils::GetProjectPath()) / "Assets" / "UrdfImporter" / directoryNameTmp;
  281. const AZ::IO::Path importDirectoryDst = AZ::IO::Path(AZ::Utils::GetProjectPath()) / "Assets" / "UrdfImporter" / directoryNameDst;
  282. fileIO->DestroyPath(importDirectoryTmp.c_str());
  283. const auto outcomeCreateDstDir = fileIO->CreatePath(importDirectoryDst.c_str());
  284. const auto outcomeCreateTmpDir = fileIO->CreatePath(importDirectoryTmp.c_str());
  285. AZ_Error("CopyAssetForURDF", outcomeCreateDstDir, "Cannot create destination directory : %s", importDirectoryDst.c_str());
  286. AZ_Error("CopyAssetForURDF", outcomeCreateTmpDir, "Cannot create temporary directory : %s", importDirectoryTmp.c_str());
  287. if (!outcomeCreateDstDir || !outcomeCreateTmpDir)
  288. {
  289. if (outcomeCreateDstDir)
  290. {
  291. fileIO->DestroyPath(importDirectoryDst.c_str());
  292. }
  293. if (outcomeCreateTmpDir)
  294. {
  295. fileIO->DestroyPath(importDirectoryTmp.c_str());
  296. }
  297. return urdfAssetMap;
  298. }
  299. auto amentPrefixPath = Utils::GetAmentPrefixPath();
  300. AZStd::unordered_map<AZStd::string, unsigned int> countFilenames;
  301. for (const auto& [unresolvedFileName, assetReferenceType] : assetFilenames)
  302. {
  303. auto resolvedPath =
  304. Utils::ResolveAssetPath(unresolvedFileName, AZ::IO::PathView(urdfFilename), amentPrefixPath, sdfBuilderSettings);
  305. if (resolvedPath.empty())
  306. {
  307. AZ_Warning("CopyAssetForURDF", false, "There is no resolved path for %s", unresolvedFileName.c_str());
  308. continue;
  309. }
  310. AZStd::string filename = resolvedPath.Filename().String();
  311. auto count = countFilenames[filename]++;
  312. if (count > 0)
  313. {
  314. AZStd::string stem = resolvedPath.Stem().String();
  315. AZStd::string extension = resolvedPath.Extension().String();
  316. filename = AZStd::string::format("%s_dup_%u%s", stem.c_str(), count, extension.c_str());
  317. }
  318. AZ::IO::Path targetPathAssetDst(importDirectoryDst / filename);
  319. AZ::IO::Path targetPathAssetTmp(importDirectoryTmp / filename);
  320. AZ::IO::Path targetPathAssetInfo(targetPathAssetDst.Native() + ".assetinfo");
  321. if (!fileIO->Exists(targetPathAssetDst.c_str()))
  322. {
  323. // copy mesh file to temporary location ignored by AP
  324. const auto outcomeCopyTmp = fileIO->Copy(resolvedPath.c_str(), targetPathAssetTmp.c_str());
  325. AZ_Printf(
  326. "CopyAssetForURDF",
  327. "Copy %s to %s, result: %d",
  328. resolvedPath.c_str(),
  329. targetPathAssetTmp.c_str(),
  330. outcomeCopyTmp.GetResultCode());
  331. if (outcomeCopyTmp)
  332. {
  333. const bool needsVisual = (assetReferenceType & ReferencedAssetType::VisualMesh) == ReferencedAssetType::VisualMesh;
  334. const bool needsCollider = (assetReferenceType & ReferencedAssetType::ColliderMesh) == ReferencedAssetType::ColliderMesh;
  335. const bool isMeshFile = (needsVisual || needsCollider);
  336. // if the asset is a mesh, create asset info at destination location using the temporary mesh file
  337. const bool assetInfoOk = isMeshFile
  338. ? CreateSceneManifest(targetPathAssetTmp, targetPathAssetInfo, needsCollider, needsVisual)
  339. : true;
  340. if (assetInfoOk)
  341. {
  342. // copy additional assets such as textures directly to destination location
  343. if (isMeshFile)
  344. {
  345. const auto& meshTextureAssets = Utils::GetMeshTextureAssets(targetPathAssetTmp);
  346. for (const auto& unresolvedAssetPath : meshTextureAssets)
  347. {
  348. // Manifest returns local path in Project's directory temp folder
  349. const AZ::IO::Path assetLocalPath(AZ::IO::Path(AZ::IO::Path(AZ::Utils::GetProjectPath()) / unresolvedAssetPath)
  350. .LexicallyRelative(importDirectoryTmp));
  351. const AZ::IO::Path assetFullPathSrc(AZ::IO::Path(resolvedPath.ParentPath()) / assetLocalPath);
  352. const AZ::IO::Path assetFullPathDst(importDirectoryDst / assetLocalPath);
  353. const auto outcomeMkdir = fileIO->CreatePath(AZ::IO::Path(assetFullPathDst.ParentPath()).c_str());
  354. if (!outcomeMkdir)
  355. {
  356. break;
  357. }
  358. const auto outcomeCopy = fileIO->Copy(assetFullPathSrc.c_str(), assetFullPathDst.c_str());
  359. if (outcomeCopy)
  360. {
  361. copiedFiles[assetFullPathSrc.String()] = assetFullPathDst.String();
  362. }
  363. }
  364. }
  365. // move asset file from temporary location to destination location
  366. const auto outcomeMoveDst = fileIO->Rename(targetPathAssetTmp.c_str(), targetPathAssetDst.c_str());
  367. AZ_Printf(
  368. "CopyAssetForURDF",
  369. "Rename file %s to %s, result: %d",
  370. targetPathAssetTmp.c_str(),
  371. targetPathAssetDst.c_str(),
  372. outcomeMoveDst.GetResultCode());
  373. // call GetAssetStatus_FlushIO to ensure the asset processor is aware of the new file
  374. AzFramework::AssetSystem::AssetStatus copiedAssetStatus =
  375. AzFramework::AssetSystem::AssetStatus::AssetStatus_Unknown;
  376. AzFramework::AssetSystemRequestBus::BroadcastResult(
  377. copiedAssetStatus,
  378. &AzFramework::AssetSystem::AssetSystemRequests::GetAssetStatus_FlushIO,
  379. targetPathAssetDst.c_str());
  380. AZ_Warning(
  381. "CopyAssetForURDF",
  382. copiedAssetStatus != AzFramework::AssetSystem::AssetStatus::AssetStatus_Unknown,
  383. "Asset processor did not recognize the new file %s.",
  384. targetPathAssetDst.c_str());
  385. if (outcomeMoveDst)
  386. {
  387. copiedFiles[unresolvedFileName] = targetPathAssetDst.String();
  388. }
  389. }
  390. }
  391. }
  392. else
  393. {
  394. AZ_Printf("CopyAssetForURDF", "File %s already exists, omitting import", targetPathAssetDst.c_str());
  395. copiedFiles[unresolvedFileName] = targetPathAssetDst.String();
  396. }
  397. Utils::UrdfAsset asset;
  398. asset.m_urdfPath = urdfFilename;
  399. asset.m_resolvedUrdfPath =
  400. Utils::ResolveAssetPath(unresolvedFileName, AZ::IO::PathView(urdfFilename), amentPrefixPath, sdfBuilderSettings);
  401. asset.m_urdfFileCRC = AZ::Crc32();
  402. urdfAssetMap.emplace(unresolvedFileName, AZStd::move(asset));
  403. }
  404. fileIO->DestroyPath(importDirectoryTmp.c_str());
  405. for (const auto& copied : copiedFiles)
  406. {
  407. AZ_Printf("CopyAssetForURDF", " %s is copied to %s", copied.first.c_str(), copied.second.c_str());
  408. }
  409. // add available asset info
  410. for (const auto& [unresolvedUrfFileName, sourceAssetGlobalPath] : copiedFiles)
  411. {
  412. AZ_Assert(
  413. urdfAssetMap.contains(unresolvedUrfFileName), "urdfAssetMap should contain urdf path %s", unresolvedUrfFileName.c_str());
  414. urdfAssetMap[unresolvedUrfFileName].m_availableAssetInfo = Utils::GetAvailableAssetInfo(sourceAssetGlobalPath.String());
  415. }
  416. return urdfAssetMap;
  417. }
  418. UrdfAssetMap FindReferencedAssets(
  419. const AssetFilenameReferences& assetFilenames,
  420. const AZStd::string& urdfFilename,
  421. const SdfAssetBuilderSettings& sdfBuilderSettings)
  422. {
  423. auto amentPrefixPath = Utils::GetAmentPrefixPath();
  424. UrdfAssetMap urdfToAsset;
  425. for (const auto& [assetPath, assetReferenceType] : assetFilenames)
  426. {
  427. Utils::UrdfAsset asset;
  428. asset.m_urdfPath = assetPath;
  429. asset.m_resolvedUrdfPath =
  430. Utils::ResolveAssetPath(asset.m_urdfPath, AZ::IO::PathView(urdfFilename), amentPrefixPath, sdfBuilderSettings);
  431. asset.m_urdfFileCRC = Utils::GetFileCRC(asset.m_resolvedUrdfPath);
  432. urdfToAsset.emplace(assetPath, AZStd::move(asset));
  433. }
  434. if (!urdfToAsset.empty())
  435. {
  436. AZStd::unordered_map<AZ::Crc32, AvailableAsset> availableAssets = Utils::GetInterestingSourceAssetsCRC();
  437. // Search for suitable mappings by comparing checksum
  438. for (auto it = urdfToAsset.begin(); it != urdfToAsset.end(); it++)
  439. {
  440. Utils::UrdfAsset& asset = it->second;
  441. auto found_source_asset = availableAssets.find(asset.m_urdfFileCRC);
  442. if (found_source_asset != availableAssets.end())
  443. {
  444. asset.m_availableAssetInfo = found_source_asset->second;
  445. }
  446. }
  447. }
  448. return urdfToAsset;
  449. }
  450. bool CreateSceneManifest(const AZ::IO::Path& sourceAssetPath, const AZ::IO::Path& assetInfoFile, const bool collider, const bool visual)
  451. {
  452. // Start with a default set of import settings.
  453. AZ::SceneAPI::SceneImportSettings importSettings;
  454. // OBJ files used for robotics might be authored with hundreds or thousands of tiny groups of meshes. By default,
  455. // AssImp splits each object or group in an OBJ into a separate submesh, creating an extremely non-optimal result.
  456. // By turning on the AssImp options to optimize the scene and the meshes, all of these submeshes will get recombined
  457. // back into just a few submeshes.
  458. if (sourceAssetPath.Extension() == ".obj")
  459. {
  460. importSettings.m_optimizeScene = true;
  461. importSettings.m_optimizeMeshes = true;
  462. }
  463. // Set the import settings into the settings registry.
  464. // This needs to happen before calling LoadScene so that the AssImp import settings are applied to the scene being
  465. // read into memory. These settings affect the list of scene nodes referenced by the MeshGroup and PhysXGroup settings,
  466. // so it's important to apply them here to get the proper node lists.
  467. if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry)
  468. {
  469. settingsRegistry->SetObject(AZ::SceneAPI::DataTypes::IImportGroup::SceneImportSettingsRegistryKey, importSettings);
  470. }
  471. AZ_Printf("CreateSceneManifest", "Creating manifest for asset %s at : %s ", sourceAssetPath.c_str(), assetInfoFile.c_str());
  472. AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene> scene;
  473. AZ::SceneAPI::Events::SceneSerializationBus::BroadcastResult(
  474. scene, &AZ::SceneAPI::Events::SceneSerialization::LoadScene, sourceAssetPath.c_str(), AZ::Uuid::CreateNull(), "");
  475. if (!scene)
  476. {
  477. AZ_Error("CreateSceneManifest", false, "Error loading collider. Invalid scene: %s", sourceAssetPath.c_str());
  478. return false;
  479. }
  480. AZ::SceneAPI::Containers::SceneManifest& manifest = scene->GetManifest();
  481. auto valueStorage = manifest.GetValueStorage();
  482. if (valueStorage.empty())
  483. {
  484. AZ_Error("CreateSceneManifest", false, "Error loading collider. Invalid value storage: %s", sourceAssetPath.c_str());
  485. return false;
  486. }
  487. // remove default configuration to avoid procedural prefab creation
  488. AZStd::vector<AZStd::shared_ptr<AZ::SceneAPI::DataTypes::IManifestObject>> toDelete;
  489. for (size_t i = 0; i < manifest.GetEntryCount(); i++)
  490. {
  491. AZStd::shared_ptr<AZ::SceneAPI::DataTypes::IManifestObject> obj = manifest.GetValue(i);
  492. toDelete.push_back(obj);
  493. }
  494. for (auto obj : toDelete)
  495. {
  496. AZ_Printf("CreateSceneManifest", "Deleting %s", obj->RTTI_GetType().ToString<AZStd::string>().c_str());
  497. manifest.RemoveEntry(obj);
  498. }
  499. // Create an entry for the import settings. This will contain default settings for most mesh files,
  500. // but will enable the import optimization settings for OBJ files.
  501. auto sceneDataImportGroup = AZStd::make_shared<AZ::SceneAPI::SceneData::ImportGroup>();
  502. sceneDataImportGroup->SetImportSettings(importSettings);
  503. manifest.AddEntry(sceneDataImportGroup);
  504. if (visual)
  505. {
  506. AZStd::shared_ptr<AZ::SceneAPI::SceneData::MeshGroup> sceneDataMeshGroup =
  507. AZStd::make_shared<AZ::SceneAPI::SceneData::MeshGroup>();
  508. // select all nodes to this mesh group
  509. AZ::SceneAPI::Utilities::SceneGraphSelector::SelectAll(scene->GetGraph(), sceneDataMeshGroup->GetSceneNodeSelectionList());
  510. // enable auto-generation of UVs
  511. sceneDataMeshGroup->GetRuleContainer().AddRule(AZStd::make_shared<AZ::SceneAPI::SceneData::UVsRule>());
  512. manifest.AddEntry(sceneDataMeshGroup);
  513. }
  514. if (collider)
  515. {
  516. AZStd::shared_ptr<UrdfPhysxMeshGroupHelper> physxDataMeshGroup = AZStd::make_shared<UrdfPhysxMeshGroupHelper>();
  517. physxDataMeshGroup->SetIsDecomposeMeshes(true);
  518. physxDataMeshGroup->SetMeshExportMethod(PhysX::Pipeline::MeshExportMethod::Convex);
  519. // select all nodes to this mesh group
  520. AZ::SceneAPI::Utilities::SceneGraphSelector::SelectAll(scene->GetGraph(), physxDataMeshGroup->GetSceneNodeSelectionList());
  521. manifest.AddEntry(physxDataMeshGroup);
  522. }
  523. // Update assetinfo
  524. AZ::SceneAPI::Events::ProcessingResultCombiner result;
  525. AZ::SceneAPI::Events::AssetImportRequestBus::BroadcastResult(
  526. result,
  527. &AZ::SceneAPI::Events::AssetImportRequest::UpdateManifest,
  528. *scene,
  529. AZ::SceneAPI::Events::AssetImportRequest::ManifestAction::Update,
  530. AZ::SceneAPI::Events::AssetImportRequest::RequestingApplication::Editor);
  531. if (result.GetResult() != AZ::SceneAPI::Events::ProcessingResult::Success)
  532. {
  533. AZ_Trace("CreateSceneManifest", "Scene updated\n");
  534. return false;
  535. }
  536. scene->GetManifest().SaveToFile(assetInfoFile.Native());
  537. AZ_Printf("CreateSceneManifest", "Saving scene manifest to %s\n", assetInfoFile.c_str());
  538. return true;
  539. }
  540. bool CreateSceneManifest(const AZ::IO::Path& sourceAssetPath, const bool collider, const bool visual)
  541. {
  542. return CreateSceneManifest(sourceAssetPath, sourceAssetPath.Native() + ".assetinfo", collider, visual);
  543. }
  544. AZStd::unordered_set<AZ::IO::Path> GetMeshTextureAssets(const AZ::IO::Path& sourceMeshAssetPath)
  545. {
  546. AZStd::shared_ptr<AZ::SceneAPI::Containers::Scene> scene;
  547. AZ::SceneAPI::Events::SceneSerializationBus::BroadcastResult(
  548. scene, &AZ::SceneAPI::Events::SceneSerialization::LoadScene, sourceMeshAssetPath.c_str(), AZ::Uuid::CreateNull(), "");
  549. if (!scene)
  550. {
  551. AZ_Error("GetMeshTextureAssets", false, "Error loading mesh assets. Invalid scene: %s", sourceMeshAssetPath.c_str());
  552. return AZStd::unordered_set<AZ::IO::Path>();
  553. }
  554. AZStd::unordered_set<AZ::IO::Path> assetsFilepaths;
  555. // Look for material files
  556. static const AZStd::array<AZ::SceneAPI::DataTypes::IMaterialData::TextureMapType, 9> allTextureTypes{
  557. AZ::SceneAPI::DataTypes::IMaterialData::TextureMapType::Diffuse,
  558. AZ::SceneAPI::DataTypes::IMaterialData::TextureMapType::Specular,
  559. AZ::SceneAPI::DataTypes::IMaterialData::TextureMapType::Bump,
  560. AZ::SceneAPI::DataTypes::IMaterialData::TextureMapType::Normal,
  561. AZ::SceneAPI::DataTypes::IMaterialData::TextureMapType::Metallic,
  562. AZ::SceneAPI::DataTypes::IMaterialData::TextureMapType::Roughness,
  563. AZ::SceneAPI::DataTypes::IMaterialData::TextureMapType::AmbientOcclusion,
  564. AZ::SceneAPI::DataTypes::IMaterialData::TextureMapType::Emissive,
  565. AZ::SceneAPI::DataTypes::IMaterialData::TextureMapType::BaseColor
  566. };
  567. auto view =
  568. AZ::SceneAPI::Containers::MakeDerivedFilterView<AZ::SceneAPI::DataTypes::IMaterialData>(scene->GetGraph().GetContentStorage());
  569. for (const auto& material : view)
  570. {
  571. for (auto textureType : allTextureTypes)
  572. {
  573. if (const AZ::IO::Path filePath(material.GetTexture(textureType)); !filePath.empty())
  574. {
  575. assetsFilepaths.emplace(filePath);
  576. }
  577. }
  578. }
  579. return assetsFilepaths;
  580. }
  581. } // namespace ROS2::Utils