MeshBuilderSkinningInfo.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  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 the required headers
  9. #include <AzCore/std/sort.h>
  10. #include <AzCore/Memory/SystemAllocator.h>
  11. #include <AzCore/std/algorithm.h>
  12. #include <AzCore/std/numeric.h>
  13. #include "MeshBuilderSkinningInfo.h"
  14. #include "MeshBuilderSubMesh.h"
  15. #include "MeshBuilder.h"
  16. #include "MeshBuilderVertexAttributeLayers.h"
  17. namespace AZ::MeshBuilder
  18. {
  19. AZ_CLASS_ALLOCATOR_IMPL(MeshBuilderSkinningInfo, AZ::SystemAllocator)
  20. // constructor
  21. MeshBuilderSkinningInfo::MeshBuilderSkinningInfo(size_t numOrgVertices)
  22. {
  23. mInfluences.resize(numOrgVertices);
  24. for (auto& subArray : mInfluences)
  25. {
  26. subArray.reserve(4);
  27. }
  28. }
  29. // optimize weights
  30. void MeshBuilderSkinningInfo::OptimizeSkinningInfluences(AZStd::vector<Influence>& influences, float tolerance, size_t maxWeights)
  31. {
  32. const auto influenceLess = [](const Influence& left, const Influence& right) { return left.mWeight < right.mWeight; };
  33. // Move all the items greater than the tolerance to the end of the array
  34. auto removePoint = AZStd::remove_if(begin(influences), end(influences), [tolerance](const Influence& influence) { return influence.mWeight < tolerance; });
  35. if (removePoint == begin(influences))
  36. {
  37. // If this would remove all influences, keep the biggest one
  38. auto [_, maxElement] = AZStd::minmax_element(begin(influences), end(influences), influenceLess);
  39. AZStd::swap(removePoint, maxElement);
  40. ++removePoint;
  41. }
  42. // remove all weights below the tolerance
  43. influences.erase(removePoint, end(influences));
  44. // reduce number of weights when needed
  45. while (influences.size() > maxWeights)
  46. {
  47. // remove this smallest weight
  48. const auto [minInfluence, _] = AZStd::minmax_element(begin(influences), end(influences), influenceLess);
  49. influences.erase(minInfluence);
  50. }
  51. // calculate the total weight
  52. const float totalWeight = AZStd::accumulate(begin(influences), end(influences), 0.0f, [](float total, const Influence& influence) { return total + influence.mWeight; });
  53. // normalize
  54. for (Influence& influence : influences)
  55. {
  56. influence.mWeight /= totalWeight;
  57. }
  58. }
  59. // sort influences on weights, from big to small
  60. void MeshBuilderSkinningInfo::SortInfluencesByWeight(AZStd::vector<Influence>& influences)
  61. {
  62. AZStd::sort(begin(influences), end(influences), [](const auto& lhs, const auto& rhs)
  63. {
  64. return lhs.mWeight > rhs.mWeight;
  65. });
  66. }
  67. // optimize the weight data
  68. void MeshBuilderSkinningInfo::Optimize(
  69. AZStd::vector<Influence>& influences, AZ::u32 maxNumWeightsPerVertex, float weightThreshold)
  70. {
  71. // gather all weights
  72. const size_t numInfluences = influences.size();
  73. if (numInfluences > 0)
  74. {
  75. // optimize the weights and sort them from big to small weight
  76. OptimizeSkinningInfluences(influences, weightThreshold, maxNumWeightsPerVertex);
  77. SortInfluencesByWeight(influences);
  78. }
  79. }
  80. } // namespace AZ::MeshBuilder