MeshBuilder.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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/Memory/SystemAllocator.h>
  9. #include <AzCore/std/algorithm.h>
  10. #include <AzCore/std/smart_ptr/unique_ptr.h>
  11. #include <AzCore/std/numeric.h>
  12. #include <AzCore/Jobs/JobFunction.h>
  13. #include <AzCore/Jobs/JobCompletion.h>
  14. #include <AzCore/Jobs/JobContext.h>
  15. #include "MeshBuilder.h"
  16. #include "MeshBuilderSkinningInfo.h"
  17. #include "MeshBuilderSubMesh.h"
  18. namespace AZ::MeshBuilder
  19. {
  20. AZ_CLASS_ALLOCATOR_IMPL(MeshBuilder, AZ::SystemAllocator)
  21. MeshBuilder::MeshBuilder(size_t numOrgVerts, bool optimizeDuplicates)
  22. : MeshBuilder(numOrgVerts, s_defaultMaxBonesPerSubMesh, s_defaultMaxSubMeshVertices, optimizeDuplicates)
  23. {
  24. }
  25. MeshBuilder::MeshBuilder(size_t numOrgVerts, size_t maxBonesPerSubMesh, size_t maxSubMeshVertices, bool optimizeDuplicates)
  26. : m_vertices(numOrgVerts)
  27. , m_maxBonesPerSubMesh(AZ::GetMax<size_t>(1, maxBonesPerSubMesh))
  28. , m_maxSubMeshVertices(AZ::GetMax<size_t>(1, maxSubMeshVertices))
  29. , m_numOrgVerts(numOrgVerts)
  30. , m_optimizeDuplicates(optimizeDuplicates)
  31. {
  32. }
  33. const MeshBuilderVertexLookup MeshBuilder::FindMatchingDuplicate(size_t orgVertexNr) const
  34. {
  35. // check with all vertex duplicates
  36. const size_t numDuplicates = m_layers[0]->GetNumDuplicates(orgVertexNr);
  37. for (size_t d = 0; d < numDuplicates; ++d)
  38. {
  39. // check if the submitted vertex data is equal in all layers for the current duplicate
  40. const bool allDataEqual = AZStd::all_of(begin(m_layers), end(m_layers), [orgVertexNr, d](const AZStd::unique_ptr<MeshBuilderVertexAttributeLayer>& layer)
  41. {
  42. return layer->CheckIfIsVertexEqual(orgVertexNr, d);
  43. });
  44. // if so, we have found a matching vertex!
  45. if (allDataEqual)
  46. {
  47. return {orgVertexNr, d};
  48. }
  49. }
  50. // no matching vertex found
  51. return {};
  52. }
  53. const MeshBuilderVertexLookup MeshBuilder::AddVertex(const size_t orgVertexNr)
  54. {
  55. // when there are no layers, there is nothing to do
  56. if (m_layers.empty())
  57. {
  58. return {};
  59. }
  60. // try to find a matching duplicate number for the current vertex
  61. const MeshBuilderVertexLookup index = m_optimizeDuplicates ? FindMatchingDuplicate(orgVertexNr) : MeshBuilderVertexLookup{};
  62. if (index.mOrgVtx != InvalidIndex)
  63. {
  64. return index;
  65. }
  66. // if there isn't a similar vertex, we have to submit it to all layers
  67. for (AZStd::unique_ptr<MeshBuilderVertexAttributeLayer>& layer : m_layers)
  68. {
  69. layer->AddVertex(orgVertexNr);
  70. }
  71. return {orgVertexNr, m_layers.back()->GetNumDuplicates(orgVertexNr) - 1};
  72. }
  73. // find the index value for the current set vertex
  74. const MeshBuilderVertexLookup MeshBuilder::FindVertexIndex(size_t orgVertexNr) const
  75. {
  76. // if there are no layers, we can't find a valid index
  77. if (m_layers.empty())
  78. {
  79. return {};
  80. }
  81. // try to locate a matching duplicate
  82. return FindMatchingDuplicate(orgVertexNr);
  83. }
  84. void MeshBuilder::BeginPolygon(size_t materialIndex)
  85. {
  86. m_materialIndex = materialIndex;
  87. m_polyIndices.clear();
  88. m_polyOrgVertexNumbers.clear();
  89. }
  90. void MeshBuilder::AddPolygonVertex(size_t orgVertexNr)
  91. {
  92. m_polyIndices.emplace_back(AddVertex(orgVertexNr));
  93. m_polyOrgVertexNumbers.emplace_back(orgVertexNr);
  94. }
  95. void MeshBuilder::EndPolygon()
  96. {
  97. AZ_Assert(m_polyIndices.size() >= 3, "Polygon should at least have three vertices.");
  98. // add the triangle
  99. AddPolygon(m_polyIndices, m_polyOrgVertexNumbers, m_materialIndex);
  100. }
  101. const MeshBuilderSubMesh* MeshBuilder::FindSubMeshForPolygon(const AZStd::vector<size_t>& orgVertexNumbers, size_t materialIndex) const
  102. {
  103. // collect all bones that influence the given polygon
  104. AZStd::vector<size_t> polyJointList;
  105. ExtractBonesForPolygon(orgVertexNumbers, polyJointList);
  106. // find the submesh with the most similar bones
  107. size_t maxMatchings = 0;
  108. const auto* bestMatchingSubMesh = m_subMeshes.cend();
  109. for (const auto* subMesh = m_subMeshes.cbegin(); subMesh != m_subMeshes.cend(); ++subMesh)
  110. {
  111. // get the number of matching bones from the current submesh and the given polygon
  112. const size_t currentNumMatches = (*subMesh)->CalcNumSimilarJoints(polyJointList);
  113. // if the current submesh has more similar bones than the current maximum we found a better one
  114. if (currentNumMatches > maxMatchings)
  115. {
  116. maxMatchings = currentNumMatches;
  117. bestMatchingSubMesh = subMesh;
  118. // check if this submesh already our perfect match
  119. if (currentNumMatches == polyJointList.size())
  120. {
  121. break;
  122. }
  123. }
  124. }
  125. // if we cannot find a submesh with enough similar joints, find any one that can handle the polygon
  126. if (bestMatchingSubMesh == m_subMeshes.cend())
  127. {
  128. bestMatchingSubMesh = AZStd::find_if(m_subMeshes.cbegin(), m_subMeshes.cend(), [&orgVertexNumbers, &materialIndex, &polyJointList](const auto& subMesh)
  129. {
  130. return subMesh->CanHandlePolygon(orgVertexNumbers, materialIndex, polyJointList);
  131. });
  132. if (bestMatchingSubMesh != m_subMeshes.cend())
  133. {
  134. return (*bestMatchingSubMesh).get();
  135. }
  136. return nullptr;
  137. }
  138. // check if the submesh which has the most common bones with the given polygon can handle it
  139. if ((*bestMatchingSubMesh)->CanHandlePolygon(orgVertexNumbers, materialIndex, polyJointList))
  140. {
  141. return (*bestMatchingSubMesh).get();
  142. }
  143. return nullptr;
  144. }
  145. void MeshBuilder::AddPolygon(const AZStd::vector<MeshBuilderVertexLookup>& indices, const AZStd::vector<size_t>& orgVertexNumbers, size_t materialIndex)
  146. {
  147. // add the polygon to the list of poly vertex counts
  148. const size_t numPolyVerts = indices.size();
  149. AZ_Assert(numPolyVerts <= 255, "Polygon has too many vertices.");
  150. m_polyVertexCounts.emplace_back(static_cast<AZ::u8>(numPolyVerts));
  151. // try to find a submesh where we can add it
  152. MeshBuilderSubMesh* subMesh = FindSubMeshForPolygon(orgVertexNumbers, materialIndex);
  153. // if there is none where we can add it to, create a new one
  154. if (!subMesh)
  155. {
  156. m_subMeshes.emplace_back(AZStd::make_unique<MeshBuilderSubMesh>(materialIndex, this));
  157. subMesh = m_subMeshes.back().get();
  158. }
  159. // add the polygon to the submesh
  160. ExtractBonesForPolygon(orgVertexNumbers, m_polyJointList);
  161. subMesh->AddPolygon(indices, m_polyJointList);
  162. }
  163. void MeshBuilder::ExtractBonesForPolygon(const AZStd::vector<size_t>& orgVertexNumbers, AZStd::vector<size_t>& outPolyJointList) const
  164. {
  165. // get rid of existing data
  166. outPolyJointList.clear();
  167. // get the skinning info, if there is any
  168. const MeshBuilderSkinningInfo* skinningInfo = GetSkinningInfo();
  169. if (skinningInfo == nullptr)
  170. {
  171. return;
  172. }
  173. // for all 3 vertices of the polygon
  174. for (size_t orgVtxNr : orgVertexNumbers)
  175. {
  176. // traverse all influences for this vertex
  177. const size_t numInfluences = skinningInfo->GetNumInfluences(orgVtxNr);
  178. for (size_t n = 0; n < numInfluences; ++n)
  179. {
  180. const size_t nodeNr = skinningInfo->GetInfluence(orgVtxNr, n).mNodeNr;
  181. // if it isn't yet in the output array with bones, add it
  182. if (AZStd::find(outPolyJointList.begin(), outPolyJointList.end(), nodeNr) == outPolyJointList.end())
  183. {
  184. outPolyJointList.emplace_back(nodeNr);
  185. }
  186. }
  187. }
  188. }
  189. size_t MeshBuilder::CalcNumIndices() const
  190. {
  191. size_t totalIndices = 0;
  192. for (const AZStd::unique_ptr<MeshBuilderSubMesh>& subMesh : m_subMeshes)
  193. {
  194. totalIndices += subMesh->GetNumIndices();
  195. }
  196. return totalIndices;
  197. }
  198. size_t MeshBuilder::CalcNumVertices() const
  199. {
  200. size_t total = 0;
  201. for (const AZStd::unique_ptr<MeshBuilderSubMesh>& subMesh : m_subMeshes)
  202. {
  203. total += subMesh->GetNumVertices();
  204. }
  205. return total;
  206. }
  207. void MeshBuilder::GenerateSubMeshVertexOrders()
  208. {
  209. AZ::JobCompletion jobCompletion;
  210. for (const AZStd::unique_ptr<MeshBuilderSubMesh>& subMesh : m_subMeshes)
  211. {
  212. AZ::JobContext* jobContext = nullptr;
  213. AZ::Job* job = AZ::CreateJobFunction([&subMesh]()
  214. {
  215. AZ_PROFILE_SCOPE(Animation, "MeshBuilder::GenerateSubMeshVertexOrders::SubMeshJob");
  216. subMesh->GenerateVertexOrder();
  217. }, true, jobContext);
  218. job->SetDependent(&jobCompletion);
  219. job->Start();
  220. }
  221. jobCompletion.StartAndWaitForCompletion();
  222. }
  223. void MeshBuilder::SetSkinningInfo(AZStd::unique_ptr<MeshBuilderSkinningInfo> skinningInfo)
  224. {
  225. m_skinningInfo = AZStd::move(skinningInfo);
  226. }
  227. size_t MeshBuilder::FindRealVertexNr(const MeshBuilderSubMesh* subMesh, size_t orgVtx, size_t dupeNr) const
  228. {
  229. for (const SubMeshVertex& subMeshVertex : m_vertices[orgVtx])
  230. {
  231. if (subMeshVertex.m_subMesh == subMesh && subMeshVertex.m_dupeNr == dupeNr)
  232. {
  233. return subMeshVertex.m_realVertexNr;
  234. }
  235. }
  236. return InvalidIndex;
  237. }
  238. void MeshBuilder::SetRealVertexNrForSubMeshVertex(const MeshBuilderSubMesh* subMesh, size_t orgVtx, size_t dupeNr, size_t realVertexNr)
  239. {
  240. SubMeshVertex* subMeshVertex = FindSubMeshVertex(subMesh, orgVtx, dupeNr);
  241. if (!subMeshVertex)
  242. {
  243. return;
  244. }
  245. subMeshVertex->m_realVertexNr = realVertexNr;
  246. }
  247. const MeshBuilder::SubMeshVertex* MeshBuilder::FindSubMeshVertex(const MeshBuilderSubMesh* subMesh, size_t orgVtx, size_t dupeNr) const
  248. {
  249. auto subMeshVertex = AZStd::find_if(m_vertices[orgVtx].begin(), m_vertices[orgVtx].end(), [subMesh, dupeNr](const SubMeshVertex& vertex)
  250. {
  251. return vertex.m_subMesh == subMesh && vertex.m_dupeNr == dupeNr;
  252. });
  253. return (subMeshVertex == m_vertices[orgVtx].end()) ? nullptr : subMeshVertex;
  254. }
  255. size_t MeshBuilder::CalcNumVertexDuplicates(const MeshBuilderSubMesh* subMesh, size_t orgVtx) const
  256. {
  257. size_t numDupes = 0;
  258. for (const SubMeshVertex& subMeshVertex : m_vertices[orgVtx])
  259. {
  260. if (subMeshVertex.m_subMesh == subMesh)
  261. {
  262. numDupes++;
  263. }
  264. }
  265. return numDupes;
  266. }
  267. size_t MeshBuilder::GetNumSubMeshVertices(size_t orgVtx) const
  268. {
  269. return m_vertices[orgVtx].size();
  270. }
  271. void MeshBuilder::AddSubMeshVertex(size_t orgVtx, SubMeshVertex&& vtx)
  272. {
  273. m_vertices[orgVtx].emplace_back(vtx);
  274. }
  275. } // namespace AZ::MeshBuilder