123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <gtest/gtest.h>
- #include <AzCore/Component/ComponentApplication.h>
- #include <AzCore/Component/Entity.h>
- #include <AzCore/Jobs/JobManagerComponent.h>
- #include <AzCore/UnitTest/TestTypes.h>
- #include <AzCore/std/smart_ptr/shared_ptr.h>
- #include <AzCore/std/smart_ptr/unique_ptr.h>
- #include <SceneAPI/SceneCore/Containers/Scene.h>
- #include <SceneAPI/SceneCore/Containers/SceneGraph.h>
- #include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
- #include <SceneAPI/SceneCore/DataTypes/GraphData/ISkinWeightData.h>
- #include <SceneAPI/SceneCore/Events/GenerateEventContext.h>
- #include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
- #include <SceneAPI/SceneData/GraphData/MeshData.h>
- #include <SceneAPI/SceneData/GraphData/SkinWeightData.h>
- #include <SceneAPI/SceneData/Groups/MeshGroup.h>
- #include <Generation/Components/MeshOptimizer/MeshOptimizerComponent.h>
- #include <InitSceneAPIFixture.h>
- namespace AZ::SceneAPI::DataTypes
- {
- void PrintTo(const ISkinWeightData::Link& link, ::std::ostream* os)
- {
- *os << '{' << link.boneId << ", " << link.weight << '}';
- }
- }
- MATCHER(VectorOfLinksEq, "")
- {
- return testing::ExplainMatchResult(
- testing::AllOf(
- testing::Field(&AZ::SceneData::GraphData::SkinWeightData::Link::boneId, testing::Eq(testing::get<0>(arg).boneId)),
- testing::Field(&AZ::SceneData::GraphData::SkinWeightData::Link::weight, testing::FloatEq(testing::get<0>(arg).weight))),
- testing::get<1>(arg), result_listener);
- }
- MATCHER(VectorOfVectorOfLinksEq, "")
- {
- return testing::ExplainMatchResult(
- testing::UnorderedPointwise(VectorOfLinksEq(), testing::get<0>(arg)), testing::get<1>(arg), result_listener);
- }
- namespace SceneProcessing
- {
- class VertexDeduplicationFixture
- : public SceneProcessing::InitSceneAPIFixture
- {
- public:
- void SetUp() override
- {
- SceneProcessing::InitSceneAPIFixture::SetUp();
- AZ::ComponentApplication::StartupParameters startupParameters;
- startupParameters.m_loadSettingsRegistry = false;
- m_systemEntity = m_app.Create({}, startupParameters);
- m_systemEntity->AddComponent(aznew AZ::JobManagerComponent());
- m_systemEntity->Init();
- m_systemEntity->Activate();
- }
- void TearDown() override
- {
- m_systemEntity->Deactivate();
- SceneProcessing::InitSceneAPIFixture::TearDown();
- }
- static AZStd::unique_ptr<AZ::SceneAPI::DataTypes::IMeshData> MakePlaneMesh()
- {
- // Create a simple plane with 2 triangles, 6 total vertices, 2 shared vertices
- // 0,5 --- 1
- // | \ |
- // | \ |
- // | \ |
- // | \ |
- // 4 --- 2,3
- const AZStd::array planeVertexPositions = {
- AZ::Vector3{0.0f, 0.0f, 0.0f},
- AZ::Vector3{0.0f, 0.0f, 1.0f},
- AZ::Vector3{1.0f, 0.0f, 1.0f},
- AZ::Vector3{1.0f, 0.0f, 1.0f},
- AZ::Vector3{1.0f, 0.0f, 0.0f},
- AZ::Vector3{0.0f, 0.0f, 0.0f},
- };
- auto mesh = AZStd::make_unique<AZ::SceneData::GraphData::MeshData>();
- int i = 0;
- for (const AZ::Vector3& position : planeVertexPositions)
- {
- mesh->AddPosition(position);
- mesh->AddNormal(AZ::Vector3::CreateAxisY());
- // This assumes that the data coming from the import process gives a unique control point
- // index to every vertex. This follows the behavior of the AssImp library.
- mesh->SetVertexIndexToControlPointIndexMap(i, i);
- ++i;
- }
- mesh->AddFace({0, 1, 2}, 0);
- mesh->AddFace({3, 4, 5}, 0);
- return mesh;
- }
- static AZStd::unique_ptr<AZ::SceneData::GraphData::SkinWeightData> MakeSkinData(
- const AZStd::vector<AZStd::vector<AZ::SceneData::GraphData::SkinWeightData::Link>>& sourceLinks)
- {
- auto skinWeights = AZStd::make_unique<AZ::SceneData::GraphData::SkinWeightData>();
- skinWeights->ResizeContainerSpace(sourceLinks.size());
- for (size_t vertexIndex = 0; vertexIndex < sourceLinks.size(); ++vertexIndex)
- {
- for (const auto& link : sourceLinks[vertexIndex])
- {
- // Make sure the bone is added to the skin weights
- skinWeights->GetBoneId(AZStd::to_string(link.boneId));
- skinWeights->AppendLink(vertexIndex, link);
- }
- }
- return skinWeights;
- }
- static AZStd::unique_ptr<AZ::SceneData::GraphData::SkinWeightData> MakeDuplicateSkinData()
- {
- auto skinWeights = AZStd::make_unique<AZ::SceneData::GraphData::SkinWeightData>();
- skinWeights->ResizeContainerSpace(6);
- // Add bones 0 and 1 to the skin weights
- skinWeights->GetBoneId("0");
- skinWeights->GetBoneId("1");
- // Vertices 0,5 and 2,3 have duplicate skin data, in addition to duplicate positions
- skinWeights->AppendLink(0, { /*.boneId=*/0, /*.weight=*/1 });
- skinWeights->AppendLink(1, { /*.boneId=*/1, /*.weight=*/1 });
- skinWeights->AppendLink(2, { /*.boneId=*/0, /*.weight=*/1 });
- skinWeights->AppendLink(3, { /*.boneId=*/0, /*.weight=*/1 });
- skinWeights->AppendLink(4, { /*.boneId=*/2, /*.weight=*/1 });
- skinWeights->AppendLink(5, { /*.boneId=*/0, /*.weight=*/1 });
- return skinWeights;
- }
- static void TestSkinDuplication(
- const AZStd::shared_ptr<AZ::SceneData::GraphData::SkinWeightData> skinData,
- const AZStd::vector<AZStd::vector<AZ::SceneData::GraphData::SkinWeightData::Link>>& expectedLinks)
- {
- AZ::SceneAPI::Containers::Scene scene("testScene");
- AZ::SceneAPI::Containers::SceneGraph& graph = scene.GetGraph();
- const auto meshNodeIndex = graph.AddChild(graph.GetRoot(), "testMesh", MakePlaneMesh());
- const auto skinDataNodeIndex = graph.AddChild(meshNodeIndex, "skinData", skinData);
- graph.MakeEndPoint(skinDataNodeIndex);
- // The original source mesh should have 6 vertices
- EXPECT_EQ(
- AZStd::rtti_pointer_cast<AZ::SceneAPI::DataTypes::IMeshData>(graph.GetNodeContent(meshNodeIndex))->GetVertexCount(), 6);
- auto meshGroup = AZStd::make_unique<AZ::SceneAPI::SceneData::MeshGroup>();
- meshGroup->GetSceneNodeSelectionList().AddSelectedNode("testMesh");
- scene.GetManifest().AddEntry(AZStd::move(meshGroup));
- AZ::SceneGenerationComponents::MeshOptimizerComponent component;
- AZ::SceneAPI::Events::GenerateSimplificationEventContext context(scene, "pc");
- component.OptimizeMeshes(context);
- AZ::SceneAPI::Containers::SceneGraph::NodeIndex optimizedNodeIndex =
- graph.Find(AZStd::string("testMesh_").append(AZ::SceneAPI::Utilities::OptimizedMeshSuffix));
- ASSERT_TRUE(optimizedNodeIndex.IsValid()) << "Mesh optimizer did not add an optimized version of the mesh";
- const auto& optimizedMesh =
- AZStd::rtti_pointer_cast<AZ::SceneAPI::DataTypes::IMeshData>(graph.GetNodeContent(optimizedNodeIndex));
- ASSERT_TRUE(optimizedMesh);
- AZ::SceneAPI::Containers::SceneGraph::NodeIndex optimizedSkinDataNodeIndex =
- graph.Find(AZStd::string("testMesh_").append(AZ::SceneAPI::Utilities::OptimizedMeshSuffix).append(".skinWeights"));
- ASSERT_TRUE(optimizedSkinDataNodeIndex.IsValid()) << "Mesh optimizer did not add an optimized version of the skin data";
- const auto& optimizedSkinWeights =
- AZStd::rtti_pointer_cast<AZ::SceneAPI::DataTypes::ISkinWeightData>(graph.GetNodeContent(optimizedSkinDataNodeIndex));
- ASSERT_TRUE(optimizedSkinWeights);
- AZStd::vector<AZStd::vector<AZ::SceneData::GraphData::SkinWeightData::Link>> gotLinks(optimizedMesh->GetVertexCount());
- for (unsigned int vertexIndex = 0; vertexIndex < optimizedMesh->GetVertexCount(); ++vertexIndex)
- {
- for (size_t linkIndex = 0; linkIndex < optimizedSkinWeights->GetLinkCount(vertexIndex); ++linkIndex)
- {
- gotLinks[vertexIndex].emplace_back(optimizedSkinWeights->GetLink(vertexIndex, linkIndex));
- }
- }
- EXPECT_THAT(gotLinks, testing::Pointwise(VectorOfVectorOfLinksEq(), expectedLinks));
- EXPECT_EQ(optimizedMesh->GetVertexCount(), expectedLinks.size());
- }
- private:
- AZ::ComponentApplication m_app;
- AZ::Entity* m_systemEntity;
- };
- TEST_F(VertexDeduplicationFixture, CanDeduplicateVertices)
- {
- AZ::SceneAPI::Containers::Scene scene("testScene");
- AZ::SceneAPI::Containers::SceneGraph& graph = scene.GetGraph();
- const auto meshNodeIndex = graph.AddChild(graph.GetRoot(), "testMesh", MakePlaneMesh());
- // The original source mesh should have 6 vertices
- EXPECT_EQ(AZStd::rtti_pointer_cast<AZ::SceneAPI::DataTypes::IMeshData>(graph.GetNodeContent(meshNodeIndex))->GetVertexCount(), 6);
- auto meshGroup = AZStd::make_unique<AZ::SceneAPI::SceneData::MeshGroup>();
- meshGroup->GetSceneNodeSelectionList().AddSelectedNode("testMesh");
- scene.GetManifest().AddEntry(AZStd::move(meshGroup));
- AZ::SceneGenerationComponents::MeshOptimizerComponent component;
- AZ::SceneAPI::Events::GenerateSimplificationEventContext context(scene, "pc");
- component.OptimizeMeshes(context);
- AZ::SceneAPI::Containers::SceneGraph::NodeIndex optimizedNodeIndex = graph.Find(AZStd::string("testMesh_").append(AZ::SceneAPI::Utilities::OptimizedMeshSuffix));
- ASSERT_TRUE(optimizedNodeIndex.IsValid()) << "Mesh optimizer did not add an optimized version of the mesh";
- const auto& optimizedMesh = AZStd::rtti_pointer_cast<AZ::SceneAPI::DataTypes::IMeshData>(graph.GetNodeContent(optimizedNodeIndex));
- ASSERT_TRUE(optimizedMesh);
- // The optimized mesh should have 4 vertices, the 2 shared vertices are welded together
- EXPECT_EQ(optimizedMesh->GetVertexCount(), 4);
- }
- TEST_F(VertexDeduplicationFixture, DeduplicatedVerticesKeepUniqueSkinInfluences)
- {
- // Vertices 0,5 and 2,3 have duplicate positions, but unique links,
- // so none of the vertices should be de-duplicated
- // and the sourceLinks should be the same as the expected links
- const AZStd::vector<AZStd::vector<AZ::SceneData::GraphData::SkinWeightData::Link>> sourceLinks{
- /*0*/ { { 0, 1.0f } },
- /*1*/ { { 0, 1.0f } },
- /*2*/ { { 0, 1.0f } },
- /*3*/ { { 1, 1.0f } },
- /*4*/ { { 1, 1.0f } },
- /*5*/ { { 1, 1.0f } },
- };
- TestSkinDuplication(MakeSkinData(sourceLinks), sourceLinks);
- }
- TEST_F(VertexDeduplicationFixture, DeduplicatedVerticesDeduplicateSkinInfluences)
- {
- // Vertices 0,5 and 2,3 have duplicate positions, and also duplicate links,
- // so they should be de-duplicated and the expected links
- // should have two fewer links
- const AZStd::vector<AZStd::vector<AZ::SceneData::GraphData::SkinWeightData::Link>> sourceLinks{
- /*0*/ { { 0, 1.0f } },
- /*1*/ { { 1, 1.0f } },
- /*2*/ { { 0, 1.0f } },
- /*3*/ { { 0, 1.0f } },
- /*4*/ { { 2, 1.0f } },
- /*5*/ { { 0, 1.0f } },
- };
- const AZStd::vector<AZStd::vector<AZ::SceneData::GraphData::SkinWeightData::Link>> expectedLinks{
- /*0*/ { { 0, 1.0f } },
- /*1*/ { { 1, 1.0f } },
- /*2*/ { { 0, 1.0f } },
- /*3*/ { { 2, 1.0f } },
- };
- TestSkinDuplication(MakeSkinData(sourceLinks), expectedLinks);
- }
- } // namespace SceneProcessing
|