// Copyright 2020 Phyronnaz #pragma once #include "CoreMinimal.h" #include "VoxelValue.h" #include "VoxelMaterial.h" #include "VoxelContainers/VoxelStaticArray.h" #include "VoxelUtilities/VoxelMiscUtilities.h" class IVoxelData; class FVoxelDataOctreeLeaf; class FVoxelGeneratorInstance; DECLARE_VOXEL_MEMORY_STAT(TEXT("Voxel UndoRedo Memory"), STAT_VoxelUndoRedoMemory, STATGROUP_VoxelMemory, VOXEL_API); enum class EVoxelUndoRedo { Undo, Redo }; class VOXEL_API FVoxelDataOctreeLeafUndoRedo { public: explicit FVoxelDataOctreeLeafUndoRedo(const FVoxelDataOctreeLeaf& Leaf); ~FVoxelDataOctreeLeafUndoRedo(); void ClearFrames(const FVoxelDataOctreeLeaf& Leaf); void SaveFrame(const FVoxelDataOctreeLeaf& Leaf, int32 HistoryPosition); template void ClearFramesOfType(); template void UndoRedo(const IVoxelData& Data, FVoxelDataOctreeLeaf& Leaf, int32 HistoryPosition); public: template inline bool CanUndoRedo(int32 HistoryPosition) const { return GetFramesStack().Num() > 0 && GetFramesStack().Last()->HistoryPosition == HistoryPosition; } inline bool IsCurrentFrameEmpty() const { return CurrentFrame->IsEmpty(); } template inline auto& GetFramesStack() { return Type == EVoxelUndoRedo::Undo ? UndoFramesStack : RedoFramesStack; } template inline const auto& GetFramesStack() const { return Type == EVoxelUndoRedo::Undo ? UndoFramesStack : RedoFramesStack; } public: template FORCEINLINE void SavePreviousValue(FVoxelCellIndex Index, T Value) { auto& AlreadyModifiedT = FVoxelUtilities::TValuesMaterialsSelector::Get(AlreadyModified); if (!AlreadyModifiedT.Test(Index)) { AlreadyModifiedT.Set(Index); FVoxelUtilities::TValuesMaterialsSelector::Get(*CurrentFrame).Emplace(Index, Value); } } private: template struct TModifiedValue { FVoxelCellIndex Index; T Value; TModifiedValue(FVoxelCellIndex Index, T Value) : Index(Index), Value(Value) {} }; struct FFrame { template explicit FFrame(const TLeaf& Leaf) : bValuesDirty(Leaf.Values.IsDirty()) , bMaterialsDirty(Leaf.Materials.IsDirty()) { } ~FFrame() { DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUndoRedoMemory, AllocatedSize); } int32 HistoryPosition = -1; const bool bValuesDirty; const bool bMaterialsDirty; TArray> Values; TArray> Materials; mutable uint32 AllocatedSize = 0; void UpdateStats() const; inline bool IsEmpty() const { return Values.Num() == 0 && Materials.Num() == 0; } }; struct FAlreadyModified { TVoxelStaticBitArray Values = ForceInit; TVoxelStaticBitArray Materials = ForceInit; }; FAlreadyModified AlreadyModified; TUniquePtr CurrentFrame; TArray> UndoFramesStack; TArray> RedoFramesStack; template void AddFrameToStack(TUniquePtr& Frame); };