SkinInfluencesTests.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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 <gtest/gtest.h>
  9. #include <AzCore/UnitTest/TestTypes.h>
  10. #include <AzCore/std/containers/array.h>
  11. #include <AzCore/Math/Random.h>
  12. #include <Generation/Components/MeshOptimizer/MeshBuilder.h>
  13. #include <Generation/Components/MeshOptimizer/MeshBuilderSkinningInfo.h>
  14. namespace AZ::MeshBuilder
  15. {
  16. struct SkinInfluencesTestParam
  17. {
  18. size_t numOrgVertices = 0;
  19. size_t maxSourceInfluences = 0;
  20. AZ::u32 maxInfluencesAfterOptimization = 0;
  21. };
  22. class SkinInfluencesFixture
  23. : public UnitTest::LeakDetectionFixture
  24. , public ::testing::WithParamInterface<SkinInfluencesTestParam>
  25. {
  26. public:
  27. static AZStd::unique_ptr<MeshBuilder> SetUpMeshBuilder(size_t numOrgVertices, size_t numSkinInfluences)
  28. {
  29. auto meshBuilder = AZStd::make_unique<MeshBuilder>(numOrgVertices);
  30. // Original vertex numbers
  31. meshBuilder->AddLayer<MeshBuilderVertexAttributeLayerUInt32>(
  32. numOrgVertices,
  33. false,
  34. false
  35. );
  36. // The positions layer
  37. meshBuilder->AddLayer<MeshBuilderVertexAttributeLayerVector3>(
  38. numOrgVertices,
  39. false,
  40. true
  41. );
  42. meshBuilder->SetSkinningInfo(SetUpSkinningInfo(numOrgVertices, numSkinInfluences));
  43. return meshBuilder;
  44. }
  45. static AZStd::unique_ptr<MeshBuilderSkinningInfo> SetUpSkinningInfo(size_t numOrgVertices, size_t numSkinInfluences)
  46. {
  47. auto skinningInfo = AZStd::make_unique<MeshBuilderSkinningInfo>(numOrgVertices);
  48. AZ::SimpleLcgRandom random;
  49. random.SetSeed(875960);
  50. const float expectedTotalWeight = 1.0f;
  51. for (size_t v = 0; v < numOrgVertices; ++v)
  52. {
  53. float totalWeight = 1.0f;
  54. for (size_t i = 0; i < numSkinInfluences; ++i)
  55. {
  56. const float influenceWeight = (i != numSkinInfluences - 1 ? fmod(random.GetRandomFloat(), totalWeight) : totalWeight);
  57. skinningInfo->AddInfluence(v, {i, influenceWeight});
  58. totalWeight -= influenceWeight;
  59. }
  60. const float totalSkinInfluenceWeight = CalcSkinInfluencesTotalWeight(skinningInfo.get(), v);
  61. EXPECT_NEAR(totalSkinInfluenceWeight, expectedTotalWeight, 0.00001f /* tolerance */) << "totalSkinInfluenceWeight should be 1.0f or near 1.0f.";
  62. }
  63. return skinningInfo;
  64. }
  65. static AZStd::vector<MeshBuilderSkinningInfo::Influence> GetInfluenceVector(const MeshBuilderSkinningInfo* skinInfo, size_t vtxNum)
  66. {
  67. const size_t numInfluence = skinInfo->GetNumInfluences(vtxNum);
  68. AZStd::vector<MeshBuilderSkinningInfo::Influence> influences;
  69. influences.reserve(numInfluence);
  70. for (size_t i = 0; i < numInfluence; ++i)
  71. {
  72. influences.push_back(skinInfo->GetInfluence(vtxNum, i));
  73. }
  74. return influences;
  75. }
  76. static float CalcSkinInfluencesTotalWeight(const MeshBuilderSkinningInfo* skinInfo, size_t vtxNum)
  77. {
  78. AZStd::vector<MeshBuilderSkinningInfo::Influence> influences = GetInfluenceVector(skinInfo, vtxNum);
  79. return CalcTotalWeight(influences);
  80. }
  81. static float CalcTotalWeight(const AZStd::vector<MeshBuilderSkinningInfo::Influence>& influences)
  82. {
  83. float totalWeight = 0.0f;
  84. for (const auto& influence : influences)
  85. {
  86. totalWeight += influence.mWeight;
  87. }
  88. return totalWeight;
  89. }
  90. };
  91. // Test that skin influence's renormalization after Optimize still has the same sum.
  92. TEST_P(SkinInfluencesFixture, RenormalizationAfterOptimizeTests)
  93. {
  94. const SkinInfluencesTestParam& testParam = GetParam();
  95. auto meshBuilder = SetUpMeshBuilder(testParam.numOrgVertices, testParam.maxSourceInfluences);
  96. MeshBuilderSkinningInfo* testSkinInfo = meshBuilder->GetSkinningInfo();
  97. const float expectedTotalWeight = 1.0f;
  98. for (size_t v = 0; v < testParam.numOrgVertices; ++v)
  99. {
  100. AZStd::vector<MeshBuilderSkinningInfo::Influence> influences = GetInfluenceVector(testSkinInfo, v);
  101. testSkinInfo->Optimize(influences, testParam.maxInfluencesAfterOptimization);
  102. EXPECT_EQ(influences.size(), testParam.maxInfluencesAfterOptimization);
  103. const float totalWeight = CalcTotalWeight(influences);
  104. EXPECT_NEAR(totalWeight, expectedTotalWeight, 0.00001f /* tolerance */) << "totalWeight of all influences in a vertex should be 1.0f.";
  105. }
  106. }
  107. static constexpr const AZStd::array skinInfluenceTestData {
  108. SkinInfluencesTestParam {/*.numOrgVertices =*/3, /*.maxSourceInfluences =*/6, /*.maxInfluencesAfterOptimization =*/1},
  109. SkinInfluencesTestParam {/*.numOrgVertices =*/3, /*.maxSourceInfluences =*/8, /*.maxInfluencesAfterOptimization =*/2},
  110. SkinInfluencesTestParam {/*.numOrgVertices =*/6, /*.maxSourceInfluences =*/8, /*.maxInfluencesAfterOptimization =*/3},
  111. SkinInfluencesTestParam {/*.numOrgVertices =*/6, /*.maxSourceInfluences =*/12, /*.maxInfluencesAfterOptimization =*/4},
  112. SkinInfluencesTestParam {/*.numOrgVertices =*/100, /*.maxSourceInfluences =*/6, /*.maxInfluencesAfterOptimization =*/1},
  113. SkinInfluencesTestParam {/*.numOrgVertices =*/300, /*.maxSourceInfluences =*/8, /*.maxInfluencesAfterOptimization =*/2},
  114. SkinInfluencesTestParam {/*.numOrgVertices =*/500, /*.maxSourceInfluences =*/12, /*.maxInfluencesAfterOptimization =*/3},
  115. SkinInfluencesTestParam {/*.numOrgVertices =*/700, /*.maxSourceInfluences =*/12, /*.maxInfluencesAfterOptimization =*/3},
  116. };
  117. INSTANTIATE_TEST_SUITE_P(SkinInfluenceOptimizeTests,
  118. SkinInfluencesFixture,
  119. ::testing::ValuesIn(skinInfluenceTestData)
  120. );
  121. } // namespace AZ::MeshBuilder