diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index 19b59fc6679ce83857ad5936b684a8ae7cbf3c46..05ba680c81fb1b6a8ffa562e19b877c137bd4e96 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -125,7 +125,11 @@ r.Shadow.Virtual.Enable = 1 r.Mobile.AntiAliasing=3 r.AntiAliasingMethod=3 vr.InstancedStereo=True -r.MobileHDR=True +r.MobileHDR=False +r.MSAACount=4 +r.Mobile.ShadingPath=1 +r.ForwardShading=True +r.CustomDepth=1 [/Script/HardwareTargeting.HardwareTargetingSettings] TargetedHardwareClass = Mobile diff --git a/Content/MetaPointMap.umap b/Content/MetaPointMap.umap index add3f06500b1b576fa486e1ede28b8e2d591a298..651dc1767d85ce66c1085a001440e676d242a400 100644 Binary files a/Content/MetaPointMap.umap and b/Content/MetaPointMap.umap differ diff --git a/Content/MetaPointMap_BuiltData.uasset b/Content/MetaPointMap_BuiltData.uasset index 1bf02fa1814c30bc66b1c21252e01906ddb1ac28..24285dcf828b76f9af8653034e84014cb1f8e362 100644 Binary files a/Content/MetaPointMap_BuiltData.uasset and b/Content/MetaPointMap_BuiltData.uasset differ diff --git a/Content/SelectionVolume.uasset b/Content/SelectionVolume.uasset new file mode 100644 index 0000000000000000000000000000000000000000..73d06d7c6830ac1fc883baf71e935610588d84e9 Binary files /dev/null and b/Content/SelectionVolume.uasset differ diff --git a/Source/MetaCastBachelor/DensityField.cpp b/Source/MetaCastBachelor/DensityField.cpp index 0cfcdc980f62fdcada431c3185338949ad7cb496..3aa6e9bcc1397e4cdfae5b1ab8fb2388fe6895b7 100644 --- a/Source/MetaCastBachelor/DensityField.cpp +++ b/Source/MetaCastBachelor/DensityField.cpp @@ -25,7 +25,7 @@ void FDensityField::InitializeDensityField(const FVector& MinBounds, const FVect for (int32 X = 0; X < XAxisNum; X++) { FVector Position(MinBounds.X + X * XStep, MinBounds.Y + Y * YStep, MinBounds.Z + Z * ZStep); - FVector GridPosition(X, Y, Z); + FIntVector3 GridPosition(X, Y, Z); VoxelList.Add(FVoxel(Position, GridPosition)); } } @@ -140,7 +140,6 @@ void FDensityField::CalculateAndStoreGradients() UE_LOG(LogTemp, Log, TEXT("Gradient calculation completed.")); } - // DEBUG FUNCTIONS void FDensityField::DrawDebugVoxelDensity(const UWorld* World, const AActor* Actor, const float Duration, const float Scale, const float DensityThreshold) const @@ -183,16 +182,11 @@ void FDensityField::DrawDebugVoxelDensity(const UWorld* World, const AActor* Act UE_LOG(LogTemp, Log, TEXT("DensityField drawn with gradient colors using logarithmic scaling!")); } -// CONVERSION FUNCTIONS - -int32 FDensityField::GridPositionToIndex(const int32 X, const int32 Y, const int32 Z) const -{ - return Z * XNum * YNum + Y * XNum + X; -} +// WORLD POSITION CONVERSION FUNCTIONS int32 FDensityField::WorldPositionToIndex(const FVector &Position) const { // Convert world position to grid position - const FVector GridPosition = WorldToGridPosition(Position); + const FIntVector3 GridPosition = WorldToGridPosition(Position); // Check if the grid position is valid if (!IsValidGridPosition(GridPosition.X, GridPosition.Y, GridPosition.Z)) { @@ -203,12 +197,12 @@ int32 FDensityField::WorldPositionToIndex(const FVector &Position) const { return GridPositionToIndex(GridPosition.X, GridPosition.Y, GridPosition.Z); } -FVector FDensityField::WorldToGridPosition(const FVector& Position) const +FIntVector3 FDensityField::WorldToGridPosition(const FVector& Position) const { if (XStep == 0 || YStep == 0 || ZStep == 0) { UE_LOG(LogTemp, Warning, TEXT("Grid steps are zero, cannot convert position to grid coordinates.")); - return FVector(-1, -1, -1); // Return an invalid grid position if any grid step is zero + return FIntVector3(-1, -1, -1); // Return an invalid grid position if any grid step is zero } // Calculate the grid coordinates by subtracting the grid origin and dividing by the grid step sizes @@ -220,20 +214,55 @@ FVector FDensityField::WorldToGridPosition(const FVector& Position) const 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 FVector(-1, -1, -1); // Return an invalid grid position if out of bounds + return FIntVector3(-1, -1, -1); // Return an invalid grid position if out of bounds } - return FVector(GridX, GridY, GridZ); + return FIntVector3(GridX, GridY, GridZ); } -bool FDensityField::IsValidGridPosition(const int32 XBin, const int32 YBin, const int32 ZBin) const +// GRID POSITION CONVERSION FUNCTIONS + +int32 FDensityField::GridPositionToIndex(const int32 X, const int32 Y, const int32 Z) const { - // Check if the provided indices are within the grid bounds - return (XBin >= 0 && XBin < XNum) && - (YBin >= 0 && YBin < YNum) && - (ZBin >= 0 && ZBin < ZNum); + return Z * XNum * YNum + Y * XNum + X; +} + +double FDensityField::GridPositionToVoxelDensity(const int32 XBin, const int32 YBin, const int32 ZBin) const +{ + // First, check if the provided indices are valid + if (IsValidGridPosition(XBin, YBin, ZBin)) + { + // Convert 3D grid coordinates to a linear index + const int32 Index = GridPositionToIndex(XBin, YBin, ZBin); + + return IndexToVoxelDensity(Index); + } + + // Return 0 if the indices are out of bounds or the index is invalid + return 0.0; +} + +FVector FDensityField::GridPositionToVoxelGradient(const int32 XBin, const int32 YBin, const int32 ZBin) const +{ + // First, check if the provided indices are valid + if (IsValidGridPosition(XBin, YBin, ZBin)) + { + // Convert 3D grid coordinates to a linear index + const int32 Index = GridPositionToIndex(XBin, YBin, ZBin); + + // Ensure the index is within the valid range + if (VoxelList.IsValidIndex(Index)) + { + // Return the gradient at the calculated index + return VoxelList[Index].GetVoxelGradient(); + } + } + // Return 0 if the indices are out of bounds or the index is invalid + return FVector::Zero(); } +// INDEX CONVERSION FUNCTIONS + FVector FDensityField::IndexToVoxelGradient(const int32 Index) const { if (VoxelList.IsValidIndex(Index)) @@ -252,30 +281,107 @@ FVector FDensityField::IndexToVoxelPosition(const int32 Index) const return -FVector::One(); } -FVector FDensityField::IndexToGridPosition(const int32 Index) const +FIntVector3 FDensityField::IndexToGridPosition(const int32 Index) const { if (IsValidIndex(Index)) { return VoxelList[Index].GetVoxelGridPos(); } - return -FVector::One(); + return FIntVector3(-1, -1, -1); } +TArray<FVector> FDensityField::IndexToVoxelCornersWorld(const int32 Index) const +{ + TArray<FVector> Corners; + + if (!VoxelList.IsValidIndex(Index)) + { + return Corners; // Return an empty array if the index is invalid + } + + FVector VoxelPosition = VoxelList[Index].GetVoxelPosition(); + + FVector HalfStep = FVector(XStep, YStep, ZStep) * 0.5f; + + // Define the 8 corners relative to the voxel's center position + Corners.Add(VoxelPosition + FVector(-HalfStep.X, -HalfStep.Y, -HalfStep.Z)); + Corners.Add(VoxelPosition + FVector(HalfStep.X, -HalfStep.Y, -HalfStep.Z)); + Corners.Add(VoxelPosition + FVector(HalfStep.X, HalfStep.Y, -HalfStep.Z)); + Corners.Add(VoxelPosition + FVector(-HalfStep.X, HalfStep.Y, -HalfStep.Z)); + Corners.Add(VoxelPosition + FVector(-HalfStep.X, -HalfStep.Y, HalfStep.Z)); + Corners.Add(VoxelPosition + FVector(HalfStep.X, -HalfStep.Y, HalfStep.Z)); + Corners.Add(VoxelPosition + FVector(HalfStep.X, HalfStep.Y, HalfStep.Z)); + Corners.Add(VoxelPosition + FVector(-HalfStep.X, HalfStep.Y, HalfStep.Z)); + + return Corners; +} -// GETTER AND SETTER FUNCTIONS +TArray<int32> FDensityField::IndexToVoxelNeighbors(const int32 Index) const +{ + TArray<int32> Neighbors; + if (IsValidIndex(Index)) + { + const FIntVector3 Pos = IndexToGridPosition(Index); + const int32 X = Pos.X; + const int32 Y = Pos.Y; + const int32 Z = Pos.Z; -FVector FDensityField::GetGridOrigin() const + // List all potential neighbors + TArray<FIntVector> PotentialNeighbors = { + FIntVector(X + 1, Y, Z), FIntVector(X - 1, Y, Z), + FIntVector(X, Y + 1, Z), FIntVector(X, Y - 1, Z), + FIntVector(X, Y, Z + 1), FIntVector(X, Y, Z - 1) + }; + + for (const FIntVector& NeighborPos : PotentialNeighbors) + { + if (IsValidGridPosition(NeighborPos.X, NeighborPos.Y, NeighborPos.Z)) + { + int NeighborIndex = GridPositionToIndex(NeighborPos.X, NeighborPos.Y, NeighborPos.Z); + Neighbors.Add(NeighborIndex); + } + } + } + return Neighbors; +} + +double FDensityField::IndexToVoxelDensity(const int32 Index) const { - return GridOrigin; + if (VoxelList.IsValidIndex(Index)) + { + return VoxelList[Index].GetVoxelDensity(); + } + return 0.0; +} + +// VALIDITY FUNCTIONS + +bool FDensityField::IsValidIndex(const int32 Index) const { + return VoxelList.IsValidIndex(Index); } -double FDensityField::GetVoxelDensityFromIndex(const int32 Index) const +bool FDensityField::IsValidWorldPosition(const FVector& Position) const { - if (VoxelList.IsValidIndex(Index)) - { - return VoxelList[Index].GetVoxelDensity(); - } - return 0.0; + // Convert the world position to grid coordinates + const FIntVector3 GridPosition = WorldToGridPosition(Position); + + // Check if the grid position is within the valid bounds + return IsValidGridPosition(GridPosition.X, GridPosition.Y, GridPosition.Z); +} + +bool FDensityField::IsValidGridPosition(const int32 XBin, const int32 YBin, const int32 ZBin) const +{ + // Check if the provided indices are within the grid bounds + return (XBin >= 0 && XBin < XNum) && + (YBin >= 0 && YBin < YNum) && + (ZBin >= 0 && ZBin < ZNum); +} + +// GETTER AND SETTER FUNCTIONS + +FVector FDensityField::GetGridOrigin() const +{ + return GridOrigin; } int32 FDensityField::GetVoxelNumber() const @@ -283,7 +389,7 @@ int32 FDensityField::GetVoxelNumber() const return VoxelList.Num(); } -void FDensityField::SetVoxelDensity(const int32 Index, const double Density) +void FDensityField::SetVoxelDensityByIndex(const int32 Index, const double Density) { if (VoxelList.IsValidIndex(Index)) { @@ -291,7 +397,7 @@ void FDensityField::SetVoxelDensity(const int32 Index, const double Density) } } -void FDensityField::SetVoxelGradient(const int32 Index, const FVector& Gradient) +void FDensityField::SetVoxelGradientByIndex(const int32 Index, const FVector& Gradient) { if (VoxelList.IsValidIndex(Index)) { @@ -299,40 +405,6 @@ void FDensityField::SetVoxelGradient(const int32 Index, const FVector& Gradient) } } -double FDensityField::GetVoxelDensityFromGridPosition(const int32 XBin, const int32 YBin, const int32 ZBin) const -{ - // First, check if the provided indices are valid - if (IsValidGridPosition(XBin, YBin, ZBin)) - { - // Convert 3D grid coordinates to a linear index - const int32 Index = GridPositionToIndex(XBin, YBin, ZBin); - - return GetVoxelDensityFromIndex(Index); - } - - // Return 0 if the indices are out of bounds or the index is invalid - return 0.0; -} - -FVector FDensityField::GetVoxelGradientFromGridPosition(const int32 XBin, const int32 YBin, const int32 ZBin) const -{ - // First, check if the provided indices are valid - if (IsValidGridPosition(XBin, YBin, ZBin)) - { - // Convert 3D grid coordinates to a linear index - const int32 Index = GridPositionToIndex(XBin, YBin, ZBin); - - // Ensure the index is within the valid range - if (VoxelList.IsValidIndex(Index)) - { - // Return the gradient at the calculated index - return VoxelList[Index].GetVoxelGradient(); - } - } - // Return 0 if the indices are out of bounds or the index is invalid - return FVector::Zero(); -} - FVector FDensityField::GetStep() const { return FVector(XStep, YStep, ZStep); @@ -348,47 +420,4 @@ int32 FDensityField::GetYNum() const { int32 FDensityField::GetZNum() const { return ZNum; -} - -bool FDensityField::IsValidWorldPosition(const FVector& Position) const -{ - // Convert the world position to grid coordinates - const FVector GridPosition = WorldToGridPosition(Position); - - // Check if the grid position is within the valid bounds - return IsValidGridPosition(GridPosition.X, GridPosition.Y, GridPosition.Z); -} - -TArray<int32> FDensityField::GetNeighborsFromIndex(const int32 Index) const -{ - TArray<int32> Neighbors; - if (VoxelList.IsValidIndex(Index)) - { - const int32 X = VoxelList[Index].GetVoxelGridPos().X; - const int32 Y = VoxelList[Index].GetVoxelGridPos().Y; - const int32 Z = VoxelList[Index].GetVoxelGridPos().Z; - - // List all potential neighbors - TArray<FIntVector> PotentialNeighbors = { - FIntVector(X+1, Y, Z), FIntVector(X-1, Y, Z), - FIntVector(X, Y+1, Z), FIntVector(X, Y-1, Z), - FIntVector(X, Y, Z+1), FIntVector(X, Y, Z-1) - }; - - for (const FIntVector& NeighborPos : PotentialNeighbors) - { - if (NeighborPos.X >= 0 && NeighborPos.X < XNum && - NeighborPos.Y >= 0 && NeighborPos.Y < YNum && - NeighborPos.Z >= 0 && NeighborPos.Z < ZNum) - { - int NeighborIndex = GridPositionToIndex(NeighborPos.Z, NeighborPos.Y, NeighborPos.Z); - Neighbors.Add(NeighborIndex); - } - } - } - return Neighbors; -} - -bool FDensityField::IsValidIndex(const int32 Index) const { - return VoxelList.IsValidIndex(Index); -} +} \ No newline at end of file diff --git a/Source/MetaCastBachelor/DensityField.h b/Source/MetaCastBachelor/DensityField.h index 3e71da479c4f0d9aac7150ba865f808cf39928f1..7477fd5c318dc75b9d8a4a11a527eaaaf6160fb5 100644 --- a/Source/MetaCastBachelor/DensityField.h +++ b/Source/MetaCastBachelor/DensityField.h @@ -12,17 +12,17 @@ struct FVoxel private: FVector WorldPosition; - FVector GridPosition; + FIntVector3 GridPosition; double VoxelDensity; FVector VoxelGradient; double ClosePointsNumber; public: - FVoxel() : WorldPosition(FVector::ZeroVector), GridPosition(FVector::ZeroVector), VoxelDensity(0.0), VoxelGradient(FVector::ZeroVector), ClosePointsNumber(0.0) {} - FVoxel(const FVector &InPosition, const FVector &InGridPos) : WorldPosition(InPosition), GridPosition(InGridPos), VoxelDensity(0.0), VoxelGradient(FVector::ZeroVector), ClosePointsNumber(0.0) {} + FVoxel() : WorldPosition(FVector::ZeroVector), GridPosition(FIntVector3::ZeroValue), VoxelDensity(0.0), VoxelGradient(FVector::ZeroVector), ClosePointsNumber(0.0) {} + FVoxel(const FVector &InPosition, const FIntVector3 &InGridPos) : WorldPosition(InPosition), GridPosition(InGridPos), VoxelDensity(0.0), VoxelGradient(FVector::ZeroVector), ClosePointsNumber(0.0) {} double GetVoxelDensity() const { return VoxelDensity; } - FVector GetVoxelGridPos() const { return GridPosition; } + FIntVector3 GetVoxelGridPos() const { return GridPosition; } FVector GetVoxelPosition() const { return WorldPosition; } FVector GetVoxelGradient() const { return VoxelGradient; } double GetClosePointsNumber() const { return ClosePointsNumber; } @@ -73,26 +73,27 @@ public: // CONVERSION FUNCTIONS int32 WorldPositionToIndex(const FVector& Position) const; - FVector WorldToGridPosition(const FVector& Position) const; + FIntVector3 WorldToGridPosition(const FVector& Position) const; bool IsValidGridPosition(int32 XBin, int32 YBin, int32 ZBin) const; int32 GridPositionToIndex(int32 X, int32 Y, int32 Z) const; + TArray<FVector> IndexToVoxelCornersWorld(const int32 Index) const; // GETTER AND SETTER FUNCTIONS FVector GetGridOrigin() const; FVector IndexToVoxelGradient(int32 Index) const; FVector IndexToVoxelPosition(int32 Index) const; - FVector IndexToGridPosition(int32 Index) const; + FIntVector3 IndexToGridPosition(int32 Index) const; int32 GetVoxelNumber() const; - double GetVoxelDensityFromIndex(int32 Index) const; - double GetVoxelDensityFromGridPosition(int32 XBin, int32 YBin, int32 ZBin) const; - FVector GetVoxelGradientFromGridPosition(int32 XBin, int32 YBin, int32 ZBin) const; + double IndexToVoxelDensity(int32 Index) const; + double GridPositionToVoxelDensity(int32 XBin, int32 YBin, int32 ZBin) const; + FVector GridPositionToVoxelGradient(int32 XBin, int32 YBin, int32 ZBin) const; FVector GetStep() const; int32 GetXNum() const; int32 GetYNum() const; int32 GetZNum() const; bool IsValidWorldPosition(const FVector& Position) const; - TArray<int32> GetNeighborsFromIndex(const int32 Index) const; + TArray<int32> IndexToVoxelNeighbors(const int32 Index) const; bool IsValidIndex(int32 Index) const; - void SetVoxelDensity(int32 Index, double Density); - void SetVoxelGradient(int32 Index, const FVector& Gradient); + 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 2e920d0f027e3aac7dc5e336b74444c611bdf2d4..dd70f5d1ad2ca193c7bfb2668b6b6e6dbc197ff4 100644 --- a/Source/MetaCastBachelor/MetaPoint.cpp +++ b/Source/MetaCastBachelor/MetaPoint.cpp @@ -1,18 +1,30 @@ #include "MetaPoint.h" #include "Utilities.h" -UMetaPoint::UMetaPoint() : Index(0), MyDensityField(nullptr), Threshold(0), MyProceduralMesh(nullptr) +UMetaPoint::UMetaPoint() : Index(0), MyDensityField(nullptr), Threshold(0), World(nullptr), MyProceduralMesh(nullptr) { // Initialize the Marching Cubes algorithm MyMarchingCubes = new MarchingCubes(); + + // Create the procedural mesh component + ProceduralMesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh")); } void UMetaPoint::BeginPlay() { Super::BeginPlay(); -} + World = GetWorld(); + + if (!World) { + UE_LOG(LogTemp, Warning, TEXT("Invalid world provided.")); + } + ProceduralMesh->AttachToComponent(MyPointCloud->PointCloudVisualizer, FAttachmentTransformRules::SnapToTargetIncludingScale); + ProceduralMesh->SetMaterial(0, SelectionVolumeMat); + ProceduralMesh->SetMaterial(1, SelectionVolumeMat); + ProceduralMesh->SetMaterial(2, SelectionVolumeMat); +} void UMetaPoint::EraseParticles(const FVector& InputPosition) { @@ -31,20 +43,10 @@ void UMetaPoint::HandleMetaSelectPressed(const FInputActionInstance& Instance) FindAndMarkLocalMaximum(); } - // Method to perform gradient ascent to find the local maximum density starting from a given position. void UMetaPoint::FindAndMarkLocalMaximum() -{ - const UWorld* World = GetWorld(); - - if (!World || !MyPointCloud) { - UE_LOG(LogTemp, Warning, TEXT("Invalid world or point cloud context provided.")); - return; - } - +{ const FVector WorldPosition = SelectionObject->GetComponentLocation(); - UE_LOG(LogTemp, Log, TEXT("World Position: %s"), *WorldPosition.ToString()); - MyDensityField = MyPointCloud->MyDensityField; // Convert the world position of the selection object to the local position relative to the point cloud @@ -52,39 +54,115 @@ void UMetaPoint::FindAndMarkLocalMaximum() // Perform gradient ascent to find the local maximum starting from the converted local position LocalMaximum = FUtilities::FollowGradientToMaximum(MyDensityField, StartPosition); - UE_LOG(LogTemp, Log, TEXT("Local maximum found at world position: %s"), *WorldMaximum.ToString()); // Convert the local maximum back to world coordinates Index = MyDensityField->WorldPositionToIndex(LocalMaximum); Threshold = FUtilities::InterpolateDensityAtPosition(MyDensityField, LocalMaximum); TestingThresholdFactor = 1; + + // Initialize Visited array to false for all voxels + Visited.Init(false, MyDensityField->GetVoxelNumber()); + + // Initialize IndicesToVisit with the starting index + IndicesToVisit.Empty(); + IndicesToVisit.Add(Index); + + // Clear FloodedIndices + FloodedIndices.Empty(); + + World = GetWorld(); } void UMetaPoint::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - TestingThresholdFactor -= DeltaTime * 0.1f; + //TestingThresholdFactor -= DeltaTime * 0.1f; + + AccumulatedTime += DeltaTime; + + constexpr float DecayRate = 0.1f; // Adjust this value to control the rate of decay + TestingThresholdFactor *= FMath::Exp(-DecayRate * DeltaTime); + + // Ensure TestingThresholdFactor never goes below a small threshold to prevent it from becoming zero + constexpr float MinThreshold = 0.001f; // Adjust as needed + if (TestingThresholdFactor < MinThreshold) + { + TestingThresholdFactor = MinThreshold; + } } void UMetaPoint::SelectParticles(const FVector& InputPosition) { - const UWorld* World = GetWorld(); - WorldMaximum = MyPointCloud->PointCloudVisualizer->GetComponentTransform().TransformPosition(LocalMaximum); - + //WorldMaximum = MyPointCloud->PointCloudVisualizer->GetComponentTransform().TransformPosition(LocalMaximum); // Draw a debug sphere at the location of the local maximum in the world - DrawDebugSphere(World, WorldMaximum, SelectionRadius / 2, 32, FColor::Red, false, 0); - auto Voxels = FUtilities::FloodFilling(MyDensityField, Index, Threshold * TestingThresholdFactor); + //DrawDebugSphere(World, WorldMaximum, SelectionRadius / 2, 32, FColor::Red, false, 0); - for (const int32 Index2 : Voxels) + if (AccumulatedTime >= 0.1f) { - MyPointCloud->DrawVoxel(Index2, 0); - } -} + AccumulatedTime = -10.0f; + AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this]() + { + //TArray<int32> Voxels = FUtilities::FloodFilling(MyDensityField, Index, Threshold * TestingThresholdFactor); + FUtilities::FloodFilling_2(MyDensityField, Threshold * TestingThresholdFactor, Visited, IndicesToVisit, FloodedIndices, IndicesToVisit); + auto Voxels = FloodedIndices; + AsyncTask(ENamedThreads::GameThread, [this, Voxels]() + { + GenerateVoxelMesh(FloodedIndices); + AccumulatedTime = 0.0f; + }); + }); + } +} +void UMetaPoint::GenerateVoxelMesh(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); + + // Convert corners to world space + for (FVector& Corner : VoxelCorners) + { + //Corner = MyPointCloud->PointCloudVisualizer->GetComponentTransform().TransformPosition(Corner); + } + + // 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::Red); + } + } + // Create the mesh section + ProceduralMesh->CreateMeshSection(0, Vertices, Triangles, TArray<FVector>(), TArray<FVector2D>(), VertexColors, TArray<FProcMeshTangent>(), true); +} diff --git a/Source/MetaCastBachelor/MetaPoint.h b/Source/MetaCastBachelor/MetaPoint.h index c95ffd91893f145d901f80b2ad764472104b68f3..ce3e88b1466bceed87a1c4f53bb1e05394e40320 100644 --- a/Source/MetaCastBachelor/MetaPoint.h +++ b/Source/MetaCastBachelor/MetaPoint.h @@ -18,6 +18,20 @@ class UMetaPoint : public UMetaCastBaseline FVector LocalMaximum; FDensityField* MyDensityField; float Threshold; + UPROPERTY() + UWorld* World; + float AccumulatedTime = 0.0; + + UPROPERTY() + UProceduralMeshComponent* ProceduralMesh; + + UPROPERTY(EditAnywhere) + UMaterialInterface* SelectionVolumeMat; + + TArray<bool> Visited; + TArray<int32> IndicesToVisit; + TArray<int32> FloodedIndices; + TArray<int32> RevisitIndices; public: @@ -38,6 +52,8 @@ public: virtual void HandleMetaSelectPressed(const FInputActionInstance& Instance) override; void FindAndMarkLocalMaximum(); + void GenerateVoxelMesh(const TArray<int32>& Voxels) const; + virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; }; diff --git a/Source/MetaCastBachelor/PointCloud.cpp b/Source/MetaCastBachelor/PointCloud.cpp index a5a93cf2dfb359253ef3d3c918e80dfee4409b63..d6482df6e1cee64a437fc509e340452d6b98d930 100644 --- a/Source/MetaCastBachelor/PointCloud.cpp +++ b/Source/MetaCastBachelor/PointCloud.cpp @@ -91,7 +91,7 @@ void APointCloud::SetupDensityFieldFromPointCloud() const constexpr int32 ZAxisNum = 100; // Number of divisions in the grid along the Z-axis MyDensityField->InitializeDensityField(MinBounds, MaxBounds, XAxisNum, YAxisNum, ZAxisNum); - constexpr float InfluenceRadius = 0.5f; // Half the size of a voxel + constexpr float InfluenceRadius = 5.0f; // Half the size of a voxel constexpr float Sigma = 3.0f; // Standard deviation for the Gaussian kernel MyDensityField->CalculateVoxelDensities(this, InfluenceRadius, Sigma); @@ -102,6 +102,7 @@ void APointCloud::SetupDensityFieldFromPointCloud() const //MyDensityField->DrawDebugVoxelDensity(World, this, 100.0f, 1.0f, 0.8f); } } + void APointCloud::UpdateSelection() { for (int32 i = 0; i < PositionVectors.Num(); i++) @@ -118,7 +119,7 @@ void APointCloud::UpdateSelection() PointCloudVisualizer->SetInputAndConvert2(PositionVectors, PointColors); } -void APointCloud::DrawVoxel(const int Index, float Time) const +void APointCloud::DrawVoxel(const int Index, const float Time) const { if(!MyDensityField->IsValidIndex(Index)) { @@ -129,11 +130,8 @@ void APointCloud::DrawVoxel(const int Index, float Time) const const FVector VoxelWorldPosition = PointCloudVisualizer->GetComponentTransform().TransformPosition(MyDensityField->IndexToVoxelPosition(Index)); const FVector WorldScale = PointCloudVisualizer->GetComponentScale(); - // Scale the voxel size by the component's world scale to ensure it reflects the actual dimensions - FVector ScaledVoxelSize = GetActorRightVector(); - // Draw the debug box with the correct world space dimensions - DrawDebugBox(GetWorld(), VoxelWorldPosition, VoxelHalfExtents, this->GetActorQuat(), FColor::Green, false, Time, 0, 0.1f); + DrawDebugBox(GetWorld(), VoxelWorldPosition, VoxelHalfExtents * WorldScale, this->GetActorQuat(), FColor::Green, false, Time, 0, 0.1f); } diff --git a/Source/MetaCastBachelor/Utilities.cpp b/Source/MetaCastBachelor/Utilities.cpp index 939fe9769c638d432410ff049faa3f206a36b063..343a2ee4f57a50551b4b9088c17277e65e9153ac 100644 --- a/Source/MetaCastBachelor/Utilities.cpp +++ b/Source/MetaCastBachelor/Utilities.cpp @@ -41,14 +41,14 @@ double FUtilities::InterpolateDensityAtPosition(const FDensityField* DensityFiel const float YFraction = GridPos.Y - YBin; const float ZFraction = GridPos.Z - ZBin; - const double D000 = DensityField->GetVoxelDensityFromGridPosition(XBin, YBin, ZBin); - const double D100 = DensityField->GetVoxelDensityFromGridPosition(XBin + 1, YBin, ZBin); - const double D010 = DensityField->GetVoxelDensityFromGridPosition(XBin, YBin + 1, ZBin); - const double D001 = DensityField->GetVoxelDensityFromGridPosition(XBin, YBin, ZBin + 1); - const double D101 = DensityField->GetVoxelDensityFromGridPosition(XBin + 1, YBin, ZBin + 1); - const double D011 = DensityField->GetVoxelDensityFromGridPosition(XBin, YBin + 1, ZBin + 1); - const double D110 = DensityField->GetVoxelDensityFromGridPosition(XBin + 1, YBin + 1, ZBin); - const double D111 = DensityField->GetVoxelDensityFromGridPosition(XBin + 1, YBin + 1, ZBin + 1); + 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); // Trilinear interpolation const double DX00 = FMath::Lerp(D000, D100, XFraction); @@ -81,14 +81,14 @@ FVector FUtilities::InterpolateGradientAtPosition(const FDensityField* DensityFi float ZFraction = GridPos.Z - ZBin; // Fetch gradients from corners of the cell - FVector G000 = DensityField->GetVoxelGradientFromGridPosition(XBin, YBin, ZBin); - FVector G100 = DensityField->GetVoxelGradientFromGridPosition(XBin + 1, YBin, ZBin); - FVector G010 = DensityField->GetVoxelGradientFromGridPosition(XBin, YBin + 1, ZBin); - FVector G001 = DensityField->GetVoxelGradientFromGridPosition(XBin, YBin, ZBin + 1); - FVector G110 = DensityField->GetVoxelGradientFromGridPosition(XBin + 1, YBin + 1, ZBin); - FVector G101 = DensityField->GetVoxelGradientFromGridPosition(XBin + 1, YBin, ZBin + 1); - FVector G011 = DensityField->GetVoxelGradientFromGridPosition(XBin, YBin + 1, ZBin + 1); - FVector G111 = DensityField->GetVoxelGradientFromGridPosition(XBin + 1, YBin + 1, ZBin + 1); + 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); // Interpolate along x for each of the yz pairs FVector G00 = FMath::Lerp(G000, G100, XFraction); @@ -152,7 +152,7 @@ FVector FUtilities::FollowGradientToMaximum(const FDensityField* DensityField, c TArray<int32> FUtilities::FloodFilling(const FDensityField* DensityField, const int32 StartIndex, const float Threshold) { - UE_LOG(LogTemp, Warning, TEXT("T: %f"), Threshold); + //UE_LOG(LogTemp, Warning, TEXT("Threshold for FloodFilling: %f"), Threshold); TArray<int32> VisitedIndices; if (!DensityField) { @@ -178,23 +178,108 @@ TArray<int32> FUtilities::FloodFilling(const FDensityField* DensityField, const Visited[CurrentIndex] = true; VisitedIndices.Add(CurrentIndex); - TArray<int32> Neighbors = DensityField->GetNeighborsFromIndex(CurrentIndex); - UE_LOG(LogTemp, Log, TEXT("Visiting Node %d at Position %s with %d neighbors."), CurrentIndex, *DensityField->IndexToGridPosition(CurrentIndex).ToString(), Neighbors.Num()); + 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) { - if (DensityField->GetVoxelDensityFromIndex(NeighborIndex) > Threshold && !Visited[NeighborIndex]) { + 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); + + if (NeighborDensity > Threshold && !Visited[NeighborIndex]) { Stack.Push(NeighborIndex); + //UE_LOG(LogTemp, Log, TEXT("Pushing Neighbor %d to stack."), NeighborIndex); } } } } // Logging final details - UE_LOG(LogTemp, Log, TEXT("Flood filling completed from start index %d. Total voxels selected: %d"), StartIndex, VisitedIndices.Num()); + //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()); + //UE_LOG(LogTemp, Log, TEXT("Selected Voxel Index: %d at Grid Position: %s"), idx, *DensityField->IndexToGridPosition(idx).ToString()); } return VisitedIndices; } + +/* +TArray<int32> FUtilities::FloodFilling_2(const FDensityField* DensityField, const float Threshold, TArray<bool>& Visited, TArray<int32>& IndicesToVisit, TArray<int32>& FloodedIndices) +{ + if (!DensityField) { + UE_LOG(LogTemp, Warning, TEXT("DensityField is null.")); + return FloodedIndices; + } + + while (IndicesToVisit.Num() > 0) + { + int32 CurrentIndex = IndicesToVisit.Pop(); + if (!Visited[CurrentIndex]) + { + Visited[CurrentIndex] = true; + FloodedIndices.Add(CurrentIndex); + + TArray<int32> Neighbors = DensityField->IndexToVoxelNeighbors(CurrentIndex); + + for (int32 NeighborIndex : Neighbors) + { + const double NeighborDensity = DensityField->IndexToVoxelDensity(NeighborIndex); + + if (NeighborDensity > Threshold && !Visited[NeighborIndex]) { + IndicesToVisit.Push(NeighborIndex); + } + } + } + } + + return FloodedIndices; +}*/ + +void FUtilities::FloodFilling_2(const FDensityField* DensityField, const float Threshold, TArray<bool>& VisitedIndices, TArray<int32>& IndicesToVisit, TArray<int32>& FloodedIndices, TArray<int32>& PotentialRevisit) +{ + if (!DensityField) { + UE_LOG(LogTemp, Warning, TEXT("DensityField is null.")); + return; + } + + TArray<int32> CurrentPotentialRevisit; + + constexpr int MaxIterations = 100; + int IterationCount = 0; + while (IndicesToVisit.Num() > 0) + { + IterationCount++; + if(IterationCount > MaxIterations) + { + while (IndicesToVisit.Num() > 0) { + CurrentPotentialRevisit.Add(IndicesToVisit.Pop()); + } + break; + } + + int32 CurrentIndex = IndicesToVisit.Pop(); + VisitedIndices[CurrentIndex] = true; + const double CurrentDensity = DensityField->IndexToVoxelDensity(CurrentIndex); + + if (CurrentDensity >= Threshold) { + FloodedIndices.Add(CurrentIndex); + + TArray<int32> Neighbors = DensityField->IndexToVoxelNeighbors(CurrentIndex); + for (int32 NeighborIndex : Neighbors) + { + if (!VisitedIndices[NeighborIndex]) + { + IndicesToVisit.Push(NeighborIndex); + } + } + } else { + CurrentPotentialRevisit.Add(CurrentIndex); + } + } + + // Update PotentialRevisit with the new list from this run + PotentialRevisit = CurrentPotentialRevisit; +} + + + diff --git a/Source/MetaCastBachelor/Utilities.h b/Source/MetaCastBachelor/Utilities.h index 72313c10410e4cf42b9138d7e62023d5a9c58b72..6d406d74976619aeb28b4b9419e1dd45daf112f1 100644 --- a/Source/MetaCastBachelor/Utilities.h +++ b/Source/MetaCastBachelor/Utilities.h @@ -11,4 +11,5 @@ public: static float GaussianKernel(float Distance, float Sigma); 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); };