// Copyright 2020 Phyronnaz #pragma once #include "CoreMinimal.h" #include "VoxelMinimal.h" #include "VoxelRange.h" #include "VoxelUtilities/VoxelRangeUtilities.h" namespace FVoxelMathNodeFunctions { FORCEINLINE void InverseTransformPositionXZ( v_flt X_X, v_flt X_Y, v_flt X_Z, v_flt Z_X, v_flt Z_Y, v_flt Z_Z, v_flt X, v_flt Y, v_flt Z, v_flt& OutX, v_flt& OutY, v_flt& OutZ) { const FVector BaseX = FVoxelVector(X_X, X_Y, X_Z).GetSafeNormal().ToFloat(); FVector BaseZ = FVoxelVector(Z_X, Z_Y, Z_Z).GetSafeNormal().ToFloat(); const FVector BaseY = BaseZ ^ BaseX; BaseZ = BaseX ^ BaseY; if (BaseX.IsNearlyZero() || BaseY.IsNearlyZero() || BaseZ.IsNearlyZero()) { OutX = 0; OutY = 0; OutZ = 0; return; } const FMatrix Matrix(BaseX, BaseY, BaseZ, FVector::ZeroVector); FMatrix InvertedMatrix; VectorMatrixInverse(&InvertedMatrix, &Matrix); const FVector Result = InvertedMatrix.TransformPosition(FVoxelVector(X, Y, Z).ToFloat()); OutX = Result.X; OutY = Result.Y; OutZ = Result.Z; } FORCEINLINE void InverseTransformPositionXZ( TVoxelRange X_X, TVoxelRange X_Y, TVoxelRange X_Z, TVoxelRange Z_X, TVoxelRange Z_Y, TVoxelRange Z_Z, TVoxelRange X, TVoxelRange Y, TVoxelRange Z, TVoxelRange& OutX, TVoxelRange& OutY, TVoxelRange& OutZ) { if (!X_X.IsSingleValue() || !X_Y.IsSingleValue() || !X_Z.IsSingleValue() || !Z_X.IsSingleValue() || !Z_Y.IsSingleValue() || !Z_Z.IsSingleValue()) { FVoxelRangeFailStatus::Get().Warning(TEXT("range analysis needs a fixed basis to work")); OutX = TVoxelRange::Infinite(); OutY = TVoxelRange::Infinite(); OutZ = TVoxelRange::Infinite(); return; } const FVector BaseX = FVoxelVector(X_X.GetSingleValue(), X_Y.GetSingleValue(), X_Z.GetSingleValue()).GetSafeNormal().ToFloat(); FVector BaseZ = FVoxelVector(Z_X.GetSingleValue(), Z_Y.GetSingleValue(), Z_Z.GetSingleValue()).GetSafeNormal().ToFloat(); const FVector BaseY = BaseZ ^ BaseX; BaseZ = BaseX ^ BaseY; if (BaseX.IsNearlyZero() || BaseY.IsNearlyZero() || BaseZ.IsNearlyZero()) { OutX = 0; OutY = 0; OutZ = 0; return; } const FMatrix Matrix = FMatrix(BaseX, BaseY, BaseZ, FVector::ZeroVector); FMatrix InvertedMatrix; VectorMatrixInverse(&InvertedMatrix, &Matrix); bool bOutSet = false; const auto Check = [&](v_flt InX, v_flt InY, v_flt InZ) { const FVector Result = InvertedMatrix.TransformPosition(FVoxelVector(InX, InY, InZ).ToFloat()); if (bOutSet) { OutX = TVoxelRange::Union(OutX, Result.X); OutY = TVoxelRange::Union(OutY, Result.Y); OutZ = TVoxelRange::Union(OutZ, Result.Z); } else { OutX = Result.X; OutY = Result.Y; OutZ = Result.Z; bOutSet = true; } }; Check(X.Min, Y.Min, Z.Min); Check(X.Max, Y.Min, Z.Min); Check(X.Min, Y.Max, Z.Min); Check(X.Max, Y.Max, Z.Min); Check(X.Min, Y.Min, Z.Max); Check(X.Max, Y.Min, Z.Max); Check(X.Min, Y.Max, Z.Max); Check(X.Max, Y.Max, Z.Max); } template FORCEINLINE void HeightSplit(v_flt Height, const TInArray& Inputs, TOutArray& Outputs) { checkVoxelSlow(Inputs.Num() % 2 == 0); checkVoxelSlow(Outputs.Num() == Inputs.Num() / 2 + 1); const int32 NumSplits = Inputs.Num() / 2; const auto GetHeight = [&](int32 Split) { return Inputs[2 * Split]; }; const auto GetFalloff = [&](int32 Split) { return Inputs[2 * Split + 1]; }; for (int32 Layer = 0; Layer < NumSplits + 1; Layer++) { float Strength = 1; // Lower bound if (Layer != 0) { const v_flt Bound = GetHeight(Layer - 1); const v_flt Falloff = GetFalloff(Layer - 1); Strength = FMath::Min(Strength, FMath::SmoothStep(Bound - Falloff, Bound + Falloff, Height)); } // Upper bound if (Layer != NumSplits) { const v_flt Bound = GetHeight(Layer); const v_flt Falloff = GetFalloff(Layer); Strength = FMath::Min(Strength, 1 - FMath::SmoothStep(Bound - Falloff, Bound + Falloff, Height)); } ensureVoxelSlow(0 <= Strength); Outputs[Layer] = Strength; } } template FORCEINLINE void HeightSplit(TVoxelRange Height, const TInArray& Inputs, TOutArray& Outputs) { for (auto& It : Outputs) { It = TVoxelRange(0, 1); } } FORCEINLINE v_flt SmoothStep(v_flt A, v_flt B, v_flt X) { if (X <= A) { return 0.0f; } else if (B <= X) { return 1.0f; } const v_flt InterpFraction = (X - A) / (B - A); return InterpFraction * InterpFraction * (3.0f - 2.0f * InterpFraction); } FORCEINLINE TVoxelRange SmoothStep(TVoxelRange A, TVoxelRange B, TVoxelRange X) { if (A.IsSingleValue() && B.IsSingleValue()) { return { SmoothStep(A.GetSingleValue(), B.GetSingleValue(), X.Min), SmoothStep(A.GetSingleValue(), B.GetSingleValue(), X.Max) }; } if (X.Max <= A.Min) { return 0.f; } if (B.Max <= X.Min) { return 1.f; } const auto InterpFraction = (X - A) / (B - A); const auto Result = InterpFraction * InterpFraction * (3.0f - 2.0f * InterpFraction); return FVoxelRangeUtilities::Clamp(Result, 0.f, 1.f); } }