From 47a868cfb784e66f62b58e38218137e0cee40ede Mon Sep 17 00:00:00 2001 From: Timon Roemer <t.roemer@vis.rwth-aachen.de> Date: Fri, 26 Jul 2024 16:56:34 +0200 Subject: [PATCH] Adds full MagicWand support with visuals --- Content/MagicWandMap.umap | Bin 45143 -> 45255 bytes Source/MetaCastBachelor/MagicWand.cpp | 173 ++++++++++-------- Source/MetaCastBachelor/MagicWand.h | 12 +- .../MagicWandSelectionManager.h | 166 +++++++++++------ 4 files changed, 214 insertions(+), 137 deletions(-) diff --git a/Content/MagicWandMap.umap b/Content/MagicWandMap.umap index ea280d4c79087af91cda2dfbfa349f9795314ad5..e503e7a9a9be7ad7408eafed26eba9c64434a752 100644 GIT binary patch delta 4228 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 I8;|J$08pl1rvLx| delta 4638 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 AGynhq diff --git a/Source/MetaCastBachelor/MagicWand.cpp b/Source/MetaCastBachelor/MagicWand.cpp index e5404af..f78a34a 100644 --- a/Source/MetaCastBachelor/MagicWand.cpp +++ b/Source/MetaCastBachelor/MagicWand.cpp @@ -9,7 +9,7 @@ class FMagicWandSelectionTask; -UMagicWand::UMagicWand() : World(nullptr) +UMagicWand::UMagicWand() : CurrentSelection(), World(nullptr) { // Create the procedural mesh component ProceduralMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh")); @@ -47,7 +47,7 @@ void UMagicWand::InitProceduralMesh() const MarchingCubeMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); MarchingCubeMesh->SetMobility(EComponentMobility::Movable); MarchingCubeMesh->SetVisibility(true); - MarchingCubeMesh->AttachToComponent(MyPointCloud->PointCloudVisualizer, FAttachmentTransformRules::SnapToTargetIncludingScale); + MarchingCubeMesh->AttachToComponent(MyPointCloud->GetRootComponent(), FAttachmentTransformRules::SnapToTargetIncludingScale); } void UMagicWand::InitReferences() @@ -120,9 +120,9 @@ void UMagicWand::GenerateVoxelMeshWithCubes(const TArray<int32> Voxels) const }); } -void UMagicWand::GenerateVoxelMeshSmooth(const TSet<int32> Voxels) +void UMagicWand::GenerateVoxelMeshSmooth(const TSet<int32>& Voxels) { - if(IsMarchingCubesRunning) + if(IsMarchingCubesRunning || Voxels.IsEmpty()) return; IsMarchingCubesRunning = true; @@ -141,7 +141,21 @@ void UMagicWand::GenerateVoxelMeshSmooth(const TSet<int32> Voxels) UE::Geometry::FMarchingCubes MarchingCubes; MarchingCubes.Bounds = UE::Geometry::TAxisAlignedBox3(Min, Max); - MarchingCubes.CubeSize = MarchingCubeSize == 0 ? MyDensityField->GetStep().X : MarchingCubeSize; + + //MarchingCubes.CubeSize = MarchingCubeSize == 0 ? MyDensityField->GetStep().X : MarchingCubeSize; + const float Volume = MarchingCubes.Bounds.Volume(); + // Logarithmic scaling of the cube size based on volume + if(Volume > 50000) { + MarchingCubes.CubeSize = (3.0f * log10((Volume + 500) / 1000) - 4); + }else if(Volume > 500000) + { + MarchingCubes.CubeSize = 4.3; + }else { + MarchingCubes.CubeSize = MyDensityField->GetStep().X; + } + + MarchingCubes.CubeSize = FMath::Clamp(MarchingCubes.CubeSize, MyDensityField->GetStep().X, 10.0f); + MarchingCubes.CancelF = [this]() { return AbortMarchingCubes; }; @@ -298,6 +312,8 @@ void UMagicWand::HandleMetaSelectPressed(const FInputActionInstance& Instance) const FVector SelectionStartPositionLocal = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(SelectionStartPositionWorld); FindSeed(SelectionStartPositionLocal); + if(SeedPointIndex == INDEX_NONE) return; + CountSelectionTime = 1 / MagicWandUpdatesPerSecond; IsMagicWandInitialized = true; } @@ -315,31 +331,36 @@ void UMagicWand::HandleMetaSelectReleased(const FInputActionInstance& Instance) { UGameplayStatics::PlaySound2D(World, SelectionEndSound); } + if(!CurrentSelection.IsValid()) return; - TArray<int32> SelectionArray = CurrentSelection.GetPointIndexList(); - for(const int Index : SelectionArray) + TArray<FAtomicBool> SelectionArray = CurrentSelection.Get()->GetSelectionFlags(); + for(int i = 0; i < SelectionArray.Num(); i++) { - if(MyPointCloud && MyPointCloud->SelectionFlags.IsValidIndex(Index)) + if(SelectionArray[i].Value.load()) { - MyPointCloud->SelectionFlags[Index] = true; + MyPointCloud->SelectionFlags[i] = true; } } - - CurrentSelection = FSelectionManager(CurrentSelection.GetSelectionFlags().Num()); + + //CurrentSelection.Reset(); } void UMagicWand::HandleMetaEraseReleased(const FInputActionInstance& Instance) { Super::HandleMetaEraseReleased(Instance); - if(NumberThreads.GetValue() > 0 || CurrentSelection.SelectedCount() == 0) + if(NumberThreads.GetValue() > 0) { return; } //AbortMagicWand.Store(true); - CurrentSelection = FSelectionManager(CurrentSelection.GetSelectionFlags().Num()); - MyPointCloud->SelectionFlags = CurrentSelection.GetSelectionFlags(); + CurrentSelection = MakeShared<FSelectionManager>(MyPointCloud->SelectionFlags.Num(), MyDensityField->GetVoxelNumber()); + + for(int i = 0; i < MyPointCloud->SelectionFlags.Num(); i++) + { + MyPointCloud->SelectionFlags[i] = false; + } } // MAGIC WAND SELECTION @@ -359,27 +380,23 @@ void UMagicWand::PerformMagicWandSelection(const float ProximityThreshold) return; } - if (SeedPointIndex != INDEX_NONE) - { - if(SelectionStartSound) - { - UGameplayStatics::PlaySound2D(World, SelectionStartSound); - } - - CurrentSelection = GetSelectionResultsFromProximity(ProximityThreshold); - CurrentSelection.SelectIndex(SeedPointIndex); + if (SeedPointIndex == INDEX_NONE) return; + + if(SelectionStartSound) UGameplayStatics::PlaySound2D(World, SelectionStartSound); + + CurrentSelection = GetSelectionCacheResultCopy(ProximityThreshold); + CurrentSelection.Get()->SelectIndex(SeedPointIndex); - TQueue<int32>* ProcessQueue = new TQueue<int32>{}; + TQueue<int32>* ProcessQueue = new TQueue<int32>{}; - for (int32 Index : CurrentSelection.GetPointIndexList()) - { - ProcessQueue->Enqueue(Index); - } - - FMagicWandSelectionTask* Task = new FMagicWandSelectionTask(this, ProcessQueue, ProximityThreshold, CurrentSelection.SelectedCount()); - NumberThreads.Increment(); - Task->Start(); + for (int32 Index : CurrentSelection.Get()->GetPointsAsIndexList()) + { + ProcessQueue->Enqueue(Index); } + + FMagicWandSelectionTask* Task = new FMagicWandSelectionTask(this, ProcessQueue, ProximityThreshold, CurrentSelection.Get()->SelectedCount()); + NumberThreads.Increment(); + Task->Start(); } void UMagicWand::FindSeed(const FVector& InputPosition) @@ -435,9 +452,7 @@ void UMagicWand::ExpandFromAllPointsInQueue(TQueue<int32>* ProcessQueue, const f { //UE_LOG(LogTemp, Warning, TEXT("Opened New Thread! Number of Threads now: %d"), NumberThreads.GetValue()); - TSet<int32> MyFullVoxelList; - MyFullVoxelList.Reserve(1000000); - FVoxelPointLookupTable MyVoxelPointLookupTable = *MyDensityField->VoxelPointLookupTable; + const FVoxelPointLookupTable MyVoxelPointLookupTable = *MyDensityField->VoxelPointLookupTable; while (!ProcessQueue->IsEmpty()) { @@ -450,7 +465,7 @@ void UMagicWand::ExpandFromAllPointsInQueue(TQueue<int32>* ProcessQueue, const f { const FVector CurrentQueuePointPosition = MyPointCloud->PositionVectors[CurrentQueuePointIndex]; - ExpandFromPoint(ProcessQueue, ProximityThreshold, ThreadLoad, CurrentQueuePointPosition, &MyFullVoxelList, &MyVoxelPointLookupTable); + ExpandFromPoint(ProcessQueue, ProximityThreshold, ThreadLoad, CurrentQueuePointPosition, &MyVoxelPointLookupTable); } ThreadLoad--; @@ -459,7 +474,7 @@ void UMagicWand::ExpandFromAllPointsInQueue(TQueue<int32>* ProcessQueue, const f FinishSelectionThread(ProximityThreshold); } -void UMagicWand::ExpandFromPoint(TQueue<int32>* ProcessQueue, const float ProximityThreshold, int& ThreadLoad, const FVector& ExpansionPoint, TSet<int32>* MyFullVoxelList, FVoxelPointLookupTable* MyVoxelPointLookupTable) +void UMagicWand::ExpandFromPoint(TQueue<int32>* ProcessQueue, const float ProximityThreshold, int& ThreadLoad, const FVector& ExpansionPoint, const FVoxelPointLookupTable* MyVoxelPointLookupTable) const { const FVector Step = MyDensityField->GetStep(); const int32 StartX = FMath::Max(0, FMath::FloorToInt((ExpansionPoint.X - ProximityThreshold - MyPointCloud->MinBounds.X) / Step.X)); @@ -478,8 +493,9 @@ void UMagicWand::ExpandFromPoint(TQueue<int32>* ProcessQueue, const float Proxim for (int32 X = StartX; X <= EndX; ++X) { const int32 CurrentVoxelComparisonIndex = MyDensityField->GridPositionToIndex(X, Y, Z); - - if(MyFullVoxelList->Contains(CurrentVoxelComparisonIndex)) + + + if(CurrentSelection.Get()->IsFullVoxel(CurrentVoxelComparisonIndex)) { continue; } @@ -489,7 +505,7 @@ void UMagicWand::ExpandFromPoint(TQueue<int32>* ProcessQueue, const float Proxim int Selected = 0; for(const int32 CurrentPointComparisonIndex : PointIndices) { - if(CurrentSelection.IsSelected(CurrentPointComparisonIndex)) + if(CurrentSelection.Get()->IsSelected(CurrentPointComparisonIndex)) { Selected++; continue; @@ -504,13 +520,13 @@ void UMagicWand::ExpandFromPoint(TQueue<int32>* ProcessQueue, const float Proxim ThreadLoad++; Selected++; - CurrentSelection.SelectIndex(CurrentPointComparisonIndex); + CurrentSelection.Get()->SelectIndex(CurrentPointComparisonIndex); } } if(Selected == PointIndices.Num()) { - MyFullVoxelList->Add(CurrentVoxelComparisonIndex); + CurrentSelection.Get()->AddFullVoxel(CurrentVoxelComparisonIndex); } } } @@ -526,7 +542,27 @@ void UMagicWand::FinishSelectionThread(const float ProximityThreshold) { CacheSelectionResults(ProximityThreshold, CurrentSelection); - //UE_LOG(LogTemp, Error, TEXT("Finished!")); + FScopeLock Lock(&SelectionCacheLock); + + if(CurrentSelection.IsValid()) + { + TSet<int32> Voxels; + TArray<int32> PointIndices = CurrentSelection.Get()->GetPointsAsIndexList(); + for (int32 i = 0; i < PointIndices.Num(); i++) + { + const int Index = PointIndices[i]; + if(MyPointCloud->PositionVectors.IsValidIndex(Index)) + { + FVector Point = MyPointCloud->PositionVectors[Index]; + int32 VoxelFromIndex = MyDensityField->WorldPositionToIndex(Point); + Voxels.Add(VoxelFromIndex); + } + } + + GenerateVoxelMeshSmooth(Voxels); + } + + //CurrentSelection.Reset(); }else if(NumberThreads.GetValue() < 0) { UE_LOG(LogTemp, Error, TEXT("More Threads closed than opened!")); @@ -551,7 +587,7 @@ void UMagicWand::CreateNewExpansionThread(TQueue<int32>* ProcessQueue, int& Numb Task->Start(); } -void UMagicWand::CacheSelectionResults(const float ProximityThreshold, const FSelectionManager& SelectionResults) +void UMagicWand::CacheSelectionResults(const float ProximityThreshold, const TSharedPtr<FSelectionManager>& SelectionResults) { FScopeLock Lock(&SelectionCacheLock); // Check if the proximity range is already present @@ -562,27 +598,30 @@ void UMagicWand::CacheSelectionResults(const float ProximityThreshold, const FSe SortedProximityRanges.Sort(); // Update the cache with new indices for a given proximity range - SelectionCache.Add(ProximityThreshold, SelectionResults); + + const TSharedRef<FSelectionManager> CacheCopy = MakeShared<FSelectionManager>(*SelectionResults.Get()); // This will return a shared pointer + SelectionCache.Add(ProximityThreshold, CacheCopy); } } -FSelectionManager UMagicWand::GetSelectionResultsFromProximity(const float ProximityThreshold) -{ +TSharedPtr<FSelectionManager> UMagicWand::GetSelectionCacheResultCopy(const float ProximityThreshold) { FScopeLock Lock(&SelectionCacheLock); - - for(int i = SortedProximityRanges.Num() - 1; i >= 0; --i) - { - const float CurrentRange = SortedProximityRanges[i]; - - if(CurrentRange <= ProximityThreshold) - { - //UE_LOG(LogTemp, Error, TEXT("Cached selection found for proximity range %f, closest smaller proximity was %f"), ProximityThreshold, CurrentRange); - return SelectionCache[CurrentRange]; + + for (int i = SortedProximityRanges.Num() - 1; i >= 0; --i) { + + if (SortedProximityRanges[i] <= ProximityThreshold) { + FSelectionManager* CopiedManager(SelectionCache[SortedProximityRanges[i]].Get()); + return MakeShared<FSelectionManager>(*CopiedManager); // This will return a shared pointer } } - //UE_LOG(LogTemp, Error, TEXT("No cached selection found for proximity range %f"), ProximityThreshold); - return FSelectionManager(MyPointCloud->SelectionFlags.Num()); + // If no valid selection manager is found, create a new one and add it to the map + TSharedPtr<FSelectionManager> NewManager = MakeShared<FSelectionManager>(MyPointCloud->SelectionFlags.Num(), MyDensityField->GetVoxelNumber()); + SelectionCache.Add(ProximityThreshold, NewManager); + SortedProximityRanges.Add(ProximityThreshold); + SortedProximityRanges.Sort(); + + return NewManager; // Return the newly created manager } // TICK @@ -619,24 +658,6 @@ void UMagicWand::SelectParticles(const FVector& InputPosition) if(CountSelectionTime >= 1.0f / MagicWandUpdatesPerSecond && NumberThreads.GetValue() == 0) { CountSelectionTime = 0; - //AbortMagicWand.Store(false); PerformMagicWandSelection(ProximityThreshold); - - FScopeLock Lock(&SelectionCacheLock); - - TSet<int32> Voxels; - TArray<int32> PointIndices = CurrentSelection.GetPointIndexList(); - for (int32 i = 0; i < PointIndices.Num(); i++) - { - const int Index = PointIndices[i]; - if(MyPointCloud->PositionVectors.IsValidIndex(Index)) - { - FVector Point = MyPointCloud->PositionVectors[Index]; - int32 VoxelFromIndex = MyDensityField->WorldPositionToIndex(Point); - Voxels.Add(VoxelFromIndex); - } - } - - GenerateVoxelMeshSmooth(Voxels); } } \ No newline at end of file diff --git a/Source/MetaCastBachelor/MagicWand.h b/Source/MetaCastBachelor/MagicWand.h index 5137d0f..ece7c86 100644 --- a/Source/MetaCastBachelor/MagicWand.h +++ b/Source/MetaCastBachelor/MagicWand.h @@ -16,8 +16,8 @@ class UMagicWand : public UMetaCastBaseline // MAGIC WAND TAtomic<bool> AbortMagicWand = false; - FSelectionManager CurrentSelection; - TMap<float, FSelectionManager> SelectionCache; + TSharedPtr<FSelectionManager> CurrentSelection; + TMap<float, TSharedPtr<FSelectionManager>> SelectionCache; TArray<float> SortedProximityRanges; FVector SeedPointPositionLocal; FDensityField* MyDensityField; @@ -100,7 +100,7 @@ public: // VISUALIZATION void GenerateVoxelMeshWithCubes(const TArray<int32> Voxels) const; - void GenerateVoxelMeshSmooth(const TSet<int32> Voxels); + void GenerateVoxelMeshSmooth(const TSet<int32>& Voxels); // MAGIC WAND SELECTION @@ -108,11 +108,11 @@ public: void PerformMagicWandSelection(const float ProximityThreshold); void FindSeed(const FVector& InputPosition); void ExpandFromAllPointsInQueue(TQueue<int32>* ProcessQueue, const float ProximityThreshold, int ThreadLoad); - void ExpandFromPoint(TQueue<int32>* ProcessQueue, const float ProximityThreshold, int& ThreadLoad, const FVector& ExpansionPoint, TSet<int32>* MyFullVoxelList, FVoxelPointLookupTable* MyVoxelPointLookupTable); + void ExpandFromPoint(TQueue<int32>* ProcessQueue, const float ProximityThreshold, int& ThreadLoad, const FVector& ExpansionPoint, const FVoxelPointLookupTable* MyVoxelPointLookupTable) const; void FinishSelectionThread(const float ProximityThreshold); void CreateNewExpansionThread(TQueue<int32>* ProcessQueue, int& NumberToProcess, const float ProximityThreshold); - void CacheSelectionResults(const float ProximityThreshold, const FSelectionManager& SelectionResults); - FSelectionManager GetSelectionResultsFromProximity(float ProximityThreshold); + void CacheSelectionResults(const float ProximityThreshold, const TSharedPtr<FSelectionManager>& SelectionResults); + TSharedPtr<FSelectionManager> GetSelectionCacheResultCopy(float ProximityThreshold); // TICK diff --git a/Source/MetaCastBachelor/MagicWandSelectionManager.h b/Source/MetaCastBachelor/MagicWandSelectionManager.h index 2c88a42..46f7213 100644 --- a/Source/MetaCastBachelor/MagicWandSelectionManager.h +++ b/Source/MetaCastBachelor/MagicWandSelectionManager.h @@ -1,58 +1,86 @@ #pragma once #include "CoreMinimal.h" +class FAtomicBool +{ +public: + FAtomicBool() : Value(false) {} + explicit FAtomicBool(const bool InValue) : Value(InValue) {} + + // Copy constructor + FAtomicBool(const FAtomicBool& Other) + { + Value.store(Other.Value.load()); + } + + // Assignment operator + FAtomicBool& operator=(const FAtomicBool& Other) + { + if (this != &Other) + { + Value.store(Other.Value.load()); + } + return *this; + } + + // Move constructor + FAtomicBool(FAtomicBool&& Other) noexcept : Value(Other.Value.load()) {} + + // Move assignment operator + FAtomicBool& operator=(FAtomicBool&& Other) noexcept + { + if (this != &Other) + { + Value.store(Other.Value.load()); + } + return *this; + } + + std::atomic<bool> Value; +}; + class FSelectionManager { - TArray<int32> PointIndexList; // List of selected indices - TArray<bool> SelectionFlags; // Flags to indicate if an index is selected - int SelectionCount; // Counter for selected indices - TSet<int32> FullVoxelList; + TArray<FAtomicBool> SelectionFlags; // Flags to indicate if an index is selected + TArray<FAtomicBool> FullVoxelFlags; // Flags to indicate if a voxel is full + FThreadSafeCounter SelectionPointCount; // Counter for selected indices public: - explicit FSelectionManager(const int32 NumElements) : SelectionCount(0) + explicit FSelectionManager(const int32 NumberOfPoints, const int32 NumberOfVoxels) : SelectionPointCount(0) { - SelectionFlags.Init(false, NumElements); - PointIndexList.Reserve(NumElements); // Preallocate space to optimize appending + SelectionFlags.Init(FAtomicBool(false), NumberOfPoints); + FullVoxelFlags.Init(FAtomicBool(false), NumberOfVoxels); } - explicit FSelectionManager(const TArray<bool>& InSelectionFlags) : SelectionFlags(InSelectionFlags) - { - SelectionCount = 0; - for (int i = 0; i < InSelectionFlags.Num(); i++) - { - if (InSelectionFlags[i]) - { - PointIndexList.Add(i); - SelectionCount++; - } - } - } - - FSelectionManager(): SelectionCount(0) {} - - + // Copy constructor + FSelectionManager(const FSelectionManager& Other) + : SelectionFlags(Other.SelectionFlags) + , FullVoxelFlags(Other.FullVoxelFlags) + , SelectionPointCount(Other.SelectionPointCount) + {} + /** Adds an index to the selection if it isn't already selected */ void SelectIndex(const int32 Index) { - if (!SelectionFlags[Index]) + if (IsValidIndex(Index) && !SelectionFlags[Index].Value.load()) { - PointIndexList.Add(Index); - SelectionFlags[Index] = true; - SelectionCount++; + //PointIndexList.Add(Index); + SelectionFlags[Index].Value.store(true); + SelectionPointCount.Increment(); } } /** Removes a specific index from the selection */ bool UnselectIndex(const int32 IndexToRemove) { - if (IsValidIndex(IndexToRemove) && SelectionFlags[IndexToRemove]) + if (IsValidIndex(IndexToRemove) && SelectionFlags[IndexToRemove].Value.load()) { - const int32 RemoveAt = PointIndexList.IndexOfByKey(IndexToRemove); - if (RemoveAt != INDEX_NONE) + //const int32 RemoveAt = PointIndexList.IndexOfByKey(IndexToRemove); + if (true/*RemoveAt != INDEX_NONE*/) { - PointIndexList.RemoveAt(RemoveAt); - SelectionFlags[IndexToRemove] = false; - SelectionCount--; + //PointIndexList.RemoveAt(RemoveAt); + SelectionFlags[IndexToRemove].Value.store(false); + SelectionPointCount.Decrement(); return true; } } @@ -62,27 +90,19 @@ public: /** Returns true if any indices are selected */ bool IsAnythingSelected() const { - return SelectionCount > 0; + return SelectionPointCount.GetValue() > 0; } /** Checks if a specific index is selected */ bool IsSelected(const int32 Index) const { - return IsValidIndex(Index) && SelectionFlags[Index]; - } - - /** Resets the selection status of all indices */ - void Reset() - { - SelectionFlags.Init(false, SelectionFlags.Num()); - PointIndexList.Empty(); - SelectionCount = 0; + return IsValidIndex(Index) && SelectionFlags[Index].Value.load(); } /** Returns the number of selected indices */ int32 SelectedCount() const { - return SelectionCount; + return SelectionPointCount.GetValue(); } /** Checks if the given index is within the valid range */ @@ -91,19 +111,55 @@ public: return Index >= 0 && Index < SelectionFlags.Num(); } - /** Returns the number of indices in the selection list (for debugging) */ - int32 QueueSize() const + const TArray<FAtomicBool>& GetSelectionFlags() const + { + return SelectionFlags; + } + + // Thread-safe method to add a full voxel + void AddFullVoxel(const int32 VoxelIndex) { - return PointIndexList.Num(); + if(FullVoxelFlags.IsValidIndex(VoxelIndex)) + { + FullVoxelFlags[VoxelIndex].Value.store(true); + }else + { + UE_LOG(LogTemp, Error, TEXT("Voxel index %d is out of bounds when writing."), VoxelIndex); + } } - const TArray<int32>& GetPointIndexList() const - { - return PointIndexList; - } + // Thread-safe method to check if a voxel is full + bool IsFullVoxel(const int32 VoxelIndex) const + { + if(FullVoxelFlags.IsValidIndex(VoxelIndex)) + { + return FullVoxelFlags[VoxelIndex].Value.load(); + } + UE_LOG(LogTemp, Error, TEXT("Voxel index %d is out of bounds when reading."), VoxelIndex); + return false; + } - const TArray<bool>& GetSelectionFlags() const - { - return SelectionFlags; - } + // Batch update method to add multiple full voxels + void BatchAddFullVoxels(const TArray<int32>& VoxelIndices) + { + for (const int32 VoxelIndex : VoxelIndices) + { + FullVoxelFlags[VoxelIndex].Value.store(true); + } + } + + TArray<int> GetPointsAsIndexList() + { + TArray<int> PointIndexList; + PointIndexList.Reserve(SelectionPointCount.GetValue()); + + for (int32 Index = 0; Index < SelectionFlags.Num(); ++Index) + { + if (SelectionFlags[Index].Value.load()) + { + PointIndexList.Add(Index); + } + } + return PointIndexList; + } }; \ No newline at end of file -- GitLab