From 8ba0e860d25b07b930c5380ec157ef99d91bc494 Mon Sep 17 00:00:00 2001
From: Timon Roemer <t.roemer@vis.rwth-aachen.de>
Date: Mon, 15 Jul 2024 17:45:07 +0200
Subject: [PATCH] Adds MarchingCubes support

---
 Content/MetaPointMap.umap                     | Bin 44870 -> 44922 bytes
 Content/SelectionVolume.uasset                | Bin 8925 -> 9146 bytes
 Source/MetaCastBachelor/DensityField.cpp      |  57 ++++---
 Source/MetaCastBachelor/DensityField.h        |  31 +---
 Source/MetaCastBachelor/MetaPoint.cpp         | 131 +++++++++++----
 Source/MetaCastBachelor/MetaPoint.h           |  13 +-
 ...{MarchingCubes.cpp => MyMarchingCubes.cpp} |  38 ++---
 .../{MarchingCubes.h => MyMarchingCubes.h}    |   6 +-
 Source/MetaCastBachelor/PointCloud.cpp        |  57 +------
 Source/MetaCastBachelor/PointCloud.h          |   4 +
 Source/MetaCastBachelor/Utilities.cpp         | 157 ++++++++++--------
 Source/MetaCastBachelor/Utilities.h           |   3 +-
 .../VoxelPointLookupTable.cpp                 |  29 ++++
 .../MetaCastBachelor/VoxelPointLookupTable.h  |  26 +++
 14 files changed, 324 insertions(+), 228 deletions(-)
 rename Source/MetaCastBachelor/{MarchingCubes.cpp => MyMarchingCubes.cpp} (86%)
 rename Source/MetaCastBachelor/{MarchingCubes.h => MyMarchingCubes.h} (99%)
 create mode 100644 Source/MetaCastBachelor/VoxelPointLookupTable.cpp
 create mode 100644 Source/MetaCastBachelor/VoxelPointLookupTable.h

diff --git a/Content/MetaPointMap.umap b/Content/MetaPointMap.umap
index f44a6fa3bd8788f0813c8f4c4351e9c5bf22240f..7e4733c7f9d52777e794102217da9ff21ea628ba 100644
GIT binary patch
delta 11015
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
L!qXZOT5kdX&{>5_

delta 10870
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
T17UElAG3*sy(u)=ZM_Kq9O7dy

diff --git a/Content/SelectionVolume.uasset b/Content/SelectionVolume.uasset
index b4a7c48878aadac755046e2d70262c2c42fa0168..f5d2c569e5837f227551627058837d7703770703 100644
GIT binary patch
delta 4694
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
Hi&7*2-}Jaj

delta 4459
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

diff --git a/Source/MetaCastBachelor/DensityField.cpp b/Source/MetaCastBachelor/DensityField.cpp
index f952939..ab49740 100644
--- a/Source/MetaCastBachelor/DensityField.cpp
+++ b/Source/MetaCastBachelor/DensityField.cpp
@@ -5,12 +5,26 @@
 
 // INITIALIZATION FUNCTIONS
 
-FDensityField::FDensityField(): XNum(0), YNum(0), ZNum(0), XStep(0), YStep(0), ZStep(0)
+FDensityField::FDensityField(): XNum(0), YNum(0), ZNum(0), XStep(0), YStep(0), ZStep(0), VoxelPointLookupTable(nullptr)
 {
 	VoxelList = TArray<FVoxel>();
 }
 
