CelticCraft/Plugins/VoxelFree/Source/Voxel/Public/VoxelTools/VoxelSurfaceToolsImpl.h

138 lines
4 KiB
C
Raw Normal View History

2023-07-03 16:17:13 +00:00
// Copyright 2020 Phyronnaz
#pragma once
#include "CoreMinimal.h"
#include "VoxelTools/VoxelToolHelpers.h"
#include "VoxelTools/VoxelSurfaceTools.h"
class FVoxelSurfaceToolsImpl
{
public:
static void ApplyConstantStrengthImpl(
TArray<FVoxelSurfaceEditsVoxel>& Voxels,
const float Strength)
{
VOXEL_TOOL_FUNCTION_COUNTER(Voxels.Num());
for (auto& Voxel : Voxels)
{
Voxel.Strength *= Strength;
}
}
template<typename T>
static void ApplyStrengthFunctionImpl(
TArray<FVoxelSurfaceEditsVoxel>& Voxels,
T GetStrength)
{
VOXEL_TOOL_FUNCTION_COUNTER(Voxels.Num());
int32 Num = 0;
for (const auto& Voxel : Voxels)
{
const float Strength = GetStrength(Voxel);
if (Strength != 0)
{
auto& NewVoxel = Voxels[Num++];
NewVoxel = Voxel;
NewVoxel.Strength *= Strength;
}
}
check(Num <= Voxels.Num());
Voxels.SetNum(Num, false);
}
template<typename T>
static void ApplyDistanceStrengthFunctionImpl(
TArray<FVoxelSurfaceEditsVoxel>& Voxels,
const FVoxelVector& Center,
bool b2D,
T GetStrengthFromDistance)
{
if (b2D)
{
ApplyStrengthFunctionImpl(Voxels, [=](const FVoxelSurfaceEditsVoxel& Voxel)
{
return GetStrengthFromDistance(FVector2D::Distance(FVector2D(Center.X, Center.Y), FVector2D(Voxel.Position.X, Voxel.Position.Y)));
});
}
else
{
ApplyStrengthFunctionImpl(Voxels, [=](const FVoxelSurfaceEditsVoxel& Voxel)
{
return GetStrengthFromDistance(FVoxelVector::Distance(Center, FVoxelVector(Voxel.Position)));
});
}
}
// Should always be called last if bExactDistanceField = true
template<typename T>
static void ApplySDFImpl(
const FVoxelSurfaceEditsVoxelsInfo& Info,
TArray<FVoxelSurfaceEditsVoxel>& Voxels,
EVoxelSDFMergeMode MergeMode,
T GetDistance)
{
VOXEL_TOOL_FUNCTION_COUNTER(Voxels.Num());
for (auto& Voxel : Voxels)
{
const float CurrentDistance = Voxel.Value;
const float OtherDistance = GetDistance(FVector(Voxel.Position));
const float WantedDistance =
MergeMode == EVoxelSDFMergeMode::Union
? FMath::Min(CurrentDistance, OtherDistance)
: MergeMode == EVoxelSDFMergeMode::Intersection
? FMath::Max(OtherDistance, CurrentDistance)
: OtherDistance; // Override
if (Info.bHasExactDistanceField)
{
// No strength should be applied after ApplySDFImpl if we want a good result
const float IntermediateDistance = FMath::Lerp(CurrentDistance, WantedDistance, Voxel.Strength);
Voxel.Strength = IntermediateDistance - CurrentDistance;
}
else
{
const float Difference = WantedDistance - Voxel.Value;
// We cannot go too fast if we didn't compute the exact distance field
Voxel.Strength *= FMath::Clamp(Difference, -1.f, 1.f);
}
}
}
static void ApplyTerraceImpl(
TArray<FVoxelSurfaceEditsVoxel>& Voxels,
const int32 TerraceHeightInVoxels,
const float Angle,
const int32 ImmutableVoxels)
{
VOXEL_TOOL_FUNCTION_COUNTER(Voxels.Num());
if (!ensure(TerraceHeightInVoxels >= 1)) return;
const float AngleLimit = FMath::DegreesToRadians(Angle);
int32 NewNum = 0;
for (auto& Voxel : Voxels)
{
const int32 RelativePosition = FVoxelUtilities::PositiveMod(Voxel.Position.Z, TerraceHeightInVoxels);
if (RelativePosition < ImmutableVoxels) continue;
const float VoxelAngle = FMath::Acos(Voxel.Normal.Z); // Dot product with UpVector. 0 when facing up, PI when facing down
ensure(VoxelAngle >= 0);
if (AngleLimit < VoxelAngle) continue;
// We want 1 went facing up, 0 when facing > angle limit
const float Strength = FMath::Max((AngleLimit - VoxelAngle) / AngleLimit, 0.f);
auto NewVoxel = Voxel;
NewVoxel.Strength *= Strength;
Voxels[NewNum++] = NewVoxel;
}
check(NewNum <= Voxels.Num());
Voxels.SetNum(NewNum, false);
}
static void ApplyFlattenImpl(
const FVoxelSurfaceEditsVoxelsInfo& Info,
TArray<FVoxelSurfaceEditsVoxel>& Voxels,
const FPlane& Plane,
EVoxelSDFMergeMode MergeMode)
{
ApplySDFImpl(Info, Voxels, MergeMode, [&](const FVector& Position) { return Plane.PlaneDot(Position); });
}
};