#if GRIFFIN using System.Collections.Generic; using UnityEngine; using Unity.Burst; using Unity.Collections; using Unity.Jobs; using UnityEngine.Rendering; namespace Pinwheel.Griffin.SplineTool { [GDisplayName("Foliage Remover")] public class GFoliageRemover : GSplineModifier { [SerializeField] private AnimationCurve falloff; public AnimationCurve Falloff { get { if (falloff == null) { falloff = AnimationCurve.EaseInOut(0, 0, 1, 1); } return falloff; } set { falloff = value; } } [SerializeField] private bool removeTrees; public bool RemoveTrees { get { return removeTrees; } set { removeTrees = value; } } [SerializeField] private bool removeGrasses; public bool RemoveGrasses { get { return removeGrasses; } set { removeGrasses = value; } } [SerializeField] private List treePrototypeIndices; public List TreePrototypeIndices { get { if (treePrototypeIndices == null) { treePrototypeIndices = new List(); } return treePrototypeIndices; } set { treePrototypeIndices = value; } } [SerializeField] private List grassPrototypeIndices; public List GrassPrototypeIndices { get { if (grassPrototypeIndices == null) { grassPrototypeIndices = new List(); } return grassPrototypeIndices; } set { grassPrototypeIndices = value; } } [SerializeField] private Texture2D falloffNoise; public Texture2D FalloffNoise { get { return falloffNoise; } set { falloffNoise = value; } } [SerializeField] private Vector2 falloffNoiseSize; public Vector2 FalloffNoiseSize { get { return falloffNoiseSize; } set { falloffNoiseSize = value; } } [SerializeField] private int maskResolution; public int MaskResolution { get { return maskResolution; } set { maskResolution = Mathf.Clamp(Mathf.ClosestPowerOfTwo(value), GCommon.TEXTURE_SIZE_MIN, GCommon.TEXTURE_SIZE_MAX); } } private Texture2D falloffTexture; private Material maskMaterial; private Material MaskMaterial { get { if (maskMaterial == null) { maskMaterial = new Material(GRuntimeSettings.Instance.internalShaders.splineMaskShader); } return maskMaterial; } } private static readonly int FALL_OFF = Shader.PropertyToID("_Falloff"); private static readonly int FALL_OFF_NOISE = Shader.PropertyToID("_FalloffNoise"); private static readonly int TERRAIN_MASK = Shader.PropertyToID("_TerrainMask"); public override void Apply() { if (SplineCreator == null) return; if (falloffTexture != null) Object.DestroyImmediate(falloffTexture); Internal_UpdateFalloffTexture(); List terrains = GUtilities.ExtractTerrainsFromOverlapTest(SplineCreator.SweepTest()); foreach (GStylizedTerrain t in terrains) { Apply(t); } } private void Apply(GStylizedTerrain t) { if (t.TerrainData == null) return; RenderTexture rt = new RenderTexture(MaskResolution, MaskResolution, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear); Internal_Apply(t, rt); Texture2D mask = GCommon.CreateTexture(MaskResolution, Color.clear); GCommon.CopyFromRT(mask, rt); mask.wrapMode = TextureWrapMode.Clamp; if (RemoveTrees) { RemoveTreeOnTerrain(t, mask); } if (RemoveGrasses) { RemoveGrassOnTerrain(t, mask); } t.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage); rt.Release(); GUtilities.DestroyObject(rt); GUtilities.DestroyObject(mask); } private void RemoveTreeOnTerrain(GStylizedTerrain t, Texture2D mask) { NativeArray positionsNA = t.TerrainData.Foliage.GetTreesPositionArray(); NativeArray resultNA = new NativeArray(positionsNA.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); GTextureNativeDataDescriptor maskNA = new GTextureNativeDataDescriptor(mask); GMaskCullingJob job = new GMaskCullingJob() { positions = positionsNA, result = resultNA, mask = maskNA }; JobHandle jHandle = job.Schedule(positionsNA.Length, 100); jHandle.Complete(); HashSet selectedPrototypes = new HashSet(TreePrototypeIndices); int index = 0; t.TerrainData.Foliage.RemoveTreeInstances(tree => { int i = index; index += 1; if (!selectedPrototypes.Contains(tree.prototypeIndex)) return false; return resultNA[i]; }); positionsNA.Dispose(); resultNA.Dispose(); } private void RemoveGrassOnTerrain(GStylizedTerrain t, Texture2D mask) { GGrassPatch[] patches = t.TerrainData.Foliage.GrassPatches; GMaskCullingDataHolder[] data = new GMaskCullingDataHolder[patches.Length]; JobHandle[] jHandles = new JobHandle[patches.Length]; GTextureNativeDataDescriptor maskNA = new GTextureNativeDataDescriptor(mask); for (int i = 0; i < patches.Length; ++i) { GMaskCullingDataHolder d = new GMaskCullingDataHolder(); d.positionsNA = patches[i].GetGrassPositionArray(); d.resultNA = new NativeArray(d.positionsNA.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); data[i] = d; GMaskCullingJob job = new GMaskCullingJob() { positions = d.positionsNA, result = d.resultNA, mask = maskNA }; JobHandle h = job.Schedule(d.positionsNA.Length, 100); jHandles[i] = h; } HashSet selectedPrototypes = new HashSet(GrassPrototypeIndices); for (int i = 0; i < patches.Length; ++i) { JobHandle h = jHandles[i]; h.Complete(); GMaskCullingDataHolder d = data[i]; int index = 0; patches[i].RemoveInstances(g => { int indexClone = index; index += 1; if (!selectedPrototypes.Contains(g.prototypeIndex)) return false; return d.resultNA[indexClone]; }); d.positionsNA.Dispose(); d.resultNA.Dispose(); } } private Material PrepareMaterial(GStylizedTerrain t, RenderTexture rt) { GCommon.ClearRT(rt); Material mat = MaskMaterial; mat.SetTexture(FALL_OFF, falloffTexture); mat.SetTexture(FALL_OFF_NOISE, FalloffNoise != null ? FalloffNoise : Texture2D.blackTexture); mat.SetTextureScale(FALL_OFF_NOISE, new Vector2( FalloffNoiseSize.x != 0 ? 1f / FalloffNoiseSize.x : 0, FalloffNoiseSize.y != 0 ? 1f / FalloffNoiseSize.y : 0)); mat.SetTextureOffset(FALL_OFF_NOISE, Vector2.zero); if (SplineCreator.EnableTerrainMask) { mat.SetTexture(TERRAIN_MASK, t.TerrainData.Mask.MaskMapOrDefault); } else { mat.SetTexture(TERRAIN_MASK, Texture2D.blackTexture); } return mat; } public void Internal_Apply(GStylizedTerrain t, RenderTexture rt) { Material mat = PrepareMaterial(t, rt); SplineCreator.DrawOnTexture(rt, t.Bounds, mat); } public void Internal_Apply(GStylizedTerrain t, RenderTexture rt, ScriptableRenderContext context) { Material mat = PrepareMaterial(t, rt); SplineCreator.DrawOnTexture(rt, t.Bounds, mat, context); } public void Reset() { SplineCreator = GetComponent(); Falloff = AnimationCurve.EaseInOut(0, 0, 1, 1); RemoveTrees = true; RemoveGrasses = true; TreePrototypeIndices = null; GrassPrototypeIndices = null; MaskResolution = 1024; } public void Internal_UpdateFalloffTexture() { falloffTexture = GCommon.CreateTextureFromCurve(Falloff, 256, 1); } } } #endif