-void FDensityField::InitializeDensityField(const FVector& MinBounds, const FVector& MaxBounds, const int32 XAxisNum, const int32 YAxisNum, const int32 ZAxisNum)
+void FDensityField::InitializeDensityField(const APointCloud* PointCloud, const FVector& MinBounds, const FVector& MaxBounds, const FIntVector AxisNumbers)
+{
+	InitializeDataStructures(MinBounds, MaxBounds, AxisNumbers.X, AxisNumbers.Y, AxisNumbers.Z);
+
+	constexpr float InfluenceRadius = 1.0f; // Size of a voxel
+	constexpr float Sigma = 1.0f;  // Standard deviation for the Gaussian kernel
+	
+	CalculateVoxelDensities(PointCloud, InfluenceRadius, Sigma);
+	CalculateAndStoreGradients();
+
+	VoxelPointLookupTable = new FVoxelPointLookupTable();
+	VoxelPointLookupTable->Initialize(PointCloud->PositionVectors, this);
+}
+
+void FDensityField::InitializeDataStructures(const FVector& MinBounds, const FVector& MaxBounds, const int32 XAxisNum, const int32 YAxisNum, const int32 ZAxisNum)
 {
 	VoxelList.Empty();
 
@@ -57,12 +71,12 @@ void FDensityField::CalculateVoxelDensities(const APointCloud* PointCloud, const
 	// Iterate over each particle
 	for (const FVector& ParticlePosition : PointCloud->PositionVectors)
 	{
-		const int32 StartX = FMath::Max(0, static_cast<int32>((ParticlePosition.X - InfluenceRadius - PointCloud->MinBounds.X) / XStep));
-	    const int32 EndX = FMath::Min(XNum - 1, static_cast<int32>((ParticlePosition.X + InfluenceRadius - PointCloud->MinBounds.X) / XStep));
-	    const int32 StartY = FMath::Max(0, static_cast<int32>((ParticlePosition.Y - InfluenceRadius - PointCloud->MinBounds.Y) / YStep));
-	    const int32 EndY = FMath::Min(YNum - 1, static_cast<int32>((ParticlePosition.Y + InfluenceRadius - PointCloud->MinBounds.Y) / YStep));
-	    const int32 StartZ = FMath::Max(0, static_cast<int32>((ParticlePosition.Z - InfluenceRadius - PointCloud->MinBounds.Z) / ZStep));
-	    const int32 EndZ = FMath::Min(ZNum - 1, static_cast<int32>((ParticlePosition.Z + InfluenceRadius - PointCloud->MinBounds.Z) / ZStep));
+		const int32 StartX = FMath::Max(0, FMath::FloorToInt((ParticlePosition.X - InfluenceRadius - PointCloud->MinBounds.X) / XStep));
+	    const int32 EndX = FMath::Min(XNum - 1, FMath::FloorToInt((ParticlePosition.X + InfluenceRadius - PointCloud->MinBounds.X) / XStep));
+	    const int32 StartY = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Y - InfluenceRadius - PointCloud->MinBounds.Y) / YStep));
+	    const int32 EndY = FMath::Min(YNum - 1, FMath::FloorToInt((ParticlePosition.Y + InfluenceRadius - PointCloud->MinBounds.Y) / YStep));
+	    const int32 StartZ = FMath::Max(0, FMath::FloorToInt((ParticlePosition.Z - InfluenceRadius - PointCloud->MinBounds.Z) / ZStep));
+	    const int32 EndZ = FMath::Min(ZNum - 1, FMath::FloorToInt((ParticlePosition.Z + InfluenceRadius - PointCloud->MinBounds.Z) / ZStep));
 
 	    for (int32 Z = StartZ; Z <= EndZ; ++Z)
 	    {
@@ -76,9 +90,11 @@ void FDensityField::CalculateVoxelDensities(const APointCloud* PointCloud, const
 	                    FVector NodePosition = VoxelList[Index].GetVoxelPosition();
 	                    const float Distance = FVector::Dist(NodePosition, ParticlePosition);
 
-	                    if (Distance < InfluenceRadius)
+	                    if (Distance <= InfluenceRadius)
 	                    {
-	                        const double Weight = FUtilities::GaussianKernel(Distance, Sigma);
+	                        //const double Weight = FUtilities::GaussianKernel(Distance, Sigma);
+	                        const double Weight = FUtilities::SmoothingKernel(Distance, InfluenceRadius);
+	                        //const double Weight = InfluenceRadius - Distance;
 	                        VoxelList[Index].AddToVoxelDensity(Weight);
 	                        VoxelList[Index].SetClosePointsNumber(VoxelList[Index].GetClosePointsNumber() + 1.0);
 	                    }
@@ -249,19 +265,19 @@ void FDensityField::DrawDebugVoxelDensity(const UWorld* World, const AActor* Act
 }
 
 
-
 //	WORLD POSITION CONVERSION FUNCTIONS
 
 int32 FDensityField::WorldPositionToIndex(const FVector &Position) const {
 	// Convert world position to grid position
 	const FIntVector3 GridPosition = WorldToGridPosition(Position);
 
-	// Check if the grid position is valid
-	if (!IsValidGridPosition(GridPosition.X, GridPosition.Y, GridPosition.Z)) {
-		UE_LOG(LogTemp, Warning, TEXT("Position out of grid bounds or invalid grid settings, cannot find box index."));
-		return -1;  // Grid position was invalid
+	// Check if the calculated grid coordinates are within the bounds
+	if (!IsValidGridPosition(GridPosition.X, GridPosition.Y, GridPosition.Z))
+	{
+		//UE_LOG(LogTemp, Warning, TEXT("Position out of grid bounds, returning invalid index."));
+		return -1; 
 	}
-
+	
 	return GridPositionToIndex(GridPosition.X, GridPosition.Y, GridPosition.Z);
 }
 
@@ -277,14 +293,7 @@ FIntVector3 FDensityField::WorldToGridPosition(const FVector& Position) const
 	const int32 GridX = FMath::FloorToInt((Position.X - GridOrigin.X) / XStep);
 	const int32 GridY = FMath::FloorToInt((Position.Y - GridOrigin.Y) / YStep);
 	const int32 GridZ = FMath::FloorToInt((Position.Z - GridOrigin.Z) / ZStep);
-
-	// Check if the calculated grid coordinates are within the bounds
-	if (GridX < 0 || GridX >= XNum || GridY < 0 || GridY >= YNum || GridZ < 0 || GridZ >= ZNum)
-	{
-		UE_LOG(LogTemp, Warning, TEXT("Position out of grid bounds, returning invalid grid coordinates."));
-		return FIntVector3(-1, -1, -1);  // Return an invalid grid position if out of bounds
-	}
-
+	
 	return FIntVector3(GridX, GridY, GridZ);
 }
 
diff --git a/Source/MetaCastBachelor/DensityField.h b/Source/MetaCastBachelor/DensityField.h
index 3f4ff10..c854dcd 100644
--- a/Source/MetaCastBachelor/DensityField.h
+++ b/Source/MetaCastBachelor/DensityField.h
@@ -1,6 +1,7 @@
 #pragma once
 
 #include "CoreMinimal.h"
+#include "VoxelPointLookupTable.h"
 #include "DensityField.generated.h"
 
 class APointCloud;
@@ -33,24 +34,6 @@ public:
     void AddToVoxelDensity(const double Dis) { VoxelDensity += Dis; }
 };
 
-USTRUCT(BlueprintType)
-struct FLUTUnit
-{
-    GENERATED_BODY()
-
-    TArray<int32> LUTUnit;
-
-    void AddToLUT(const int32 TargetInt)
-    {
-        LUTUnit.Add(TargetInt);
-    }
-
-    TArray<int32> GetLTUnit() const
-    {
-        return LUTUnit;
-    }
-};
-
 class FDensityField
 {
 	//	VARIABLES
@@ -58,17 +41,20 @@ class FDensityField
     int32 XNum, YNum, ZNum;
     float XStep, YStep, ZStep;
 	FVector GridOrigin;
+
+	void CalculateVoxelDensities(const APointCloud* PointCloud, float InfluenceRadius, float Sigma);
+	void CalculateVoxelDensitiesByNumber(const APointCloud* PointCloud, float InfluenceRadius);
+	void CalculateAndStoreGradients();
+	void InitializeDataStructures(const FVector& MinBounds, const FVector& MaxBounds, int32 XAxisNum, int32 YAxisNum, int32 ZAxisNum);
 	
 public:
+	FVoxelPointLookupTable* VoxelPointLookupTable;
 	mutable FCriticalSection DataGuard;
 	// CONSTRUCTOR
 	FDensityField();
 
 	// INITIALIZATION FUNCTIONS
-	void InitializeDensityField(const FVector& MinBounds, const FVector& MaxBounds, const int32 XAxisNum, const int32 YAxisNum, const int32 ZAxisNum);
-    void CalculateVoxelDensities(const APointCloud* PointCloud, float InfluenceRadius, float Sigma);
-	void CalculateVoxelDensitiesByNumber(const APointCloud* PointCloud, float InfluenceRadius);
-	void CalculateAndStoreGradients();
+	void InitializeDensityField(const APointCloud* PointCloud, const FVector& MinBounds, const FVector& MaxBounds, const FIntVector AxisNumbers);
 
 	//	DEBUG FUNCTIONS
     void DrawDebugVoxelDensity(const UWorld* World, const AActor* Actor, float Duration, float Scale, float DensityThreshold) const;
@@ -97,7 +83,6 @@ public:
 	bool IsValidWorldPosition(const FVector& Position) const;
 	TArray<int32> IndexToVoxelNeighbors(const int32 Index) const;
 	bool IsValidIndex(int32 Index) const;
-	bool IsValid();
 	void SetVoxelDensityByIndex(int32 Index, double Density);
     void SetVoxelGradientByIndex(int32 Index, const FVector& Gradient);
 };
diff --git a/Source/MetaCastBachelor/MetaPoint.cpp b/Source/MetaCastBachelor/MetaPoint.cpp
index 4a71e32..cde81c9 100644
--- a/Source/MetaCastBachelor/MetaPoint.cpp
+++ b/Source/MetaCastBachelor/MetaPoint.cpp
@@ -1,10 +1,11 @@
 #include "MetaPoint.h"
 #include "Utilities.h"
+#include "Generators/MarchingCubes.h"
 
 UMetaPoint::UMetaPoint() : LocalMaximumIndex(0), MyDensityField(nullptr), MetaPointThreshold(0), World(nullptr)
 {
 	// Initialize the Marching Cubes algorithm
-	MyMarchingCubes = new MarchingCubes();
+	//MyMarchingCubes = new FMarchingCubes();
 
 	// Create the procedural mesh component
 	ProceduralMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh"));
@@ -24,6 +25,13 @@ void UMetaPoint::BeginPlay()
 	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);
+
 }
 
 void UMetaPoint::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
@@ -31,6 +39,8 @@ void UMetaPoint::TickComponent(float DeltaTime, ELevelTick TickType, FActorCompo
 	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
 
 	AccumulatedTime += DeltaTime;
+
+	ProceduralMesh->SetVisibility(Select);
 }
 
 void UMetaPoint::EraseParticles(const FVector& InputPosition)
@@ -42,8 +52,8 @@ void UMetaPoint::HandleMetaSelectReleased(const FInputActionInstance& Instance)
 {
 	Super::HandleMetaSelectReleased(Instance);
 
-	ProceduralMesh->ClearAllMeshSections();
-	ProceduralMesh->SetVisibility(false);
+	//ProceduralMesh->ClearAllMeshSections();
+	//ProceduralMesh->SetVisibility(false);
 
 	AsyncTask(ENamedThreads::Type::AnyBackgroundHiPriTask, [this]()
 	{
@@ -72,6 +82,8 @@ void UMetaPoint::InitMetaPointSelection()
 	// Convert the local maximum back to world coordinates
 	LocalMaximumIndex = MyDensityField->WorldPositionToIndex(LocalMaximum);
 
+	SelectStartPosition = LocalMaximum;
+
 	MetaPointThreshold = FUtilities::InterpolateDensityAtPosition(MyDensityField, SelectionLocalPosition);
 
 	// Initialize Visited array to false for all voxels
@@ -87,6 +99,8 @@ void UMetaPoint::InitMetaPointSelection()
 
 	ProceduralMesh->ClearAllMeshSections();
 	World = GetWorld();
+
+	LastHandInput = SelectionWorldPosition;
 }
 
 void UMetaPoint::SelectParticles(const FVector& InputPosition)
@@ -98,6 +112,15 @@ void UMetaPoint::SelectParticles(const FVector& InputPosition)
 		AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this]()
 		{
 			const FVector SelectionWorldPosition = SelectionObject->GetComponentLocation();
+			
+			if(FVector::Distance(LastHandInput, SelectionWorldPosition) < MinimalHandMovement)
+			{
+				AccumulatedTime = 0.0f;
+				return;
+			}
+			
+			LastHandInput = SelectionWorldPosition;
+						
 			// Convert the world position of the selection object to the local position relative to the point cloud
 			const FVector SelectionLocalPosition = MyPointCloud->PointCloudVisualizer->GetComponentTransform().InverseTransformPosition(SelectionWorldPosition);
 
@@ -105,19 +128,19 @@ void UMetaPoint::SelectParticles(const FVector& InputPosition)
 			LocalMaximum = FUtilities::FollowGradientToMaximum(MyDensityField, SelectionLocalPosition);
 
 			// Convert the local maximum back to world coordinates
-			LocalMaximumIndex = MyDensityField->WorldPositionToIndex(LocalMaximum);
+			//LocalMaximumIndex = MyDensityField->WorldPositionToIndex(LocalMaximum);
 
 			MetaPointThreshold = FUtilities::InterpolateDensityAtPosition(MyDensityField, SelectionLocalPosition);
-			FloodedIndices = FUtilities::FloodFilling(MyDensityField, LocalMaximumIndex, MetaPointThreshold);
-			
-			GenerateVoxelMesh(FloodedIndices);
+
+			const float MaxDistance = FVector::Distance(SelectStartPosition, SelectionLocalPosition);
+			FloodedIndices = FUtilities::FloodFilling_2(MyDensityField, LocalMaximumIndex, MetaPointThreshold, MaxDistance);
 			
-			AccumulatedTime = 0.0f;
+			GenerateVoxelMeshSmooth(FloodedIndices);
 		});
 	}
 }
 
-void UMetaPoint::GenerateVoxelMesh(const TArray<int32> Voxels) const
+void UMetaPoint::GenerateVoxelMeshWithCubes(const TArray<int32> Voxels) const
 {
 	TArray<FVector> Vertices;
 	TArray<int32> Triangles;
@@ -164,35 +187,79 @@ void UMetaPoint::GenerateVoxelMesh(const TArray<int32> Voxels) const
 	});
 }
 
-void UMetaPoint::CreateMeshSections(const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FColor>& VertexColors, const int32 MaxVerticesPerSection) const
+void UMetaPoint::GenerateVoxelMeshSmooth(TArray<int32> Voxels)
 {
-	const int32 NumVertices = Vertices.Num();
-	const int32 NumTriangles = Triangles.Num() / 3;
-
-	for (int32 SectionIndex = 0, VertexIndex = 0, TriangleIndex = 0; VertexIndex < NumVertices && TriangleIndex < NumTriangles;
-		 ++SectionIndex, VertexIndex += MaxVerticesPerSection, TriangleIndex += MaxVerticesPerSection / 3)
+	AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this, Voxels]()
 	{
-		const int32 NumVerticesInSection = FMath::Min(MaxVerticesPerSection, NumVertices - VertexIndex);
-		const int32 NumTrianglesInSection = FMath::Min(MaxVerticesPerSection / 3, NumTriangles - TriangleIndex);
-
-		TArray<FVector> SectionVertices;
-		TArray<FColor> SectionVertexColors;
-		SectionVertices.Reserve(NumVerticesInSection);
-		SectionVertexColors.Reserve(NumVerticesInSection);
-		for (int32 i = 0; i < NumVerticesInSection; ++i)
+		FVector Min(FLT_MAX, FLT_MAX, FLT_MAX);
+		FVector Max(-FLT_MAX, -FLT_MAX, -FLT_MAX);
+		
+		for(const int FloodedIndex : Voxels)
 		{
-			SectionVertices.Add(Vertices[VertexIndex + i]);
-			SectionVertexColors.Add(VertexColors[VertexIndex + i]);
+			Min = FVector::Min(Min, MyDensityField->IndexToVoxelPosition(FloodedIndex));
+			Max = FVector::Max(Max, MyDensityField->IndexToVoxelPosition(FloodedIndex));
 		}
 
-		TArray<int32> SectionTriangles;
-		SectionTriangles.Reserve(NumTrianglesInSection * 3);
-		for (int32 i = 0; i < NumTrianglesInSection * 3; ++i)
+		UE::Geometry::FMarchingCubes MarchingCubes;
+		MarchingCubes.Bounds = UE::Geometry::TAxisAlignedBox3(Min, Max);
+		MarchingCubes.CubeSize = MyDensityField->GetStep().X;
+		MarchingCubes.IsoValue = 0;
+
+		// Define the implicit function
+		MarchingCubes.Implicit = [this, Voxels](const FVector3d& Pos) {
+			const FVector PosConverted = FVector(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)
 		{
-			SectionTriangles.Add(Triangles[TriangleIndex * 3 + i] - VertexIndex);
+			Vertices.Add(FVector(Vertex.X, Vertex.Y, Vertex.Z));
 		}
 
-		// Create or update the mesh section
-		ProceduralMesh->CreateMeshSection(SectionIndex, SectionVertices, SectionTriangles, TArray<FVector>(), TArray<FVector2D>(), SectionVertexColors, TArray<FProcMeshTangent>(), false);
-	}
+		// 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]()
+		{
+			ProceduralMesh->CreateMeshSection(0, Vertices, Indices, Normals, TArray<FVector2D>(), VertexColors, TArray<FProcMeshTangent>(), false);
+			ProceduralMesh->SetVisibility(true);
+			AccumulatedTime = 0.0f;
+		});
+	});
+	
+
+	
 }
\ No newline at end of file
diff --git a/Source/MetaCastBachelor/MetaPoint.h b/Source/MetaCastBachelor/MetaPoint.h
index e221d59..ecb85c6 100644
--- a/Source/MetaCastBachelor/MetaPoint.h
+++ b/Source/MetaCastBachelor/MetaPoint.h
@@ -4,7 +4,7 @@
 #include "CoreMinimal.h"
 #include "ProceduralMeshComponent.h"
 #include "MetaCastBaseline.h"
-#include "MetaCastBachelor/MarchingCubes.h"
+#include "Generators/MarchingCubes.h"
 #include "MetaPoint.generated.h"
 
 UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
@@ -12,7 +12,6 @@ class UMetaPoint : public UMetaCastBaseline
 {
 	GENERATED_BODY()
 	
-	MarchingCubes* MyMarchingCubes;
 	FVector WorldMaximum;
 	int32 LocalMaximumIndex;
 	FVector LocalMaximum;
@@ -21,6 +20,10 @@ class UMetaPoint : public UMetaCastBaseline
 	UPROPERTY()
 	UWorld* World;
 	float AccumulatedTime = 0.0;
+	FVector SelectStartPosition;
+	FVector LastHandInput;
+	UPROPERTY(EditAnywhere)
+	float MinimalHandMovement = 0.01;
 
 	UPROPERTY()
 	UProceduralMeshComponent* ProceduralMesh;
@@ -34,7 +37,7 @@ class UMetaPoint : public UMetaCastBaseline
 	TArray<int32> RevisitIndices;
 
 	UPROPERTY(EditAnywhere)
-	int MetaCastPerSecond = 10;
+	int MetaCastPerSecond = 1;
 	
 public:
 	UMetaPoint();
@@ -48,8 +51,8 @@ public:
 	virtual void HandleMetaSelectPressed(const FInputActionInstance& Instance) override;
 	void InitMetaPointSelection();
 
-	void GenerateVoxelMesh(const TArray<int32> Voxels) const;
-	void CreateMeshSections(const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FColor>& VertexColors, int32 MaxVerticesPerSection) const;
+	void GenerateVoxelMeshWithCubes(const TArray<int32> Voxels) const;
+	void GenerateVoxelMeshSmooth(const TArray<int32> Voxels);
 
 	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
 };
diff --git a/Source/MetaCastBachelor/MarchingCubes.cpp b/Source/MetaCastBachelor/MyMarchingCubes.cpp
similarity index 86%
rename from Source/MetaCastBachelor/MarchingCubes.cpp
rename to Source/MetaCastBachelor/MyMarchingCubes.cpp
index b44eb56..5786201 100644
--- a/Source/MetaCastBachelor/MarchingCubes.cpp
+++ b/Source/MetaCastBachelor/MyMarchingCubes.cpp
@@ -1,16 +1,16 @@
 // Copyright 2017 Patrick Chadbourne (PChadbourne). All Rights Reserved.
 
-#include "MarchingCubes.h"
+#include "MyMarchingCubes.h"
 
-MarchingCubes::MarchingCubes()
+FMyMarchingCubes::FMyMarchingCubes()
 {
 }
 
-MarchingCubes::~MarchingCubes()
+FMyMarchingCubes::~FMyMarchingCubes()
 {
 }
 
-void MarchingCubes::Polyganize(FCell Cell, FMeshData* Data)
+void FMyMarchingCubes::Polyganize(FCell Cell, FMeshData* Data)
 {
     int CubeIndex = 0;
     FVector VertList[12];
@@ -72,12 +72,10 @@ void MarchingCubes::Polyganize(FCell Cell, FMeshData* Data)
     }
 }
 
