MeshOptimizerComponent.cpp 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  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 <Generation/Components/MeshOptimizer/MeshOptimizerComponent.h>
  9. #include <AzCore/Casting/numeric_cast.h>
  10. #include <AzCore/Debug/Trace.h>
  11. #include <AzCore/RTTI/RTTI.h>
  12. #include <AzCore/Serialization/SerializeContext.h>
  13. #include <AzCore/base.h>
  14. #include <AzCore/std/algorithm.h>
  15. #include <AzCore/std/containers/array.h>
  16. #include <AzCore/std/containers/list.h>
  17. #include <AzCore/std/containers/unordered_map.h>
  18. #include <AzCore/std/containers/vector.h>
  19. #include <AzCore/std/iterator.h>
  20. #include <AzCore/std/limits.h>
  21. #include <AzCore/std/reference_wrapper.h>
  22. #include <AzCore/std/smart_ptr/make_shared.h>
  23. #include <AzCore/std/smart_ptr/shared_ptr.h>
  24. #include <AzCore/std/smart_ptr/unique_ptr.h>
  25. #include <AzCore/std/string/string_view.h>
  26. #include <AzCore/std/typetraits/add_pointer.h>
  27. #include <AzCore/std/typetraits/remove_cvref.h>
  28. #include <AzCore/std/utils.h>
  29. #include <SceneAPI/SceneCore/Components/GenerationComponent.h>
  30. #include <SceneAPI/SceneCore/Containers/Scene.h>
  31. #include <SceneAPI/SceneCore/Containers/SceneGraph.h>
  32. #include <SceneAPI/SceneCore/Containers/Views/PairIterator.h>
  33. #include <SceneAPI/SceneCore/Containers/Views/SceneGraphChildIterator.h>
  34. #include <SceneAPI/SceneCore/Containers/Utilities/Filters.h>
  35. #include <SceneAPI/SceneCore/Containers/Utilities/SceneGraphUtilities.h>
  36. #include <SceneAPI/SceneCore/Containers/Views/ConvertIterator.h>
  37. #include <SceneAPI/SceneCore/Containers/Views/FilterIterator.h>
  38. #include <SceneAPI/SceneCore/Containers/Views/View.h>
  39. #include <SceneAPI/SceneCore/DataTypes/GraphData/IBlendShapeData.h>
  40. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
  41. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexBitangentData.h>
  42. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexColorData.h>
  43. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexTangentData.h>
  44. #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexUVData.h>
  45. #include <SceneAPI/SceneCore/DataTypes/GraphData/ISkinWeightData.h>
  46. #include <SceneAPI/SceneCore/DataTypes/Groups/IMeshGroup.h>
  47. #include <SceneAPI/SceneCore/DataTypes/ManifestBase/ISceneNodeSelectionList.h>
  48. #include <SceneAPI/SceneCore/DataTypes/Rules/ILodRule.h>
  49. #include <SceneAPI/SceneCore/DataTypes/Rules/ISkinRule.h>
  50. #include <SceneAPI/SceneCore/Events/GenerateEventContext.h>
  51. #include <SceneAPI/SceneCore/Events/ProcessingResult.h>
  52. #include <SceneAPI/SceneCore/Utilities/Reporting.h>
  53. #include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
  54. #include <SceneAPI/SceneData/GraphData/BlendShapeData.h>
  55. #include <SceneAPI/SceneData/GraphData/CustomPropertyData.h>
  56. #include <SceneAPI/SceneData/GraphData/MeshData.h>
  57. #include <SceneAPI/SceneData/GraphData/MeshVertexBitangentData.h>
  58. #include <SceneAPI/SceneData/GraphData/MeshVertexColorData.h>
  59. #include <SceneAPI/SceneData/GraphData/MeshVertexTangentData.h>
  60. #include <SceneAPI/SceneData/GraphData/MeshVertexUVData.h>
  61. #include <SceneAPI/SceneData/GraphData/SkinWeightData.h>
  62. #include <Generation/Components/MeshOptimizer/MeshBuilder.h>
  63. #include <Generation/Components/MeshOptimizer/MeshBuilderSkinningInfo.h>
  64. #include <Generation/Components/MeshOptimizer/MeshBuilderVertexAttributeLayers.h>
  65. namespace AZ { class ReflectContext; }
  66. namespace AZ::MeshBuilder
  67. {
  68. using MeshBuilderVertexAttributeLayerColor = MeshBuilderVertexAttributeLayerT<AZ::SceneAPI::DataTypes::Color>;
  69. AZ_CLASS_ALLOCATOR_IMPL_TEMPLATE(MeshBuilderVertexAttributeLayerColor, AZ::SystemAllocator)
  70. using MeshBuilderVertexAttributeLayerSkinInfluence = MeshBuilderVertexAttributeLayerT<AZ::SceneAPI::DataTypes::ISkinWeightData::Link>;
  71. AZ_CLASS_ALLOCATOR_IMPL_TEMPLATE(MeshBuilderVertexAttributeLayerSkinInfluence, AZ::SystemAllocator)
  72. } // namespace AZ::MeshBuilder
  73. namespace AZ::SceneGenerationComponents
  74. {
  75. using AZ::SceneAPI::Containers::SceneGraph;
  76. using AZ::SceneAPI::DataTypes::IBlendShapeData;
  77. using AZ::SceneAPI::DataTypes::ILodRule;
  78. using AZ::SceneAPI::DataTypes::IMeshData;
  79. using AZ::SceneAPI::DataTypes::IMeshGroup;
  80. using AZ::SceneAPI::DataTypes::IMeshVertexBitangentData;
  81. using AZ::SceneAPI::DataTypes::IMeshVertexTangentData;
  82. using AZ::SceneAPI::DataTypes::IMeshVertexUVData;
  83. using AZ::SceneAPI::DataTypes::IMeshVertexColorData;
  84. using AZ::SceneAPI::DataTypes::ISkinWeightData;
  85. using AZ::SceneAPI::DataTypes::ICustomPropertyData;
  86. using AZ::SceneAPI::Events::ProcessingResult;
  87. using AZ::SceneAPI::Events::GenerateSimplificationEventContext;
  88. using AZ::SceneAPI::SceneCore::GenerationComponent;
  89. using AZ::SceneData::GraphData::BlendShapeData;
  90. using AZ::SceneData::GraphData::MeshData;
  91. using AZ::SceneData::GraphData::MeshVertexBitangentData;
  92. using AZ::SceneData::GraphData::MeshVertexColorData;
  93. using AZ::SceneData::GraphData::MeshVertexTangentData;
  94. using AZ::SceneData::GraphData::MeshVertexUVData;
  95. using AZ::SceneData::GraphData::SkinWeightData;
  96. using NodeIndex = AZ::SceneAPI::Containers::SceneGraph::NodeIndex;
  97. namespace Containers = AZ::SceneAPI::Containers;
  98. namespace Views = Containers::Views;
  99. // @brief A class to map from a mesh's vertex index to it's welded vertex index
  100. //
  101. // When the mesh optimizer runs, it welds nearby vertices (if there are no blendshapes). This class provides a
  102. // constant time lookup to map from an unwelded vertex index to the welded one.
  103. // The welding works by rounding the vertex's position to the given position tolerance, then uses that rounded
  104. // Vector3 as a key into a unordered_map.
  105. template <class MeshDataType>
  106. class Vector3Map
  107. {
  108. public:
  109. Vector3Map(const MeshDataType* meshData, bool hasBlendShapes, float positionTolerance)
  110. : m_meshData(meshData)
  111. , m_hasBlendShapes(hasBlendShapes)
  112. , m_positionTolerance(positionTolerance)
  113. , m_positionToleranceReciprocal(1.0f / positionTolerance)
  114. {
  115. }
  116. AZ::u32 operator[](const AZ::u32 vertexIndex)
  117. {
  118. if (m_hasBlendShapes)
  119. {
  120. // Don't attempt to weld similar vertices if there's blendshapes
  121. // Welding the vertices here based on position could cause the vertices of a base shape to be welded,
  122. // and the vertices of the blendshape to not be welded, resulting in a vertex count mismatch between
  123. // the two
  124. return m_meshData->GetUsedPointIndexForControlPoint(m_meshData->GetControlPointIndex(vertexIndex));
  125. }
  126. const auto& [iter, didInsert] = m_map.try_emplace(GetPositionForIndex(vertexIndex), m_currentOriginalVertexIndex);
  127. if (didInsert)
  128. {
  129. ++m_currentOriginalVertexIndex;
  130. }
  131. return iter->second;
  132. }
  133. [[nodiscard]] AZ::u32 at(const AZ::u32 vertexIndex) const
  134. {
  135. if (m_hasBlendShapes)
  136. {
  137. // Don't attempt to weld similar vertices if there's blendshapes
  138. // Welding the vertices here based on position could cause the vertices of a base shape to be welded,
  139. // and the vertices of the blendshape to not be welded, resulting in a vertex count mismatch between
  140. // the two
  141. return m_meshData->GetUsedPointIndexForControlPoint(m_meshData->GetControlPointIndex(vertexIndex));
  142. }
  143. auto iter = m_map.find(GetPositionForIndex(vertexIndex));
  144. AZSTD_CONTAINER_ASSERT(iter != m_map.end(), "Element with key is not present");
  145. return iter->second;
  146. }
  147. [[nodiscard]] size_t size() const
  148. {
  149. if (m_hasBlendShapes)
  150. {
  151. // Since blend shapes are present, the vertex welding is disabled, and the map will always be empty.
  152. // Use the underlying mesh's vertex count instead.
  153. return m_meshData->GetUsedControlPointCount();
  154. }
  155. return m_map.size();
  156. }
  157. void reserve(size_t count)
  158. {
  159. if (m_hasBlendShapes)
  160. {
  161. // Since blend shapes are present, the vertex welding is disabled, and the map will always be empty.
  162. return;
  163. }
  164. m_map.reserve(count);
  165. }
  166. private:
  167. AZ::Vector3 GetPositionForIndex(const AZ::u32 vertexIndex) const
  168. {
  169. // Round the vertex position so that a float comparison can be made with entires in the map
  170. // pos = floor( x * 10 + 0.5) * 0.1
  171. return AZ::Vector3(
  172. AZ::Simd::Vec3::Floor(
  173. (m_meshData->GetPosition(vertexIndex) * m_positionToleranceReciprocal + AZ::Vector3(0.5f)).GetSimdValue()
  174. )
  175. ) * m_positionTolerance;
  176. }
  177. AZStd::unordered_map<AZ::Vector3, AZ::u32> m_map;
  178. const MeshDataType* m_meshData;
  179. bool m_hasBlendShapes;
  180. float m_positionTolerance;
  181. float m_positionToleranceReciprocal;
  182. AZ::u32 m_currentOriginalVertexIndex = 0;
  183. };
  184. template<class MeshDataType>
  185. Vector3Map(const MeshDataType*) -> Vector3Map<const MeshDataType>;
  186. MeshOptimizerComponent::MeshOptimizerComponent()
  187. {
  188. BindToCall(&MeshOptimizerComponent::OptimizeMeshes);
  189. }
  190. void MeshOptimizerComponent::Reflect(AZ::ReflectContext* context)
  191. {
  192. auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  193. if (serializeContext)
  194. {
  195. serializeContext->Class<MeshOptimizerComponent, GenerationComponent>()->Version(12); // Fix vertex welding
  196. }
  197. }
  198. static AZStd::vector<AZ::MeshBuilder::MeshBuilderSkinningInfo::Influence> ExtractSkinningInfo(
  199. const AZStd::vector<MeshBuilder::MeshBuilderVertexAttributeLayerSkinInfluence*>& skinningInfluencesLayers,
  200. const AZ::MeshBuilder::MeshBuilderVertexLookup& vertexLookup,
  201. AZ::u32 maxWeightsPerVertex,
  202. float weightThreshold)
  203. {
  204. AZ::MeshBuilder::MeshBuilderSkinningInfo skinningInfo(1);
  205. AZStd::vector<AZ::MeshBuilder::MeshBuilderSkinningInfo::Influence> influences;
  206. for (const auto& skinLayer : skinningInfluencesLayers)
  207. {
  208. const ISkinWeightData::Link& link = skinLayer->GetVertexValue(vertexLookup.mOrgVtx, vertexLookup.mDuplicateNr);
  209. influences.push_back({ aznumeric_caster(link.boneId), link.weight });
  210. }
  211. skinningInfo.Optimize(influences, maxWeightsPerVertex, weightThreshold);
  212. return influences;
  213. }
  214. // Recurse through the SceneAPI's iterator types, extracting the real underlying iterator.
  215. struct ConvertToHierarchyIterator
  216. {
  217. template<typename T, typename U>
  218. static auto Unwrap(const Containers::Views::ConvertIterator<T, U>& it)
  219. {
  220. return Unwrap(it.GetBaseIterator());
  221. }
  222. template<typename T, typename U>
  223. static auto Unwrap(const Containers::Views::FilterIterator<T, U>& it)
  224. {
  225. return Unwrap(it.GetBaseIterator());
  226. }
  227. template<typename T>
  228. static auto Unwrap(const Containers::Views::SceneGraphChildIterator<T>& it)
  229. {
  230. return it.GetHierarchyIterator();
  231. }
  232. };
  233. bool MeshOptimizerComponent::HasAnyBlendShapeChild(const AZ::SceneAPI::Containers::SceneGraph& graph, const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& nodeIndex)
  234. {
  235. return !Containers::MakeDerivedFilterView<IBlendShapeData>(
  236. Views::MakeSceneGraphChildView(graph, nodeIndex, graph.GetContentStorage().cbegin(), true)
  237. ).empty();
  238. }
  239. static ICustomPropertyData::PropertyMap& FindOrCreateCustomPropertyData(
  240. Containers::SceneGraph& graph, const Containers::SceneGraph::NodeIndex& nodeIndex)
  241. {
  242. NodeIndex customPropertyIndex =
  243. SceneAPI::Utilities::GetImmediateChildOfType(graph, nodeIndex, azrtti_typeid<ICustomPropertyData>());
  244. if (!customPropertyIndex.IsValid())
  245. {
  246. // If no custom property data node exists, insert one
  247. AZStd::shared_ptr<SceneData::GraphData::CustomPropertyData> createdCustumPropertyData =
  248. AZStd::make_shared<SceneData::GraphData::CustomPropertyData>();
  249. customPropertyIndex = graph.AddChild(nodeIndex, "custom_properties", AZStd::move(createdCustumPropertyData));
  250. }
  251. ICustomPropertyData* customPropertyDataNode =
  252. azrtti_cast<ICustomPropertyData*>(graph.GetNodeContent(customPropertyIndex).get());
  253. return customPropertyDataNode->GetPropertyMap();
  254. }
  255. static bool HasOptimizedMeshNode(ICustomPropertyData::PropertyMap& propertyMap)
  256. {
  257. // Now look up the optimized index
  258. auto iter = propertyMap.find(SceneAPI::Utilities::OptimizedMeshPropertyMapKey);
  259. if (iter != propertyMap.end())
  260. {
  261. const auto& [key, optimizedAnyIndex] = *iter;
  262. if (!optimizedAnyIndex.empty() && optimizedAnyIndex.is<NodeIndex>())
  263. {
  264. return true;
  265. }
  266. }
  267. return false;
  268. }
  269. ProcessingResult MeshOptimizerComponent::OptimizeMeshes(GenerateSimplificationEventContext& context) const
  270. {
  271. // Iterate over all graph content and filter out all meshes.
  272. SceneGraph& graph = context.GetScene().GetGraph();
  273. // Build a list of mesh data nodes.
  274. const AZStd::vector<AZStd::pair<const IMeshData*, NodeIndex>> meshes = [](const SceneGraph& graph)
  275. {
  276. AZStd::vector<AZStd::pair<const IMeshData*, NodeIndex>> meshes;
  277. const auto meshNodes = Containers::MakeDerivedFilterView<IMeshData>(graph.GetContentStorage());
  278. for (auto it = meshNodes.cbegin(); it != meshNodes.cend(); ++it)
  279. {
  280. // Get the mesh data and node index and store them in the vector as a pair, so we can iterate over them later.
  281. // The sequential calls to GetBaseIterator unwrap the layers of FilterIterators from the MakeDerivedFilterView
  282. meshes.emplace_back(&(*it), graph.ConvertToNodeIndex(it.GetBaseIterator().GetBaseIterator().GetBaseIterator()));
  283. }
  284. return meshes;
  285. }(graph);
  286. const auto meshGroups = Containers::MakeDerivedFilterView<IMeshGroup>(context.GetScene().GetManifest().GetValueStorage());
  287. const AZStd::unordered_map<const IMeshGroup*, AZStd::vector<AZStd::string_view>> selectedNodes = [&meshGroups]
  288. {
  289. AZStd::unordered_map<const IMeshGroup*, AZStd::vector<AZStd::string_view>> selectedNodes;
  290. const auto addSelectionListToMap = [&selectedNodes](const IMeshGroup& meshGroup, const SceneAPI::DataTypes::ISceneNodeSelectionList& selectionList)
  291. {
  292. selectionList.EnumerateSelectedNodes(
  293. [&selectedNodes, &meshGroup](const AZStd::string& name)
  294. {
  295. selectedNodes[&meshGroup].emplace_back(name);
  296. return true;
  297. });
  298. };
  299. for (const IMeshGroup& meshGroup : meshGroups)
  300. {
  301. addSelectionListToMap(meshGroup, meshGroup.GetSceneNodeSelectionList());
  302. const ILodRule* lodRule = meshGroup.GetRuleContainerConst().FindFirstByType<SceneAPI::DataTypes::ILodRule>().get();
  303. if (lodRule)
  304. {
  305. for (size_t lod = 0; lod < lodRule->GetLodCount(); ++lod)
  306. {
  307. addSelectionListToMap(meshGroup, lodRule->GetSceneNodeSelectionList(lod));
  308. }
  309. }
  310. }
  311. return selectedNodes;
  312. }();
  313. const auto childNodes = [&graph](NodeIndex nodeIndex) { return Views::MakeSceneGraphChildView(graph, nodeIndex, graph.GetContentStorage().cbegin(), true); };
  314. const auto nodeIndexes = [&graph](const auto& view)
  315. {
  316. AZStd::vector<NodeIndex> indexes;
  317. indexes.reserve(AZStd::distance(view.begin(), view.end()));
  318. for (auto it = view.begin(); it != view.end(); ++it)
  319. {
  320. indexes.emplace_back(graph.ConvertToNodeIndex(ConvertToHierarchyIterator::Unwrap(it)));
  321. }
  322. return indexes;
  323. };
  324. // Iterate over them. We had to build the array before as this method can insert new nodes, so using the iterator directly would fail.
  325. for (const auto& [mesh, nodeIndex] : meshes)
  326. {
  327. // A Mesh can have multiple child nodes that contain other data streams, like uvs and tangents
  328. const auto uvDatasView = Containers::MakeDerivedFilterView<IMeshVertexUVData>(childNodes(nodeIndex));
  329. const auto tangentDatasView = Containers::MakeDerivedFilterView<IMeshVertexTangentData>(childNodes(nodeIndex));
  330. const auto bitangentDatasView = Containers::MakeDerivedFilterView<IMeshVertexBitangentData>(childNodes(nodeIndex));
  331. const auto skinWeightDatasView = Containers::MakeDerivedFilterView<ISkinWeightData>(childNodes(nodeIndex));
  332. const auto colorDatasView = Containers::MakeDerivedFilterView<IMeshVertexColorData>(childNodes(nodeIndex));
  333. const AZStd::vector<AZStd::reference_wrapper<const IMeshVertexUVData>> uvDatas(uvDatasView.begin(), uvDatasView.end());
  334. const AZStd::vector<AZStd::reference_wrapper<const IMeshVertexTangentData>> tangentDatas(tangentDatasView.begin(), tangentDatasView.end());
  335. const AZStd::vector<AZStd::reference_wrapper<const IMeshVertexBitangentData>> bitangentDatas(bitangentDatasView.begin(), bitangentDatasView.end());
  336. const AZStd::vector<AZStd::reference_wrapper<const ISkinWeightData>> skinWeightDatas(skinWeightDatasView.begin(), skinWeightDatasView.end());
  337. const AZStd::vector<AZStd::reference_wrapper<const IMeshVertexColorData>> colorDatas(colorDatasView.begin(), colorDatasView.end());
  338. const AZStd::string_view nodePath(graph.GetNodeName(nodeIndex).GetPath(), graph.GetNodeName(nodeIndex).GetPathLength());
  339. for (const IMeshGroup& meshGroup : meshGroups)
  340. {
  341. if (!selectedNodes.contains(&meshGroup))
  342. {
  343. AZ_Warning(
  344. AZ::SceneAPI::Utilities::LogWindow,
  345. false,
  346. "MeshGroup %s wasn't found in the list of selected nodes.",
  347. meshGroup.GetName().c_str());
  348. continue;
  349. }
  350. // Skip meshes that are not used by this mesh group
  351. if (AZStd::find(selectedNodes.at(&meshGroup).cbegin(), selectedNodes.at(&meshGroup).cend(), nodePath) == selectedNodes.at(&meshGroup).cend())
  352. {
  353. continue;
  354. }
  355. ICustomPropertyData::PropertyMap& unoptimizedPropertyMap = FindOrCreateCustomPropertyData(graph, nodeIndex);
  356. if (HasOptimizedMeshNode(unoptimizedPropertyMap))
  357. {
  358. // There is already an optimized mesh node for this mesh, so skip it.
  359. // There must be another mesh group already referencing this mesh node.
  360. continue;
  361. }
  362. const bool hasBlendShapes = HasAnyBlendShapeChild(graph, nodeIndex);
  363. auto [optimizedMesh, optimizedUVs, optimizedTangents, optimizedBitangents, optimizedVertexColors, optimizedSkinWeights] = OptimizeMesh(mesh, mesh, uvDatas, tangentDatas, bitangentDatas, colorDatas, skinWeightDatas, meshGroup, hasBlendShapes);
  364. AZ_TracePrintf(AZ::SceneAPI::Utilities::LogWindow, "Optimized mesh '%s': Original: %zu vertices -> optimized: %zu vertices, %0.02f%% of the original (hasBlendShapes=%s)",
  365. graph.GetNodeName(nodeIndex).GetName(),
  366. mesh->GetUsedControlPointCount(),
  367. optimizedMesh->GetUsedControlPointCount(),
  368. ((float)optimizedMesh->GetUsedControlPointCount() / (float)mesh->GetUsedControlPointCount()) * 100.0f,
  369. hasBlendShapes ? "Yes" : "No"
  370. );
  371. // Insert a new node for the optimized mesh
  372. const AZStd::string name =
  373. SceneAPI::Utilities::SceneGraphSelector::GenerateOptimizedMeshNodeName(graph, nodeIndex, meshGroup);
  374. const NodeIndex optimizedMeshNodeIndex =
  375. graph.AddChild(graph.GetNodeParent(nodeIndex), name.c_str(), AZStd::move(optimizedMesh));
  376. if (!optimizedMeshNodeIndex.IsValid())
  377. {
  378. // An invalid node index usually happens when the name is invalid.
  379. // An error will already be printed so no need for one here.
  380. return ProcessingResult::Failure;
  381. }
  382. // Copy any custom properties from the original mesh to the optimized mesh
  383. ICustomPropertyData::PropertyMap& optimizedPropertyMap = FindOrCreateCustomPropertyData(graph, optimizedMeshNodeIndex);
  384. optimizedPropertyMap = unoptimizedPropertyMap;
  385. // Add a mapping from the optimized node back to the original node so it can also be looked up later
  386. optimizedPropertyMap[SceneAPI::Utilities::OriginalUnoptimizedMeshPropertyMapKey] =
  387. AZStd::make_any<NodeIndex>(nodeIndex);
  388. // Add the optimized node index to the original mesh's custom property map so it can be looked up later
  389. unoptimizedPropertyMap[SceneAPI::Utilities::OptimizedMeshPropertyMapKey] =
  390. AZStd::make_any<NodeIndex>(optimizedMeshNodeIndex);
  391. auto addOptimizedNodes = [&graph, &optimizedMeshNodeIndex](const auto& originalNodeIndexes, auto& optimizedNodes)
  392. {
  393. AZ_PUSH_DISABLE_WARNING(, "-Wrange-loop-analysis") // remove when we upgrade from clang 6.0
  394. for (const auto& [originalNodeIndex, optimizedNode] : Containers::Views::MakePairView(originalNodeIndexes, optimizedNodes))
  395. AZ_POP_DISABLE_WARNING
  396. {
  397. const AZStd::string optimizedName {graph.GetNodeName(originalNodeIndex).GetName(), graph.GetNodeName(originalNodeIndex).GetNameLength()};
  398. const NodeIndex optimizedNodeIndex = graph.AddChild(optimizedMeshNodeIndex, optimizedName.c_str(), AZStd::move(optimizedNode));
  399. if (graph.IsNodeEndPoint(originalNodeIndex))
  400. {
  401. graph.MakeEndPoint(optimizedNodeIndex);
  402. }
  403. }
  404. };
  405. addOptimizedNodes(nodeIndexes(Containers::MakeDerivedFilterView<IMeshVertexUVData>(childNodes(nodeIndex))), optimizedUVs);
  406. addOptimizedNodes(nodeIndexes(Containers::MakeDerivedFilterView<IMeshVertexTangentData>(childNodes(nodeIndex))), optimizedTangents);
  407. addOptimizedNodes(nodeIndexes(Containers::MakeDerivedFilterView<IMeshVertexBitangentData>(childNodes(nodeIndex))), optimizedBitangents);
  408. addOptimizedNodes(nodeIndexes(Containers::MakeDerivedFilterView<IMeshVertexColorData>(childNodes(nodeIndex))), optimizedVertexColors);
  409. if (optimizedSkinWeights)
  410. {
  411. const NodeIndex optimizedSkinNodeIndex = graph.AddChild(optimizedMeshNodeIndex, "skinWeights", AZStd::move(optimizedSkinWeights));
  412. graph.MakeEndPoint(optimizedSkinNodeIndex);
  413. }
  414. for (const NodeIndex& blendShapeNodeIndex : nodeIndexes(Containers::MakeDerivedFilterView<IBlendShapeData>(childNodes(nodeIndex))))
  415. {
  416. const IBlendShapeData* blendShapeNode = static_cast<IBlendShapeData*>(graph.GetNodeContent(blendShapeNodeIndex).get());
  417. auto [optimizedBlendShape, _1, _2, _3 , _4, _5] = OptimizeMesh(blendShapeNode, mesh, {}, {}, {}, {}, {}, meshGroup, hasBlendShapes);
  418. const AZStd::string optimizedName {graph.GetNodeName(blendShapeNodeIndex).GetName(), graph.GetNodeName(blendShapeNodeIndex).GetNameLength()};
  419. const NodeIndex optimizedNodeIndex = graph.AddChild(optimizedMeshNodeIndex, optimizedName.c_str(), AZStd::move(optimizedBlendShape));
  420. if (graph.IsNodeEndPoint(blendShapeNodeIndex))
  421. {
  422. graph.MakeEndPoint(optimizedNodeIndex);
  423. }
  424. }
  425. const AZStd::array skippedChildTypes {
  426. // Skip copying the optimized nodes since we've already
  427. // populated those nodes with the optimized data
  428. azrtti_typeid<IMeshData>(),
  429. azrtti_typeid<IMeshVertexUVData>(),
  430. azrtti_typeid<IMeshVertexTangentData>(),
  431. azrtti_typeid<IMeshVertexBitangentData>(),
  432. azrtti_typeid<IMeshVertexColorData>(),
  433. azrtti_typeid<ISkinWeightData>(),
  434. azrtti_typeid<IBlendShapeData>(),
  435. // Skip copying the custom property data because we've already copied it above
  436. azrtti_typeid<ICustomPropertyData>()
  437. };
  438. // Copy the children of the original mesh node, but skip any nodes we have already populated
  439. for (const NodeIndex& childNodeIndex : nodeIndexes(childNodes(nodeIndex)))
  440. {
  441. const AZStd::shared_ptr<SceneAPI::DataTypes::IGraphObject>& childNode = graph.GetNodeContent(childNodeIndex);
  442. if (!AZStd::any_of(
  443. skippedChildTypes.begin(),
  444. skippedChildTypes.end(),
  445. [&childNode](const AZ::Uuid& typeId)
  446. {
  447. return AZ::RttiIsTypeOf(typeId, childNode.get());
  448. }))
  449. {
  450. const AZStd::string optimizedName {graph.GetNodeName(childNodeIndex).GetName(), graph.GetNodeName(childNodeIndex).GetNameLength()};
  451. const NodeIndex optimizedNodeIndex = graph.AddChild(optimizedMeshNodeIndex, optimizedName.c_str(), childNode);
  452. if (graph.IsNodeEndPoint(childNodeIndex))
  453. {
  454. graph.MakeEndPoint(optimizedNodeIndex);
  455. }
  456. }
  457. }
  458. }
  459. }
  460. return ProcessingResult::Success;
  461. }
  462. template<class DataNodeType, class MeshBuilderLayerType>
  463. AZStd::vector<AZStd::unique_ptr<DataNodeType>> makeSceneGraphNodesForMeshBuilderLayers(const MeshBuilderLayerType& meshBuilderLayers)
  464. {
  465. AZStd::vector<AZStd::unique_ptr<DataNodeType>> layers(meshBuilderLayers.size());
  466. AZStd::generate(layers.begin(), layers.end(), []
  467. {
  468. return AZStd::make_unique<DataNodeType>();
  469. });
  470. return layers;
  471. };
  472. template<class SkinWeightDataView>
  473. static const AZStd::vector<MeshBuilder::MeshBuilderVertexAttributeLayerSkinInfluence*> MakeSkinInfluenceLayers(
  474. AZ::MeshBuilder::MeshBuilder& meshBuilder,
  475. const SkinWeightDataView& skinWeights,
  476. size_t vertexCount)
  477. {
  478. if (skinWeights.empty())
  479. {
  480. return {};
  481. }
  482. size_t maxInfluenceCount = 0;
  483. AZStd::vector<MeshBuilder::MeshBuilderVertexAttributeLayerSkinInfluence*> outLayers;
  484. // Do a pass over the skin influences, and determine the max influence count for any one vertex,
  485. // which will be the number of influence layers we add
  486. for (const auto& skinData : skinWeights)
  487. {
  488. for (size_t controlPointIndex = 0; controlPointIndex < skinData.get().GetVertexCount(); ++controlPointIndex)
  489. {
  490. const size_t linkCount = skinData.get().GetLinkCount(controlPointIndex);
  491. maxInfluenceCount = AZStd::max(maxInfluenceCount, linkCount);
  492. }
  493. }
  494. // Create the influence layers
  495. for (size_t i = 0; i < maxInfluenceCount; ++i)
  496. {
  497. outLayers.push_back(meshBuilder.AddLayer<MeshBuilder::MeshBuilderVertexAttributeLayerSkinInfluence>(vertexCount));
  498. }
  499. return outLayers;
  500. }
  501. template<class MeshDataType>
  502. AZStd::tuple<
  503. AZStd::unique_ptr<MeshDataType>,
  504. AZStd::vector<AZStd::unique_ptr<MeshVertexUVData>>,
  505. AZStd::vector<AZStd::unique_ptr<MeshVertexTangentData>>,
  506. AZStd::vector<AZStd::unique_ptr<MeshVertexBitangentData>>,
  507. AZStd::vector<AZStd::unique_ptr<MeshVertexColorData>>,
  508. AZStd::unique_ptr<AZ::SceneAPI::DataTypes::ISkinWeightData>
  509. > MeshOptimizerComponent::OptimizeMesh(
  510. const MeshDataType* meshData,
  511. const IMeshData* baseMesh,
  512. const AZStd::vector<AZStd::reference_wrapper<const IMeshVertexUVData>>& uvs,
  513. const AZStd::vector<AZStd::reference_wrapper<const IMeshVertexTangentData>>& tangents,
  514. const AZStd::vector<AZStd::reference_wrapper<const IMeshVertexBitangentData>>& bitangents,
  515. const AZStd::vector<AZStd::reference_wrapper<const IMeshVertexColorData>>& vertexColors,
  516. const AZStd::vector<AZStd::reference_wrapper<const ISkinWeightData>>& skinWeights,
  517. const AZ::SceneAPI::DataTypes::IMeshGroup& meshGroup,
  518. bool hasBlendShapes)
  519. {
  520. const size_t vertexCount = meshData->GetUsedControlPointCount();
  521. AZ::MeshBuilder::MeshBuilder meshBuilder(vertexCount, AZStd::numeric_limits<size_t>::max(), AZStd::numeric_limits<size_t>::max(), /*optimizeDuplicates=*/ !hasBlendShapes);
  522. // Make the layers to hold the vertex data
  523. auto* controlPointLayer = meshBuilder.AddLayer<MeshBuilder::MeshBuilderVertexAttributeLayerUInt32>(vertexCount);
  524. auto* posLayer = meshBuilder.AddLayer<MeshBuilder::MeshBuilderVertexAttributeLayerVector3>(vertexCount, false, true);
  525. auto* normalsLayer = meshBuilder.AddLayer<MeshBuilder::MeshBuilderVertexAttributeLayerVector3>(vertexCount, false, true);
  526. const auto makeLayersForData = [&meshBuilder, vertexCount](const auto& dataView)
  527. {
  528. using InputDataType = typename AZStd::remove_cvref_t<decltype(*dataView.begin())>::type;
  529. // Determine the layer data type to use in the mesh builder based on the type of scene graph node
  530. // IMeshVertexUVData -> MeshBuilderVertexAttributeLayerVector2
  531. // IMeshVertexTangentData -> MeshBuilderVertexAttributeLayerVector4
  532. // IMeshVertexBitangentData -> MeshBuilderVertexAttributeLayerVector3
  533. // IMeshVertexColorData -> MeshBuilderVertexAttributeLayerColor
  534. struct ViewTypeToLayerType
  535. {
  536. static constexpr auto type(const IMeshVertexUVData*) -> MeshBuilder::MeshBuilderVertexAttributeLayerVector2;
  537. static constexpr auto type(const IMeshVertexTangentData*) -> MeshBuilder::MeshBuilderVertexAttributeLayerVector4;
  538. static constexpr auto type(const IMeshVertexBitangentData*) -> MeshBuilder::MeshBuilderVertexAttributeLayerVector3;
  539. static constexpr auto type(const IMeshVertexColorData*) -> MeshBuilder::MeshBuilderVertexAttributeLayerColor;
  540. };
  541. using ResultingLayerType = decltype(ViewTypeToLayerType::type(AZStd::add_pointer_t<InputDataType>{}));
  542. // the views provided by SceneAPI do not have a size() method, so compute it
  543. const size_t layerCount = AZStd::distance(dataView.begin(), dataView.end());
  544. AZStd::vector<ResultingLayerType*> layers(layerCount);
  545. AZStd::generate(layers.begin(), layers.end(), [&meshBuilder = meshBuilder, vertexCount = vertexCount]
  546. {
  547. return meshBuilder.AddLayer<ResultingLayerType>(vertexCount);
  548. });
  549. return layers;
  550. };
  551. const AZStd::vector<MeshBuilder::MeshBuilderVertexAttributeLayerVector2*> uvLayers = makeLayersForData(uvs);
  552. const AZStd::vector<MeshBuilder::MeshBuilderVertexAttributeLayerVector4*> tangentLayers = makeLayersForData(tangents);
  553. const AZStd::vector<MeshBuilder::MeshBuilderVertexAttributeLayerVector3*> bitangentLayers = makeLayersForData(bitangents);
  554. const AZStd::vector<MeshBuilder::MeshBuilderVertexAttributeLayerColor*> vertexColorLayers = makeLayersForData(vertexColors);
  555. const AZStd::vector<MeshBuilder::MeshBuilderVertexAttributeLayerSkinInfluence*> skinningInfluencesLayers =
  556. MakeSkinInfluenceLayers(meshBuilder, skinWeights, vertexCount);
  557. constexpr float positionTolerance = 0.0001f;
  558. Vector3Map positionMap(meshData, hasBlendShapes, positionTolerance);
  559. positionMap.reserve(vertexCount);
  560. // Add the vertex data to all the layers
  561. const AZ::u32 faceCount = meshData->GetFaceCount();
  562. for (AZ::u32 faceIndex = 0; faceIndex < faceCount; ++faceIndex)
  563. {
  564. meshBuilder.BeginPolygon(baseMesh->GetFaceMaterialId(faceIndex));
  565. for (const AZ::u32 vertexIndex : meshData->GetFaceInfo(faceIndex).vertexIndex)
  566. {
  567. const AZ::u32 controlPointVertexIndex = positionMap[vertexIndex];
  568. controlPointLayer->SetCurrentVertexValue(controlPointVertexIndex);
  569. posLayer->SetCurrentVertexValue(meshData->GetPosition(vertexIndex));
  570. normalsLayer->SetCurrentVertexValue(meshData->GetNormal(vertexIndex));
  571. AZ_PUSH_DISABLE_WARNING(, "-Wrange-loop-analysis") // remove when we upgrade from clang 6.0
  572. for (const auto& [uvData, uvLayer] : Containers::Views::MakePairView(uvs, uvLayers))
  573. {
  574. uvLayer->SetCurrentVertexValue(uvData.get().GetUV(vertexIndex));
  575. }
  576. for (const auto& [tangentData, tangentLayer] : Containers::Views::MakePairView(tangents, tangentLayers))
  577. {
  578. tangentLayer->SetCurrentVertexValue(tangentData.get().GetTangent(vertexIndex));
  579. }
  580. for (const auto& [bitangentData, bitangentLayer] : Containers::Views::MakePairView(bitangents, bitangentLayers))
  581. {
  582. bitangentLayer->SetCurrentVertexValue(bitangentData.get().GetBitangent(vertexIndex));
  583. }
  584. for (const auto& [vertexColorData, vertexColorLayer] : Containers::Views::MakePairView(vertexColors, vertexColorLayers))
  585. {
  586. vertexColorLayer->SetCurrentVertexValue(vertexColorData.get().GetColor(vertexIndex));
  587. }
  588. // Initialize skin weights to 0, 0.0
  589. for (auto& skinInfluenceLayer : skinningInfluencesLayers)
  590. {
  591. skinInfluenceLayer->SetCurrentVertexValue(ISkinWeightData::Link{ 0, 0.0f });
  592. }
  593. #if defined(AZ_ENABLE_TRACING)
  594. bool influencesFoundForThisVertex = false;
  595. #endif
  596. // Set any real weights, if they exist
  597. for (const auto& skinWeightData : skinWeights)
  598. {
  599. const size_t linkCount = skinWeightData.get().GetLinkCount(vertexIndex);
  600. AZ_Assert(
  601. linkCount <= skinningInfluencesLayers.size(),
  602. "MeshOptimizer - The previously calculated maximum influence count is less than the current link count.");
  603. // Check that either the current skinWeightData doesn't have any influences for this vertex,
  604. // or that none of the ones which came before it had any influences for this vertex.
  605. AZ_Assert(
  606. linkCount == 0 || influencesFoundForThisVertex == false,
  607. "Two different skinWeightData instances in skinWeights apply to the same vertex. "
  608. "The mesh optimizer assumes there will only ever be one skinWeightData that impacts a given vertex.");
  609. #if defined(AZ_ENABLE_TRACING)
  610. // Mark that at least one influence has been found for this vertex
  611. influencesFoundForThisVertex |= linkCount > 0;
  612. #endif
  613. for (size_t linkIndex = 0; linkIndex < linkCount; ++linkIndex)
  614. {
  615. const ISkinWeightData::Link& link = skinWeightData.get().GetLink(vertexIndex, linkIndex);
  616. skinningInfluencesLayers[linkIndex]->SetCurrentVertexValue(link);
  617. }
  618. }
  619. AZ_POP_DISABLE_WARNING
  620. meshBuilder.AddPolygonVertex(controlPointVertexIndex);
  621. }
  622. meshBuilder.EndPolygon();
  623. }
  624. const auto* skinRule = meshGroup.GetRuleContainerConst().FindFirstByType<SceneAPI::DataTypes::ISkinRule>().get();
  625. const AZ::u32 maxWeightsPerVertex = skinRule ? skinRule->GetMaxWeightsPerVertex() : 4;
  626. const float weightThreshold = skinRule ? skinRule->GetWeightThreshold() : 0.001f;
  627. meshBuilder.GenerateSubMeshVertexOrders();
  628. const size_t optimizedVertexCount = meshBuilder.CalcNumVertices();
  629. // Create the resulting nodes
  630. struct ResultingType
  631. {
  632. // When this method is called with an IMeshData node, it is generating a MeshData node. When called on an
  633. // IBlendShapeData node, it is generating a BlendShapeData node.
  634. static constexpr auto type(const IMeshData*) -> MeshData;
  635. static constexpr auto type(const IBlendShapeData*) -> BlendShapeData;
  636. };
  637. auto optimizedMesh = AZStd::make_unique<decltype(ResultingType::type(meshData))>();
  638. optimizedMesh->CloneAttributesFrom(meshData);
  639. AZStd::vector<AZStd::unique_ptr<MeshVertexUVData>> optimizedUVs = makeSceneGraphNodesForMeshBuilderLayers<MeshVertexUVData>(uvLayers);
  640. AZStd::vector<AZStd::unique_ptr<MeshVertexTangentData>> optimizedTangents = makeSceneGraphNodesForMeshBuilderLayers<MeshVertexTangentData>(tangentLayers);
  641. AZStd::vector<AZStd::unique_ptr<MeshVertexBitangentData>> optimizedBitangents = makeSceneGraphNodesForMeshBuilderLayers<MeshVertexBitangentData>(bitangentLayers);
  642. AZStd::vector<AZStd::unique_ptr<MeshVertexColorData>> optimizedVertexColors = makeSceneGraphNodesForMeshBuilderLayers<MeshVertexColorData>(vertexColorLayers);
  643. AZStd::unique_ptr<SkinWeightData> optimizedSkinWeights = nullptr;
  644. if (!skinningInfluencesLayers.empty())
  645. {
  646. optimizedSkinWeights = AZStd::make_unique<SkinWeightData>();
  647. optimizedSkinWeights->ResizeContainerSpace(optimizedVertexCount);
  648. }
  649. // Copy node attributes
  650. AZStd::apply([]([[maybe_unused]] const auto&&... nodePairView) {
  651. ((AZStd::for_each(begin(nodePairView), end(nodePairView), [](const auto& nodePair) {
  652. auto& originalNode = nodePair.first;
  653. auto& optimizedNode = nodePair.second;
  654. optimizedNode->CloneAttributesFrom(&originalNode.get());
  655. })), ...);
  656. }, std::tuple {
  657. Views::MakePairView(uvs, optimizedUVs),
  658. Views::MakePairView(tangents, optimizedTangents),
  659. Views::MakePairView(bitangents, optimizedBitangents),
  660. Views::MakePairView(vertexColors, optimizedVertexColors),
  661. });
  662. unsigned int indexOffset = 0;
  663. for (size_t subMeshIndex = 0; subMeshIndex < meshBuilder.GetNumSubMeshes(); ++subMeshIndex)
  664. {
  665. const AZ::MeshBuilder::MeshBuilderSubMesh* subMesh = meshBuilder.GetSubMesh(subMeshIndex);
  666. for (size_t subMeshVertexIndex = 0; subMeshVertexIndex < subMesh->GetNumVertices(); ++subMeshVertexIndex)
  667. {
  668. const AZ::MeshBuilder::MeshBuilderVertexLookup& vertexLookup = subMesh->GetVertex(subMeshVertexIndex);
  669. optimizedMesh->AddPosition(posLayer->GetVertexValue(vertexLookup.mOrgVtx, vertexLookup.mDuplicateNr));
  670. optimizedMesh->AddNormal(normalsLayer->GetVertexValue(vertexLookup.mOrgVtx, vertexLookup.mDuplicateNr));
  671. int modelVertexIndex = optimizedMesh->GetVertexCount() - 1;
  672. optimizedMesh->SetVertexIndexToControlPointIndexMap(
  673. modelVertexIndex,
  674. controlPointLayer->GetVertexValue(vertexLookup.mOrgVtx, vertexLookup.mDuplicateNr)
  675. );
  676. for (auto [uvLayer, optimizedUVNode] : Containers::Views::MakePairView(uvLayers, optimizedUVs))
  677. {
  678. optimizedUVNode->AppendUV(uvLayer->GetVertexValue(vertexLookup.mOrgVtx, vertexLookup.mDuplicateNr));
  679. }
  680. for (auto [tangentLayer, optimizedTangentNode] : Containers::Views::MakePairView(tangentLayers, optimizedTangents))
  681. {
  682. optimizedTangentNode->AppendTangent(tangentLayer->GetVertexValue(vertexLookup.mOrgVtx, vertexLookup.mDuplicateNr));
  683. }
  684. for (auto [bitangentLayer, optimizedBitangentNode] : Containers::Views::MakePairView(bitangentLayers, optimizedBitangents))
  685. {
  686. optimizedBitangentNode->AppendBitangent(bitangentLayer->GetVertexValue(vertexLookup.mOrgVtx, vertexLookup.mDuplicateNr));
  687. }
  688. for (auto [vertexColorLayer, optimizedVertexColorNode] : Containers::Views::MakePairView(vertexColorLayers, optimizedVertexColors))
  689. {
  690. optimizedVertexColorNode->AppendColor(vertexColorLayer->GetVertexValue(vertexLookup.mOrgVtx, vertexLookup.mDuplicateNr));
  691. }
  692. if (optimizedSkinWeights)
  693. {
  694. AZStd::vector<AZ::MeshBuilder::MeshBuilderSkinningInfo::Influence> influences =
  695. ExtractSkinningInfo(skinningInfluencesLayers, vertexLookup, maxWeightsPerVertex, weightThreshold);
  696. for (const auto& influence : influences)
  697. {
  698. const int boneId =
  699. optimizedSkinWeights->GetBoneId(skinWeights[0].get().GetBoneName(aznumeric_caster(influence.mNodeNr)));
  700. optimizedSkinWeights->AppendLink(aznumeric_caster(modelVertexIndex), { boneId, influence.mWeight });
  701. }
  702. }
  703. }
  704. AZStd::unordered_set<size_t> usedIndexes;
  705. for (size_t polygonIndex = 0; polygonIndex < subMesh->GetNumPolygons(); ++polygonIndex)
  706. {
  707. AddFace(
  708. optimizedMesh.get(),
  709. aznumeric_caster(indexOffset + subMesh->GetIndex(polygonIndex * 3 + 0)),
  710. aznumeric_caster(indexOffset + subMesh->GetIndex(polygonIndex * 3 + 1)),
  711. aznumeric_caster(indexOffset + subMesh->GetIndex(polygonIndex * 3 + 2)),
  712. aznumeric_caster(subMesh->GetMaterialIndex())
  713. );
  714. const auto& faceInfo = optimizedMesh->GetFaceInfo(optimizedMesh->GetFaceCount() - 1);
  715. AZStd::copy(AZStd::begin(faceInfo.vertexIndex), AZStd::end(faceInfo.vertexIndex), AZStd::inserter(usedIndexes, usedIndexes.begin()));
  716. }
  717. indexOffset += static_cast<unsigned int>(usedIndexes.size());
  718. }
  719. return AZStd::make_tuple(
  720. AZStd::move(optimizedMesh),
  721. AZStd::move(optimizedUVs),
  722. AZStd::move(optimizedTangents),
  723. AZStd::move(optimizedBitangents),
  724. AZStd::move(optimizedVertexColors),
  725. AZStd::move(optimizedSkinWeights)
  726. );
  727. }
  728. void MeshOptimizerComponent::AddFace(AZ::SceneData::GraphData::BlendShapeData* blendShape, unsigned int index1, unsigned int index2, unsigned int index3, [[maybe_unused]] unsigned int faceMaterialId)
  729. {
  730. blendShape->AddFace({index1, index2, index3});
  731. }
  732. void MeshOptimizerComponent::AddFace(AZ::SceneData::GraphData::MeshData* mesh, unsigned int index1, unsigned int index2, unsigned int index3, unsigned int faceMaterialId)
  733. {
  734. mesh->AddFace({index1, index2, index3}, faceMaterialId);
  735. }
  736. } // namespace AZ::SceneGenerationComponents