// Copyright 2020 Phyronnaz #include "VoxelData/VoxelDataOctreeLeafUndoRedo.h" #include "VoxelData/VoxelDataOctree.h" FVoxelDataOctreeLeafUndoRedo::FVoxelDataOctreeLeafUndoRedo(const FVoxelDataOctreeLeaf& Leaf) : CurrentFrame(MakeUnique(Leaf)) { INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUndoRedoMemory, sizeof(FVoxelDataOctreeLeafUndoRedo)); } FVoxelDataOctreeLeafUndoRedo::~FVoxelDataOctreeLeafUndoRedo() { DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUndoRedoMemory, sizeof(FVoxelDataOctreeLeafUndoRedo)); } void FVoxelDataOctreeLeafUndoRedo::ClearFrames(const FVoxelDataOctreeLeaf& Leaf) { CurrentFrame = MakeUnique(Leaf); UndoFramesStack.Empty(); RedoFramesStack.Empty(); } void FVoxelDataOctreeLeafUndoRedo::SaveFrame(const FVoxelDataOctreeLeaf& Leaf, int32 HistoryPosition) { VOXEL_SLOW_FUNCTION_COUNTER(); if (!CurrentFrame->IsEmpty()) { CurrentFrame->HistoryPosition = HistoryPosition; AddFrameToStack(CurrentFrame); check(!CurrentFrame); CurrentFrame = MakeUnique(Leaf); AlreadyModified.Values.Clear(); AlreadyModified.Materials.Clear(); } if (RedoFramesStack.Num() > 0) { RedoFramesStack.Empty(); } } template void FVoxelDataOctreeLeafUndoRedo::ClearFramesOfType() { const auto ClearFrame = [](FFrame& Frame) { FVoxelUtilities::TValuesMaterialsSelector::Get(Frame).Empty(); }; ClearFrame(*CurrentFrame); for (auto& Frame : UndoFramesStack) { ClearFrame(*Frame); } for (auto& Frame : RedoFramesStack) { ClearFrame(*Frame); } } template VOXEL_API void FVoxelDataOctreeLeafUndoRedo::ClearFramesOfType(); template VOXEL_API void FVoxelDataOctreeLeafUndoRedo::ClearFramesOfType(); template void FVoxelDataOctreeLeafUndoRedo::UndoRedo(const IVoxelData& Data, FVoxelDataOctreeLeaf& Leaf, int32 HistoryPosition) { check(CurrentFrame->IsEmpty()); check(CanUndoRedo(HistoryPosition)); const TUniquePtr Frame = GetFramesStack().Pop(false); check(Frame->HistoryPosition == HistoryPosition); TUniquePtr NewFrame = MakeUnique(Leaf); // If Type is Undo NewFrame is a redo frame, so + 1. Else it's an undo frame so -1 NewFrame->HistoryPosition = HistoryPosition + (Type == EVoxelUndoRedo::Undo ? 1 : -1); check(!Frame->IsEmpty()); const auto Apply = [&](auto TypeInst) { using T = decltype(TypeInst); const TArray>& FrameData = FVoxelUtilities::TValuesMaterialsSelector::Get(*Frame); TArray>& NewFrameData = FVoxelUtilities::TValuesMaterialsSelector::Get(*NewFrame); TVoxelDataOctreeLeafData& DataHolder = FVoxelUtilities::TValuesMaterialsSelector::Get(Leaf); if (FrameData.Num() == 0) return; if (!DataHolder.HasData()) { // Data was reverted to generator value DataHolder.CreateData(Data, [&](T* RESTRICT DataPtr) { TVoxelQueryZone QueryZone(Leaf.GetBounds(), DataPtr); Leaf.GetFromGeneratorAndAssets(*Data.Generator, QueryZone, 0); }); } DataHolder.PrepareForWrite(Data); NewFrameData.SetNumUninitialized(FrameData.Num()); const TModifiedValue* RESTRICT const FrameDataPtr = FrameData.GetData(); TModifiedValue* RESTRICT const NewFrameDataPtr = NewFrameData.GetData(); checkVoxelSlow(FrameDataPtr); checkVoxelSlow(NewFrameDataPtr); for (int32 Index = 0; Index < FrameData.Num(); Index++) { checkVoxelSlow(FrameData.IsValidIndex(Index)); checkVoxelSlow(NewFrameData.IsValidIndex(Index)); const auto ModifiedValue = FrameDataPtr[Index]; auto& ValueRef = DataHolder.GetRef(ModifiedValue.Index); NewFrameDataPtr[Index] = TModifiedValue(ModifiedValue.Index, ValueRef); ValueRef = ModifiedValue.Value; } if (TIsSame::Value) DataHolder.SetIsDirty(Frame->bValuesDirty, Data); if (TIsSame::Value) DataHolder.SetIsDirty(Frame->bMaterialsDirty, Data); }; Apply(FVoxelValue()); Apply(FVoxelMaterial()); AddFrameToStack(NewFrame); } template VOXEL_API void FVoxelDataOctreeLeafUndoRedo::UndoRedo(const IVoxelData&, FVoxelDataOctreeLeaf&, int32); template VOXEL_API void FVoxelDataOctreeLeafUndoRedo::UndoRedo(const IVoxelData&, FVoxelDataOctreeLeaf&, int32); void FVoxelDataOctreeLeafUndoRedo::FFrame::UpdateStats() const { DEC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUndoRedoMemory, AllocatedSize); AllocatedSize = sizeof(FFrame) + Values.GetAllocatedSize() + Materials.GetAllocatedSize(); INC_VOXEL_MEMORY_STAT_BY(STAT_VoxelUndoRedoMemory, AllocatedSize); } template void FVoxelDataOctreeLeafUndoRedo::AddFrameToStack(TUniquePtr& Frame) { { VOXEL_SLOW_SCOPE_COUNTER("Shrink"); Frame->Values.Shrink(); Frame->Materials.Shrink(); } Frame->UpdateStats(); GetFramesStack().Add(MoveTemp(Frame)); check(!Frame); }