-
-
-void MarchingCubes::ProcessGrid(TArray<float> Grid, int Width, FMeshData* Data, FVector A, FVector B, FVector C, FVector D)
+void FMyMarchingCubes::ProcessGrid(TArray<float> Grid, int Width, FMeshData* Data, FVector A, FVector B, FVector C, FVector D)
 {
-    int WidthSquared = Width * Width;
-    int GridSize = Width * Width * Width;
+	const int WidthSquared = Width * Width;
+	const int GridSize = Width * Width * Width;
     
     // Ensure the Grid array has enough elements
     if (Grid.Num() < GridSize)
@@ -94,14 +92,14 @@ void MarchingCubes::ProcessGrid(TArray<float> Grid, int Width, FMeshData* Data,
         {
             for (int k = 0; k < Width - 1; k++)
             {
-                int idx7 = i + Width * j + WidthSquared * k;
-                int idx4 = idx7 + 1;
-                int idx6 = idx7 + Width;
-                int idx5 = idx6 + 1;
-                int idx3 = idx7 + WidthSquared;
-                int idx0 = idx3 + 1;
-                int idx2 = idx3 + Width;
-                int idx1 = idx2 + 1;
+	            const int idx7 = i + Width * j + WidthSquared * k;
+                const int idx4 = idx7 + 1;
+                const int idx6 = idx7 + Width;
+                const int idx5 = idx6 + 1;
+                const int idx3 = idx7 + WidthSquared;
+                const int idx0 = idx3 + 1;
+                const int idx2 = idx3 + Width;
+                const int idx1 = idx2 + 1;
 
                 // Ensure all indices are within bounds
                 if (idx7 >= Grid.Num() || idx4 >= Grid.Num() || idx6 >= Grid.Num() || idx5 >= Grid.Num() ||
@@ -156,9 +154,7 @@ void MarchingCubes::ProcessGrid(TArray<float> Grid, int Width, FMeshData* Data,
     }
 }
 
-
-
-FVector MarchingCubes::VertexInterp(FVector P1, FVector P2, float P1Val, float P2Val, float Value)
+FVector FMyMarchingCubes::VertexInterp(FVector P1, FVector P2, float P1Val, float P2Val, float Value)
 {
 
 
@@ -179,7 +175,7 @@ FVector MarchingCubes::VertexInterp(FVector P1, FVector P2, float P1Val, float P
 	return(P);
 }
 
-FVector MarchingCubes::CalculateTriangularInterpolation(int i, int j, int k, FVector A, FVector B, FVector C, FVector D, int Width)
+FVector FMyMarchingCubes::CalculateTriangularInterpolation(int i, int j, int k, FVector A, FVector B, FVector C, FVector D, int Width)
 {
 	if (i + j < Width) {
 		FVector alpha = FMath::Lerp(A, B, (i*2.0f) / (float)Width);
diff --git a/Source/MetaCastBachelor/MarchingCubes.h b/Source/MetaCastBachelor/MyMarchingCubes.h
similarity index 99%
rename from Source/MetaCastBachelor/MarchingCubes.h
rename to Source/MetaCastBachelor/MyMarchingCubes.h
index ea31fc9..d48a2ed 100644
--- a/Source/MetaCastBachelor/MarchingCubes.h
+++ b/Source/MetaCastBachelor/MyMarchingCubes.h
@@ -19,11 +19,11 @@ public:
 	TArray<FVector> N;
 };
 
-class MarchingCubes
+class FMyMarchingCubes
 {
 public:
-	MarchingCubes();
-	~MarchingCubes();
+	FMyMarchingCubes();
+	~FMyMarchingCubes();
 
 	void ProcessGrid(TArray<float> Grid, int Width, FMeshData* Data, FVector A, FVector B, FVector C, FVector D);
 
diff --git a/Source/MetaCastBachelor/PointCloud.cpp b/Source/MetaCastBachelor/PointCloud.cpp
index 747d186..d319ce9 100644
--- a/Source/MetaCastBachelor/PointCloud.cpp
+++ b/Source/MetaCastBachelor/PointCloud.cpp
@@ -85,22 +85,9 @@ void APointCloud::UpdateBounds()
 void APointCloud::SetupDensityFieldFromPointCloud() const
 {
 	UE_LOG(LogTemp, Log, TEXT("Initializing DensityField!"));
-	
-	constexpr int32 XAxisNum = 100; // Number of divisions in the grid along the X-axis
-	constexpr int32 YAxisNum = 100; // Number of divisions in the grid along the Y-axis
-	constexpr int32 ZAxisNum = 100; // Number of divisions in the grid along the Z-axis
-	MyDensityField->InitializeDensityField(MinBounds, MaxBounds, XAxisNum, YAxisNum, ZAxisNum);
-
-	constexpr float InfluenceRadius = 2.0f; // Half the size of a voxel
-	constexpr float Sigma = 0.5f;  // Standard deviation for the Gaussian kernel
-	
-	MyDensityField->CalculateVoxelDensitiesByNumber(this, InfluenceRadius);
-	MyDensityField->CalculateAndStoreGradients();
 
-	if (const UWorld* World = GetWorld())
-	{
-		//MyDensityField->DrawDebugVoxelDensity(World, this, 100.0f, 1.0f, 0.8f);
-	}
+	const FIntVector AxisNumbers(100, 100, 100);
+	MyDensityField->InitializeDensityField(this, MinBounds, MaxBounds, AxisNumbers);
 }
 
 void APointCloud::UpdateSelection()
@@ -111,7 +98,7 @@ void APointCloud::UpdateSelection()
 		{
 			if (SelectionFlags[i])
 			{
-				PointColors[i] = FColor::Red;
+				PointColors[i] = FColor(0, 200, 0);
 			}else
 			{
 				PointColors[i] = FColor::Orange;
@@ -149,57 +136,31 @@ void APointCloud::DrawVoxel(const int Index, const float Time) const
 
 void APointCloud::ColorPointsInVoxels(const TArray<int32> VoxelIndices)
 {
+	FScopeLock Lock(&DataGuard);
 	if (!MyDensityField)
 	{
 		UE_LOG(LogTemp, Warning, TEXT("DensityField is not initialized."));
 		return;
 	}
 
-	TArray<FVector> MyPositionVectors = PositionVectors;
-
-	FScopeLock Lock(&DataGuard);
-	FScopeLock Lock2(&MyDensityField->DataGuard);
-	// Iterate through each index in VoxelIndices
 	for (const int32 VoxelIndex : VoxelIndices)
 	{
-		// Ensure the voxel index is valid
-		if (!MyDensityField || !MyDensityField->IsValidIndex(VoxelIndex))
-		{
-			UE_LOG(LogTemp, Warning, TEXT("Invalid voxel index: %d"), VoxelIndex);
-			continue;
-		}
-
-		// Get the bounds of the voxel
-		FVector VoxelMin, VoxelMax;
-		MyDensityField->IndexToVoxelBounds(VoxelIndex, VoxelMin, VoxelMax);
-
-		// Iterate over all points to check if they are within this voxel
-		for (int32 i = 0; i < MyPositionVectors.Num(); ++i)
+		const TArray<int32>& PointIndices = MyDensityField->VoxelPointLookupTable->GetPointsInVoxel(VoxelIndex);
+		for (const int32 PointIndex : PointIndices)
 		{
-			if(!MyPositionVectors.IsValidIndex(i))
+			if(SelectionFlags.IsValidIndex(PointIndex))
 			{
-				continue;
-			}
-			const FVector Point = MyPositionVectors[i];
-
-			// Check if the point is within the voxel bounds
-			if (Point.X >= VoxelMin.X && Point.X <= VoxelMax.X &&
-				Point.Y >= VoxelMin.Y && Point.Y <= VoxelMax.Y &&
-				Point.Z >= VoxelMin.Z && Point.Z <= VoxelMax.Z)
-			{
-				SelectionFlags[i] = true;
+				SelectionFlags[PointIndex] = true;
 			}
 		}
 	}
-
-	//UpdateSelection();
 }
 
+
 // Called every frame
 void APointCloud::Tick(float DeltaTime)
 {
 	Super::Tick(DeltaTime);
-
 	
 	UpdateSelection();
 }
diff --git a/Source/MetaCastBachelor/PointCloud.h b/Source/MetaCastBachelor/PointCloud.h
index 4770f1b..6aae126 100644
--- a/Source/MetaCastBachelor/PointCloud.h
+++ b/Source/MetaCastBachelor/PointCloud.h
@@ -13,9 +13,13 @@ class APointCloud : public AActor
 	
 public:
 	FDensityField* MyDensityField;
+	UPROPERTY()
 	TArray<FVector> PositionVectors;
+	UPROPERTY()
 	TArray<bool> DefaultFlags;
+	UPROPERTY()
 	TArray<bool> SelectionFlags;
+	UPROPERTY()
 	TArray<FColor> PointColors;
 	mutable FCriticalSection DataGuard;
 
diff --git a/Source/MetaCastBachelor/Utilities.cpp b/Source/MetaCastBachelor/Utilities.cpp
index 2362648..91ab449 100644
--- a/Source/MetaCastBachelor/Utilities.cpp
+++ b/Source/MetaCastBachelor/Utilities.cpp
@@ -31,24 +31,21 @@ double FUtilities::InterpolateDensityAtPosition(const FDensityField* DensityFiel
     if (!DensityField)
         return 0.0;
 
-    const FVector GridPos = (Position - DensityField->GetGridOrigin()) / DensityField->GetStep();
-
-    const int32 XBin = FMath::FloorToInt(GridPos.X);
-    const int32 YBin = FMath::FloorToInt(GridPos.Y);
-    const int32 ZBin = FMath::FloorToInt(GridPos.Z);
-
-    const float XFraction = GridPos.X - XBin;
-    const float YFraction = GridPos.Y - YBin;
-    const float ZFraction = GridPos.Z - ZBin;
-
-    const double D000 = DensityField->GridPositionToVoxelDensity(XBin, YBin, ZBin);
-    const double D100 = DensityField->GridPositionToVoxelDensity(XBin + 1, YBin, ZBin);
-    const double D010 = DensityField->GridPositionToVoxelDensity(XBin, YBin + 1, ZBin);
-    const double D001 = DensityField->GridPositionToVoxelDensity(XBin, YBin, ZBin + 1);
-    const double D101 = DensityField->GridPositionToVoxelDensity(XBin + 1, YBin, ZBin + 1);
-    const double D011 = DensityField->GridPositionToVoxelDensity(XBin, YBin + 1, ZBin + 1);
-    const double D110 = DensityField->GridPositionToVoxelDensity(XBin + 1, YBin + 1, ZBin);
-    const double D111 = DensityField->GridPositionToVoxelDensity(XBin + 1, YBin + 1, ZBin + 1);
+    const FVector GridPositionFraction = (Position - DensityField->GetGridOrigin()) / DensityField->GetStep();
+    const FIntVector3 GridPosition = DensityField->WorldToGridPosition(Position - DensityField->GetGridOrigin());
+	
+    const float XFraction = GridPositionFraction.X - GridPosition.X;
+    const float YFraction = GridPositionFraction.Y - GridPosition.Y;
+    const float ZFraction = GridPositionFraction.Z - GridPosition.Z;
+
+    const double D000 = DensityField->GridPositionToVoxelDensity(GridPosition.X, GridPosition.Y, GridPosition.Z);
+    const double D100 = DensityField->GridPositionToVoxelDensity(GridPosition.X + 1, GridPosition.Y, GridPosition.Z);
+    const double D010 = DensityField->GridPositionToVoxelDensity(GridPosition.X, GridPosition.Y + 1, GridPosition.Z);
+    const double D001 = DensityField->GridPositionToVoxelDensity(GridPosition.X, GridPosition.Y, GridPosition.Z + 1);
+    const double D101 = DensityField->GridPositionToVoxelDensity(GridPosition.X + 1, GridPosition.Y, GridPosition.Z + 1);
+    const double D011 = DensityField->GridPositionToVoxelDensity(GridPosition.X, GridPosition.Y + 1, GridPosition.Z + 1);
+    const double D110 = DensityField->GridPositionToVoxelDensity(GridPosition.X + 1, GridPosition.Y + 1, GridPosition.Z);
+    const double D111 = DensityField->GridPositionToVoxelDensity(GridPosition.X + 1, GridPosition.Y + 1, GridPosition.Z + 1);
 
     // Trilinear interpolation
     const double DX00 = FMath::Lerp(D000, D100, XFraction);
@@ -70,25 +67,22 @@ FVector FUtilities::InterpolateGradientAtPosition(const FDensityField* DensityFi
 		return FVector::ZeroVector;
 
 	FVector GridPos = (Position - DensityField->GetGridOrigin()) / DensityField->GetStep();
-
-	int32 XBin = FMath::FloorToInt(GridPos.X);
-	int32 YBin = FMath::FloorToInt(GridPos.Y);
-	int32 ZBin = FMath::FloorToInt(GridPos.Z);
+	const FIntVector3 GridPosition = DensityField->WorldToGridPosition(Position - DensityField->GetGridOrigin());
 
 	// Fractional parts to interpolate
-	float XFraction = GridPos.X - XBin;
-	float YFraction = GridPos.Y - YBin;
-	float ZFraction = GridPos.Z - ZBin;
+	float XFraction = GridPos.X - GridPosition.X;
+	float YFraction = GridPos.Y - GridPosition.Y;
+	float ZFraction = GridPos.Z - GridPosition.Z;
 
 	// Fetch gradients from corners of the cell
-	FVector G000 = DensityField->GridPositionToVoxelGradient(XBin, YBin, ZBin);
-	FVector G100 = DensityField->GridPositionToVoxelGradient(XBin + 1, YBin, ZBin);
-	FVector G010 = DensityField->GridPositionToVoxelGradient(XBin, YBin + 1, ZBin);
-	FVector G001 = DensityField->GridPositionToVoxelGradient(XBin, YBin, ZBin + 1);
-	FVector G110 = DensityField->GridPositionToVoxelGradient(XBin + 1, YBin + 1, ZBin);
-	FVector G101 = DensityField->GridPositionToVoxelGradient(XBin + 1, YBin, ZBin + 1);
-	FVector G011 = DensityField->GridPositionToVoxelGradient(XBin, YBin + 1, ZBin + 1);
-	FVector G111 = DensityField->GridPositionToVoxelGradient(XBin + 1, YBin + 1, ZBin + 1);
+	FVector G000 = DensityField->GridPositionToVoxelGradient(GridPosition.X, GridPosition.Y, GridPosition.Z);
+	FVector G100 = DensityField->GridPositionToVoxelGradient(GridPosition.X + 1, GridPosition.Y, GridPosition.Z);
+	FVector G010 = DensityField->GridPositionToVoxelGradient(GridPosition.X, GridPosition.Y + 1, GridPosition.Z);
+	FVector G001 = DensityField->GridPositionToVoxelGradient(GridPosition.X, GridPosition.Y, GridPosition.Z + 1);
+	FVector G110 = DensityField->GridPositionToVoxelGradient(GridPosition.X + 1, GridPosition.Y + 1, GridPosition.Z);
+	FVector G101 = DensityField->GridPositionToVoxelGradient(GridPosition.X + 1, GridPosition.Y, GridPosition.Z + 1);
+	FVector G011 = DensityField->GridPositionToVoxelGradient(GridPosition.X, GridPosition.Y + 1, GridPosition.Z + 1);
+	FVector G111 = DensityField->GridPositionToVoxelGradient(GridPosition.X + 1, GridPosition.Y + 1, GridPosition.Z + 1);
 
 	// Interpolate along x for each of the yz pairs
 	FVector G00 = FMath::Lerp(G000, G100, XFraction);
@@ -110,6 +104,11 @@ float FUtilities::GaussianKernel(const float Distance, const float Sigma) {
 	return FMath::Exp(-0.5 * FMath::Square(Distance / Sigma));
 }
 
+float FUtilities::SmoothingKernel(const float Distance, const float Radius) {
+	const float Value = FMath::Max(0.0f, Radius * Radius - Distance * Distance);
+	return Value * Value * Value;
+}
+
 FVector FUtilities::FollowGradientToMaximum(const FDensityField* DensityField, const FVector& StartPosition)
 {
 	FVector CurrentPosition = StartPosition;
@@ -117,20 +116,20 @@ FVector FUtilities::FollowGradientToMaximum(const FDensityField* DensityField, c
 
 	int Iterations = 0;
 	constexpr int MaxIterations = 100;  // Prevent infinite loops
-	constexpr float StepSize = 0.1f;  // Define a reasonable step size
+	constexpr float StepSize = 0.005f;  // Define a reasonable step size
 
 	while (Iterations < MaxIterations)
 	{
 		FVector Gradient = InterpolateGradientAtPosition(DensityField, CurrentPosition);
 		if (Gradient.IsNearlyZero()) {
-			UE_LOG(LogTemp, Log, TEXT("Gradient is zero at position %s"), *CurrentPosition.ToString());
+			//UE_LOG(LogTemp, Log, TEXT("Gradient is zero at position %s"), *CurrentPosition.ToString());
 			break;  // Gradient is zero, likely at a local maximum
 		}
 
 		// Move towards the direction of the gradient
 		FVector NextPosition = CurrentPosition + (StepSize * Gradient.GetSafeNormal());
 		if (!DensityField->IsValidWorldPosition(NextPosition)) {
-			UE_LOG(LogTemp, Warning, TEXT("Next position out of bounds: %s"), *NextPosition.ToString());
+			//UE_LOG(LogTemp, Warning, TEXT("Next position out of bounds: %s"), *NextPosition.ToString());
 			break;  // Next position is out of valid bounds
 		}
 
@@ -184,9 +183,9 @@ TArray<int32> FUtilities::FloodFilling(const FDensityField* DensityField, const
 			for (int32 NeighborIndex : Neighbors)
 			{
 				const double NeighborDensity = DensityField->IndexToVoxelDensity(NeighborIndex);
-				//UE_LOG(LogTemp, Log, TEXT("Lookign at Neighbor %d at Position %s with density: %f."), NeighborIndex, *DensityField->IndexToGridPosition(NeighborIndex).ToString(), NeighborDensity);
+				//UE_LOG(LogTemp, Log, TEXT("Looking at Neighbor %d at Position %s with density: %f."), NeighborIndex, *DensityField->IndexToGridPosition(NeighborIndex).ToString(), NeighborDensity);
 				
-				if (NeighborDensity > Threshold && !Visited[NeighborIndex]) {
+				if (NeighborDensity > Threshold * 1.1f && !Visited[NeighborIndex]) {
 					Stack.Push(NeighborIndex);
 					//UE_LOG(LogTemp, Log, TEXT("Pushing Neighbor %d to stack."), NeighborIndex);
 				}
@@ -203,55 +202,71 @@ TArray<int32> FUtilities::FloodFilling(const FDensityField* DensityField, const
 	return VisitedIndices;
 }
 
-void FUtilities::FloodFilling_2(const FDensityField* DensityField, const float Threshold, TArray<bool>& VisitedIndices, TArray<int32>& IndicesToVisit, TArray<int32>& FloodedIndices, TArray<int32>& PotentialRevisit)
+TArray<int32> FUtilities::FloodFilling_2(const FDensityField* DensityField, const int32 StartIndex, const float Threshold, const float MaxDistance)
 {
+	//UE_LOG(LogTemp, Warning, TEXT("Threshold for FloodFilling: %f"), Threshold);
+	
+	TArray<int32> VisitedIndices;
 	if (!DensityField) {
 		UE_LOG(LogTemp, Warning, TEXT("DensityField is null."));
-		return;
+		return VisitedIndices;
 	}
 
-	TArray<int32> CurrentPotentialRevisit;
+	if (!DensityField->IsValidIndex(StartIndex)) {
+		UE_LOG(LogTemp, Warning, TEXT("Start index %d is invalid."), StartIndex);
+		return VisitedIndices;
+	}
 
-	
-	constexpr int MaxIterations = 2000;
-	int IterationCount = 0;
-	
-	while (IndicesToVisit.Num() > 0)
+	TArray<int32> Stack;
+	Stack.Push(StartIndex);
+	TArray<bool> Visited;
+	Visited.Init(false, DensityField->GetVoxelNumber());
+
+	while (Stack.Num() > 0)
 	{
-		
-		IterationCount++;
-		if(IterationCount > MaxIterations)
+		int32 CurrentIndex = Stack.Pop();
+		if (!Visited[CurrentIndex])
 		{
-			while (IndicesToVisit.Num() > 0) {
-				CurrentPotentialRevisit.Add(IndicesToVisit.Pop());
-			}
-			break;
-		}
-		
-		int32 CurrentIndex = IndicesToVisit.Pop();
+			Visited[CurrentIndex] = true;
+			VisitedIndices.Add(CurrentIndex);
 
-		if(DensityField->IsValidIndex(CurrentIndex))
-		{
-			VisitedIndices[CurrentIndex] = true;
-			const double CurrentDensity = DensityField->IndexToVoxelDensity(CurrentIndex);
+			TArray<int32> Neighbors = DensityField->IndexToVoxelNeighbors(CurrentIndex);
+			//UE_LOG(LogTemp, Log, TEXT("Visiting Node %d at Position %s with %d neighbors."), CurrentIndex, *DensityField->IndexToGridPosition(CurrentIndex).ToString(), Neighbors.Num());
+            
+			for (int32 NeighborIndex : Neighbors)
+			{
+				const double NeighborDensity = DensityField->IndexToVoxelDensity(NeighborIndex);
+				//UE_LOG(LogTemp, Log, TEXT("Looking at Neighbor %d at Position %s with density: %f."), NeighborIndex, *DensityField->IndexToGridPosition(NeighborIndex).ToString(), NeighborDensity);
 
-			if (CurrentDensity >= Threshold) {
-				FloodedIndices.Add(CurrentIndex);
+				const float DistanceToStart = FVector::Dist(DensityField->IndexToVoxelPosition(StartIndex), DensityField->IndexToVoxelPosition(NeighborIndex));
+				
+				if (!Visited[NeighborIndex]){
+					if(DistanceToStart <= MaxDistance)
+					{
+						if(NeighborDensity > Threshold * 1.0f)
+						{
+							Stack.Push(NeighborIndex);
+						}
 
-				TArray<int32> Neighbors = DensityField->IndexToVoxelNeighbors(CurrentIndex);
-				for (int32 NeighborIndex : Neighbors)
-				{
-					if (!VisitedIndices[NeighborIndex])
+					}else
 					{
-						IndicesToVisit.Push(NeighborIndex);
+						const float Multiplier = 1 + FMath::Pow(DistanceToStart - MaxDistance, 2);
+						if(NeighborDensity > Threshold * Multiplier)
+						{
+							Stack.Push(NeighborIndex);
+						}
 					}
+					//UE_LOG(LogTemp, Log, TEXT("Pushing Neighbor %d to stack."), NeighborIndex);
 				}
-			} else {
-				CurrentPotentialRevisit.Add(CurrentIndex);
 			}
 		}
 	}
 
-	// Update PotentialRevisit with the new list from this run
-	PotentialRevisit = CurrentPotentialRevisit;
+	// Logging final details
+	//UE_LOG(LogTemp, Log, TEXT("Flood filling completed from start index %d. Total voxels selected: %d"), StartIndex, VisitedIndices.Num());
+	for (const int32 idx : VisitedIndices) {
+		//UE_LOG(LogTemp, Log, TEXT("Selected Voxel Index: %d at Grid Position: %s"), idx, *DensityField->IndexToGridPosition(idx).ToString());
+	}
+
+	return VisitedIndices;
 }
diff --git a/Source/MetaCastBachelor/Utilities.h b/Source/MetaCastBachelor/Utilities.h
index 6d406d7..b453f82 100644
--- a/Source/MetaCastBachelor/Utilities.h
+++ b/Source/MetaCastBachelor/Utilities.h
@@ -9,7 +9,8 @@ public:
 	static double InterpolateDensityAtPosition(const FDensityField* DensityField, const FVector& Position);
 	static FVector InterpolateGradientAtPosition(const FDensityField* DensityField, const FVector& Position);
 	static float GaussianKernel(float Distance, float Sigma);
+	static float SmoothingKernel(float Distance, float Radius);
 	static FVector FollowGradientToMaximum(const FDensityField* DensityField, const FVector& StartPosition);
 	static TArray<int32> FloodFilling(const FDensityField* DensityField, const int32 StartIndex, const float Threshold);
-	static void FloodFilling_2(const FDensityField* DensityField, const float Threshold, TArray<bool>& VisitedIndices, TArray<int32>& IndicesToVisit, TArray<int32>& FloodedIndices, TArray<int32>& PotentialRevisit);
+	static TArray<int32> FloodFilling_2(const FDensityField* DensityField, const int32 StartIndex, const float Threshold, const float MaxDistance);
 };
diff --git a/Source/MetaCastBachelor/VoxelPointLookupTable.cpp b/Source/MetaCastBachelor/VoxelPointLookupTable.cpp
new file mode 100644
index 0000000..a9499c2
--- /dev/null
+++ b/Source/MetaCastBachelor/VoxelPointLookupTable.cpp
@@ -0,0 +1,29 @@
+#include "VoxelPointLookupTable.h"
+#include "DensityField.h"
+
+
+FVoxelPointLookupTable::FVoxelPointLookupTable() : LinkedDensityField(nullptr) {}
+
+FVoxelPointLookupTable::~FVoxelPointLookupTable(){}
+
+void FVoxelPointLookupTable::Initialize(const TArray<FVector>& PositionVectors, const FDensityField* DensityField)
+{
+	LinkedDensityField = DensityField;
+	if (!LinkedDensityField) return;
+
+	for (int32 i = 0; i < PositionVectors.Num(); ++i)
+	{
+		const FVector& Point = PositionVectors[i];
+		int32 VoxelIndex = LinkedDensityField->WorldPositionToIndex(Point);
+		if (VoxelIndex != -1)
+		{
+			VoxelToPointIndicesMap.FindOrAdd(VoxelIndex).Add(i);
+		}
+	}
+}
+
+TArray<int32> FVoxelPointLookupTable::GetPointsInVoxel(const int32 VoxelIndex) const
+{
+	return VoxelToPointIndicesMap.Contains(VoxelIndex) ? VoxelToPointIndicesMap[VoxelIndex] : TArray<int32>();
+}
+
diff --git a/Source/MetaCastBachelor/VoxelPointLookupTable.h b/Source/MetaCastBachelor/VoxelPointLookupTable.h
new file mode 100644
index 0000000..52076a3
--- /dev/null
+++ b/Source/MetaCastBachelor/VoxelPointLookupTable.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "CoreMinimal.h"
+
+class FDensityField;
+
+class FVoxelPointLookupTable
+{
+
+public:
+	FVoxelPointLookupTable();
+
+	virtual ~FVoxelPointLookupTable();
+
+	// Initializes the lookup table with position vectors and a reference to the density field
+	void Initialize(const TArray<FVector>& PositionVectors, const FDensityField* DensityField);
+
+	// Retrieves the array of point indices for a specific voxel index
+	TArray<int32> GetPointsInVoxel(int32 VoxelIndex) const;
+
+private:
+	// Maps voxel indices to arrays of point indices
+	TMap<int32, TArray<int32>> VoxelToPointIndicesMap;
+
+	// Reference to the density field for voxel bounds queries
+	const FDensityField* LinkedDensityField;
+};
-- 
GitLab