You're breathtaking!
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Tools.Terrain.Undo
|
||||
{
|
||||
/// <summary>
|
||||
/// The terrain heightmap editing action that records before and after states to swap between unmodified and modified terrain data.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.IUndoAction" />
|
||||
/// <seealso cref="EditTerrainMapAction" />
|
||||
[Serializable]
|
||||
unsafe class EditTerrainHeightMapAction : EditTerrainMapAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EditTerrainHeightMapAction"/> class.
|
||||
/// </summary>
|
||||
/// <param name="terrain">The terrain.</param>
|
||||
public EditTerrainHeightMapAction(FlaxEngine.Terrain terrain)
|
||||
: base(terrain, sizeof(float))
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ActionString => "Edit terrain heightmap";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IntPtr GetData(ref Int2 patchCoord, object tag)
|
||||
{
|
||||
return new IntPtr(TerrainTools.GetHeightmapData(Terrain, ref patchCoord));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SetData(ref Int2 patchCoord, IntPtr data, object tag)
|
||||
{
|
||||
var offset = Int2.Zero;
|
||||
var size = new Int2((int)Mathf.Sqrt(_heightmapLength));
|
||||
if (TerrainTools.ModifyHeightMap(Terrain, ref patchCoord, (float*)data, ref offset, ref size))
|
||||
throw new FlaxException("Failed to modify the heightmap.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Tools.Terrain.Undo
|
||||
{
|
||||
/// <summary>
|
||||
/// The terrain holes mask editing action that records before and after states to swap between unmodified and modified terrain data.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.IUndoAction" />
|
||||
/// <seealso cref="EditTerrainMapAction" />
|
||||
[Serializable]
|
||||
unsafe class EditTerrainHolesMapAction : EditTerrainMapAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EditTerrainHolesMapAction"/> class.
|
||||
/// </summary>
|
||||
/// <param name="terrain">The terrain.</param>
|
||||
public EditTerrainHolesMapAction(FlaxEngine.Terrain terrain)
|
||||
: base(terrain, sizeof(byte))
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ActionString => "Edit terrain holes";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IntPtr GetData(ref Int2 patchCoord, object tag)
|
||||
{
|
||||
return new IntPtr(TerrainTools.GetHolesMaskData(Terrain, ref patchCoord));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SetData(ref Int2 patchCoord, IntPtr data, object tag)
|
||||
{
|
||||
var offset = Int2.Zero;
|
||||
var size = new Int2((int)Mathf.Sqrt(_heightmapLength));
|
||||
if (TerrainTools.ModifyHolesMask(Terrain, ref patchCoord, (byte*)data, ref offset, ref size))
|
||||
throw new FlaxException("Failed to modify the terrain holes.");
|
||||
}
|
||||
}
|
||||
}
|
||||
216
Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs
Normal file
216
Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs
Normal file
@@ -0,0 +1,216 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Tools.Terrain.Undo
|
||||
{
|
||||
/// <summary>
|
||||
/// The terrain heightmap or visibility map editing action that records before and after states to swap between unmodified and modified terrain data.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.IUndoAction" />
|
||||
[Serializable]
|
||||
abstract class EditTerrainMapAction : IUndoAction
|
||||
{
|
||||
/// <summary>
|
||||
/// The compact data for the terrain patch modification.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
protected struct PatchData
|
||||
{
|
||||
/// <summary>
|
||||
/// The patch coordinates.
|
||||
/// </summary>
|
||||
public Int2 PatchCoord;
|
||||
|
||||
/// <summary>
|
||||
/// The data before (allocated on heap memory or null).
|
||||
/// </summary>
|
||||
public IntPtr Before;
|
||||
|
||||
/// <summary>
|
||||
/// The data after (allocated on heap memory or null).
|
||||
/// </summary>
|
||||
public IntPtr After;
|
||||
|
||||
/// <summary>
|
||||
/// The custom tag.
|
||||
/// </summary>
|
||||
public object Tag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The terrain (actor Id).
|
||||
/// </summary>
|
||||
[Serialize]
|
||||
protected readonly Guid _terrain;
|
||||
|
||||
/// <summary>
|
||||
/// The heightmap length (vertex count).
|
||||
/// </summary>
|
||||
[Serialize]
|
||||
protected readonly int _heightmapLength;
|
||||
|
||||
/// <summary>
|
||||
/// The heightmap data size (in bytes).
|
||||
/// </summary>
|
||||
[Serialize]
|
||||
protected readonly int _heightmapDataSize;
|
||||
|
||||
/// <summary>
|
||||
/// The terrain patches
|
||||
/// </summary>
|
||||
[Serialize]
|
||||
protected readonly List<PatchData> _patches;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this action has any modification to the terrain (recorded patches changes).
|
||||
/// </summary>
|
||||
[NoSerialize]
|
||||
public bool HasAnyModification => _patches.Count > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the terrain.
|
||||
/// </summary>
|
||||
[NoSerialize]
|
||||
public FlaxEngine.Terrain Terrain
|
||||
{
|
||||
get
|
||||
{
|
||||
var terrainId = _terrain;
|
||||
return FlaxEngine.Object.Find<FlaxEngine.Terrain>(ref terrainId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EditTerrainMapAction"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>Use <see cref="AddPatch"/> to mark new patches to record and <see cref="OnEditingEnd"/> to finalize patches data after editing action.</remarks>
|
||||
/// <param name="terrain">The terrain.</param>
|
||||
/// <param name="stride">The data stride (eg. sizeof(float)).</param>
|
||||
protected EditTerrainMapAction(FlaxEngine.Terrain terrain, int stride)
|
||||
{
|
||||
_terrain = terrain.ID;
|
||||
_patches = new List<PatchData>(4);
|
||||
var chunkSize = terrain.ChunkSize;
|
||||
var heightmapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1;
|
||||
_heightmapLength = heightmapSize * heightmapSize;
|
||||
_heightmapDataSize = _heightmapLength * stride;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the patch at the given coordinates has been already added.
|
||||
/// </summary>
|
||||
/// <param name="patchCoord">The patch coordinates.</param>
|
||||
/// <returns>True if patch has been added, otherwise false.</returns>
|
||||
public bool HashPatch(ref Int2 patchCoord)
|
||||
{
|
||||
for (int i = 0; i < _patches.Count; i++)
|
||||
{
|
||||
if (_patches[i].PatchCoord == patchCoord)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the patch to the action and records its current state.
|
||||
/// </summary>
|
||||
/// <param name="patchCoord">The patch coordinates.</param>
|
||||
/// <param name="tag">The custom argument (per patch).</param>
|
||||
public void AddPatch(ref Int2 patchCoord, object tag = null)
|
||||
{
|
||||
var data = Marshal.AllocHGlobal(_heightmapDataSize);
|
||||
var source = GetData(ref patchCoord, tag);
|
||||
Utils.MemoryCopy(source, data, _heightmapDataSize);
|
||||
_patches.Add(new PatchData
|
||||
{
|
||||
PatchCoord = patchCoord,
|
||||
Before = data,
|
||||
After = IntPtr.Zero,
|
||||
Tag = tag,
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when terrain action editing ends. Record the `after` state of the patches.
|
||||
/// </summary>
|
||||
public void OnEditingEnd()
|
||||
{
|
||||
if (_patches.Count == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < _patches.Count; i++)
|
||||
{
|
||||
var patch = _patches[i];
|
||||
if (patch.After != IntPtr.Zero)
|
||||
throw new InvalidOperationException("Invalid terrain edit undo action usage.");
|
||||
|
||||
var data = Marshal.AllocHGlobal(_heightmapDataSize);
|
||||
var source = GetData(ref patch.PatchCoord, patch.Tag);
|
||||
Utils.MemoryCopy(source, data, _heightmapDataSize);
|
||||
patch.After = data;
|
||||
_patches[i] = patch;
|
||||
}
|
||||
|
||||
Editor.Instance.Scene.MarkSceneEdited(Terrain.Scene);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string ActionString { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Do()
|
||||
{
|
||||
Set(x => x.After);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Undo()
|
||||
{
|
||||
Set(x => x.Before);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
// Ensure to release memory
|
||||
for (int i = 0; i < _patches.Count; i++)
|
||||
{
|
||||
Marshal.FreeHGlobal(_patches[i].Before);
|
||||
Marshal.FreeHGlobal(_patches[i].After);
|
||||
}
|
||||
_patches.Clear();
|
||||
}
|
||||
|
||||
private void Set(Func<PatchData, IntPtr> dataGetter)
|
||||
{
|
||||
for (int i = 0; i < _patches.Count; i++)
|
||||
{
|
||||
var patch = _patches[i];
|
||||
var data = dataGetter(patch);
|
||||
SetData(ref patch.PatchCoord, data, patch.Tag);
|
||||
}
|
||||
|
||||
Editor.Instance.Scene.MarkSceneEdited(Terrain.Scene);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the patch data.
|
||||
/// </summary>
|
||||
/// <param name="patchCoord">The patch coordinates.</param>
|
||||
/// <param name="tag">The custom argument (per patch).</param>
|
||||
/// <returns>The data buffer (pointer to unmanaged memory).</returns>
|
||||
protected abstract IntPtr GetData(ref Int2 patchCoord, object tag);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the patch data.
|
||||
/// </summary>
|
||||
/// <param name="patchCoord">The patch coordinates.</param>
|
||||
/// <param name="data">The patch data.</param>
|
||||
/// <param name="tag">The custom argument (per patch).</param>
|
||||
protected abstract void SetData(ref Int2 patchCoord, IntPtr data, object tag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Tools.Terrain.Undo
|
||||
{
|
||||
/// <summary>
|
||||
/// The terrain splatmap editing action that records before and after states to swap between unmodified and modified terrain data.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.IUndoAction" />
|
||||
/// <seealso cref="EditTerrainMapAction" />
|
||||
[Serializable]
|
||||
unsafe class EditTerrainSplatMapAction : EditTerrainMapAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EditTerrainSplatMapAction"/> class.
|
||||
/// </summary>
|
||||
/// <param name="terrain">The terrain.</param>
|
||||
public EditTerrainSplatMapAction(FlaxEngine.Terrain terrain)
|
||||
: base(terrain, Color32.SizeInBytes)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ActionString => "Edit terrain splatmap";
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override IntPtr GetData(ref Int2 patchCoord, object tag)
|
||||
{
|
||||
return new IntPtr(TerrainTools.GetSplatMapData(Terrain, ref patchCoord, (int)tag));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SetData(ref Int2 patchCoord, IntPtr data, object tag)
|
||||
{
|
||||
var offset = Int2.Zero;
|
||||
var size = new Int2((int)Mathf.Sqrt(_heightmapLength));
|
||||
if (TerrainTools.ModifySplatMap(Terrain, ref patchCoord, (int)tag, (Color32*)data, ref offset, ref size))
|
||||
throw new FlaxException("Failed to modify the splatmap.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user