using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; [RequireComponent(typeof(MeshFilter))] [ExecuteInEditMode] public class GrassPainter : MonoBehaviour { public Mesh mesh; MeshFilter filter; public Color AdjustedColor; [Range(1, 600000)] public int grassLimit = 50000; private Vector3 lastPosition = Vector3.zero; public int toolbarInt = 0; public int toolbarIntEdit = 0; [SerializeField] List positions = new List(); [SerializeField] List colors = new List(); [SerializeField] List indicies = new List(); [SerializeField] List normals = new List(); [SerializeField] List length = new List(); public int i = 0; public float sizeWidth = 1f; public float sizeLength = 1f; public float density = 1f; public float normalLimit = 1; public float rangeR, rangeG, rangeB; public LayerMask hitMask = 1; public LayerMask paintMask = 1; public float brushSize; public float brushFalloffSize; public float Flow; private int flowTimer; Vector3 mousePos; [HideInInspector] public Vector3 hitPosGizmo; Vector3 hitPos; [HideInInspector] public Vector3 hitNormal; int[] indi; #if UNITY_EDITOR void OnFocus() { // Remove delegate listener if it has previously // been assigned. SceneView.duringSceneGui -= this.OnScene; // Add (or re-add) the delegate. SceneView.duringSceneGui += this.OnScene; } public void SaveAsset() { if (filter) { string s = System.Guid.NewGuid().ToString(); string savePath = "Assets/CustomEffects/GrassMesh/" + gameObject.name + s + ".asset"; Debug.Log("Saved Mesh to:" + savePath); AssetDatabase.CreateAsset(filter.sharedMesh, savePath); GameObject newObj = new GameObject(); newObj.transform.position = transform.position; newObj.transform.rotation = transform.rotation; MeshFilter newFilter = newObj.AddComponent(); newFilter.sharedMesh = AssetDatabase.LoadAssetAtPath(savePath); MeshRenderer newRend = newObj.AddComponent(); newRend.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; newObj.transform.name = s; ClearMesh(); } } void OnDestroy() { // When the window is destroyed, remove the delegate // so that it will no longer do any drawing. SceneView.duringSceneGui -= this.OnScene; } private void OnEnable() { filter = GetComponent(); SceneView.duringSceneGui += this.OnScene; } public void ClearMesh() { i = 0; positions = new List(); indicies = new List(); colors = new List(); normals = new List(); length = new List(); } void OnScene(SceneView scene) { // only allow painting while this object is selected if (this != null && this.gameObject != null && (Selection.Contains(gameObject))) { Event e = Event.current; RaycastHit terrainHit; mousePos = e.mousePosition; float ppp = EditorGUIUtility.pixelsPerPoint; mousePos.y = scene.camera.pixelHeight - mousePos.y * ppp; mousePos.x *= ppp; // ray for gizmo(disc) Ray rayGizmo = scene.camera.ScreenPointToRay(mousePos); RaycastHit hitGizmo; if (Physics.Raycast(rayGizmo, out hitGizmo, 200f, hitMask.value)) { hitPosGizmo = hitGizmo.point; } if (e.type == EventType.MouseDrag && e.button == 1 && toolbarInt == 0) { // place based on density for (int k = 0; k < density; k++) { // brushrange float t = 2f * Mathf.PI * Random.Range(0f, brushSize); float u = Random.Range(0f, brushSize) + Random.Range(0f, brushSize); float r = (u > 1 ? 2 - u : u); Vector3 origin = Vector3.zero; // place random in radius, except for first one if (k != 0) { origin.x += r * Mathf.Cos(t); origin.y += r * Mathf.Sin(t); } else { origin = Vector3.zero; } // add random range to ray Ray ray = scene.camera.ScreenPointToRay(mousePos); ray.origin += origin; // if the ray hits something thats on the layer mask, within the grass limit and within the y normal limit if (Physics.Raycast(ray, out terrainHit, 200f, hitMask.value) && i < grassLimit && terrainHit.normal.y <= (1 + normalLimit) && terrainHit.normal.y >= (1 - normalLimit)) { if ((paintMask.value & (1 << terrainHit.transform.gameObject.layer)) > 0) { hitPos = terrainHit.point; hitNormal = terrainHit.normal; if (k != 0) { var grassPosition = hitPos;// + Vector3.Cross(origin, hitNormal); grassPosition -= this.transform.position; positions.Add((grassPosition)); indicies.Add(i); length.Add(new Vector2(sizeWidth, sizeLength)); // add random color variations colors.Add(new Color(AdjustedColor.r + (Random.Range(0, 1.0f) * rangeR), AdjustedColor.g + (Random.Range(0, 1.0f) * rangeG), AdjustedColor.b + (Random.Range(0, 1.0f) * rangeB), 1)); //colors.Add(temp); normals.Add(terrainHit.normal); i++; } else {// to not place everything at once, check if the first placed point far enough away from the last placed first one if (Vector3.Distance(terrainHit.point, lastPosition) > brushSize) { var grassPosition = hitPos; grassPosition -= this.transform.position; positions.Add((grassPosition)); indicies.Add(i); length.Add(new Vector2(sizeWidth, sizeLength)); colors.Add(new Color(AdjustedColor.r + (Random.Range(0, 1.0f) * rangeR), AdjustedColor.g + (Random.Range(0, 1.0f) * rangeG), AdjustedColor.b + (Random.Range(0, 1.0f) * rangeB), 1)); normals.Add(terrainHit.normal); i++; if (origin == Vector3.zero) { lastPosition = hitPos; } } } } } } e.Use(); } // removing mesh points if (e.type == EventType.MouseDrag && e.button == 1 && toolbarInt == 1) { Ray ray = scene.camera.ScreenPointToRay(mousePos); if (Physics.Raycast(ray, out terrainHit, 200f, hitMask.value)) { hitPos = terrainHit.point; hitPosGizmo = hitPos; hitNormal = terrainHit.normal; for (int j = 0; j < positions.Count; j++) { Vector3 pos = positions[j]; pos += this.transform.position; float dist = Vector3.Distance(terrainHit.point, pos); // if its within the radius of the brush, remove all info if (dist <= brushSize) { positions.RemoveAt(j); colors.RemoveAt(j); normals.RemoveAt(j); length.RemoveAt(j); indicies.RemoveAt(j); i--; for (int i = 0; i < indicies.Count; i++) { indicies[i] = i; } } } } e.Use(); } //edit if (e.type == EventType.MouseDrag && e.button == 1 && toolbarInt == 2) { Ray ray = scene.camera.ScreenPointToRay(mousePos); if (Physics.Raycast(ray, out terrainHit, 200f, hitMask.value)) { hitPos = terrainHit.point; hitPosGizmo = hitPos; hitNormal = terrainHit.normal; for (int j = 0; j < positions.Count; j++) { Vector3 pos = positions[j]; pos += this.transform.position; float dist = Vector3.Distance(terrainHit.point, pos); // if its within the radius of the brush, remove all info if (dist <= brushSize) { brushFalloffSize = Mathf.Clamp(brushFalloffSize, 0, brushSize); float falloff = Mathf.Clamp01((dist - brushFalloffSize) / (brushSize - brushFalloffSize)); Color OrigColor = colors[j]; Color newCol = (new Color(AdjustedColor.r + (Random.Range(0, 1.0f) * rangeR), AdjustedColor.g + (Random.Range(0, 1.0f) * rangeG), AdjustedColor.b + (Random.Range(0, 1.0f) * rangeB), 1)); Vector2 origLength = length[j]; Vector2 newLength = new Vector2(sizeWidth, sizeLength); ; flowTimer++; if (flowTimer > Flow) { if (toolbarIntEdit == 0 || toolbarIntEdit == 2) { colors[j] = Color.Lerp(newCol, OrigColor, falloff); } if (toolbarIntEdit == 1 || toolbarIntEdit == 2) { length[j] = Vector2.Lerp(newLength, origLength, falloff); } flowTimer = 0; } } } } e.Use(); } // set all info to mesh mesh = new Mesh(); mesh.SetVertices(positions); indi = indicies.ToArray(); mesh.SetIndices(indi, MeshTopology.Points, 0); mesh.SetUVs(0, length); mesh.SetColors(colors); mesh.SetNormals(normals); filter.mesh = mesh; } } #endif }