Skip to content
Snippets Groups Projects
Commit d389c2f5 authored by Timon Römer's avatar Timon Römer
Browse files

Improves MagicWand Selection

parent f94254d9
No related branches found
No related tags found
No related merge requests found
...@@ -129,7 +129,8 @@ r.MobileHDR=False ...@@ -129,7 +129,8 @@ r.MobileHDR=False
r.MSAACount=4 r.MSAACount=4
r.Mobile.ShadingPath=1 r.Mobile.ShadingPath=1
r.ForwardShading=True r.ForwardShading=True
r.CustomDepth=1 r.CustomDepth=0
r.CustomDepthTemporalAAJitter=False
[/Script/HardwareTargeting.HardwareTargetingSettings] [/Script/HardwareTargeting.HardwareTargetingSettings]
TargetedHardwareClass = Mobile TargetedHardwareClass = Mobile
......
No preview for this file type
No preview for this file type
#include "MagicWand.h" #include "MagicWand.h"
#include "Utilities.h" #include "Utilities.h"
#include "Components/LineBatchComponent.h"
#include "Generators/MarchingCubes.h" #include "Generators/MarchingCubes.h"
// INITIALIZATION // INITIALIZATION
...@@ -8,6 +9,7 @@ UMagicWand::UMagicWand() : World(nullptr) ...@@ -8,6 +9,7 @@ UMagicWand::UMagicWand() : World(nullptr)
{ {
// Create the procedural mesh component // Create the procedural mesh component
ProceduralMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh")); ProceduralMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh"));
MyLineBatchComponent = CreateDefaultSubobject<ULineBatchComponent>(TEXT("LineDrawer"));
} }
void UMagicWand::BeginPlay() void UMagicWand::BeginPlay()
...@@ -20,7 +22,7 @@ void UMagicWand::BeginPlay() ...@@ -20,7 +22,7 @@ void UMagicWand::BeginPlay()
void UMagicWand::InitProceduralMesh() const void UMagicWand::InitProceduralMesh() const
{ {
ProceduralMesh->AttachToComponent(MyPointCloud->PointCloudVisualizer, FAttachmentTransformRules::SnapToTargetIncludingScale); //ProceduralMesh->AttachToComponent(MyPointCloud->PointCloudVisualizer, FAttachmentTransformRules::SnapToTargetIncludingScale);
ProceduralMesh->SetMaterial(0, SelectionVolumeMat); ProceduralMesh->SetMaterial(0, SelectionVolumeMat);
ProceduralMesh->SetMaterial(1, SelectionVolumeMat); ProceduralMesh->SetMaterial(1, SelectionVolumeMat);
ProceduralMesh->SetMaterial(2, SelectionVolumeMat); ProceduralMesh->SetMaterial(2, SelectionVolumeMat);
...@@ -182,37 +184,111 @@ void UMagicWand::GenerateVoxelMeshSmooth(const TArray<int32> Voxels) ...@@ -182,37 +184,111 @@ void UMagicWand::GenerateVoxelMeshSmooth(const TArray<int32> Voxels)
FScopeLock MeshLock(&ProceduralMeshGuard); FScopeLock MeshLock(&ProceduralMeshGuard);
ProceduralMesh->CreateMeshSection(0, Vertices, Indices, Normals, TArray<FVector2D>(), VertexColors, TArray<FProcMeshTangent>(), false); ProceduralMesh->CreateMeshSection(0, Vertices, Indices, Normals, TArray<FVector2D>(), VertexColors, TArray<FProcMeshTangent>(), false);
ProceduralMesh->SetVisibility(true); ProceduralMesh->SetVisibility(true);
AccumulatedTime.Store(0);
IsMarchingCubesRunning = false; IsMarchingCubesRunning = false;
}); });
}); });
} }
// INPUT HANDLING 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::HandleMetaSelectReleased(const FInputActionInstance& Instance) void UMagicWand::GenerateCylinderBetweenPoints(const FVector& Start, const FVector& End, const float Radius, const int32 NumSides, const FColor Color, const int MeshSectionIndex) const
{ {
Super::HandleMetaSelectReleased(Instance); FVector Axis = End - Start;
Axis.Normalize();
const FVector SelectionWorldPosition = SelectionObject->GetComponentLocation(); TArray<FVector> Vertices;
const FVector SelectionLocalPosition = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(SelectionWorldPosition); TArray<int32> Triangles;
TArray<FVector> Normals;
TArray<FVector2D> UVs;
TArray<FProcMeshTangent> Tangents;
ProximityThreshold = FMath::Clamp(FVector::Dist(SelectionStartPosition, SelectionLocalPosition) / 10, 0.1, 8); // 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));
InitMagicWandSelection(); if (i > 0)
{
Triangles.Add(VertIndex);
Triangles.Add(VertIndex + i);
Triangles.Add(VertIndex + i + 1);
}
}
AsyncTask(ENamedThreads::Type::AnyBackgroundHiPriTask, [this, &SelectionLocalPosition]() // 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++)
{ {
PerformMagicWandSelection(SelectionStartPosition); 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::HandleMetaSelectPressed(const FInputActionInstance& Instance) void UMagicWand::HandleMetaSelectPressed(const FInputActionInstance& Instance)
{ {
Super::HandleMetaSelectPressed(Instance); Super::HandleMetaSelectPressed(Instance);
const FVector SelectionStartPositionWorld = SelectionObject->GetComponentLocation();
const FVector SelectionStartPositionLocal = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(SelectionStartPositionWorld);
FindSeed(SelectionStartPositionLocal);
}
void UMagicWand::HandleMetaSelectReleased(const FInputActionInstance& Instance)
{
Super::HandleMetaSelectReleased(Instance);
const FVector SelectionWorldPosition = SelectionObject->GetComponentLocation(); const FVector SelectionWorldPosition = SelectionObject->GetComponentLocation();
SelectionStartPosition = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(SelectionWorldPosition); const FVector SelectionLocalPosition = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(SelectionWorldPosition);
InitMagicWandSelection();
AsyncTask(ENamedThreads::Type::AnyBackgroundHiPriTask, [this, &SelectionLocalPosition]()
{
PerformMagicWandSelection();
});
} }
void UMagicWand::HandleMetaEraseReleased(const FInputActionInstance& Instance) void UMagicWand::HandleMetaEraseReleased(const FInputActionInstance& Instance)
...@@ -235,11 +311,10 @@ void UMagicWand::InitMagicWandSelection() ...@@ -235,11 +311,10 @@ void UMagicWand::InitMagicWandSelection()
{ {
ProceduralMesh->ClearAllMeshSections(); ProceduralMesh->ClearAllMeshSections();
AbortMarchingCubes = true; AbortMarchingCubes = true;
if(AccumulatedTime.Load() >= 0) AccumulatedTime.Store(1 / EvaluationsPerSecond);
AbortMagicWand.Store(false); AbortMagicWand.Store(false);
} }
void UMagicWand::PerformMagicWandSelection(const FVector& InputPosition) void UMagicWand::PerformMagicWandSelection()
{ {
if (MyPointCloud->SelectionFlags.Num() != MyPointCloud->PositionVectors.Num()) if (MyPointCloud->SelectionFlags.Num() != MyPointCloud->PositionVectors.Num())
{ {
...@@ -250,28 +325,22 @@ void UMagicWand::PerformMagicWandSelection(const FVector& InputPosition) ...@@ -250,28 +325,22 @@ void UMagicWand::PerformMagicWandSelection(const FVector& InputPosition)
//UE_LOG(LogTemp, Warning, TEXT("Starting MagicWand Selection!")); //UE_LOG(LogTemp, Warning, TEXT("Starting MagicWand Selection!"));
// Find the closest point to the input position as the seed // Find the closest point to the input position as the seed
int32 SeedIndex;
const int32 InputVoxelIndex = MyDensityField->WorldPositionToIndex(InputPosition);
FindSeedIndex(InputPosition, InputVoxelIndex, SeedIndex);
if (SeedIndex != INDEX_NONE) if (SeedPointIndex != INDEX_NONE)
{
ExpandSelectionFromPointIndex(SeedIndex);
}else
{ {
AccumulatedTime.Store( 0); ExpandSelectionFromPointIndex(SeedPointIndex);
} }
} }
void UMagicWand::FindSeedIndex(const FVector& InputPosition, const int32 InputVoxelIndex, int32& SeedIndex) const void UMagicWand::FindSeed(const FVector& InputPosition)
{ {
const int32 InputVoxelIndex = MyDensityField->WorldPositionToIndex(InputPosition);
TArray<int32> Neighbors = MyDensityField->IndexToVoxelNeighbors(InputVoxelIndex); TArray<int32> Neighbors = MyDensityField->IndexToVoxelNeighbors(InputVoxelIndex);
Neighbors.Add(InputVoxelIndex); Neighbors.Add(InputVoxelIndex);
SeedIndex = INDEX_NONE;
float MinDistance = FLT_MAX; float MinDistance = FLT_MAX;
SeedPointIndex = INDEX_NONE;
for(int NeighborDistance = 0; NeighborDistance < 4; ++NeighborDistance) for(int NeighborDistance = 0; NeighborDistance < 5; ++NeighborDistance)
{ {
const int NumNeighbors = Neighbors.Num(); const int NumNeighbors = Neighbors.Num();
...@@ -290,25 +359,29 @@ void UMagicWand::FindSeedIndex(const FVector& InputPosition, const int32 InputVo ...@@ -290,25 +359,29 @@ void UMagicWand::FindSeedIndex(const FVector& InputPosition, const int32 InputVo
} }
} }
for (const int CurrentNeighborIndex : Neighbors) for (const int CurrentNeighborIndex : Neighbors)
{ {
TArray<int32> PointIndices = MyDensityField->VoxelPointLookupTable->GetPointsInVoxel(CurrentNeighborIndex); TArray<int32> PointIndices = MyDensityField->VoxelPointLookupTable->GetPointsInVoxel(CurrentNeighborIndex);
for(const int32 CurrentIndex : PointIndices) for(const int32 CurrentIndex : PointIndices)
{ {
FVector CurrentPosition = MyDensityField->IndexToVoxelPosition(CurrentIndex); FVector CurrentPosition = MyPointCloud->PositionVectors[CurrentIndex];
const float Distance = FVector::Dist(InputPosition, CurrentPosition); const float Distance = FVector::Dist(InputPosition, CurrentPosition);
if (Distance < MinDistance) if (Distance < MinDistance)
{ {
MinDistance = Distance; MinDistance = Distance;
SeedIndex = CurrentIndex; SeedPointIndex = CurrentIndex;
}
} }
} }
if(SeedPointIndex != INDEX_NONE)
{
SeedPointPositionLocal = MyPointCloud->PositionVectors[SeedPointIndex];
} }
} }
void UMagicWand::ExpandSelectionFromPointIndex(const int32 SeedIndex) void UMagicWand::ExpandSelectionFromPointIndex(const int32 PointIndex)
{ {
{ {
FScopeLock Lock(&ThreadNumberLock); FScopeLock Lock(&ThreadNumberLock);
...@@ -316,8 +389,8 @@ void UMagicWand::ExpandSelectionFromPointIndex(const int32 SeedIndex) ...@@ -316,8 +389,8 @@ void UMagicWand::ExpandSelectionFromPointIndex(const int32 SeedIndex)
} }
TQueue<int32>* ProcessQueue = new TQueue<int32>{}; TQueue<int32>* ProcessQueue = new TQueue<int32>{};
ProcessQueue->Enqueue(SeedIndex); ProcessQueue->Enqueue(PointIndex);
MyPointCloud->SelectionFlags[SeedIndex] = true; MyPointCloud->SelectionFlags[PointIndex] = true;
const float SquaredProximityThreshold = ProximityThreshold * ProximityThreshold; const float SquaredProximityThreshold = ProximityThreshold * ProximityThreshold;
int NumberToProcess = 0; int NumberToProcess = 0;
...@@ -419,7 +492,6 @@ void UMagicWand::AbortSelection() ...@@ -419,7 +492,6 @@ void UMagicWand::AbortSelection()
//UE_LOG(LogTemp, Warning, TEXT("Thread Closed! Number of Threads now: %d"), NumberThreads.GetValue()); //UE_LOG(LogTemp, Warning, TEXT("Thread Closed! Number of Threads now: %d"), NumberThreads.GetValue());
if(NumberThreads.GetValue() == 0) if(NumberThreads.GetValue() == 0)
{ {
AccumulatedTime.Store(0);
//AbortMagicWand.Store(false); //AbortMagicWand.Store(false);
//UE_LOG(LogTemp, Warning, TEXT("Aborted!")); //UE_LOG(LogTemp, Warning, TEXT("Aborted!"));
}else if(NumberThreads.GetValue() < 0) }else if(NumberThreads.GetValue() < 0)
...@@ -460,11 +532,16 @@ void UMagicWand::TickComponent(const float DeltaTime, const ELevelTick TickType, ...@@ -460,11 +532,16 @@ void UMagicWand::TickComponent(const float DeltaTime, const ELevelTick TickType,
void UMagicWand::SelectParticles(const FVector& InputPosition) void UMagicWand::SelectParticles(const FVector& InputPosition)
{ {
if (AccumulatedTime.Load() >= 1 / EvaluationsPerSecond) const FVector CurrentSelectionPositionWorld = SelectionObject->GetComponentLocation();
{ const FVector CurrentSelectionPositionLocal = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(CurrentSelectionPositionWorld);
AccumulatedTime.Store(FLT_MIN); ProximityThreshold = FMath::Clamp(FVector::Dist(SeedPointPositionLocal, CurrentSelectionPositionLocal) / ThresholdDistanceScaling, MinThreshold, MaxThreshold);
}
const FVector SelectionWorldPosition = SelectionObject->GetComponentLocation(); const FVector SelectionDirectionLocal = CurrentSelectionPositionLocal - SeedPointPositionLocal;
DrawDebugLine(World, SelectionWorldPosition, MyPointCloud->PointCloudVisualizer->GetComponentTransform().TransformPosition(SelectionStartPosition), FColor::Red, false, 0, 0, 0.1f); const FVector ProximityEndPointLocal = SeedPointPositionLocal + (SelectionDirectionLocal.GetSafeNormal() * ProximityThreshold);
const FVector SelectionStartPositionWorld = MyPointCloud->PointCloudVisualizer->GetComponentTransform().TransformPosition(SeedPointPositionLocal);
const FVector ProximityEndPointWorld = MyPointCloud->PointCloudVisualizer->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);
} }
\ No newline at end of file
...@@ -25,19 +25,27 @@ class UMagicWand : public UMetaCastBaseline ...@@ -25,19 +25,27 @@ class UMagicWand : public UMetaCastBaseline
UProceduralMeshComponent* ProceduralMesh; UProceduralMeshComponent* ProceduralMesh;
UPROPERTY() UPROPERTY()
UWorld* World; UWorld* World;
TAtomic<float> AccumulatedTime = 0.0; FVector SeedPointPositionLocal;
FVector SelectionStartPosition; UPROPERTY()
ULineBatchComponent* MyLineBatchComponent;
float ProximityThreshold = 0.1f;
float MarchingCubeSize = 0;
int32 SeedPointIndex;
// USER EXPORTED // USER EXPORTED
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)
float MarchingCubeSize = 0; float ThresholdDistanceScaling = 20;
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)
int EvaluationsPerSecond = 10; float MinThreshold = 0.1;
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)
float ProximityThreshold = 0.1f; float MaxThreshold = 8;
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)
UMaterialInterface* SelectionVolumeMat; UMaterialInterface* SelectionVolumeMat;
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)
int MaxThreadCount = 100; int MaxThreadCount = 100;
...@@ -64,9 +72,10 @@ public: ...@@ -64,9 +72,10 @@ public:
// MAGIC WAND SELECTION // MAGIC WAND SELECTION
void InitMagicWandSelection(); void InitMagicWandSelection();
void FindSeedIndex(const FVector& InputPosition, int32 InputVoxelIndex, int32& SeedIndex) const; void FindSeedIndex(const FVector& InputPosition, int32& SeedIndex) const;
void PerformMagicWandSelection(const FVector& InputPosition); void PerformMagicWandSelection();
void ExpandSelectionFromPointIndex(int32 SeedIndex); void FindSeed(const FVector& InputPosition);
void ExpandSelectionFromPointIndex(int32 PointIndex);
void ExpandSelectionFromPointIndexQueue(TQueue<int32>* ProcessQueue); void ExpandSelectionFromPointIndexQueue(TQueue<int32>* ProcessQueue);
void ExpandThroughNeighborsInRange(TQueue<int32>* ProcessQueue, const float SquaredProximityThreshold, int& NumberToProcess, const FVector& CurrentQueuePointPosition) const; void ExpandThroughNeighborsInRange(TQueue<int32>* ProcessQueue, const float SquaredProximityThreshold, int& NumberToProcess, const FVector& CurrentQueuePointPosition) const;
void AbortSelection(); void AbortSelection();
...@@ -76,4 +85,6 @@ public: ...@@ -76,4 +85,6 @@ public:
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
virtual void SelectParticles(const FVector& InputPosition) override; virtual void SelectParticles(const FVector& InputPosition) override;
void DrawPersistentLine(const FVector& Start, const FVector& End, const FColor& Color, float Duration, float Thickness) const;
void GenerateCylinderBetweenPoints(const FVector& Start, const FVector& End, const float Radius, const int32 NumSides, const FColor Color, const int MeshSectionIndex) const;
}; };
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment