Files
FlaxEngine/Source/Editor/Tools/Terrain/Paint/SingleLayerMode.cs
2024-02-17 11:36:13 +01:00

125 lines
4.6 KiB
C#

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using FlaxEngine;
namespace FlaxEditor.Tools.Terrain.Paint
{
/// <summary>
/// Paint tool mode. Edits terrain splatmap by painting with the single layer on top of the others.
/// </summary>
/// <seealso cref="FlaxEditor.Tools.Terrain.Paint.Mode" />
[HideInEditor]
public sealed class SingleLayerMode : Mode
{
/// <summary>
/// The paint layers.
/// </summary>
public enum Layers
{
/// <summary>
/// The layer 0.
/// </summary>
Layer0,
/// <summary>
/// The layer 0.
/// </summary>
Layer1,
/// <summary>
/// The layer 2.
/// </summary>
Layer2,
/// <summary>
/// The layer 3.
/// </summary>
Layer3,
/// <summary>
/// The layer 4.
/// </summary>
Layer4,
/// <summary>
/// The layer 5.
/// </summary>
Layer5,
/// <summary>
/// The layer 6.
/// </summary>
Layer6,
/// <summary>
/// The layer 7.
/// </summary>
Layer7,
}
/// <summary>
/// The layer to paint with it.
/// </summary>
[EditorOrder(10), Tooltip("The layer to paint with it. Terrain material can access per-layer blend weight to perform materials or textures blending.")]
public Layers Layer = Layers.Layer0;
/// <inheritdoc />
public override int ActiveSplatmapIndex => (int)Layer < 4 ? 0 : 1;
/// <inheritdoc />
public override unsafe void Apply(ref ApplyParams p)
{
var strength = p.Strength;
var layer = (int)Layer;
var brushPosition = p.Gizmo.CursorPosition;
var c = layer % 4;
// Apply brush modification
Profiler.BeginEvent("Apply Brush");
bool otherModified = false;
for (int z = 0; z < p.ModifiedSize.Y; z++)
{
var zz = z + p.ModifiedOffset.Y;
for (int x = 0; x < p.ModifiedSize.X; x++)
{
var xx = x + p.ModifiedOffset.X;
var src = (Color)p.SourceData[zz * p.HeightmapSize + xx];
var samplePositionLocal = p.PatchPositionLocal + new Vector3(xx * FlaxEngine.Terrain.UnitsPerVertex, 0, zz * FlaxEngine.Terrain.UnitsPerVertex);
Vector3.Transform(ref samplePositionLocal, ref p.TerrainWorld, out Vector3 samplePositionWorld);
var sample = Mathf.Saturate(p.Brush.Sample(ref brushPosition, ref samplePositionWorld));
var paintAmount = sample * strength;
if (paintAmount < 0.0f)
continue; // Skip when pixel won't be affected
// Paint on the active splatmap texture
src[c] = Mathf.Saturate(src[c] + paintAmount);
src[(c + 1) % 4] = Mathf.Saturate(src[(c + 1) % 4] - paintAmount);
src[(c + 2) % 4] = Mathf.Saturate(src[(c + 2) % 4] - paintAmount);
src[(c + 3) % 4] = Mathf.Saturate(src[(c + 3) % 4] - paintAmount);
p.TempBuffer[z * p.ModifiedSize.X + x] = src;
var other = (Color)p.SourceDataOther[zz * p.HeightmapSize + xx];
//if (other.ValuesSum > 0.0f) // Skip editing the other splatmap if it's empty
{
// Remove 'paint' from the other splatmap texture
other[c] = Mathf.Saturate(other[c] - paintAmount);
other[(c + 1) % 4] = Mathf.Saturate(other[(c + 1) % 4] - paintAmount);
other[(c + 2) % 4] = Mathf.Saturate(other[(c + 2) % 4] - paintAmount);
other[(c + 3) % 4] = Mathf.Saturate(other[(c + 3) % 4] - paintAmount);
p.TempBufferOther[z * p.ModifiedSize.X + x] = other;
otherModified = true;
}
}
}
Profiler.EndEvent();
// Update terrain patch
TerrainTools.ModifySplatMap(p.Terrain, ref p.PatchCoord, p.SplatmapIndex, p.TempBuffer, ref p.ModifiedOffset, ref p.ModifiedSize);
if (otherModified)
TerrainTools.ModifySplatMap(p.Terrain, ref p.PatchCoord, p.SplatmapIndexOther, p.TempBufferOther, ref p.ModifiedOffset, ref p.ModifiedSize);
}
}
}