783 lines
No EOL
20 KiB
C++
783 lines
No EOL
20 KiB
C++
// Copyright 2020 Phyronnaz
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "VoxelUtilities/VoxelVectorUtilities.h"
|
|
#include "VoxelUtilities/VoxelIntVectorUtilities.h"
|
|
#include "Async/ParallelFor.h"
|
|
#include "VoxelIntBox.generated.h"
|
|
|
|
enum class EInverseTransform : uint8
|
|
{
|
|
True,
|
|
False
|
|
};
|
|
|
|
/**
|
|
* A Box with int32 coordinates
|
|
*/
|
|
USTRUCT(BlueprintType, meta=(HasNativeMake="Voxel.VoxelIntBoxLibrary.MakeIntBox", HasNativeBreak="Voxel.VoxelIntBoxLibrary.BreakIntBox"))
|
|
struct VOXEL_API FVoxelIntBox
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
// Min of the box. Inclusive
|
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel")
|
|
FIntVector Min;
|
|
|
|
// Max of the box. Exclusive
|
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Voxel")
|
|
FIntVector Max;
|
|
|
|
const static FVoxelIntBox Infinite;
|
|
|
|
FVoxelIntBox()
|
|
: Min(FIntVector::ZeroValue)
|
|
, Max(FIntVector::ZeroValue)
|
|
{
|
|
}
|
|
|
|
FVoxelIntBox(const FIntVector& InMin, const FIntVector& InMax)
|
|
: Min(InMin)
|
|
, Max(InMax)
|
|
{
|
|
if (!ensureMsgf(Min.X <= Max.X, TEXT("%d <= %d"), Min.X, Max.X)) Max.X = Min.X;
|
|
if (!ensureMsgf(Min.Y <= Max.Y, TEXT("%d <= %d"), Min.Y, Max.Y)) Max.Y = Min.Y;
|
|
if (!ensureMsgf(Min.Z <= Max.Z, TEXT("%d <= %d"), Min.Z, Max.Z)) Max.Z = Min.Z;
|
|
}
|
|
explicit FVoxelIntBox(int32 Min, const FIntVector& Max)
|
|
: FVoxelIntBox(FIntVector(Min), Max)
|
|
{
|
|
}
|
|
explicit FVoxelIntBox(const FIntVector& Min, int32 Max)
|
|
: FVoxelIntBox(Min, FIntVector(Max))
|
|
{
|
|
}
|
|
explicit FVoxelIntBox(int32 Min, int32 Max)
|
|
: FVoxelIntBox(FIntVector(Min), FIntVector(Max))
|
|
{
|
|
}
|
|
explicit FVoxelIntBox(const FVector& Min, const FVector& Max)
|
|
: FVoxelIntBox(FVoxelUtilities::FloorToInt(Min), FVoxelUtilities::CeilToInt(Max) + 1)
|
|
{
|
|
}
|
|
explicit FVoxelIntBox(const FVoxelVector& Min, const FVoxelVector& Max)
|
|
: FVoxelIntBox(FVoxelUtilities::FloorToInt(Min), FVoxelUtilities::CeilToInt(Max) + 1)
|
|
{
|
|
}
|
|
|
|
explicit FVoxelIntBox(const FVector& Position)
|
|
: FVoxelIntBox(Position, Position)
|
|
{
|
|
}
|
|
explicit FVoxelIntBox(const FVoxelVector& Position)
|
|
: FVoxelIntBox(Position, Position)
|
|
{
|
|
}
|
|
|
|
explicit FVoxelIntBox(const FIntVector& Position)
|
|
: FVoxelIntBox(Position, Position + 1)
|
|
{
|
|
}
|
|
explicit FVoxelIntBox(const FBox& Box)
|
|
: FVoxelIntBox(Box.Min, Box.Max)
|
|
{
|
|
}
|
|
|
|
explicit FVoxelIntBox(int32 X, int32 Y, int32 Z)
|
|
: FVoxelIntBox(FIntVector(X, Y, Z), FIntVector(X + 1, Y + 1, Z + 1))
|
|
{
|
|
}
|
|
|
|
template<typename T>
|
|
explicit FVoxelIntBox(const TArray<T>& Data)
|
|
{
|
|
if (!ensure(Data.Num() > 0))
|
|
{
|
|
Min = Max = FIntVector::ZeroValue;
|
|
return;
|
|
}
|
|
|
|
*this = FVoxelIntBox(Data[0]);
|
|
for (int32 Index = 1; Index < Data.Num(); Index++)
|
|
{
|
|
*this = *this + Data[Index];
|
|
}
|
|
}
|
|
|
|
FORCEINLINE static FVoxelIntBox SafeConstruct(const FIntVector& A, const FIntVector& B)
|
|
{
|
|
FVoxelIntBox Box;
|
|
Box.Min = FVoxelUtilities::ComponentMin(A, B);
|
|
Box.Max = FVoxelUtilities::ComponentMax3(A, B, Box.Min + FIntVector(1, 1, 1));
|
|
return Box;
|
|
}
|
|
FORCEINLINE static FVoxelIntBox SafeConstruct(const FVoxelVector& A, const FVoxelVector& B)
|
|
{
|
|
FVoxelIntBox Box;
|
|
Box.Min = FVoxelUtilities::FloorToInt(FVoxelUtilities::ComponentMin(A, B));
|
|
Box.Max = FVoxelUtilities::CeilToInt(FVoxelUtilities::ComponentMax3(A, B, Box.Min + FIntVector(1, 1, 1)));
|
|
return Box;
|
|
}
|
|
|
|
FORCEINLINE FIntVector Size() const
|
|
{
|
|
ensure(SizeIs32Bit());
|
|
return Max - Min;
|
|
}
|
|
FORCEINLINE FVoxelVector GetCenter() const
|
|
{
|
|
return FVoxelVector(Min + Max) / 2.f;
|
|
}
|
|
FORCEINLINE uint64 Count() const
|
|
{
|
|
return
|
|
uint64(Max.X - Min.X) *
|
|
uint64(Max.Y - Min.Y) *
|
|
uint64(Max.Z - Min.Z);
|
|
}
|
|
|
|
FORCEINLINE bool SizeIs32Bit() const
|
|
{
|
|
return
|
|
int64(Max.X) - int64(Min.X) < MAX_int32 &&
|
|
int64(Max.Y) - int64(Min.Y) < MAX_int32 &&
|
|
int64(Max.Z) - int64(Min.Z) < MAX_int32;
|
|
}
|
|
|
|
/**
|
|
* Get the corners that are inside the box (max - 1)
|
|
*/
|
|
TVoxelStaticArray<FIntVector, 8> GetCorners(int32 MaxBorderSize) const
|
|
{
|
|
return {
|
|
FIntVector(Min.X, Min.Y, Min.Z),
|
|
FIntVector(Max.X - MaxBorderSize, Min.Y, Min.Z),
|
|
FIntVector(Min.X, Max.Y - MaxBorderSize, Min.Z),
|
|
FIntVector(Max.X - MaxBorderSize, Max.Y - MaxBorderSize, Min.Z),
|
|
FIntVector(Min.X, Min.Y, Max.Z - MaxBorderSize),
|
|
FIntVector(Max.X - MaxBorderSize, Min.Y, Max.Z - MaxBorderSize),
|
|
FIntVector(Min.X, Max.Y - MaxBorderSize, Max.Z - MaxBorderSize),
|
|
FIntVector(Max.X - MaxBorderSize, Max.Y - MaxBorderSize, Max.Z - MaxBorderSize)
|
|
};
|
|
}
|
|
FString ToString() const
|
|
{
|
|
return FString::Printf(TEXT("(%d/%d, %d/%d, %d/%d)"), Min.X, Max.X, Min.Y, Max.Y, Min.Z, Max.Z);
|
|
}
|
|
|
|
FORCEINLINE bool IsValid() const
|
|
{
|
|
return Min.X < Max.X && Min.Y < Max.Y && Min.Z < Max.Z;
|
|
}
|
|
|
|
template<typename T>
|
|
FORCEINLINE bool ContainsTemplate(T X, T Y, T Z) const
|
|
{
|
|
return ((X >= Min.X) && (X < Max.X) && (Y >= Min.Y) && (Y < Max.Y) && (Z >= Min.Z) && (Z < Max.Z));
|
|
}
|
|
template<typename T>
|
|
FORCEINLINE typename TEnableIf<TOr<TIsSame<T, FVector>, TIsSame<T, FVoxelVector>, TIsSame<T, FIntVector>>::Value, bool>::Type ContainsTemplate(const T& V) const
|
|
{
|
|
return ContainsTemplate(V.X, V.Y, V.Z);
|
|
}
|
|
template<typename T>
|
|
FORCEINLINE typename TEnableIf<TOr<TIsSame<T, FBox>, TIsSame<T, FVoxelIntBox>>::Value, bool>::Type ContainsTemplate(const T& Other) const
|
|
{
|
|
return Min.X <= Other.Min.X && Min.Y <= Other.Min.Y && Min.Z <= Other.Min.Z &&
|
|
Max.X >= Other.Max.X && Max.Y >= Other.Max.Y && Max.Z >= Other.Max.Z;
|
|
}
|
|
|
|
FORCEINLINE bool Contains(int32 X, int32 Y, int32 Z) const
|
|
{
|
|
return ContainsTemplate(X, Y, Z);
|
|
}
|
|
FORCEINLINE bool Contains(const FIntVector& V) const
|
|
{
|
|
return ContainsTemplate(V);
|
|
}
|
|
FORCEINLINE bool Contains(const FVoxelIntBox& Other) const
|
|
{
|
|
return ContainsTemplate(Other);
|
|
}
|
|
|
|
// Not an overload as the float behavior can be a bit tricky. Use ContainsTemplate if the input type is unknown
|
|
FORCEINLINE bool ContainsFloat(float X, float Y, float Z) const
|
|
{
|
|
return ContainsTemplate(X, Y, Z);
|
|
}
|
|
FORCEINLINE bool ContainsFloat(const FVector& V) const
|
|
{
|
|
return ContainsTemplate(V);
|
|
}
|
|
FORCEINLINE bool ContainsFloat(const FVoxelVector& V) const
|
|
{
|
|
return ContainsTemplate(V);
|
|
}
|
|
FORCEINLINE bool ContainsFloat(const FBox& Other) const
|
|
{
|
|
return ContainsTemplate(Other);
|
|
}
|
|
|
|
template<typename T>
|
|
bool Contains(T X, T Y, T Z) const = delete;
|
|
|
|
template<typename T>
|
|
FORCEINLINE FIntVector Clamp(T P) const
|
|
{
|
|
Clamp(P.X, P.Y, P.Z);
|
|
return P;
|
|
}
|
|
FORCEINLINE void Clamp(int32& X, int32& Y, int32& Z) const
|
|
{
|
|
X = FMath::Clamp(X, Min.X, Max.X - 1);
|
|
Y = FMath::Clamp(Y, Min.Y, Max.Y - 1);
|
|
Z = FMath::Clamp(Z, Min.Z, Max.Z - 1);
|
|
ensureVoxelSlowNoSideEffects(Contains(X, Y, Z));
|
|
}
|
|
template<typename T>
|
|
FORCEINLINE void Clamp(T& X, T& Y, T& Z) const
|
|
{
|
|
// Note: use - 1 even if that's not the closest value for which Contains would return true
|
|
// because it's really hard to figure out that value (largest float f such that f < i)
|
|
X = FMath::Clamp<T>(X, Min.X, Max.X - 1);
|
|
Y = FMath::Clamp<T>(Y, Min.Y, Max.Y - 1);
|
|
Z = FMath::Clamp<T>(Z, Min.Z, Max.Z - 1);
|
|
ensureVoxelSlowNoSideEffects(ContainsTemplate(X, Y, Z));
|
|
}
|
|
FORCEINLINE FVoxelIntBox Clamp(const FVoxelIntBox& Other) const
|
|
{
|
|
// It's not valid to call Clamp if we're not intersecting Other
|
|
ensureVoxelSlowNoSideEffects(Intersect(Other));
|
|
|
|
FVoxelIntBox Result;
|
|
|
|
Result.Min.X = FMath::Clamp(Other.Min.X, Min.X, Max.X - 1);
|
|
Result.Min.Y = FMath::Clamp(Other.Min.Y, Min.Y, Max.Y - 1);
|
|
Result.Min.Z = FMath::Clamp(Other.Min.Z, Min.Z, Max.Z - 1);
|
|
|
|
Result.Max.X = FMath::Clamp(Other.Max.X, Min.X + 1, Max.X);
|
|
Result.Max.Y = FMath::Clamp(Other.Max.Y, Min.Y + 1, Max.Y);
|
|
Result.Max.Z = FMath::Clamp(Other.Max.Z, Min.Z + 1, Max.Z);
|
|
|
|
ensureVoxelSlowNoSideEffects(Other.Contains(Result));
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* Checks whether the given bounding box intersects this bounding box.
|
|
*
|
|
* @param Other The bounding box to intersect with.
|
|
* @return true if the boxes intersect, false otherwise.
|
|
*/
|
|
template<typename TBox>
|
|
FORCEINLINE bool Intersect(const TBox& Other) const
|
|
{
|
|
if ((Min.X >= Other.Max.X) || (Other.Min.X >= Max.X))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ((Min.Y >= Other.Max.Y) || (Other.Min.Y >= Max.Y))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ((Min.Z >= Other.Max.Z) || (Other.Min.Z >= Max.Z))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
/**
|
|
* Useful for templates taking a box or coordinates
|
|
*/
|
|
template<typename TNumeric>
|
|
FORCEINLINE bool Intersect(TNumeric X, TNumeric Y, TNumeric Z) const
|
|
{
|
|
return ContainsTemplate(X, Y, Z);
|
|
}
|
|
FVoxelIntBox Overlap(const FVoxelIntBox& Other) const
|
|
{
|
|
if (!Intersect(Other))
|
|
{
|
|
return FVoxelIntBox();
|
|
}
|
|
|
|
// otherwise they overlap
|
|
// so find overlapping box
|
|
FIntVector MinVector, MaxVector;
|
|
|
|
MinVector.X = FMath::Max(Min.X, Other.Min.X);
|
|
MaxVector.X = FMath::Min(Max.X, Other.Max.X);
|
|
|
|
MinVector.Y = FMath::Max(Min.Y, Other.Min.Y);
|
|
MaxVector.Y = FMath::Min(Max.Y, Other.Max.Y);
|
|
|
|
MinVector.Z = FMath::Max(Min.Z, Other.Min.Z);
|
|
MaxVector.Z = FMath::Min(Max.Z, Other.Max.Z);
|
|
|
|
return FVoxelIntBox(MinVector, MaxVector);
|
|
}
|
|
FVoxelIntBox Union(const FVoxelIntBox& Other) const
|
|
{
|
|
FIntVector MinVector, MaxVector;
|
|
|
|
MinVector.X = FMath::Min(Min.X, Other.Min.X);
|
|
MaxVector.X = FMath::Max(Max.X, Other.Max.X);
|
|
|
|
MinVector.Y = FMath::Min(Min.Y, Other.Min.Y);
|
|
MaxVector.Y = FMath::Max(Max.Y, Other.Max.Y);
|
|
|
|
MinVector.Z = FMath::Min(Min.Z, Other.Min.Z);
|
|
MaxVector.Z = FMath::Max(Max.Z, Other.Max.Z);
|
|
|
|
return FVoxelIntBox(MinVector, MaxVector);
|
|
}
|
|
|
|
// union(return value, Other) = this
|
|
TArray<FVoxelIntBox, TFixedAllocator<6>> Difference(const FVoxelIntBox& Other) const
|
|
{
|
|
if (!Intersect(Other))
|
|
{
|
|
return { *this };
|
|
}
|
|
|
|
TArray<FVoxelIntBox, TFixedAllocator<6>> OutBoxes;
|
|
|
|
if (Min.Z < Other.Min.Z)
|
|
{
|
|
// Add bottom
|
|
OutBoxes.Emplace(Min, FIntVector(Max.X, Max.Y, Other.Min.Z));
|
|
}
|
|
if (Other.Max.Z < Max.Z)
|
|
{
|
|
// Add top
|
|
OutBoxes.Emplace(FIntVector(Min.X, Min.Y, Other.Max.Z), Max);
|
|
}
|
|
|
|
const int32 MinZ = FMath::Max(Min.Z, Other.Min.Z);
|
|
const int32 MaxZ = FMath::Min(Max.Z, Other.Max.Z);
|
|
|
|
if (Min.X < Other.Min.X)
|
|
{
|
|
// Add X min
|
|
OutBoxes.Emplace(FIntVector(Min.X, Min.Y, MinZ), FIntVector(Other.Min.X, Max.Y, MaxZ));
|
|
}
|
|
if (Other.Max.X < Max.X)
|
|
{
|
|
// Add X max
|
|
OutBoxes.Emplace(FIntVector(Other.Max.X, Min.Y, MinZ), FIntVector(Max.X, Max.Y, MaxZ));
|
|
}
|
|
|
|
const int32 MinX = FMath::Max(Min.X, Other.Min.X);
|
|
const int32 MaxX = FMath::Min(Max.X, Other.Max.X);
|
|
|
|
if (Min.Y < Other.Min.Y)
|
|
{
|
|
// Add Y min
|
|
OutBoxes.Emplace(FIntVector(MinX, Min.Y, MinZ), FIntVector(MaxX, Other.Min.Y, MaxZ));
|
|
}
|
|
if (Other.Max.Y < Max.Y)
|
|
{
|
|
// Add Y max
|
|
OutBoxes.Emplace(FIntVector(MinX, Other.Max.Y, MinZ), FIntVector(MaxX, Max.Y, MaxZ));
|
|
}
|
|
|
|
return OutBoxes;
|
|
}
|
|
|
|
FORCEINLINE uint64 ComputeSquaredDistanceFromBoxToPoint(const FIntVector& Point) const
|
|
{
|
|
// Accumulates the distance as we iterate axis
|
|
uint64 DistSquared = 0;
|
|
|
|
// Check each axis for min/max and add the distance accordingly
|
|
if (Point.X < Min.X)
|
|
{
|
|
DistSquared += FMath::Square<uint64>(Min.X - Point.X);
|
|
}
|
|
else if (Point.X > Max.X)
|
|
{
|
|
DistSquared += FMath::Square<uint64>(Point.X - Max.X);
|
|
}
|
|
|
|
if (Point.Y < Min.Y)
|
|
{
|
|
DistSquared += FMath::Square<uint64>(Min.Y - Point.Y);
|
|
}
|
|
else if (Point.Y > Max.Y)
|
|
{
|
|
DistSquared += FMath::Square<uint64>(Point.Y - Max.Y);
|
|
}
|
|
|
|
if (Point.Z < Min.Z)
|
|
{
|
|
DistSquared += FMath::Square<uint64>(Min.Z - Point.Z);
|
|
}
|
|
else if (Point.Z > Max.Z)
|
|
{
|
|
DistSquared += FMath::Square<uint64>(Point.Z - Max.Z);
|
|
}
|
|
|
|
return DistSquared;
|
|
}
|
|
|
|
FORCEINLINE bool IsMultipleOf(int32 Step) const
|
|
{
|
|
return Min.X % Step == 0 && Min.Y % Step == 0 && Min.Z % Step == 0 &&
|
|
Max.X % Step == 0 && Max.Y % Step == 0 && Max.Z % Step == 0;
|
|
}
|
|
|
|
// OldBox included in NewBox, but NewBox not included in OldBox
|
|
FORCEINLINE FVoxelIntBox MakeMultipleOfBigger(int32 Step) const
|
|
{
|
|
FVoxelIntBox NewBox;
|
|
NewBox.Min = FVoxelUtilities::DivideFloor(Min, Step) * Step;
|
|
NewBox.Max = FVoxelUtilities::DivideCeil(Max, Step) * Step;
|
|
return NewBox;
|
|
}
|
|
// NewBox included in OldBox, but OldBox not included in NewBox
|
|
FORCEINLINE FVoxelIntBox MakeMultipleOfSmaller(int32 Step) const
|
|
{
|
|
FVoxelIntBox NewBox;
|
|
NewBox.Min = FVoxelUtilities::DivideCeil(Min, Step) * Step;
|
|
NewBox.Max = FVoxelUtilities::DivideFloor(Max, Step) * Step;
|
|
return NewBox;
|
|
}
|
|
FORCEINLINE FVoxelIntBox MakeMultipleOfRoundUp(int32 Step) const
|
|
{
|
|
FVoxelIntBox NewBox;
|
|
NewBox.Min = FVoxelUtilities::DivideCeil(Min, Step) * Step;
|
|
NewBox.Max = FVoxelUtilities::DivideCeil(Max, Step) * Step;
|
|
return NewBox;
|
|
}
|
|
|
|
// Guarantee: union(OutChilds).Contains(this)
|
|
template<typename T>
|
|
bool Subdivide(int32 ChildrenSize, TArray<FVoxelIntBox, T>& OutChildren, int32 MaxChildren = -1) const
|
|
{
|
|
const FIntVector LowerBound = FVoxelUtilities::DivideFloor(Min, ChildrenSize) * ChildrenSize;
|
|
const FIntVector UpperBound = FVoxelUtilities::DivideCeil(Max, ChildrenSize) * ChildrenSize;
|
|
for (int32 X = LowerBound.X; X < UpperBound.X; X += ChildrenSize)
|
|
{
|
|
for (int32 Y = LowerBound.Y; Y < UpperBound.Y; Y += ChildrenSize)
|
|
{
|
|
for (int32 Z = LowerBound.Z; Z < UpperBound.Z; Z += ChildrenSize)
|
|
{
|
|
OutChildren.Emplace(FIntVector(X, Y, Z), FIntVector(X + ChildrenSize, Y + ChildrenSize, Z + ChildrenSize));
|
|
if (MaxChildren != -1 && OutChildren.Num() > MaxChildren)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBox Scale(v_flt S) const
|
|
{
|
|
return { FVoxelUtilities::FloorToInt(FVoxelVector(Min) * S), FVoxelUtilities::CeilToInt(FVoxelVector(Max) * S) };
|
|
}
|
|
FORCEINLINE FVoxelIntBox Scale(const FVoxelVector& S) const
|
|
{
|
|
return SafeConstruct(FVoxelVector(Min) * S, FVoxelVector(Max) * S);
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBox Extend(const FIntVector& Amount) const
|
|
{
|
|
return { Min - Amount, Max + Amount };
|
|
}
|
|
FORCEINLINE FVoxelIntBox Extend(int32 Amount) const
|
|
{
|
|
return Extend(FIntVector(Amount));
|
|
}
|
|
FORCEINLINE FVoxelIntBox Translate(const FIntVector& Position) const
|
|
{
|
|
return FVoxelIntBox(Min + Position, Max + Position);
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBox RemoveTranslation() const
|
|
{
|
|
return FVoxelIntBox(0, Max - Min);
|
|
}
|
|
// Will move the box so that GetCenter = 0,0,0. Will extend it if its size is odd
|
|
FVoxelIntBox Center() const
|
|
{
|
|
FIntVector NewMin = Min;
|
|
FIntVector NewMax = Max;
|
|
if (FVoxelVector(GetCenter().ToInt()) != GetCenter())
|
|
{
|
|
NewMax = NewMax + 1;
|
|
}
|
|
ensure(FVoxelVector(GetCenter().ToInt()) == GetCenter());
|
|
const FIntVector Offset = GetCenter().ToInt();
|
|
NewMin -= Offset;
|
|
NewMax -= Offset;
|
|
ensure(NewMin + NewMax == FIntVector(0));
|
|
return FVoxelIntBox(NewMin, NewMax);
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBox& operator*=(int32 Scale)
|
|
{
|
|
Min *= Scale;
|
|
Max *= Scale;
|
|
return *this;
|
|
}
|
|
|
|
FORCEINLINE bool operator==(const FVoxelIntBox& Other) const
|
|
{
|
|
return Min == Other.Min && Max == Other.Max;
|
|
}
|
|
FORCEINLINE bool operator!=(const FVoxelIntBox& Other) const
|
|
{
|
|
return Min != Other.Min || Max != Other.Max;
|
|
}
|
|
|
|
// More expensive, but should be more random
|
|
FORCEINLINE uint32 GetMurmurHash() const
|
|
{
|
|
return FVoxelUtilities::MurmurHash(Min) ^ FVoxelUtilities::MurmurHash(Max);
|
|
}
|
|
|
|
template<typename T>
|
|
FORCEINLINE void Iterate(T Lambda) const
|
|
{
|
|
for (int32 X = Min.X; X < Max.X; X++)
|
|
{
|
|
for (int32 Y = Min.Y; Y < Max.Y; Y++)
|
|
{
|
|
for (int32 Z = Min.Z; Z < Max.Z; Z++)
|
|
{
|
|
Lambda(X, Y, Z);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
template<typename T>
|
|
FORCEINLINE void Iterate(int32 Step, T Lambda) const
|
|
{
|
|
for (int32 X = Min.X; X < Max.X; X += Step)
|
|
{
|
|
for (int32 Y = Min.Y; Y < Max.Y; Y += Step)
|
|
{
|
|
for (int32 Z = Min.Z; Z < Max.Z; Z += Step)
|
|
{
|
|
Lambda(X, Y, Z);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
FORCEINLINE void ParallelSplit(T Lambda, bool bForceSingleThread = false) const
|
|
{
|
|
const FIntVector Half = (Min + Max) / 2;
|
|
ParallelFor(8, [&](int32 Index)
|
|
{
|
|
const FVoxelIntBox LocalBounds(
|
|
FIntVector(
|
|
(Index & 0x1) ? Half.X : Min.X,
|
|
(Index & 0x2) ? Half.Y : Min.Y,
|
|
(Index & 0x4) ? Half.Z : Min.Z),
|
|
FIntVector(
|
|
(Index & 0x1) ? Max.X : Half.X,
|
|
(Index & 0x2) ? Max.Y : Half.Y,
|
|
(Index & 0x4) ? Max.Z : Half.Z));
|
|
Lambda(LocalBounds);
|
|
}, bForceSingleThread);
|
|
}
|
|
template<typename T>
|
|
FORCEINLINE void ParallelIterate(T Lambda, bool bForceSingleThread = false) const
|
|
{
|
|
ParallelFor(Size().X, [&](int32 Index)
|
|
{
|
|
const int32 X = Min.X + Index;
|
|
checkVoxelSlow(X < Max.X);
|
|
for (int32 Y = Min.Y; Y < Max.Y; Y++)
|
|
{
|
|
for (int32 Z = Min.Z; Z < Max.Z; Z++)
|
|
{
|
|
Lambda(X, Y, Z);
|
|
}
|
|
}
|
|
}, bForceSingleThread);
|
|
}
|
|
|
|
// MaxBorderSize: if we do a 180 rotation for example, min and max are inverted
|
|
// If we don't work on values that are actually inside the box, the resulting box will be wrong
|
|
template<EInverseTransform Inverse = EInverseTransform::False>
|
|
FVoxelIntBox ApplyTransform(const FTransform& Transform, int32 MaxBorderSize = 1) const
|
|
{
|
|
const auto Corners = GetCorners(MaxBorderSize);
|
|
|
|
FIntVector NewMin(MAX_int32);
|
|
FIntVector NewMax(MIN_int32);
|
|
for (int32 Index = 0; Index < 8; Index++)
|
|
{
|
|
const FVector P =
|
|
Inverse == EInverseTransform::True
|
|
? Transform.InverseTransformPosition(FVector(Corners[Index]))
|
|
: Transform.TransformPosition(FVector(Corners[Index]));
|
|
NewMin = FVoxelUtilities::ComponentMin(NewMin, FVoxelUtilities::FloorToInt(P));
|
|
NewMax = FVoxelUtilities::ComponentMax(NewMax, FVoxelUtilities::CeilToInt(P));
|
|
}
|
|
return FVoxelIntBox(NewMin, NewMax + MaxBorderSize);
|
|
}
|
|
};
|
|
|
|
FORCEINLINE uint32 GetTypeHash(const FVoxelIntBox& Box)
|
|
{
|
|
static_assert(sizeof(FVoxelIntBox) == 6 * sizeof(int32), "Alignement error");
|
|
return FCrc::MemCrc32(&Box, sizeof(FVoxelIntBox));
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBox operator*(const FVoxelIntBox& Box, int32 Scale)
|
|
{
|
|
FVoxelIntBox Copy = Box;
|
|
return Copy *= Scale;
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBox operator*(int32 Scale, const FVoxelIntBox& Box)
|
|
{
|
|
FVoxelIntBox Copy = Box;
|
|
return Copy *= Scale;
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBox operator+(const FVoxelIntBox& Box, const FVoxelIntBox& Other)
|
|
{
|
|
FVoxelIntBox Copy = Box;
|
|
Copy.Min = FVoxelUtilities::ComponentMin(Copy.Min, Other.Min);
|
|
Copy.Max = FVoxelUtilities::ComponentMax(Copy.Max, Other.Max);
|
|
return Copy;
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBox operator+(const FVoxelIntBox& Box, const FIntVector& Point)
|
|
{
|
|
return Box + FVoxelIntBox(Point);
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBox operator+(const FVoxelIntBox& Box, const FVector& Point)
|
|
{
|
|
return Box + FVoxelIntBox(Point);
|
|
}
|
|
|
|
FORCEINLINE FArchive& operator<<(FArchive& Ar, FVoxelIntBox& Box)
|
|
{
|
|
Ar << Box.Min;
|
|
Ar << Box.Max;
|
|
return Ar;
|
|
}
|
|
|
|
// Voxel Int Box with a IsValid flag
|
|
USTRUCT(BlueprintType, meta=(HasNativeMake="Voxel.VoxelIntBoxLibrary.MakeIntBoxWithValidity", HasNativeBreak="Voxel.VoxelIntBoxLibrary.BreakIntBoxWithValidity"))
|
|
struct FVoxelIntBoxWithValidity
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
FVoxelIntBoxWithValidity() = default;
|
|
FVoxelIntBoxWithValidity(const FVoxelIntBox& Box)
|
|
: Box(Box)
|
|
, bValid(true)
|
|
{
|
|
}
|
|
|
|
FORCEINLINE const FVoxelIntBox& GetBox() const
|
|
{
|
|
check(IsValid());
|
|
return Box;
|
|
}
|
|
|
|
FORCEINLINE bool IsValid() const
|
|
{
|
|
return bValid;
|
|
}
|
|
FORCEINLINE void Reset()
|
|
{
|
|
bValid = false;
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBoxWithValidity& operator=(const FVoxelIntBox& Other)
|
|
{
|
|
Box = Other;
|
|
bValid = true;
|
|
return *this;
|
|
}
|
|
|
|
FORCEINLINE bool operator==(const FVoxelIntBoxWithValidity& Other) const
|
|
{
|
|
if (bValid != Other.bValid)
|
|
{
|
|
return false;
|
|
}
|
|
if (!bValid && !Other.bValid)
|
|
{
|
|
return true;
|
|
}
|
|
return Box == Other.Box;
|
|
}
|
|
FORCEINLINE bool operator!=(const FVoxelIntBoxWithValidity& Other) const
|
|
{
|
|
return !(*this == Other);
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBoxWithValidity& operator+=(const FVoxelIntBox& Other)
|
|
{
|
|
if (bValid)
|
|
{
|
|
Box = Box + Other;
|
|
}
|
|
else
|
|
{
|
|
Box = Other;
|
|
bValid = true;
|
|
}
|
|
return *this;
|
|
}
|
|
FORCEINLINE FVoxelIntBoxWithValidity& operator+=(const FVoxelIntBoxWithValidity& Other)
|
|
{
|
|
if (Other.bValid)
|
|
{
|
|
*this += Other.GetBox();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
FORCEINLINE FVoxelIntBoxWithValidity& operator+=(const FIntVector& Point)
|
|
{
|
|
return *this += FVoxelIntBox(Point);
|
|
}
|
|
|
|
template<typename T>
|
|
FORCEINLINE FVoxelIntBoxWithValidity& operator+=(const TArray<T>& Other)
|
|
{
|
|
for (auto& It : Other)
|
|
{
|
|
*this += It;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
UPROPERTY()
|
|
FVoxelIntBox Box;
|
|
|
|
UPROPERTY()
|
|
bool bValid = false;
|
|
};
|
|
|
|
template<typename T>
|
|
FORCEINLINE FVoxelIntBoxWithValidity operator+(const FVoxelIntBoxWithValidity& Box, const T& Other)
|
|
{
|
|
FVoxelIntBoxWithValidity Copy = Box;
|
|
return Copy += Other;
|
|
}
|
|
|
|
UE_DEPRECATED(4.24, "Please use FVoxelIntBox instead of FIntBox.")
|
|
typedef FVoxelIntBox FIntBox;
|
|
|
|
UE_DEPRECATED(4.24, "Please use FVoxelIntBoxWithValidity instead of FIntBoxWithValidity.")
|
|
typedef FVoxelIntBoxWithValidity FIntBoxWithValidity; |