예제 #1
0
        private void Editor_CreateHistory(GFoliagePainterArgs args)
        {
            if (!Editor_EnableHistory)
            {
                return;
            }
            if (EditedTerrains.Count == 0)
            {
                return;
            }

            List <GTerrainResourceFlag> flags = new List <GTerrainResourceFlag>();

            flags.AddRange(ActivePainter.GetResourceFlagForHistory(args));

            List <GStylizedTerrain> terrainList = new List <GStylizedTerrain>(EditedTerrains);
            string backupName = GBackup.TryCreateBackup(ActivePainter.HistoryPrefix, terrainList[0], flags, false);

            if (!string.IsNullOrEmpty(backupName))
            {
                for (int i = 1; i < terrainList.Count; ++i)
                {
                    GBackup.BackupTerrain(terrainList[i], backupName, flags);
                }
            }
        }
예제 #2
0
        private void Editor_CreateHistory(GFoliagePainterArgs args)
        {
            if (!Editor_EnableHistory)
            {
                return;
            }
            List <GTerrainResourceFlag> flags = new List <GTerrainResourceFlag>();

            flags.AddRange(ActivePainter.GetResourceFlagForHistory(args));
            GBackup.TryCreateBackup(ActivePainter.HistoryPrefix, GroupId, ActivePainter.GetResourceFlagForHistory(args), false);
        }
예제 #3
0
        public void Paint(GFoliagePainterArgs args)
        {
            IGFoliagePainter p = ActivePainter;

            if (p == null)
            {
                return;
            }

            args.Radius        = BrushRadius;
            args.Rotation      = BrushRotation;
            args.Density       = BrushDensity;
            args.EraseRatio    = EraseRatio;
            args.ScaleStrength = ScaleStrength;
            args.TreeIndices   = SelectedTreeIndices;
            args.GrassIndices  = SelectedGrassIndices;

            args.CustomArgs = CustomPainterArgs;
            if (SelectedBrushMaskIndex >= 0 && SelectedBrushMaskIndex < BrushMasks.Count)
            {
                args.Mask = BrushMasks[SelectedBrushMaskIndex];
            }
            args.Filters = GetComponents <GSpawnFilter>();

            ProcessBrushDynamic(ref args);
            Vector3[] corners = GCommon.GetBrushQuadCorners(args.HitPoint, args.Radius, args.Rotation);
            args.WorldPointCorners = corners;

#if UNITY_EDITOR
            if (args.MouseEventType == GPainterMouseEventType.Down &&
                args.ShouldCommitNow == false)
            {
                Editor_CreateInitialHistoryEntry(args);
            }
#endif

            IEnumerator <GStylizedTerrain> terrains = GStylizedTerrain.ActiveTerrains.GetEnumerator();
            while (terrains.MoveNext())
            {
                if (terrains.Current.GroupId != GroupId && GroupId >= 0)
                {
                    continue;
                }
                GStylizedTerrain t = terrains.Current;
                p.Paint(t, args);
            }

#if UNITY_EDITOR
            if (args.MouseEventType == GPainterMouseEventType.Up)
            {
                Editor_CreateHistory(args);
            }
#endif
        }
예제 #4
0
        public void Paint(Pinwheel.Griffin.GStylizedTerrain terrain, GFoliagePainterArgs args)
        {
            if (args.TreeIndices.Count == 0)
            {
                return;
            }
            if (terrain.TerrainData == null)
            {
                return;
            }
            if (terrain.TerrainData.Foliage.Trees == null)
            {
                return;
            }
            if (args.MouseEventType == GPainterMouseEventType.Up || args.ShouldCommitNow)
            {
                GCommon.SetDirty(terrain.TerrainData.Foliage);
                terrain.UpdateTreesPosition();
                terrain.TerrainData.Foliage.ClearTreeDirtyRegions();
                GRuntimeSettings.Instance.isEditingFoliage = false;
                return;
            }

            Vector2[] uvCorners = new Vector2[args.WorldPointCorners.Length];
            for (int i = 0; i < uvCorners.Length; ++i)
            {
                uvCorners[i] = terrain.WorldPointToUV(args.WorldPointCorners[i]);
            }

            Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);

            if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
            {
                return;
            }

            GRuntimeSettings.Instance.isEditingFoliage = true;
            if (args.ActionType == GPainterActionType.Normal)
            {
                HandleSpawnTree(terrain, args);
            }
            else
            {
                HandleEraseTree(terrain, args);
            }

            terrain.TerrainData.Foliage.SetTreeRegionDirty(dirtyRect);
            terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
            GUtilities.MarkCurrentSceneDirty();
        }
예제 #5
0
        private void ProcessBrushDynamic(ref GFoliagePainterArgs args)
        {
            Rand rand = GetRandomGenerator();

            args.Radius   -= BrushRadius * BrushRadiusJitter * (float)rand.NextDouble();
            args.Rotation += Mathf.Sign((float)rand.NextDouble() - 0.5f) * BrushRotation * BrushRotationJitter * (float)rand.NextDouble();
            args.Density  -= Mathf.RoundToInt(BrushDensity * BrushDensityJitter * (float)rand.NextDouble());

            Vector3 scatterDir = new Vector3((float)(rand.NextDouble() * 2 - 1), 0, (float)(rand.NextDouble() * 2 - 1)).normalized;
            float   scatterLengthMultiplier = BrushScatter - (float)rand.NextDouble() * BrushScatterJitter;
            float   scatterLength           = args.Radius * scatterLengthMultiplier;

            args.HitPoint += scatterDir * scatterLength;
        }
예제 #6
0
 private void OnSceneGUI()
 {
     HandleTerrainEditingInSceneView();
     HandleBrushSettingsShortcuts();
     HandleFunctionKeys();
     if (Event.current != null && Event.current.type == EventType.MouseMove)
     {
         SceneView.RepaintAll();
     }
     if (Event.current != null && Event.current.type == EventType.MouseLeaveWindow)
     {
         GFoliagePainterArgs args = new GFoliagePainterArgs();
         args.ShouldCommitNow = true;
         painter.Paint(args);
     }
 }
예제 #7
0
        private void Editor_CreateInitialHistoryEntry(GFoliagePainterArgs args, List <GStylizedTerrain> overlappedTerrains)
        {
            if (!Editor_EnableHistory)
            {
                return;
            }
            if (overlappedTerrains.Count == 0)
            {
                return;
            }

            List <GTerrainResourceFlag> flags = new List <GTerrainResourceFlag>();

            flags.AddRange(ActivePainter.GetResourceFlagForHistory(args));

            if (InitialRecordedTerrains.Count == 0)
            {
                currentInitialBackupName = GBackup.TryCreateInitialBackup(ActivePainter.HistoryPrefix, overlappedTerrains[0], flags, false);
                if (!string.IsNullOrEmpty(currentInitialBackupName))
                {
                    InitialRecordedTerrains.Add(overlappedTerrains[0]);
                }
            }
            else
            {
                if (!string.IsNullOrEmpty(currentInitialBackupName))
                {
                    for (int i = 0; i < overlappedTerrains.Count; ++i)
                    {
                        if (InitialRecordedTerrains.Contains(overlappedTerrains[i]))
                        {
                            continue;
                        }
                        GBackup.BackupTerrain(overlappedTerrains[i], currentInitialBackupName, flags);
                        InitialRecordedTerrains.Add(overlappedTerrains[i]);
                    }
                }
            }
        }
예제 #8
0
        private void Paint(RaycastHit hit)
        {
            GFoliagePainterArgs args = new GFoliagePainterArgs();

            args.HitPoint         = hit.point;
            args.Collider         = hit.collider;
            args.Transform        = hit.transform;
            args.UV               = hit.textureCoord;
            args.TriangleIndex    = hit.triangleIndex;
            args.BarycentricCoord = hit.barycentricCoordinate;
            args.Distance         = hit.distance;
            args.Normal           = hit.normal;
            args.LightMapCoord    = hit.lightmapCoord;

            args.MouseEventType =
                Event.current.type == EventType.MouseDown ? GPainterMouseEventType.Down :
                Event.current.type == EventType.MouseDrag ? GPainterMouseEventType.Drag :
                GPainterMouseEventType.Up;
            args.ActionType =
                Event.current.shift ? GPainterActionType.Alternative :
                Event.current.control ? GPainterActionType.Negative :
                GPainterActionType.Normal;
            painter.Paint(args);
        }
예제 #9
0
        private void HandleSpawnGrass(GStylizedTerrain terrain, GFoliagePainterArgs args)
        {
            int        grassIndex   = -1;
            Vector3    randomPos    = Vector3.zero;
            Vector3    rayOrigin    = Vector3.zero;
            Vector3    rayDirection = Vector3.down;
            float      sqrtTwo      = Mathf.Sqrt(2);
            Ray        ray          = new Ray();
            RaycastHit samplePoint;
            Vector3    bary0  = Vector3.zero;
            Vector3    bary1  = Vector3.zero;
            Vector2    maskUv = Vector2.zero;
            Vector2    samplePointTexcoord = Vector2.zero;
            Color      maskColor           = Color.white;
            Texture2D  clonedMask          = null;
            Texture2D  terrainMask         = null;

            if (args.Mask != null)
            {
                clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
            }
            if (args.EnableTerrainMask)
            {
                terrainMask = terrain.TerrainData.Mask.MaskMap;
            }

            int prototypeCount = terrain.TerrainData.Foliage.Grasses.Prototypes.Count;
            int sampleCount    = args.Density;
            List <GGrassInstance> newInstances = new List <GGrassInstance>();

            for (int i = 0; i < sampleCount; ++i)
            {
                grassIndex = args.GrassIndices[Random.Range(0, args.GrassIndices.Count)];
                if (grassIndex < 0 || grassIndex >= prototypeCount)
                {
                    continue;
                }
                randomPos = args.HitPoint + Random.insideUnitSphere * args.Radius * sqrtTwo;
                rayOrigin.Set(
                    randomPos.x,
                    10000,
                    randomPos.z);
                ray.origin    = rayOrigin;
                ray.direction = rayDirection;
                if (terrain.Raycast(ray, out samplePoint, float.MaxValue))
                {
                    GUtilities.CalculateBarycentricCoord(
                        new Vector2(samplePoint.point.x, samplePoint.point.z),
                        new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
                        new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
                        new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
                        ref bary0);
                    GUtilities.CalculateBarycentricCoord(
                        new Vector2(samplePoint.point.x, samplePoint.point.z),
                        new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
                        new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
                        new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
                        ref bary1);
                    if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
                    {
                        maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
                    }
                    else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
                    {
                        maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
                    }
                    else
                    {
                        continue;
                    }

                    //sample mask
                    if (clonedMask != null)
                    {
                        maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
                        if (Random.value > maskColor.grayscale)
                        {
                            continue;
                        }
                    }
                    //sample terrain mask
                    if (args.EnableTerrainMask)
                    {
                        samplePointTexcoord = samplePoint.textureCoord;
                        maskColor           = terrainMask.GetPixelBilinear(samplePointTexcoord.x, samplePointTexcoord.y);
                        if (Random.value < maskColor.r)
                        {
                            continue;
                        }
                    }

                    //apply filter
                    GSpawnFilterArgs filterArgs = GSpawnFilterArgs.Create();
                    filterArgs.Terrain         = terrain;
                    filterArgs.Position        = samplePoint.point;
                    filterArgs.SurfaceNormal   = samplePoint.normal;
                    filterArgs.SurfaceTexcoord = samplePoint.textureCoord;

                    List <Type> suitableFilter = SuitableFilterTypes;
                    if (args.Filters != null)
                    {
                        for (int fIndex = 0; fIndex < args.Filters.Length; ++fIndex)
                        {
                            if (args.Filters[fIndex] != null &&
                                args.Filters[fIndex].Ignore != true)
                            {
                                if (suitableFilter.Contains(args.Filters[fIndex].GetType()))
                                {
                                    args.Filters[fIndex].Apply(ref filterArgs);
                                }
                            }
                            if (filterArgs.ShouldExclude)
                            {
                                break;
                            }
                        }
                    }

                    //spawn
                    if (filterArgs.ShouldExclude)
                    {
                        continue;
                    }

                    GGrassInstance grass = GGrassInstance.Create(grassIndex);
                    grass.Position = terrain.WorldPointToNormalized(filterArgs.Position);
                    grass.Rotation = filterArgs.Rotation;
                    grass.Scale    = filterArgs.Scale;
                    newInstances.Add(grass);
                }
            }

            terrain.TerrainData.Foliage.AddGrassInstances(newInstances);
            if (clonedMask != null)
            {
                Object.DestroyImmediate(clonedMask);
            }
        }
