// Copyright 2020 Phyronnaz #pragma once #include "CoreMinimal.h" #include "Containers/StaticArray.h" #include "VoxelMinimal.h" #include "VoxelContext.h" #include "VoxelGraphConstants.h" #include "VoxelGenerators/VoxelGeneratorHelpers.h" #include "VoxelGenerators/VoxelGeneratorInstance.inl" #include "VoxelGraphGeneratorHelpers.generated.h" // See https://godbolt.org/z/4IzS-b #if defined(_MSC_VER) && !defined(INTELLISENSE_PARSER) #define MSVC_TEMPLATE #else #define MSVC_TEMPLATE template #endif struct FVoxelGraphOutputsInit { EVoxelMaterialConfig MaterialConfig; }; template class TVoxelGraphGeneratorInstanceHelper : public TVoxelTransformableGeneratorInstanceHelper { public: using FVoxelGeneratorInstance::TOutputFunctionPtr; using FVoxelGeneratorInstance::TRangeOutputFunctionPtr; using FVoxelTransformableGeneratorInstance::TOutputFunctionPtr_Transform; using FVoxelTransformableGeneratorInstance::TRangeOutputFunctionPtr_Transform; using FVoxelGeneratorInstance::FCustomFunctionPtrs; using FVoxelTransformableGeneratorInstance::FCustomFunctionPtrs_Transform; DEPRECATED_VOXEL_GRAPH_FUNCTION() TVoxelGraphGeneratorInstanceHelper( const TMap& FloatOutputs, const TMap& Int32Outputs, const TMap& ColorOutputs, const FCustomFunctionPtrs& CustomFunctionPtrs, const FCustomFunctionPtrs_Transform& CustomFunctionPtrs_Transform, bool bEnableRangeAnalysis) : TVoxelTransformableGeneratorInstanceHelper(nullptr, CustomFunctionPtrs, CustomFunctionPtrs_Transform) , bEnableRangeAnalysis(bEnableRangeAnalysis) , CustomOutputsNames(InPlace, FName()) { auto& Array = const_cast&>(CustomOutputsNames); for (auto& It : FloatOutputs) { ensure(Array[It.Value] == FName()); Array[It.Value] = It.Key; } for (auto& It : Int32Outputs) { ensure(Array[It.Value] == FName()); Array[It.Value] = It.Key; } for (auto& It : ColorOutputs) { ensure(Array[It.Value] == FName()); Array[It.Value] = It.Key; } } TVoxelGraphGeneratorInstanceHelper( const TMap& FloatOutputs, const TMap& Int32Outputs, const TMap& ColorOutputs, const FCustomFunctionPtrs& CustomFunctionPtrs, const FCustomFunctionPtrs_Transform& CustomFunctionPtrs_Transform, UWorldObject& Object) : TVoxelTransformableGeneratorInstanceHelper(&Object, CustomFunctionPtrs, CustomFunctionPtrs_Transform) , bEnableRangeAnalysis(Object.bEnableRangeAnalysis) , CustomOutputsNames(InPlace, FName()) { auto& Array = const_cast&>(CustomOutputsNames); for (auto& It : FloatOutputs) { ensure(Array[It.Value] == FName()); Array[It.Value] = It.Key; } for (auto& It : Int32Outputs) { ensure(Array[It.Value] == FName()); Array[It.Value] = It.Key; } for (auto& It : ColorOutputs) { ensure(Array[It.Value] == FName()); Array[It.Value] = It.Key; } } public: template T GetOutput(const FTransform& LocalToWorld, T DefaultValue, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const { ensure(bInit); auto&& Target = This().template GetTarget(); auto Outputs = Target.GetOutputs(); Outputs.Init(FVoxelGraphOutputsInit{ MaterialConfig }); Outputs.template Set(DefaultValue); FVoxelContext Context(LOD, Items, LocalToWorld, bCustomTransform); Context.UpdateCoordinates(X, Y, Z); Target.ComputeXYZWithoutCache(Context, Outputs); return Outputs.template Get(); } template TVoxelRange GetOutputRange(const FTransform& LocalToWorld, TVoxelRange DefaultValue, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const { ensure(bInit); if (!bEnableRangeAnalysis) { return TVoxelRange::Infinite(); } auto&& Target = This().template GetRangeTarget(); auto Outputs = Target.GetOutputs(); Outputs.Init(FVoxelGraphOutputsInit{ MaterialConfig }); Outputs.template Set(DefaultValue); auto& RangeFailStatus = FVoxelRangeFailStatus::Get(); ensure(!RangeFailStatus.HasFailed()); RangeFailStatus.Reset(); const FVoxelContextRange Context(LOD, Items, LocalToWorld, bCustomTransform, WorldBounds); Target.ComputeXYZWithoutCache(Context, Outputs); if (RangeFailStatus.HasFailed()) { RangeFailStatus.Reset(); return TVoxelRange::Infinite(); } return Outputs.template Get(); } template void GetOutput(const FTransform& LocalToWorld, T DefaultValue, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const { ensure(bInit); auto&& Target = This().template GetTarget(); FVoxelContext Context(LOD, Items, LocalToWorld, bCustomTransform); if (!bCustomTransform) { // We can only use the dependencies analysis if we don't have a transform, or if it's only translation + scale // (and thus not changing the axis). Not checking that second case though. for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) { Context.LocalX = Context.WorldX = X; auto BufferX = Target.GetBufferX(); Target.ComputeX(Context, BufferX); for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) { Context.LocalY = Context.WorldY = Y; auto BufferXY = Target.GetBufferXY(); Target.ComputeXYWithCache(Context, BufferX, BufferXY); for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) { Context.LocalZ = Context.WorldZ = Z; auto Outputs = Target.GetOutputs(); Outputs.Init(FVoxelGraphOutputsInit{ MaterialConfig }); Outputs.template Set(DefaultValue); Target.ComputeXYZWithCache(Context, static_cast(BufferX), static_cast(BufferXY), Outputs); QueryZone.Set(X, Y, Z, QueryZoneType(Outputs.template Get())); } } } } else { // Have to query all the voxels individually for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, X)) { for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Y)) { for (VOXEL_QUERY_ZONE_ITERATE(QueryZone, Z)) { Context.UpdateCoordinates(X, Y, Z); auto Outputs = Target.GetOutputs(); Outputs.Init(FVoxelGraphOutputsInit{ MaterialConfig }); Outputs.template Set(DefaultValue); Target.ComputeXYZWithoutCache(Context, Outputs); QueryZone.Set(X, Y, Z, QueryZoneType(Outputs.template Get())); } } } } } template T GetDataImpl(const FTransform& LocalToWorld, T DefaultValue, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const { return GetOutput(LocalToWorld, DefaultValue, X, Y, Z, LOD, Items); } template void GetData(const FTransform& LocalToWorld, T DefaultValue, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const { return GetOutput(LocalToWorld, DefaultValue, QueryZone, LOD, Items); } template T GetCustomOutputImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const { static_assert(Index < MAX_VOXELGRAPH_OUTPUTS, ""); return GetOutput(LocalToWorld, T{}, X, Y, Z, LOD, Items); } template TVoxelRange GetCustomOutputRangeImpl(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const { static_assert(Index < MAX_VOXELGRAPH_OUTPUTS, ""); return GetOutputRange(LocalToWorld, T{}, WorldBounds, LOD, Items); } template T GetCustomOutputWithTransform(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const { return GetCustomOutputImpl(LocalToWorld, X, Y, Z, LOD, Items); } template TVoxelRange GetCustomOutputRangeWithTransform(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const { return GetCustomOutputRangeImpl(LocalToWorld, WorldBounds, LOD, Items); } template T GetCustomOutputNoTransform(v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const { return GetCustomOutputImpl(FTransform(), X, Y, Z, LOD, Items); } template TVoxelRange GetCustomOutputRangeNoTransform(const FVoxelIntBox& Bounds, int32 LOD, const FVoxelItemStack& Items) const { return GetCustomOutputRangeImpl(FTransform(), Bounds, LOD, Items); } public: //~ Begin FVoxelGeneratorInstance Interface virtual void Init(const FVoxelGeneratorInit& InitStruct) override final { bInit = true; MaterialConfig = InitStruct.MaterialConfig; InitGraph(InitStruct); } template v_flt GetValueImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const { return GetDataImpl(LocalToWorld, 1, X, Y, Z, LOD, Items); } template FVoxelMaterial GetMaterialImpl(const FTransform& LocalToWorld, v_flt X, v_flt Y, v_flt Z, int32 LOD, const FVoxelItemStack& Items) const { return GetDataImpl(LocalToWorld, FVoxelMaterial::Default(), X, Y, Z, LOD, Items); } template TVoxelRange GetValueRangeImpl(const FTransform& LocalToWorld, const FVoxelIntBox& WorldBounds, int32 LOD, const FVoxelItemStack& Items) const { return GetOutputRange(LocalToWorld, 1, WorldBounds, LOD, Items); } virtual void GetValues(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override final { GetData(FTransform(), 1, QueryZone, LOD, Items); } virtual void GetMaterials(TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override final { GetData(FTransform(), FVoxelMaterial::Default(), QueryZone, LOD, Items); } virtual void GetValues_Transform(const FTransform& LocalToWorld, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override final { GetData(LocalToWorld, 1, QueryZone, LOD, Items); } virtual void GetMaterials_Transform(const FTransform& LocalToWorld, TVoxelQueryZone& QueryZone, int32 LOD, const FVoxelItemStack& Items) const override final { GetData(LocalToWorld, FVoxelMaterial::Default(), QueryZone, LOD, Items); } virtual FVector GetUpVector(v_flt X, v_flt Y, v_flt Z) const override final { auto&& Target = This().template GetTarget< FVoxelGraphOutputsIndices::UpVectorXIndex, FVoxelGraphOutputsIndices::UpVectorYIndex, FVoxelGraphOutputsIndices::UpVectorZIndex>(); auto Outputs = Target.GetOutputs(); Outputs.Init(FVoxelGraphOutputsInit{ MaterialConfig }); Outputs.template Set(0); Outputs.template Set(0); Outputs.template Set(1); FVoxelContext Context(0, FVoxelItemStack::Empty, FTransform::Identity, false); Context.UpdateCoordinates(X, Y, Z); Target.ComputeXYZWithoutCache(Context, Outputs); return FVector( Outputs.template Get(), Outputs.template Get(), Outputs.template Get()).GetSafeNormal(); } //~ End FVoxelGeneratorInstance Interface public: virtual void InitGraph(const FVoxelGeneratorInit& InitStruct) = 0; protected: template struct NoTransformAccessor { template static ReturnType Get() { return static_cast(&TChild::template GetCustomOutputNoTransform); } }; template struct NoTransformRangeAccessor { template static ReturnType Get() { return static_cast(&TChild::template GetCustomOutputRangeNoTransform); } }; template struct WithTransformAccessor { template static ReturnType Get() { return static_cast(&TChild::template GetCustomOutputWithTransform); } }; template struct WithTransformRangeAccessor { template static ReturnType Get() { return static_cast(&TChild::template GetCustomOutputRangeWithTransform); } }; private: const bool bEnableRangeAnalysis; // Used to forward the custom output calls to the generator in the stack const TStaticArray CustomOutputsNames; bool bInit = false; EVoxelMaterialConfig MaterialConfig = EVoxelMaterialConfig(-1); const TChild& This() const { return static_cast(*this); } TChild& This() { return static_cast(*this); } }; UCLASS(Abstract) class VOXELGRAPH_API UVoxelGraphGeneratorHelper : public UVoxelTransformableGenerator { GENERATED_BODY() public: // Range analysis gives a pretty significant speed-up. You should not disable it UPROPERTY(EditAnywhere, AdvancedDisplay, Category = "Misc", meta = (HideInGenerator)) bool bEnableRangeAnalysis = true; protected: DEPRECATED_VOXEL_GRAPH_FUNCTION() virtual TMap GetDefaultSeeds() const { return {}; } };