Select Git revision
MagicWand.cpp
-
Timon Römer authoredTimon Römer authored
MagicWand.cpp 22.39 KiB
#include "MagicWand.h"
#include "FMagicWandSelectionTask.h"
#include "Components/LineBatchComponent.h"
#include "Generators/MarchingCubes.h"
#include "Kismet/GameplayStatics.h"
#include "MetaCastBachelor/PointStorage/PointCloud.h"
// INITIALIZATION
class FMagicWandSelectionTask;
UMagicWand::UMagicWand() : CurrentSelection(), World(nullptr)
{
// Create the procedural mesh component
ProceduralMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh"));
MarchingCubeMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh2"));
MyLineBatchComponent = CreateDefaultSubobject<ULineBatchComponent>(TEXT("LineDrawer"));
}
void UMagicWand::BeginPlay()
{
Super::BeginPlay();
InitReferences();
InitProceduralMesh();
}
void UMagicWand::InitProceduralMesh() const
{
//ProceduralMesh->AttachToComponent(MyPointCloud->PointCloudVisualizer, FAttachmentTransformRules::SnapToTargetIncludingScale);
ProceduralMesh->SetMaterial(0, SelectionVolumeMat);
ProceduralMesh->SetMaterial(1, SelectionVolumeMat);
ProceduralMesh->SetMaterial(2, SelectionVolumeMat);
ProceduralMesh->bCastDynamicShadow = false;
ProceduralMesh->bRenderCustomDepth = true;
ProceduralMesh->SetCastShadow(false);
ProceduralMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
ProceduralMesh->SetMobility(EComponentMobility::Movable);
ProceduralMesh->SetVisibility(true);
MarchingCubeMesh->SetMaterial(0, SelectionVolumeMat);
MarchingCubeMesh->SetMaterial(1, SelectionVolumeMat);
MarchingCubeMesh->SetMaterial(2, SelectionVolumeMat);
MarchingCubeMesh->bCastDynamicShadow = false;
MarchingCubeMesh->bRenderCustomDepth = true;
MarchingCubeMesh->SetCastShadow(false);
MarchingCubeMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
MarchingCubeMesh->SetMobility(EComponentMobility::Movable);
MarchingCubeMesh->SetVisibility(true);
MarchingCubeMesh->AttachToComponent(MyPointCloud->GetRootComponent(), FAttachmentTransformRules::SnapToTargetIncludingScale);
}
void UMagicWand::InitReferences()
{
if (!MyPointCloud || !MyPointCloud->MyDensityField) {
UE_LOG(LogTemp, Error, TEXT("Invalid Point Cloud or DensityField provided!"));
}else
{
MyDensityField = MyPointCloud->MyDensityField;
}
World = GetWorld();
if (!World) {
UE_LOG(LogTemp, Error, TEXT("Invalid world provided."));
}
}
void UMagicWand::EraseParticles(const FVector& InputPosition)
{
}
// VISUALIZATION
void UMagicWand::GenerateVoxelMeshWithCubes(const TArray<int32> Voxels) const
{
TArray<FVector> Vertices;
TArray<int32> Triangles;
TArray<FColor> VertexColors;
// Generate cube vertices and triangles for each voxel
for (const int32 VoxelIndex : Voxels)
{
if(!MyDensityField->IsValidIndex(VoxelIndex))
{
continue;
}
TArray<FVector> VoxelCorners = MyDensityField->IndexToVoxelCornersWorld(VoxelIndex);
// Add vertices for the voxel
const int32 BaseIndex = Vertices.Num();
Vertices.Append(VoxelCorners);
// Define the triangles (12 triangles for 6 faces of the cube)
// Each face of the cube has 2 triangles (6 faces * 2 triangles per face = 12 triangles)
Triangles.Append({
BaseIndex + 0, BaseIndex + 1, BaseIndex + 2, BaseIndex + 0, BaseIndex + 2, BaseIndex + 3, // Bottom face
BaseIndex + 4, BaseIndex + 6, BaseIndex + 5, BaseIndex + 4, BaseIndex + 7, BaseIndex + 6, // Top face
BaseIndex + 0, BaseIndex + 4, BaseIndex + 1, BaseIndex + 1, BaseIndex + 4, BaseIndex + 5, // Front face
BaseIndex + 1, BaseIndex + 5, BaseIndex + 2, BaseIndex + 2, BaseIndex + 5, BaseIndex + 6, // Right face
BaseIndex + 2, BaseIndex + 6, BaseIndex + 3, BaseIndex + 3, BaseIndex + 6, BaseIndex + 7, // Back face
BaseIndex + 3, BaseIndex + 7, BaseIndex + 0, BaseIndex + 0, BaseIndex + 7, BaseIndex + 4 // Left face
});
// Add red color for each corner vertex
for (int32 i = 0; i < 8; ++i)
{
VertexColors.Add(FColor::Green);
}
}
// Create the mesh section
AsyncTask(ENamedThreads::Type::GameThread, [this, Vertices, Triangles, VertexColors]()
{
//CreateMeshSections(Vertices, Triangles, VertexColors, 1000);
ProceduralMesh->CreateMeshSection(0, Vertices, Triangles, TArray<FVector>(), TArray<FVector2D>(), VertexColors, TArray<FProcMeshTangent>(), false);
ProceduralMesh->SetVisibility(true);
});
}
void UMagicWand::GenerateVoxelMeshSmooth(const TSet<int32>& Voxels)
{
if(IsMarchingCubesRunning || Voxels.IsEmpty())
return;
IsMarchingCubesRunning = true;
FScopeLock Lock(&ProceduralMeshGuard);
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this, Voxels]()
{
FVector Min(FLT_MAX, FLT_MAX, FLT_MAX);
FVector Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
for(const int FloodedIndex : Voxels)
{
Min = FVector::Min(Min, MyDensityField->IndexToVoxelPosition(FloodedIndex));
Max = FVector::Max(Max, MyDensityField->IndexToVoxelPosition(FloodedIndex));
}
UE::Geometry::FMarchingCubes MarchingCubes;
MarchingCubes.Bounds = UE::Geometry::TAxisAlignedBox3(Min, Max);
//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;
};
MarchingCubes.IsoValue = 0;
AbortMarchingCubes = false;
// Define the implicit function
MarchingCubes.Implicit = [this, Voxels](const FVector3d& Pos) {
const FVector PosConverted(Pos.X, Pos.Y, Pos.Z);
const int Index = MyDensityField->WorldPositionToIndex(PosConverted);
return Voxels.Contains(Index) ? 1 : -1;
};
MarchingCubes.bParallelCompute = true;
const UE::Geometry::FMeshShapeGenerator& Generator = MarchingCubes.Generate();
TArray<FVector3d> Vertices3d = Generator.Vertices;
TArray<UE::Geometry::FIndex3i> Triangles = Generator.Triangles;
TArray<FVector3f> Normals3F = Generator.Normals;
// Convert FVector3d to FVector
TArray<FVector> Vertices;
Vertices.Reserve(Generator.Vertices.Num());
for (const FVector3d& Vertex : Vertices3d)
{
Vertices.Add(FVector(Vertex.X, Vertex.Y, Vertex.Z));
}
// Convert FIndex3i to int32
TArray<int32> Indices;
Indices.Reserve(Generator.Triangles.Num() * 3);
for (const UE::Geometry::FIndex3i& Triangle : Triangles)
{
Indices.Add(Triangle.A);
Indices.Add(Triangle.B);
Indices.Add(Triangle.C);
}
// Convert FVector3f to FVector
TArray<FVector> Normals;
Normals.Reserve(Generator.Normals.Num());
for (const FVector3f& Normal : Normals3F)
{
Normals.Add(FVector(Normal.X, Normal.Y, Normal.Z));
}
TArray<FColor> VertexColors;
VertexColors.Reserve(Vertices.Num());
for (int32 i = 0; i < Vertices.Num(); ++i)
{
VertexColors.Add(FColor::Green);
}
AsyncTask(ENamedThreads::Type::GameThread, [this, Vertices, Indices, Normals, VertexColors]()
{
FScopeLock MeshLock(&ProceduralMeshGuard);
MarchingCubeMesh->CreateMeshSection(0, Vertices, Indices, Normals, TArray<FVector2D>(), VertexColors, TArray<FProcMeshTangent>(), false);
MarchingCubeMesh->SetVisibility(true);
IsMarchingCubesRunning = false;
});
});
}
void UMagicWand::DrawPersistentLine(const FVector& Start, const FVector& End, const FColor& Color, const float Duration, const float Thickness) const
{
if (MyLineBatchComponent)
{
MyLineBatchComponent->DrawLine(Start, End, Color, 0, Thickness, Duration);
}
}
void UMagicWand::GenerateCylinderBetweenPoints(const FVector& Start, const FVector& End, const float Radius, const int32 NumSides, const FColor Color, const int MeshSectionIndex) const
{
FVector Axis = End - Start;
Axis.Normalize();
TArray<FVector> Vertices;
TArray<int32> Triangles;
TArray<FVector> Normals;
TArray<FVector2D> UVs;
TArray<FProcMeshTangent> Tangents;
// Bottom cap
int32 VertIndex = 0;
Vertices.Add(Start); // Center bottom
Normals.Add(-Axis);
UVs.Add(FVector2D(0.5f, 0.5f));
for (int i = 0; i <= NumSides; i++)
{
const float Angle = FMath::DegreesToRadians((360.f / NumSides) * i);
FVector Point = Start + Radius * FVector(FMath::Cos(Angle), FMath::Sin(Angle), 0);
Vertices.Add(Point);
Normals.Add(-Axis);
UVs.Add(FVector2D(FMath::Cos(Angle) * 0.5f + 0.5f, FMath::Sin(Angle) * 0.5f + 0.5f));
if (i > 0)
{
Triangles.Add(VertIndex);
Triangles.Add(VertIndex + i);
Triangles.Add(VertIndex + i + 1);
}
}
// Side faces
const int32 BottomCenterIndex = VertIndex;
const int32 TopCenterIndex = Vertices.Num();
Vertices.Add(End); // Center top
Normals.Add(Axis);
UVs.Add(FVector2D(0.5f, 0.5f));
VertIndex++;
for (int i = 0; i <= NumSides; i++)
{
const float Angle = FMath::DegreesToRadians((360.f / NumSides) * i);
FVector Point = End + Radius * FVector(FMath::Cos(Angle), FMath::Sin(Angle), 0);
Vertices.Add(Point);
Normals.Add(Axis);
UVs.Add(FVector2D(FMath::Cos(Angle) * 0.5f + 0.5f, FMath::Sin(Angle) * 0.5f + 0.5f));
if (i > 0)
{
// Connect bottom and top
Triangles.Add(BottomCenterIndex + i);
Triangles.Add(TopCenterIndex + i);
Triangles.Add(BottomCenterIndex + i + 1);
Triangles.Add(TopCenterIndex + i);
Triangles.Add(TopCenterIndex + i + 1);
Triangles.Add(BottomCenterIndex + i + 1);
}
}
TArray<FLinearColor> VertexColors;
VertexColors.Init(Color, Vertices.Num());
// Create mesh
ProceduralMesh->CreateMeshSection_LinearColor(MeshSectionIndex, Vertices, Triangles, Normals, TArray<FVector2D>(), VertexColors, TArray<FProcMeshTangent>(), false);
}
// INPUT HANDLING
void UMagicWand::SelectionStartAction()
{
if(NumberThreads.GetValue() > 0) return;
ProceduralMesh->ClearAllMeshSections();
FScopeLock Lock(&SelectionCacheLock);
SelectionCache.Empty();
SortedProximityRanges.Empty();
SeedPointIndex = INDEX_NONE;
const FVector SelectionStartPositionWorld = SelectionObject->GetComponentLocation();
const FVector SelectionStartPositionLocal = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().InverseTransformPosition(SelectionStartPositionWorld);
FindSeed(SelectionStartPositionLocal);
if(SeedPointIndex == INDEX_NONE) return;
CountSelectionTime = 1 / MagicWandUpdatesPerSecond;
IsMagicWandInitialized = true;
}
void UMagicWand::SelectionEndAction()
{
IsMagicWandInitialized = false;
ProceduralMesh->ClearAllMeshSections();
MarchingCubeMesh->SetVisibility(false);
FScopeLock Lock(&SelectionCacheLock);
if(SelectionEndSound)
{
UGameplayStatics::PlaySound2D(World, SelectionEndSound);
}
if(!CurrentSelection.IsValid()) return;
TArray<FAtomicBool> SelectionArray = CurrentSelection.Get()->GetSelectionFlags();
for(int i = 0; i < SelectionArray.Num(); i++)
{
if(SelectionArray[i].Value.load())
{
MyPointCloud->SetSelectionFlag(i, true);
}
}
MyPointCloud->SaveStateAndUpdate();
}
void UMagicWand::HandleMetaEraseReleased(const FInputActionInstance& Instance)
{
if(NumberThreads.GetValue() > 0)
{
return;
}
Super::HandleMetaEraseReleased(Instance);
CurrentSelection = MakeShared<FSelectionManager>(MyPointCloud->GetNumberOfPoints(), MyDensityField->GetVoxelNumber());
for(int i = 0; i < MyPointCloud->GetNumberOfPoints(); i++)
{
MyPointCloud->SetSelectionFlag(i, false);
}
MyPointCloud->SaveStateAndUpdate();
}
void UMagicWand::HandleUndoAction(const FInputActionInstance& Instance)
{
if(NumberThreads.GetValue() > 0) return;
Super::HandleUndoAction(Instance);
}
void UMagicWand::HandleRedoAction(const FInputActionInstance& Instance)
{
if(NumberThreads.GetValue() > 0) return;
Super::HandleRedoAction(Instance);
}
// MAGIC WAND SELECTION
void UMagicWand::InitMagicWandSelection()
{
ProceduralMesh->ClearAllMeshSections();
AbortMarchingCubes = true;
//AbortMagicWand.Store(false);
}
void UMagicWand::PerformMagicWandSelection(const float ProximityThreshold)
{
if (SeedPointIndex == INDEX_NONE) return;
if(SelectionStartSound) UGameplayStatics::PlaySound2D(World, SelectionStartSound);
CurrentSelection = GetSelectionCacheResultCopy(ProximityThreshold);
CurrentSelection.Get()->SelectIndex(SeedPointIndex);
TQueue<int32>* ProcessQueue = new TQueue<int32>{};
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)
{
const int32 InputVoxelIndex = MyDensityField->WorldPositionToIndex(InputPosition);
TArray<int32> Neighbors = MyDensityField->IndexToVoxelNeighbors(InputVoxelIndex);
Neighbors.Add(InputVoxelIndex);
float MinDistance = FLT_MAX;
SeedPointIndex = INDEX_NONE;
for(int NeighborDistance = 0; NeighborDistance < 5; ++NeighborDistance)
{
const int NumNeighbors = Neighbors.Num();
for (int i = 0; i < NumNeighbors; ++i)
{
const int CurrentNeighborIndex = Neighbors[i];
TArray<int32> NeighborsOfNeighbors = MyDensityField->IndexToVoxelNeighbors(CurrentNeighborIndex);
for (int CurrentNeighborOfNeighborIndex : NeighborsOfNeighbors)
{
if (!Neighbors.Contains(CurrentNeighborOfNeighborIndex))
{
Neighbors.Add(CurrentNeighborOfNeighborIndex);
}
}
}
}
for (const int CurrentNeighborIndex : Neighbors)
{
TArray<int32> PointIndices = MyDensityField->VoxelPointLookupTable->GetPointsInVoxel(CurrentNeighborIndex);
for(const int32 CurrentIndex : PointIndices)
{
FVector CurrentPosition = MyPointCloud->GetPositionVectors()[CurrentIndex];
const float Distance = FVector::Dist(InputPosition, CurrentPosition);
if (Distance < MinDistance)
{
MinDistance = Distance;
SeedPointIndex = CurrentIndex;
}
}
}
if(SeedPointIndex != INDEX_NONE)
{
SeedPointPositionLocal = MyPointCloud->GetPositionVectors()[SeedPointIndex];
}
}
void UMagicWand::ExpandFromAllPointsInQueue(TQueue<int32>* ProcessQueue, const float ProximityThreshold, int ThreadLoad)
{
//UE_LOG(LogTemp, Warning, TEXT("Opened New Thread! Number of Threads now: %d"), NumberThreads.GetValue());
const FVoxelPointLookupTable MyVoxelPointLookupTable = *MyDensityField->VoxelPointLookupTable;
while (!ProcessQueue->IsEmpty())
{
if(ThreadLoad > 1000 && NumberThreads.GetValue() < MaxThreadCount) CreateNewExpansionThread(ProcessQueue, ThreadLoad, ProximityThreshold);
int32 CurrentQueuePointIndex;
ProcessQueue->Dequeue(CurrentQueuePointIndex);
if(MyPointCloud && MyPointCloud->GetPositionVectors().IsValidIndex(CurrentQueuePointIndex))
{
const FVector CurrentQueuePointPosition = MyPointCloud->GetPositionVectors()[CurrentQueuePointIndex];
ExpandFromPoint(ProcessQueue, ProximityThreshold, ThreadLoad, CurrentQueuePointPosition, &MyVoxelPointLookupTable);
}
ThreadLoad--;
}
FinishSelectionThread(ProximityThreshold);
}
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->GetMinBounds().X) / Step.X));
const int32 EndX = FMath::Min(MyDensityField->GetXNum() - 1, FMath::FloorToInt((ExpansionPoint.X + ProximityThreshold - MyPointCloud->GetMinBounds().X) / Step.X));
const int32 StartY = FMath::Max(0, FMath::FloorToInt((ExpansionPoint.Y - ProximityThreshold - MyPointCloud->GetMinBounds().Y) / Step.Y));
const int32 EndY = FMath::Min(MyDensityField->GetYNum() - 1, FMath::FloorToInt((ExpansionPoint.Y + ProximityThreshold - MyPointCloud->GetMinBounds().Y) / Step.Y));
const int32 StartZ = FMath::Max(0, FMath::FloorToInt((ExpansionPoint.Z - ProximityThreshold - MyPointCloud->GetMinBounds().Z) / Step.Z));
const int32 EndZ = FMath::Min(MyDensityField->GetZNum() - 1, FMath::FloorToInt((ExpansionPoint.Z + ProximityThreshold - MyPointCloud->GetMinBounds().Z) / Step.Z));
const float SquaredProximityThreshold = ProximityThreshold * ProximityThreshold;
for (int32 Z = StartZ; Z <= EndZ; ++Z)
{
for (int32 Y = StartY; Y <= EndY; ++Y)
{
for (int32 X = StartX; X <= EndX; ++X)
{
const int32 CurrentVoxelComparisonIndex = MyDensityField->GridPositionToIndex(X, Y, Z);
if(CurrentSelection.Get()->IsFullVoxel(CurrentVoxelComparisonIndex))
{
continue;
}
TArray<int32> PointIndices = MyVoxelPointLookupTable->GetPointsInVoxel(CurrentVoxelComparisonIndex);
int Selected = 0;
for(const int32 CurrentPointComparisonIndex : PointIndices)
{
if(CurrentSelection.Get()->IsSelected(CurrentPointComparisonIndex))
{
Selected++;
continue;
}
FVector CurrentComparisonPosition = MyPointCloud->GetPositionVectors()[CurrentPointComparisonIndex];
const double SquaredDistance = FVector::DistSquared(ExpansionPoint, CurrentComparisonPosition);
if (SquaredDistance <= SquaredProximityThreshold)
{
ProcessQueue->Enqueue(CurrentPointComparisonIndex);
ThreadLoad++;
Selected++;
CurrentSelection.Get()->SelectIndex(CurrentPointComparisonIndex);
}
}
if(Selected == PointIndices.Num())
{
CurrentSelection.Get()->AddFullVoxel(CurrentVoxelComparisonIndex);
}
}
}
}
}
void UMagicWand::FinishSelectionThread(const float ProximityThreshold)
{
NumberThreads.Decrement();
//UE_LOG(LogTemp, Warning, TEXT("Thread Closed! Number of Threads now: %d"), NumberThreads.GetValue());
if(NumberThreads.GetValue() == 0)
{
CacheSelectionResults(ProximityThreshold, CurrentSelection);
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->GetPositionVectors().IsValidIndex(Index))
{
FVector Point = MyPointCloud->GetPositionVectors()[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!"));
}
}
void UMagicWand::CreateNewExpansionThread(TQueue<int32>* ProcessQueue, int& NumberToProcess, const float ProximityThreshold)
{
NumberThreads.Increment();
TQueue<int32>* TempQueue = new TQueue<int32>{};
const int NumberRemove = NumberToProcess / 2;
for(int i = 0; i < NumberRemove; ++i)
{
int32 CurrentQueuePointIndex;
ProcessQueue->Dequeue(CurrentQueuePointIndex);
NumberToProcess--;
TempQueue->Enqueue(CurrentQueuePointIndex);
}
FMagicWandSelectionTask* Task = new FMagicWandSelectionTask(this, TempQueue, ProximityThreshold, NumberRemove);
Task->Start();
}
void UMagicWand::CacheSelectionResults(const float ProximityThreshold, const TSharedPtr<FSelectionManager>& SelectionResults)
{
FScopeLock Lock(&SelectionCacheLock);
// Check if the proximity range is already present
if (!SortedProximityRanges.Contains(ProximityThreshold))
{
// Add new range to the array and sort it
SortedProximityRanges.Add(ProximityThreshold);
SortedProximityRanges.Sort();
// Update the cache with new indices for a given proximity range
const TSharedRef<FSelectionManager> CacheCopy = MakeShared<FSelectionManager>(*SelectionResults.Get()); // This will return a shared pointer
SelectionCache.Add(ProximityThreshold, CacheCopy);
}
}
TSharedPtr<FSelectionManager> UMagicWand::GetSelectionCacheResultCopy(const float ProximityThreshold) {
FScopeLock Lock(&SelectionCacheLock);
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
}
}
// If no valid selection manager is found, create a new one and add it to the map
TSharedPtr<FSelectionManager> NewManager = MakeShared<FSelectionManager>(MyPointCloud->GetNumberOfPoints(), MyDensityField->GetVoxelNumber());
SelectionCache.Add(ProximityThreshold, NewManager);
SortedProximityRanges.Add(ProximityThreshold);
SortedProximityRanges.Sort();
return NewManager; // Return the newly created manager
}
// TICK
void UMagicWand::TickComponent(const float DeltaTime, const ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
CountSelectionTime += DeltaTime;
if(!Select)
{
MarchingCubeMesh->SetVisibility(false);
}
}
void UMagicWand::SelectParticles(const FVector& InputPosition)
{
if(!IsMagicWandInitialized) return;
const FVector CurrentSelectionPositionWorld = SelectionObject->GetComponentLocation();
const FVector CurrentSelectionPositionLocal = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().InverseTransformPosition(CurrentSelectionPositionWorld);
const float ProximityThreshold = FMath::Clamp(FVector::Dist(SeedPointPositionLocal, CurrentSelectionPositionLocal) / ThresholdDistanceScaling, MinThreshold, MaxThreshold);
const FVector SelectionDirectionLocal = CurrentSelectionPositionLocal - SeedPointPositionLocal;
const FVector ProximityEndPointLocal = SeedPointPositionLocal + (SelectionDirectionLocal.GetSafeNormal() * ProximityThreshold);
const FVector SelectionStartPositionWorld = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().TransformPosition(SeedPointPositionLocal);
const FVector ProximityEndPointWorld = MyPointCloud->GetPointCloudVisualizer()->GetComponentTransform().TransformPosition(ProximityEndPointLocal);
GenerateCylinderBetweenPoints(SelectionStartPositionWorld, ProximityEndPointWorld, 0.05, 8, FColor::Red, 1);
GenerateCylinderBetweenPoints((CurrentSelectionPositionWorld - ProximityEndPointWorld).GetSafeNormal() * 0.05 + ProximityEndPointWorld, CurrentSelectionPositionWorld, 0.05, 8, FColor::Green, 0);
if(CountSelectionTime >= 1.0f / MagicWandUpdatesPerSecond && NumberThreads.GetValue() == 0)
{
CountSelectionTime = 0;
PerformMagicWandSelection(ProximityThreshold);
}
}