예제 #10
0
 public List <GTerrainResourceFlag> GetResourceFlagForHistory(GFoliagePainterArgs args)
 {
     return(GCommon.GrassInstancesResourceFlags);
 }
예제 #11
0
        private void HandleEraseGrass(GStylizedTerrain terrain, GFoliagePainterArgs args)
        {
            Vector2[] uvCorners = new Vector2[args.WorldPointCorners.Length];
            for (int i = 0; i < uvCorners.Length; ++i)
            {
                uvCorners[i] = terrain.WorldPointToUV(args.WorldPointCorners[i]);
            }
            Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);

            int     grassIndex  = -1;
            Vector3 terrainSize = new Vector3(
                terrain.TerrainData.Geometry.Width,
                terrain.TerrainData.Geometry.Height,
                terrain.TerrainData.Geometry.Length);
            Vector3   localPos    = Vector3.zero;
            Vector3   worldPos    = Vector3.zero;
            Vector3   bary0       = Vector3.zero;
            Vector3   bary1       = Vector3.zero;
            Vector2   maskUv      = Vector2.zero;
            Color     maskColor   = Color.white;
            Texture2D clonedMask  = null;
            Texture2D terrainMask = null;

            if (args.Mask != null)
            {
                clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
            }
            if (args.EnableTerrainMask)
            {
                terrainMask = terrain.TerrainData.Mask.MaskMap;
            }

            GGrassPatch[] patches = terrain.TerrainData.Foliage.GrassPatches;

            for (int i = 0; i < patches.Length; ++i)
            {
                if (!patches[i].GetUvRange().Overlaps(dirtyRect))
                {
                    continue;
                }

                if (args.ActionType == GPainterActionType.Alternative)
                {
                    patches[i].RequireFullUpdate = true;
                }

                patches[i].RemoveInstances(grass =>
                {
                    grassIndex = args.GrassIndices[Random.Range(0, args.GrassIndices.Count)];
                    localPos.Set(
                        grass.position.x * terrainSize.x,
                        grass.position.y * terrainSize.y,
                        grass.position.z * terrainSize.z);
                    worldPos = terrain.transform.TransformPoint(localPos);
                    GUtilities.CalculateBarycentricCoord(
                        new Vector2(worldPos.x, worldPos.z),
                        new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
                        new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
                        new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
                        ref bary0);
                    GUtilities.CalculateBarycentricCoord(
                        new Vector2(worldPos.x, worldPos.z),
                        new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
                        new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
                        new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
                        ref bary1);
                    if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
                    {
                        maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
                    }
                    else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
                    {
                        maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
                    }
                    else
                    {
                        return(false);
                    }

                    //sample mask
                    if (clonedMask != null)
                    {
                        maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
                        if (Random.value > maskColor.grayscale * args.EraseRatio)
                        {
                            return(false);
                        }
                    }
                    //sample terrain mask
                    if (args.EnableTerrainMask)
                    {
                        maskColor = terrainMask.GetPixelBilinear(grass.position.x, grass.position.z);
                        if (Random.value < maskColor.r)
                        {
                            return(false);
                        }
                    }

                    if (args.ActionType == GPainterActionType.Negative &&
                        grass.PrototypeIndex == grassIndex)
                    {
                        return(true);
                    }
                    else if (args.ActionType == GPainterActionType.Alternative)
                    {
                        return(true);
                    }

                    return(false);
                });
            }

            if (clonedMask != null)
            {
                Object.DestroyImmediate(clonedMask);
            }
        }
