Advertisement
Guest User

GrassPainter.cs

a guest
Feb 27th, 2022
7,470
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.58 KB | None | 0 0
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4.  
  5. using UnityEditor;
  6.  
  7.  
  8.  
  9. [RequireComponent(typeof(MeshFilter))]
  10. [ExecuteInEditMode]
  11. public class GrassPainter : MonoBehaviour
  12. {
  13.  
  14. public Mesh mesh;
  15. MeshFilter filter;
  16. public int grassLimit = 50000;
  17.  
  18. // options
  19. public int toolbarInt = 0;
  20. public int toolbarIntEdit = 0;
  21.  
  22. // mesh lists
  23. [SerializeField]
  24. List<Vector3> positions = new List<Vector3>();
  25. [SerializeField]
  26. List<Color> colors = new List<Color>();
  27. [SerializeField]
  28. List<int> indicies = new List<int>();
  29. [SerializeField]
  30. List<Vector3> normals = new List<Vector3>();
  31. [SerializeField]
  32. List<Vector2> length = new List<Vector2>();
  33. int[] indi;
  34. public int i = 0;
  35.  
  36. // brush settings
  37. public LayerMask hitMask = 1;
  38. public LayerMask paintMask = 1;
  39. public float brushSize;
  40. public float brushFalloffSize;
  41. public float Flow;
  42. public float density = 1f;
  43. public float normalLimit = 1;
  44.  
  45. // length/width
  46. public float sizeWidth = 1f;
  47. public float sizeLength = 1f;
  48.  
  49. // reproject settings
  50. public float reprojectOffset = 1f;
  51.  
  52. // color settings
  53. public float rangeR, rangeG, rangeB;
  54. public Color AdjustedColor;
  55.  
  56. // raycast vars
  57. [HideInInspector]
  58. public Vector3 hitPosGizmo;
  59. Vector3 mousePos;
  60. Vector3 hitPos;
  61. [HideInInspector]
  62. public Vector3 hitNormal;
  63.  
  64. private int flowTimer;
  65. private Vector3 lastPosition = Vector3.zero;
  66.  
  67. #if UNITY_EDITOR
  68. void OnFocus()
  69. {
  70. // Remove delegate listener if it has previously
  71. // been assigned.
  72. if (gameObject == null)
  73. {
  74. return;
  75. }
  76. SceneView.duringSceneGui -= this.OnScene;
  77. // Add (or re-add) the delegate.
  78. SceneView.duringSceneGui += this.OnScene;
  79. }
  80.  
  81. public void HandleUndo()
  82. {
  83. if (mesh)
  84. {
  85. mesh.GetVertices(positions);
  86. if (positions.Count == 0)
  87. {
  88. ClearMesh();
  89. return;
  90. }
  91. i = positions.Count;
  92. mesh.GetIndices(indicies, 0);
  93. indi = indicies.ToArray();
  94. mesh.GetUVs(0, length);
  95. mesh.GetColors(colors);
  96. mesh.GetNormals(normals);
  97. RebuildMesh();
  98. }
  99. SceneView.RepaintAll();
  100. }
  101.  
  102. void OnDestroy()
  103. {
  104. // When the window is destroyed, remove the delegate
  105. // so that it will no longer do any drawing.
  106. SceneView.duringSceneGui -= this.OnScene;
  107. Undo.undoRedoPerformed -= this.HandleUndo;
  108. }
  109.  
  110. private void OnEnable()
  111. {
  112. if (filter == null)
  113. {
  114. filter = GetComponent<MeshFilter>();
  115. }
  116. SceneView.duringSceneGui += this.OnScene;
  117. Undo.undoRedoPerformed += this.HandleUndo;
  118.  
  119. SetupMesh();
  120. }
  121.  
  122. void SetupMesh()
  123. {
  124. mesh.GetVertices(positions);
  125. i = positions.Count;
  126. mesh.GetIndices(indicies, 0);
  127. indi = indicies.ToArray();
  128. mesh.GetUVs(0, length);
  129. mesh.GetColors(colors);
  130. mesh.GetNormals(normals);
  131. }
  132.  
  133. public void ClearMesh()
  134. {
  135. Undo.RegisterCompleteObjectUndo(mesh, "Cleared Grass");
  136. i = 0;
  137. positions = new List<Vector3>();
  138. indicies = new List<int>();
  139. colors = new List<Color>();
  140. normals = new List<Vector3>();
  141. length = new List<Vector2>();
  142. RebuildMesh();
  143. }
  144.  
  145. public void FloodColor()
  146. {
  147. Undo.RegisterCompleteObjectUndo(mesh, "Flooded Color");
  148. for (int i = 0; i < colors.Count; i++)
  149. {
  150. colors[i] = AdjustedColor;
  151. }
  152. RebuildMesh();
  153. }
  154.  
  155. public void FloodLengthAndWidth()
  156. {
  157. Undo.RegisterCompleteObjectUndo(mesh, "Flooded Length/Width");
  158. for (int i = 0; i < length.Count; i++)
  159. {
  160. length[i] = new Vector2(sizeWidth, sizeLength);
  161. }
  162. RebuildMesh();
  163. }
  164.  
  165. void OnScene(SceneView scene)
  166. {
  167. if (this != null)
  168. {
  169. // only allow painting while this object is selected
  170. if ((Selection.Contains(gameObject)))
  171. {
  172. Event e = Event.current;
  173. RaycastHit terrainHit;
  174. mousePos = e.mousePosition;
  175. float ppp = EditorGUIUtility.pixelsPerPoint;
  176. mousePos.y = scene.camera.pixelHeight - mousePos.y * ppp;
  177. mousePos.x *= ppp;
  178. mousePos.z = 0;
  179.  
  180. // ray for gizmo(disc)
  181. Ray rayGizmo = scene.camera.ScreenPointToRay(mousePos);
  182. RaycastHit hitGizmo;
  183.  
  184. if (Physics.Raycast(rayGizmo, out hitGizmo, 200f, hitMask.value))
  185. {
  186. hitPosGizmo = hitGizmo.point;
  187. }
  188. // undo system
  189. if (e.type == EventType.MouseDown && e.button == 1)
  190. {
  191. if (toolbarInt == 0)
  192. {
  193. Undo.RegisterCompleteObjectUndo(mesh, "Added Grass");
  194. }
  195. else if (toolbarInt == 1)
  196. {
  197. Undo.RegisterCompleteObjectUndo(mesh, "Removed Grass");
  198. }
  199. else if (toolbarInt == 2)
  200. {
  201. Undo.RegisterCompleteObjectUndo(mesh, "Edited Grass");
  202. }
  203. else if (toolbarInt == 3)
  204. {
  205. Undo.RegisterCompleteObjectUndo(mesh, "Reprojected Grass");
  206. }
  207. }
  208. if (e.type == EventType.MouseDrag && e.button == 1)
  209. {
  210. if (toolbarInt == 0)
  211. {
  212. // place based on density
  213. for (int k = 0; k < density * brushSize; k++)
  214. {
  215.  
  216. // brushrange
  217. float t = 2f * Mathf.PI * Random.Range(0f, brushSize);
  218. float u = Random.Range(0f, brushSize) + Random.Range(0f, brushSize);
  219. float r = (u > 1 ? 2 - u : u);
  220. Vector3 origin = Vector3.zero;
  221.  
  222. // place random in radius, except for first one
  223. if (k != 0)
  224. {
  225. origin.x += r * Mathf.Cos(t);
  226. origin.y += r * Mathf.Sin(t);
  227. }
  228. else
  229. {
  230. origin = Vector3.zero;
  231. }
  232.  
  233. // add random range to ray
  234. Ray ray = scene.camera.ScreenPointToRay(mousePos);
  235. ray.origin += origin;
  236.  
  237. // if the ray hits something thats on the layer mask, within the grass limit and within the y normal limit
  238. if (Physics.Raycast(ray, out terrainHit, 200f, hitMask.value) && i < grassLimit && terrainHit.normal.y <= (1 + normalLimit) && terrainHit.normal.y >= (1 - normalLimit))
  239. {
  240. if ((paintMask.value & (1 << terrainHit.transform.gameObject.layer)) > 0)
  241. {
  242. hitPos = terrainHit.point;
  243. hitNormal = terrainHit.normal;
  244. if (k != 0)
  245. {
  246. var grassPosition = hitPos;// + Vector3.Cross(origin, hitNormal);
  247. grassPosition -= this.transform.position;
  248.  
  249. positions.Add((grassPosition));
  250. indicies.Add(i);
  251. length.Add(new Vector2(sizeWidth, sizeLength));
  252. // add random color variations
  253. 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));
  254.  
  255. //colors.Add(temp);
  256. normals.Add(terrainHit.normal);
  257. i++;
  258. }
  259. else
  260. {// to not place everything at once, check if the first placed point far enough away from the last placed first one
  261. if (Vector3.Distance(terrainHit.point, lastPosition) > brushSize)
  262. {
  263. var grassPosition = hitPos;
  264. grassPosition -= this.transform.position;
  265. positions.Add((grassPosition));
  266. indicies.Add(i);
  267. length.Add(new Vector2(sizeWidth, sizeLength));
  268. 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));
  269. normals.Add(terrainHit.normal);
  270. i++;
  271.  
  272. if (origin == Vector3.zero)
  273. {
  274. lastPosition = hitPos;
  275. }
  276. }
  277. }
  278. }
  279. }
  280. }
  281. e.Use();
  282. }
  283. // removing mesh points
  284. if (toolbarInt == 1)
  285. {
  286. Ray ray = scene.camera.ScreenPointToRay(mousePos);
  287. if (Physics.Raycast(ray, out terrainHit, 200f, hitMask.value))
  288. {
  289. hitPos = terrainHit.point;
  290. hitPosGizmo = hitPos;
  291. hitNormal = terrainHit.normal;
  292. for (int j = 0; j < positions.Count; j++)
  293. {
  294. Vector3 pos = positions[j];
  295. pos += this.transform.position;
  296. float dist = Vector3.Distance(terrainHit.point, pos);
  297.  
  298. // if its within the radius of the brush, remove all info
  299. if (dist <= brushSize)
  300. {
  301. positions.RemoveAt(j);
  302. colors.RemoveAt(j);
  303. normals.RemoveAt(j);
  304. length.RemoveAt(j);
  305. indicies.RemoveAt(j);
  306. i--;
  307.  
  308. }
  309. }
  310. for (int ii = 0; ii < indicies.Count; ii++)
  311. {
  312. indicies[ii] = ii;
  313. }
  314. }
  315. e.Use();
  316. }
  317. //edit
  318. if (toolbarInt == 2)
  319. {
  320. Ray ray = scene.camera.ScreenPointToRay(mousePos);
  321.  
  322. if (Physics.Raycast(ray, out terrainHit, 200f, hitMask.value))
  323. {
  324. hitPos = terrainHit.point;
  325. hitPosGizmo = hitPos;
  326. hitNormal = terrainHit.normal;
  327. for (int j = 0; j < positions.Count; j++)
  328. {
  329. Vector3 pos = positions[j];
  330.  
  331. pos += this.transform.position;
  332. float dist = Vector3.Distance(terrainHit.point, pos);
  333.  
  334. // if its within the radius of the brush, remove all info
  335. if (dist <= brushSize)
  336. {
  337.  
  338. float falloff = Mathf.Clamp01((dist / (brushFalloffSize * brushSize)));
  339.  
  340. //store the original color
  341. Color OrigColor = colors[j];
  342.  
  343. // add in the new color
  344. 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));
  345.  
  346. Vector2 origLength = length[j];
  347. Vector2 newLength = new Vector2(sizeWidth, sizeLength); ;
  348.  
  349. flowTimer++;
  350. if (flowTimer > Flow)
  351. {
  352. // edit colors
  353. if (toolbarIntEdit == 0 || toolbarIntEdit == 2)
  354. {
  355. colors[j] = Color.Lerp(newCol, OrigColor, falloff);
  356. }
  357. // edit grass length
  358. if (toolbarIntEdit == 1 || toolbarIntEdit == 2)
  359. {
  360. length[j] = Vector2.Lerp(newLength, origLength, falloff);
  361. }
  362. flowTimer = 0;
  363. }
  364. }
  365. }
  366. }
  367. e.Use();
  368. }
  369.  
  370. // Reproject mesh points
  371. if (toolbarInt == 3)
  372. {
  373. Ray ray = scene.camera.ScreenPointToRay(mousePos);
  374.  
  375. if (Physics.Raycast(ray, out terrainHit, 200f, hitMask.value))
  376. {
  377. hitPos = terrainHit.point;
  378. hitPosGizmo = hitPos;
  379. hitNormal = terrainHit.normal;
  380.  
  381. for (int j = 0; j < positions.Count; j++)
  382. {
  383. Vector3 pos = positions[j];
  384. pos += this.transform.position;
  385. float dist = Vector3.Distance(terrainHit.point, pos);
  386.  
  387. // if its within the radius of the brush, raycast to a new position
  388. if (dist <= brushSize)
  389. {
  390. RaycastHit raycastHit;
  391. Vector3 meshPoint = new Vector3(pos.x, pos.y + reprojectOffset, pos.z);
  392. if (Physics.Raycast(meshPoint, Vector3.down, out raycastHit, 200f, paintMask.value))
  393. {
  394. Vector3 newPoint = raycastHit.point - this.transform.position;
  395. positions[j] = newPoint;
  396. }
  397. }
  398. }
  399. }
  400. e.Use();
  401. }
  402. RebuildMesh();
  403. }
  404. }
  405. }
  406. }
  407.  
  408. void RebuildMesh()
  409. {
  410. if (mesh == null)
  411. {
  412. mesh = new Mesh();
  413. }
  414. mesh.Clear();
  415. mesh.SetVertices(positions);
  416. indi = indicies.ToArray();
  417. mesh.SetIndices(indi, MeshTopology.Points, 0);
  418. mesh.SetUVs(0, length);
  419. mesh.SetColors(colors);
  420. mesh.SetNormals(normals);
  421. mesh.RecalculateBounds();
  422. filter.sharedMesh = mesh;
  423. }
  424. #endif
  425. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement