Files
UnrealEngine/Engine/Plugins/Experimental/Fracture/Source/FractureEngine/Private/FractureEngineSelection.cpp
2025-05-18 13:04:45 +08:00

429 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "FractureEngineSelection.h"
#include "Dataflow/DataflowSelection.h"
#include "GeometryCollection/GeometryCollectionClusteringUtility.h"
#include "Algo/RemoveIf.h"
#include "GeometryCollection/GeometryCollection.h"
#include "GeometryCollection/TransformCollection.h"
static void ConvertSelectionSetToSelectionArr(const TSet<int32>& SelectionSet, TArray<int32>& SelectionArr)
{
SelectionArr.Empty(SelectionSet.Num());
for (auto& BoneIdx : SelectionSet)
{
SelectionArr.Add(BoneIdx);
}
}
bool FFractureEngineSelection::IsBoneSelectionValid(const FManagedArrayCollection& Collection, const TArray<int32>& SelectedBones)
{
return IsSelectionValid(Collection, SelectedBones, FGeometryCollection::TransformGroup);
}
bool FFractureEngineSelection::IsSelectionValid(const FManagedArrayCollection& Collection, const TArray<int32>& SelectedItems, const FName ItemGroup)
{
int32 NumItems = Collection.NumElements(ItemGroup);
for (int32 Idx : SelectedItems)
{
if (Idx < 0 || Idx >= NumItems)
{
return false;
}
}
return true;
}
void FFractureEngineSelection::GetRootBones(const FManagedArrayCollection& Collection, TArray<int32>& RootBonesOut)
{
if (Collection.HasGroup(FGeometryCollection::TransformGroup) &&
Collection.HasAttribute("Parent", FGeometryCollection::TransformGroup))
{
const TManagedArray<int32>& Parents = Collection.GetAttribute<int32>("Parent", FGeometryCollection::TransformGroup);
for (int32 Idx = 0; Idx < Parents.Num(); ++Idx)
{
if (Parents[Idx] == FGeometryCollection::Invalid)
{
RootBonesOut.Add(Idx);
}
}
}
}
void FFractureEngineSelection::GetRootBones(const FManagedArrayCollection& Collection, FDataflowTransformSelection& TransformSelection)
{
if (Collection.HasGroup(FGeometryCollection::TransformGroup) &&
Collection.HasAttribute("Parent", FGeometryCollection::TransformGroup))
{
const TManagedArray<int32>& Parents = Collection.GetAttribute<int32>("Parent", FGeometryCollection::TransformGroup);
for (int32 Idx = 0; Idx < Parents.Num(); ++Idx)
{
if (Parents[Idx] == FGeometryCollection::Invalid)
{
TransformSelection.SetSelected(Idx);
}
}
}
}
void FFractureEngineSelection::SelectParent(const FManagedArrayCollection& Collection, TArray<int32>& SelectedBones)
{
if (Collection.HasGroup(FGeometryCollection::TransformGroup) &&
Collection.HasAttribute("Parent", FGeometryCollection::TransformGroup))
{
const TManagedArray<int32>& Parents = Collection.GetAttribute<int32>("Parent", FGeometryCollection::TransformGroup);
TSet<int32> NewSelection;
for (int32 Bone : SelectedBones)
{
int32 ParentBone = Parents[Bone];
if (ParentBone != FGeometryCollection::Invalid)
{
NewSelection.Add(ParentBone);
}
}
ConvertSelectionSetToSelectionArr(NewSelection, SelectedBones);
}
}
void FFractureEngineSelection::SelectParent(const FManagedArrayCollection& Collection, FDataflowTransformSelection& TransformSelection)
{
TArray<int32> SelectionArr;
TransformSelection.AsArray(SelectionArr);
SelectParent(Collection, SelectionArr);
TransformSelection.SetFromArray(SelectionArr);
}
void FFractureEngineSelection::SelectChildren(const FManagedArrayCollection& Collection, TArray<int32>& SelectedBones)
{
if (Collection.HasGroup(FGeometryCollection::TransformGroup) &&
Collection.HasAttribute("Children", FGeometryCollection::TransformGroup))
{
const TManagedArray<TSet<int32>>& Children = Collection.GetAttribute<TSet<int32>>("Children", FGeometryCollection::TransformGroup);
TSet<int32> NewSelection;
for (int32 Bone : SelectedBones)
{
if (Children[Bone].IsEmpty())
{
NewSelection.Add(Bone);
continue;
}
for (int32 Child : Children[Bone])
{
NewSelection.Add(Child);
}
}
ConvertSelectionSetToSelectionArr(NewSelection, SelectedBones);
}
}
void FFractureEngineSelection::SelectChildren(const FManagedArrayCollection& Collection, FDataflowTransformSelection& TransformSelection)
{
TArray<int32> SelectionArr;
TransformSelection.AsArray(SelectionArr);
SelectChildren(Collection, SelectionArr);
TransformSelection.SetFromArray(SelectionArr);
}
void FFractureEngineSelection::SelectSiblings(const FManagedArrayCollection& Collection, TArray<int32>& SelectedBones)
{
if (Collection.HasGroup(FGeometryCollection::TransformGroup) &&
Collection.HasAttribute("Parent", FGeometryCollection::TransformGroup) &&
Collection.HasAttribute("Children", FGeometryCollection::TransformGroup))
{
const TManagedArray<int32>& Parents = Collection.GetAttribute<int32>("Parent", FGeometryCollection::TransformGroup);
const TManagedArray<TSet<int32>>& Children = Collection.GetAttribute<TSet<int32>>("Children", FGeometryCollection::TransformGroup);
TSet<int32> NewSelection;
for (int32 Bone : SelectedBones)
{
int32 ParentBone = Parents[Bone];
if (ParentBone != FGeometryCollection::Invalid)
{
for (int32 Child : Children[ParentBone])
{
NewSelection.Add(Child);
}
}
}
ConvertSelectionSetToSelectionArr(NewSelection, SelectedBones);
}
}
void FFractureEngineSelection::SelectSiblings(const FManagedArrayCollection& Collection, FDataflowTransformSelection& TransformSelection)
{
TArray<int32> SelectionArr;
TransformSelection.AsArray(SelectionArr);
SelectSiblings(Collection, SelectionArr);
TransformSelection.SetFromArray(SelectionArr);
}
void FFractureEngineSelection::SelectLevel(const FManagedArrayCollection& Collection, TArray<int32>& SelectedBones)
{
if (Collection.HasGroup(FGeometryCollection::TransformGroup) &&
Collection.HasAttribute("Level", FGeometryCollection::TransformGroup))
{
const TManagedArray<int32>& Levels = Collection.GetAttribute<int32>("Levels", FGeometryCollection::TransformGroup);
TSet<int32> NewSelection;
for (int32 Bone : SelectedBones)
{
int32 Level = Levels[Bone];
for (int32 TransformIdx = 0; TransformIdx < Collection.NumElements(FTransformCollection::TransformGroup); ++TransformIdx)
{
if (Levels[TransformIdx] == Level)
{
NewSelection.Add(TransformIdx);
}
}
}
ConvertSelectionSetToSelectionArr(NewSelection, SelectedBones);
}
}
void FFractureEngineSelection::SelectLevel(const FManagedArrayCollection& Collection, FDataflowTransformSelection& TransformSelection)
{
TArray<int32> SelectionArr;
TransformSelection.AsArray(SelectionArr);
SelectLevel(Collection, SelectionArr);
TransformSelection.SetFromArray(SelectionArr);
}
void FFractureEngineSelection::SelectContact(FGeometryCollection& GeometryCollection, TArray<int32>& SelectedBones)
{
if (GeometryCollection.HasGroup(FGeometryCollection::TransformGroup) &&
GeometryCollection.HasAttribute("TransformIndex", FGeometryCollection::GeometryGroup) &&
GeometryCollection.HasAttribute("TransformToGeometryIndex", FGeometryCollection::TransformGroup) &&
GeometryCollection.HasGroup(FGeometryCollection::GeometryGroup) &&
GeometryCollection.HasAttribute("Proximity", FGeometryCollection::GeometryGroup))
{
FGeometryCollectionProximityUtility ProximityUtility(&GeometryCollection);
ProximityUtility.RequireProximity();
const TManagedArray<int32>& TransformIndex = GeometryCollection.GetAttribute<int32>("TransformIndex", FGeometryCollection::GeometryGroup);
const TManagedArray<int32>& TransformToGeometryIndex = GeometryCollection.GetAttribute<int32>("TransformToGeometryIndex", FGeometryCollection::TransformGroup);
const TManagedArray<TSet<int32>>& Proximity = GeometryCollection.GetAttribute<TSet<int32>>("Proximity", FGeometryCollection::GeometryGroup);
TSet<int32> NewSelection;
for (int32 Bone : SelectedBones)
{
NewSelection.Add(Bone);
int32 GeometryIdx = TransformToGeometryIndex[Bone];
if (GeometryIdx != INDEX_NONE)
{
const TSet<int32>& Neighbors = Proximity[GeometryIdx];
for (int32 NeighborGeometryIndex : Neighbors)
{
NewSelection.Add(TransformIndex[NeighborGeometryIndex]);
}
}
}
ConvertSelectionSetToSelectionArr(NewSelection, SelectedBones);
}
}
void FFractureEngineSelection::SelectContact(FGeometryCollection& GeometryCollection, FDataflowTransformSelection& TransformSelection)
{
TArray<int32> SelectionArr;
TransformSelection.AsArray(SelectionArr);
SelectContact(GeometryCollection, SelectionArr);
TransformSelection.SetFromArray(SelectionArr);
}
void FFractureEngineSelection::SelectLeaf(const FGeometryCollection& GeometryCollection, TArray<int32>& SelectedBones)
{
if (GeometryCollection.HasGroup(FGeometryCollection::TransformGroup) &&
GeometryCollection.HasAttribute("Level", FGeometryCollection::TransformGroup) &&
GeometryCollection.HasAttribute("SimulationType", FGeometryCollection::TransformGroup))
{
SelectedBones.Empty();
int32 ViewLevel = -1;
FGeometryCollectionClusteringUtility::GetBonesToLevel(&GeometryCollection, ViewLevel, SelectedBones, true, true);
const TManagedArray<int32>& SimType = GeometryCollection.GetAttribute<int32>("SimulationType", FGeometryCollection::TransformGroup);
const TManagedArray<int32>* Levels = GeometryCollection.FindAttributeTyped<int32>("Level", FGeometryCollection::TransformGroup);
SelectedBones.SetNum(Algo::RemoveIf(SelectedBones, [&](int32 BoneIdx)
{
return SimType[BoneIdx] != FGeometryCollection::ESimulationTypes::FST_Rigid
|| (ViewLevel != -1 && Levels && (*Levels)[BoneIdx] != ViewLevel);
}));
}
}
void FFractureEngineSelection::SelectLeaf(const FGeometryCollection& GeometryCollection, FDataflowTransformSelection& TransformSelection)
{
TArray<int32> SelectionArr;
SelectLeaf(GeometryCollection, SelectionArr);
TransformSelection.SetFromArray(SelectionArr);
}
void FFractureEngineSelection::SelectCluster(const FGeometryCollection& GeometryCollection, TArray<int32>& SelectedBones)
{
if (GeometryCollection.HasGroup(FGeometryCollection::TransformGroup) &&
GeometryCollection.HasAttribute("Level", FGeometryCollection::TransformGroup) &&
GeometryCollection.HasAttribute("SimulationType", FGeometryCollection::TransformGroup))
{
SelectedBones.Empty();
int32 ViewLevel = -1;
FGeometryCollectionClusteringUtility::GetBonesToLevel(&GeometryCollection, ViewLevel, SelectedBones, true, true);
const TManagedArray<int32>& SimType = GeometryCollection.GetAttribute<int32>("SimulationType", FGeometryCollection::TransformGroup);
const TManagedArray<int32>* Levels = GeometryCollection.FindAttributeTyped<int32>("Level", FGeometryCollection::TransformGroup);
SelectedBones.SetNum(Algo::RemoveIf(SelectedBones, [&](int32 BoneIdx)
{
return SimType[BoneIdx] != FGeometryCollection::ESimulationTypes::FST_Rigid
|| (ViewLevel != -1 && Levels && (*Levels)[BoneIdx] != ViewLevel);
}));
}
}
void FFractureEngineSelection::SelectCluster(const FGeometryCollection& GeometryCollection, FDataflowTransformSelection& TransformSelection)
{
TArray<int32> SelectionArr;
SelectCluster(GeometryCollection, SelectionArr);
TransformSelection.SetFromArray(SelectionArr);
}
static void RandomShuffleArray(TArray<int32>& InArray, const bool Deterministic, const float RandomSeed)
{
FRandomStream Stream(RandomSeed);
int32 LastIndex = InArray.Num() - 1;
static const int32 NumIterationsMin = 10;
static const int32 NumIterationsMax = 50;
int32 NumIterations = Deterministic ? Stream.RandRange(NumIterationsMin, NumIterationsMax) : FMath::RandRange(NumIterationsMin, NumIterationsMax);
for (int32 IterIdx = 0; IterIdx < NumIterations; ++IterIdx)
{
for (int32 Idx = 0; Idx <= LastIndex; ++Idx)
{
int32 Index;
if (Deterministic)
{
Index = Stream.RandRange(Idx, LastIndex);
}
else
{
Index = FMath::RandRange(Idx, LastIndex);
}
if (Idx != Index)
{
InArray.Swap(Idx, Index);
}
}
}
}
void FFractureEngineSelection::SelectByPercentage(TArray<int32>& SelectedBones, const int32 Percentage, const bool Deterministic, const float RandomSeed)
{
RandomShuffleArray(SelectedBones, Deterministic, RandomSeed);
int32 NewNumElements = FMath::RoundToInt32(SelectedBones.Num() * Percentage * 0.01f);
SelectedBones.SetNum(NewNumElements);
}
void FFractureEngineSelection::SelectByPercentage(FDataflowTransformSelection& TransformSelection, const int32 Percentage, const bool Deterministic, const float RandomSeed)
{
TArray<int32> SelectionArr;
TransformSelection.AsArray(SelectionArr);
SelectByPercentage(SelectionArr, Percentage, Deterministic, RandomSeed);
TransformSelection.SetFromArray(SelectionArr);
}
void FFractureEngineSelection::SelectBySize(FGeometryCollection& GeometryCollection, TArray<int32>& SelectedBones, const float SizeMin, const float SizeMax)
{
if (GeometryCollection.HasGroup(FGeometryCollection::TransformGroup) &&
GeometryCollection.HasAttribute("Size", FGeometryCollection::TransformGroup))
{
SelectedBones.Empty();
FGeometryCollectionConvexUtility::SetVolumeAttributes(&GeometryCollection);
const TManagedArray<float>& Sizes = GeometryCollection.GetAttribute<float>("Size", FTransformCollection::TransformGroup);
for (int32 BoneIdx = 0; BoneIdx < Sizes.Num(); ++BoneIdx)
{
if (Sizes[BoneIdx] >= SizeMin && Sizes[BoneIdx] <= SizeMax)
{
SelectedBones.Add(BoneIdx);
}
}
}
}
void FFractureEngineSelection::SelectBySize(FGeometryCollection& GeometryCollection, FDataflowTransformSelection& TransformSelection, const float SizeMin, const float SizeMax)
{
TArray<int32> SelectionArr;
SelectBySize(GeometryCollection, SelectionArr, SizeMin, SizeMax);
TransformSelection.SetFromArray(SelectionArr);
}
void FFractureEngineSelection::SelectByVolume(FGeometryCollection& GeometryCollection, TArray<int32>& SelectedBones, const float VolumeMin, const float VolumeMax)
{
if (GeometryCollection.HasGroup(FGeometryCollection::TransformGroup) &&
GeometryCollection.HasAttribute("Volume", FGeometryCollection::TransformGroup))
{
SelectedBones.Empty();
FGeometryCollectionConvexUtility::SetVolumeAttributes(&GeometryCollection);
const TManagedArray<float>& Volumes = GeometryCollection.GetAttribute<float>("Volume", FTransformCollection::TransformGroup);
for (int32 BoneIdx = 0; BoneIdx < Volumes.Num(); ++BoneIdx)
{
if (Volumes[BoneIdx] >= VolumeMin && Volumes[BoneIdx] <= VolumeMax)
{
SelectedBones.Add(BoneIdx);
}
}
}
}
void FFractureEngineSelection::SelectByVolume(FGeometryCollection& GeometryCollection, FDataflowTransformSelection& TransformSelection, const float VolumeMin, const float VolumeMax)
{
TArray<int32> SelectionArr;
SelectByVolume(GeometryCollection, SelectionArr, VolumeMin, VolumeMax);
TransformSelection.SetFromArray(SelectionArr);
}