// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using FlaxEngine; namespace FlaxEditor.Tools.Terrain.Paint { /// /// Paint tool mode. Edits terrain splatmap by painting with the single layer on top of the others. /// /// [HideInEditor] public sealed class SingleLayerMode : Mode { /// /// The paint layers. /// public enum Layers { /// /// The layer 0. /// Layer0, /// /// The layer 0. /// Layer1, /// /// The layer 2. /// Layer2, /// /// The layer 3. /// Layer3, /// /// The layer 4. /// Layer4, /// /// The layer 5. /// Layer5, /// /// The layer 6. /// Layer6, /// /// The layer 7. /// Layer7, } /// /// The layer to paint with it. /// [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; /// public override int ActiveSplatmapIndex => (int)Layer < 4 ? 0 : 1; /// 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); } } }