예제 #12
0
        private void HandleEraseTree(GStylizedTerrain terrain, GFoliagePainterArgs args)
        {
            int     treeIndex   = -1;
            Vector3 terrainSize = new Vector3(
                terrain.TerrainData.Geometry.Width,
                terrain.TerrainData.Geometry.Height,
                terrain.TerrainData.Geometry.Length);
            Vector3   localPos    = Vector3.zero;
            Vector3   worldPos    = Vector3.zero;
            Vector3   bary0       = Vector3.zero;
            Vector3   bary1       = Vector3.zero;
            Vector2   maskUv      = Vector2.zero;
            Color     maskColor   = Color.white;
            Texture2D clonedMask  = null;
            Texture2D terrainMask = null;

            if (args.Mask != null)
            {
                clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
            }
            if (args.EnableTerrainMask)
            {
                terrainMask = terrain.TerrainData.Mask.MaskMap;
            }

            terrain.TerrainData.Foliage.RemoveTreeInstances(tree =>
            {
                treeIndex = args.TreeIndices[Random.Range(0, args.TreeIndices.Count)];
                localPos.Set(
                    tree.position.x * terrainSize.x,
                    tree.position.y * terrainSize.y,
                    tree.position.z * terrainSize.z);
                worldPos = terrain.transform.TransformPoint(localPos);
                GUtilities.CalculateBarycentricCoord(
                    new Vector2(worldPos.x, worldPos.z),
                    new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
                    new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
                    new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
                    ref bary0);
                GUtilities.CalculateBarycentricCoord(
                    new Vector2(worldPos.x, worldPos.z),
                    new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
                    new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
                    new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
                    ref bary1);
                if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
                {
                    maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
                }
                else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
                {
                    maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
                }
                else
                {
                    return(false);
                }

                //sample mask
                if (clonedMask != null)
                {
                    maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
                    if (Random.value > maskColor.grayscale * args.EraseRatio)
                    {
                        return(false);
                    }
                }

                //sample terrain mask
                if (args.EnableTerrainMask)
                {
                    maskColor = terrainMask.GetPixelBilinear(tree.position.x, tree.position.z);
                    if (Random.value < maskColor.r)
                    {
                        return(false);
                    }
                }

                if (args.ActionType == GPainterActionType.Negative &&
                    tree.PrototypeIndex == treeIndex)
                {
                    return(true);
                }
                else if (args.ActionType == GPainterActionType.Alternative)
                {
                    return(true);
                }

                return(false);
            });

            if (clonedMask != null)
            {
                Object.DestroyImmediate(clonedMask);
            }
        }
예제 #13
0
        public void Paint(Pinwheel.Griffin.GStylizedTerrain terrain, GFoliagePainterArgs args)
        {
            if (args.GrassIndices.Count == 0)
            {
                return;
            }
            if (terrain.TerrainData == null)
            {
                return;
            }
            if (terrain.TerrainData.Foliage.Grasses == null)
            {
                return;
            }
            if (args.MouseEventType == GPainterMouseEventType.Up || args.ShouldCommitNow)
            {
                terrain.TerrainData.SetDirty(GTerrainData.DirtyFlags.Foliage);
                GRuntimeSettings.Instance.isEditingFoliage = false;
                return;
            }

            Vector2[] uvCorners = new Vector2[args.WorldPointCorners.Length];
            for (int i = 0; i < uvCorners.Length; ++i)
            {
                uvCorners[i] = terrain.WorldPointToUV(args.WorldPointCorners[i]);
            }

            Rect dirtyRect = GUtilities.GetRectContainsPoints(uvCorners);

            if (!dirtyRect.Overlaps(new Rect(0, 0, 1, 1)))
            {
                return;
            }

            GRuntimeSettings.Instance.isEditingFoliage = true;
            Texture2D clonedMask  = null;
            Texture2D terrainMask = null;

            if (args.Mask != null)
            {
                clonedMask = GCommon.CloneAndResizeTexture(args.Mask, 256, 256);
            }
            if (args.EnableTerrainMask)
            {
                terrainMask = terrain.TerrainData.Mask.MaskMap;
            }

            int     multiplier  = args.ActionType == GPainterActionType.Normal ? 1 : -1;
            int     grassIndex  = -1;
            Vector3 terrainSize = new Vector3(
                terrain.TerrainData.Geometry.Width,
                terrain.TerrainData.Geometry.Height,
                terrain.TerrainData.Geometry.Length);
            Vector3 localPos  = Vector3.zero;
            Vector3 worldPos  = Vector3.zero;
            Vector3 bary0     = Vector3.zero;
            Vector3 bary1     = Vector3.zero;
            Vector2 maskUv    = Vector2.zero;
            Color   maskColor = Color.white;
            Vector3 scale     = Vector3.zero;

            GGrassPatch[] patches = terrain.TerrainData.Foliage.GrassPatches;
            for (int p = 0; p < patches.Length; ++p)
            {
                if (!patches[p].GetUvRange().Overlaps(dirtyRect))
                {
                    continue;
                }

                List <GGrassInstance> instances = patches[p].Instances;
                int instanceCount = instances.Count;
                for (int i = 0; i < instanceCount; ++i)
                {
                    grassIndex = args.GrassIndices[Random.Range(0, args.GrassIndices.Count)];
                    GGrassInstance grass = instances[i];
                    if (grass.PrototypeIndex != grassIndex)
                    {
                        continue;
                    }

                    localPos.Set(
                        grass.position.x * terrainSize.x,
                        grass.position.y * terrainSize.y,
                        grass.position.z * terrainSize.z);
                    worldPos = terrain.transform.TransformPoint(localPos);
                    GUtilities.CalculateBarycentricCoord(
                        new Vector2(worldPos.x, worldPos.z),
                        new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
                        new Vector2(args.WorldPointCorners[1].x, args.WorldPointCorners[1].z),
                        new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
                        ref bary0);
                    GUtilities.CalculateBarycentricCoord(
                        new Vector2(worldPos.x, worldPos.z),
                        new Vector2(args.WorldPointCorners[0].x, args.WorldPointCorners[0].z),
                        new Vector2(args.WorldPointCorners[2].x, args.WorldPointCorners[2].z),
                        new Vector2(args.WorldPointCorners[3].x, args.WorldPointCorners[3].z),
                        ref bary1);
                    if (bary0.x >= 0 && bary0.y >= 0 && bary0.z >= 0)
                    {
                        maskUv = bary0.x * Vector2.zero + bary0.y * Vector2.up + bary0.z * Vector2.one;
                    }
                    else if (bary1.x >= 0 && bary1.y >= 0 && bary1.z >= 0)
                    {
                        maskUv = bary1.x * Vector2.zero + bary1.y * Vector2.one + bary1.z * Vector2.right;
                    }
                    else
                    {
                        continue;
                    }

                    if (clonedMask != null)
                    {
                        maskColor = clonedMask.GetPixelBilinear(maskUv.x, maskUv.y);
                        if (Random.value > maskColor.grayscale)
                        {
                            continue;
                        }
                    }
                    //sample terrain mask
                    if (args.EnableTerrainMask)
                    {
                        maskColor = terrainMask.GetPixelBilinear(grass.position.x, grass.position.z);
                        if (Random.value < maskColor.r)
                        {
                            continue;
                        }
                    }

                    scale.Set(
                        Mathf.Max(0, grass.scale.x + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME),
                        Mathf.Max(0, grass.scale.y + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME),
                        Mathf.Max(0, grass.scale.z + multiplier * maskColor.grayscale * args.ScaleStrength * GUtilities.DELTA_TIME));

                    GSpawnFilterArgs filterArgs = GSpawnFilterArgs.Create();
                    filterArgs.Terrain  = terrain;
                    filterArgs.Position = worldPos;
                    filterArgs.Rotation = grass.Rotation;
                    filterArgs.Scale    = scale;
                    List <Type> suitableFilter = SuitableFilterTypes;
                    if (args.Filters != null)
                    {
                        for (int fIndex = 0; fIndex < args.Filters.Length; ++fIndex)
                        {
                            if (args.Filters[fIndex] != null &&
                                args.Filters[fIndex].Ignore != true)
                            {
                                if (suitableFilter.Contains(args.Filters[fIndex].GetType()))
                                {
                                    args.Filters[fIndex].Apply(ref filterArgs);
                                }
                            }
                            if (filterArgs.ShouldExclude)
                            {
                                break;
                            }
                        }
                    }

                    grass.scale  = filterArgs.Scale;
                    instances[i] = grass;
                }
                patches[p].Changed();
            }

            terrain.TerrainData.Foliage.SetGrassRegionDirty(dirtyRect);
            GUtilities.MarkCurrentSceneDirty();
        }