CelticCraft/Plugins/VoxelFree/Source/Voxel/Public/VoxelAssets/VoxelHeightmapAssetInstance.h

125 lines
3.7 KiB
C
Raw Normal View History

2023-07-03 16:17:13 +00:00
// Copyright 2020 Phyronnaz
#pragma once
#include "CoreMinimal.h"
#include "VoxelAssets/VoxelHeightmapAsset.h"
#include "VoxelAssets/VoxelHeightmapAssetSamplerWrapper.h"
#include "VoxelGenerators/VoxelGeneratorHelpers.h"
template<typename T>
struct TVoxelHeightmapAssetSelector;
template<>
struct TVoxelHeightmapAssetSelector<float>
{
using Type = UVoxelHeightmapAssetFloat;
};
template<>
struct TVoxelHeightmapAssetSelector<uint16>
{
using Type = UVoxelHeightmapAssetUINT16;
};
template<typename T>
class TVoxelHeightmapAssetInstance : public TVoxelGeneratorInstanceHelper<TVoxelHeightmapAssetInstance<T>, typename TVoxelHeightmapAssetSelector<T>::Type>
{
public:
using Super = TVoxelGeneratorInstanceHelper<TVoxelHeightmapAssetInstance<T>, typename TVoxelHeightmapAssetSelector<T>::Type>;
const TVoxelHeightmapAssetSamplerWrapper<T> Wrapper;
const float Precision;
const bool bInfiniteExtent;
const FVoxelIntBox WorldBounds;
public:
explicit TVoxelHeightmapAssetInstance(typename TVoxelHeightmapAssetSelector<T>::Type& Asset)
: Super(&Asset)
, Wrapper(&Asset)
, Precision(Asset.Precision)
, bInfiniteExtent(Asset.bInfiniteExtent)
, WorldBounds(Asset.GetBounds())
{
}
//~ Begin FVoxelGeneratorInstance Interface
FORCEINLINE v_flt GetValueImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const
{
if (bInfiniteExtent || WorldBounds.ContainsFloat(X, Y, Z)) // Note: it's safe to access outside the bounds
{
const float Height = Wrapper.GetHeight(X + Wrapper.GetWidth() / 2, Y + Wrapper.GetHeight() / 2, EVoxelSamplerMode::Clamp);
return (Z - Height) / Precision;
}
else
{
// Outside asset bounds
return 1;
}
}
FORCEINLINE FVoxelMaterial GetMaterialImpl(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const
{
return Wrapper.GetMaterial(X + Wrapper.GetWidth() / 2, Y + Wrapper.GetHeight() / 2, EVoxelSamplerMode::Clamp);
}
TVoxelRange<v_flt> GetValueRangeImpl(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const
{
if (!bInfiniteExtent && !Bounds.Intersect(WorldBounds))
{
return 1;
}
const bool bEntirelyContained = WorldBounds.Contains(Bounds);
const auto XRange = TVoxelRange<v_flt>(Bounds.Min.X, Bounds.Max.X) + Wrapper.GetWidth() / 2;
const auto YRange = TVoxelRange<v_flt>(Bounds.Min.Y, Bounds.Max.Y) + Wrapper.GetHeight() / 2;
const auto ZRange = TVoxelRange<v_flt>(Bounds.Min.Z, Bounds.Max.Z);
auto HeightRange = TVoxelRange<v_flt>(Wrapper.GetHeightRange(XRange, YRange, EVoxelSamplerMode::Clamp));
if (!bEntirelyContained && bInfiniteExtent)
{
// Height will be 0 outside the boundaries if bInfiniteExtent = true
HeightRange = TVoxelRange<v_flt>::Union(HeightRange, 0.f);
}
auto Range = (ZRange - HeightRange) / Precision;
if (!bEntirelyContained && !bInfiniteExtent)
{
// Intersects with the boundary
Range = TVoxelRange<v_flt>::Union(1.f, Range);
}
return Range;
}
virtual void GetValues(TVoxelQueryZone<FVoxelValue>& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override final
{
for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X))
{
for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y))
{
const float Height = Wrapper.GetHeight(X + Wrapper.GetWidth() / 2, Y + Wrapper.GetHeight() / 2, EVoxelSamplerMode::Clamp);
for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z))
{
FVoxelValue Value;
if (bInfiniteExtent || WorldBounds.Contains(X, Y, Z))
{
Value = FVoxelValue((Z - Height) / Precision);
}
else
{
// Outside asset bounds
Value = FVoxelValue::Empty();
}
QueryZone.Set(X, Y, Z, Value);
}
}
}
}
virtual FVector GetUpVector(v_flt X, v_flt Y, v_flt Z) const override final
{
return FVector::UpVector;
}
//~ End FVoxelGeneratorInstance Interface
};