// Copyright 2020 Phyronnaz #pragma once #include "VoxelUtilities/VoxelDistanceFieldUtilities.h" #include "VoxelUtilities/VoxelMathUtilities.h" #include "VoxelUtilities/VoxelMiscUtilities.h" template void FVoxelDistanceFieldUtilities::GetSurfacePositionsFromDensities( const FIntVector& Size, TArrayView Densities, TArrayView OutDistances, TArrayView OutSurfacePositions, TLambda GetFloatFromT) { VOXEL_ASYNC_FUNCTION_COUNTER(); const FIntVector DensitiesSize = Size + 2; const FIntVector DensitiesOffset = FIntVector(-1, -1, -1); check(Densities.Num() == DensitiesSize.X * DensitiesSize.Y * DensitiesSize.Z); check(OutDistances.Num() == Size.X * Size.Y * Size.Z); check(OutSurfacePositions.Num() == Size.X * Size.Y * Size.Z); for (int32 X = 0; X < Size.X; X++) { for (int32 Y = 0; Y < Size.Y; Y++) { for (int32 Z = 0; Z < Size.Z; Z++) { const FIntVector Position(X, Y, Z); const float Value = GetFloatFromT(FVoxelUtilities::Get3D(Densities, DensitiesSize, Position, DensitiesOffset)); const int32 Index = FVoxelUtilities::Get3DIndex(Size, Position); FVoxelUtilities::Get(OutDistances, Index) = FMath::Sign(Value); // Static branch const auto Lambda = [&](auto IsNegative) { // Take the max: this is the one that will "push" the value the closest to us // Only consider positive values, so that there's a surface between us // By symmetry, take the min value negative if Value is positive float MaxNeighborValue = 0.f; FIntVector MaxNeighborPosition; #define CheckNeighbor(DX, DY, DZ) \ { \ const FIntVector NeighborPosition = Position + FIntVector(DX, DY, DZ); \ const float NeighborValue = GetFloatFromT(FVoxelUtilities::Get3D(Densities, DensitiesSize, NeighborPosition, DensitiesOffset)); \ \ if (IsNegative ? (NeighborValue > MaxNeighborValue) : (NeighborValue < MaxNeighborValue)) \ { \ MaxNeighborValue = NeighborValue; \ MaxNeighborPosition = NeighborPosition; \ } \ } CheckNeighbor(-1, 0, 0); CheckNeighbor(+1, 0, 0); CheckNeighbor(0, -1, 0); CheckNeighbor(0, +1, 0); CheckNeighbor(0, 0, -1); CheckNeighbor(0, 0, +1); #undef CheckNeighbor if (MaxNeighborValue == 0.f) { FVoxelUtilities::Get(OutSurfacePositions, Index) = MakeInvalidSurfacePosition(); } else { const float Alpha = Value / (Value - MaxNeighborValue); FVoxelUtilities::Get(OutSurfacePositions, Index) = FMath::Lerp(FVector3f(Position), FVector3f(MaxNeighborPosition), Alpha); } }; if (Value <= 0) { Lambda(FVoxelUtilities::FTrueType()); } else { Lambda(FVoxelUtilities::FFalseType()); } } } } }