// Copyright Epic Games, Inc. All Rights Reserved. #include "GeometryCollection/GeometryCollectionTestDecimation.h" #include "GeometryCollection/GeometryCollection.h" #include "GeometryCollection/GeometryCollectionAlgo.h" #include "GeometryCollection/GeometryCollectionUtility.h" #include "GeometryCollection/TransformCollection.h" #include "GeometryCollection/GeometryCollectionProximityUtility.h" #include "Chaos/ArrayCollectionArray.h" #include "Chaos/Particles.h" #include "Chaos/TriangleMesh.h" #include "Chaos/Vector.h" #include "UObject/Package.h" #include "UObject/UObjectGlobals.h" #include "../Resource/BoxGeometry.h" #include "../Resource/CylinderGeometry.h" #include "../Resource/EllipsoidGeometry.h" #include "../Resource/EllipsoidGeometry2.h" #include "../Resource/EllipsoidGeometry3.h" #include "../Resource/FracturedGeometry.h" #include "../Resource/SphereGeometry.h" #include "../Resource/TorusGeometry.h" //#define VERBOSE #define WRITE_OBJ_FILES namespace GeometryCollectionTest { using namespace Chaos; Chaos::FParticles BuildParticlesFeomGeomCollection(FGeometryCollection *TestCollection) { TManagedArray &Vertex = TestCollection->Vertex; const int numParticles = Vertex.Num(); Chaos::FParticles particles; particles.AddParticles(numParticles); for (int i = 0; i < numParticles; i++) particles.SetX(i, FVec3(Vertex[i][0], Vertex[i][1], Vertex[i][2])); return particles; } Chaos::FTriangleMesh BuildTriMeshFromGeomCollection(FGeometryCollection *TestCollection) { TManagedArray& Indices = TestCollection->Indices; const int numTris = Indices.Num(); TArray> tris; tris.SetNumUninitialized(numTris); for (int i = 0; i < numTris; i++) tris[i] = Chaos::TVec3(Indices[i][0], Indices[i][1], Indices[i][2]); Chaos::FTriangleMesh triMesh(MoveTemp(tris)); return triMesh; } void PrintBoolArray(const TManagedArray &bArray) { #ifdef VERBOSE for (int j = 0; j < bArray.Num(); j++) std::cout << (bArray[j] ? "1" : "0"); std::cout << std::endl; #endif } void WriteImportanceOrderObjs( FGeometryCollection *TestCollection, const TArray &importance, const TArray &coincidentVertices, const char *baseName, const char *path) { // Add an array to the vertices group for visibility TManagedArray& visibility = TestCollection->AddAttribute("VertexVisibility", FGeometryCollection::VerticesGroup); // Initialize visibility const int numParticles = importance.Num();// visibility.Num(); check(numParticles <= visibility.Num()); const int numGoodParticles = numParticles - coincidentVertices.Num(); check(numParticles >= numGoodParticles); for (int i = 0; i < numParticles; i++) visibility[i] = false; #ifdef VERBOSE std::cout << baseName << " - Num points: " << numParticles << " Num coincident: " << coincidentVertices.Num() << " - visibility:" << std::endl; #endif #ifdef WRITE_OBJ_FILES TestCollection->WriteDataToOBJFile(FString(baseName), path, true, false); #endif int i = 0; for (; i < FMath::Min(4, numGoodParticles); i++) visibility[importance[i]] = true; #ifdef WRITE_OBJ_FILES TestCollection->WriteDataToOBJFile(FString(baseName)+"_4", path, false, true); #endif PrintBoolArray(visibility); for (; i < FMath::Min(8, numGoodParticles); i++) visibility[importance[i]] = true; #ifdef WRITE_OBJ_FILES TestCollection->WriteDataToOBJFile(FString(baseName) +"_8", path, false, true); #endif PrintBoolArray(visibility); for (; i < static_cast(ceil(numGoodParticles * .1)); i++) visibility[importance[i]] = true; #ifdef WRITE_OBJ_FILES TestCollection->WriteDataToOBJFile(FString(baseName) +"_10pct", path, false, true); #endif PrintBoolArray(visibility); for (; i < static_cast(ceil(numGoodParticles * .25)); i++) visibility[importance[i]] = true; #ifdef WRITE_OBJ_FILES TestCollection->WriteDataToOBJFile(FString(baseName) +"_25pct", path, false, true); #endif PrintBoolArray(visibility); for (; i < static_cast(ceil(numGoodParticles * .5)); i++) visibility[importance[i]] = true; #ifdef WRITE_OBJ_FILES TestCollection->WriteDataToOBJFile(FString(baseName) +"_50pct", path, false, true); #endif PrintBoolArray(visibility); } bool RunGeomDecimationTest(FGeometryCollection* TestCollection, const char *BaseName, const char *OutputDir, const uint32 ExpectedHash, const bool RestrictToLocalIndexRange = false) { Chaos::FParticles Particles = BuildParticlesFeomGeomCollection(TestCollection); Chaos::FTriangleMesh TriMesh = BuildTriMeshFromGeomCollection(TestCollection); TArray CoincidentVertices; const TArray Importance = TriMesh.GetVertexImportanceOrdering(Particles.X(), &CoincidentVertices, RestrictToLocalIndexRange); check(CoincidentVertices.Num() < Importance.Num()); const int numParticles = Particles.Size(); // got the right number of indices if (RestrictToLocalIndexRange) { EXPECT_LE(Importance.Num(), numParticles); } else { EXPECT_EQ(Importance.Num(), numParticles); } EXPECT_EQ(TSet(Importance).Num(), Importance.Num()); // indices were unique WriteImportanceOrderObjs( TestCollection, Importance, CoincidentVertices, BaseName, OutputDir); auto CalcImportanceHash = [](const TArray& InImportance) { uint32 Hash = 0; for(const int32 Value : InImportance) { Hash = HashCombine(Hash, GetTypeHash(Value)); } return Hash; }; const uint32 hash = CalcImportanceHash(Importance); #ifdef VERBOSE std::cout << BaseName << " importance ordering hash: " << hash << std::endl; #endif if (hash != ExpectedHash) { std::cout << "GeometryCollectionTestDecimation - " << BaseName << " - expected importance ordering hash: " << ExpectedHash << " got: " << hash << ". Failing." << std::endl; } return hash == ExpectedHash; //return true; //todo: update hash } template bool RunGeomDecimationTest(const char *BaseName, const char *OutputDir, const uint32 ExpectedHash, const bool RestrictToLocalIndexRange = false) { TGeom Geom; FGeometryCollection* TestCollection = FGeometryCollection::NewGeometryCollection( Geom.RawVertexArray, Geom.RawIndicesArray); return RunGeomDecimationTest(TestCollection, BaseName, OutputDir, ExpectedHash, RestrictToLocalIndexRange); } template bool RunGlobalGeomDecimationTest(const char *BaseName, const char *OutputDir, const uint32 ExpectedHash, const bool RestrictToLocalIndexRange = true) { TGeom Geom; FGeometryCollection* TestCollection = FGeometryCollection::NewGeometryCollection( Geom.RawVertexArray, Geom.RawIndicesArray1); return RunGeomDecimationTest(TestCollection, BaseName, OutputDir, ExpectedHash, RestrictToLocalIndexRange); } void TestGeometryDecimation() { bool Success = true; // If E:\TestGeometry\Decimation doesn't already exist, the files aren't written. // Standalone point pools. Success &= RunGeomDecimationTest("box", "E:\\TestGeometry\\Decimation\\", 4024338882); Success &= RunGeomDecimationTest("cylinder", "E:\\TestGeometry\\Decimation\\", 2477299646); Success &= RunGeomDecimationTest("ellipsoid", "E:\\TestGeometry\\Decimation\\", 1158371240); Success &= RunGeomDecimationTest("ellipsoid2", "E:\\TestGeometry\\Decimation\\", 554754926); Success &= RunGeomDecimationTest("ellipsoid3", "E:\\TestGeometry\\Decimation\\", 2210765036); Success &= RunGeomDecimationTest("fractured", "E:\\TestGeometry\\Decimation\\", 2030682536); Success &= RunGeomDecimationTest("sphere", "E:\\TestGeometry\\Decimation\\", 4119721232); Success &= RunGeomDecimationTest("torus", "E:\\TestGeometry\\Decimation\\", 2519379615); // Geometry in a global point pool. Success &= RunGeomDecimationTest("globalFractured", "E:\\TestGeometry\\Decimation\\", 4227374796, true); Success &= RunGlobalGeomDecimationTest("globalFracturedMerged", "E:\\TestGeometry\\Decimation\\", 4227374796, true); EXPECT_TRUE(Success); } }