using System.Collections; using System.Collections.Generic; using UnityEngine; namespace AwesomeTechnologies.Grass { [RequireComponent(typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer))] [System.Serializable] public class ProceduralGrassPlane : MonoBehaviour { public enum AnchorPoint { TopLeft, TopHalf, TopRight, RightHalf, BottomRight, BottomHalf, BottomLeft, LeftHalf, Center } public int widthSegments = 5; public int heightSegments = 4; public float width = 1.0f; public float height = 0.5f; public AnchorPoint anchor = AnchorPoint.Center; private UnityEngine.Vector2 anchorOffset; public int Index = 0; //private string anchorId; public float Offset1 = 0.3f; public float Offset2 = 0.15f; public float MinimumBendHeight = 0.25f; public float CurveOffset = 0.25f; public int LODLevel = 0; public Material Material; public bool BakePhase; public bool BakeBend; public bool BakeAO; public float Phase; public AnimationCurve BendCurve; public AnimationCurve AmbientOcclusionCurve; public bool GenerateBackside = true; void Start() { } void SetAncorPoints() { switch (anchor) { case AnchorPoint.TopLeft: anchorOffset = new UnityEngine.Vector2(-width / 2.0f, height / 2.0f); //anchorId = "TL"; break; case AnchorPoint.TopHalf: anchorOffset = new UnityEngine.Vector2(0.0f, height / 2.0f); //anchorId = "TH"; break; case AnchorPoint.TopRight: anchorOffset = new UnityEngine.Vector2(width / 2.0f, height / 2.0f); //anchorId = "TR"; break; case AnchorPoint.RightHalf: anchorOffset = new UnityEngine.Vector2(width / 2.0f, 0.0f); //anchorId = "RH"; break; case AnchorPoint.BottomRight: anchorOffset = new UnityEngine.Vector2(width / 2.0f, -height / 2.0f); //anchorId = "BR"; break; case AnchorPoint.BottomHalf: anchorOffset = new UnityEngine.Vector2(0.0f, -height / 2.0f); //anchorId = "BH"; break; case AnchorPoint.BottomLeft: anchorOffset = new UnityEngine.Vector2(-width / 2.0f, -height / 2.0f); //anchorId = "BL"; break; case AnchorPoint.LeftHalf: anchorOffset = new UnityEngine.Vector2(-width / 2.0f, 0.0f); //anchorId = "LH"; break; case AnchorPoint.Center: default: anchorOffset = UnityEngine.Vector2.zero; //anchorId = "C"; break; } } void ApplyPhaseAndBend(Mesh mesh, int currentLOD) { Color[] meshColors; Vector3[] vertex = mesh.vertices; if (mesh.colors.Length == 0) { meshColors = new Color[mesh.vertexCount]; } else { meshColors = mesh.colors; } byte phaseByte = 255; byte bendByte = 255; byte ambientByte = 255; if (BakePhase) { phaseByte = (byte)(Phase * 255); } for (int i = 0; i <= meshColors.Length -1; i++) { float vertexHeight = (vertex[i].y + height / 2f) / height; vertexHeight = Mathf.Clamp(vertexHeight, 0, 1); if (BakeBend) { float bendCurveOutput = BendCurve.Evaluate(vertexHeight); bendCurveOutput = Mathf.Clamp(bendCurveOutput,0f,1f); bendByte = (byte)(bendCurveOutput * 255); } if (BakeAO) { float ambientOcculsionCurveOutput = AmbientOcclusionCurve.Evaluate(vertexHeight); ambientOcculsionCurveOutput = Mathf.Clamp(ambientOcculsionCurveOutput, 0f, 1f); ambientByte = (byte)(ambientOcculsionCurveOutput * 255); } meshColors[i] = new Color32(ambientByte, phaseByte, bendByte, bendByte); } mesh.colors = meshColors; // float fadeOutPlane = 0; // // switch (currentLOD) // { // case 0: // if (Index % 2 != 1) // { // fadeOutPlane = 1; // } // break; // case 1: // if (Index % 4 != 1) // { // fadeOutPlane = 1; // } // break; // } // Vector2[] uv2s; // if (mesh.uv2.Length == 0) // { // uv2s = new Vector2[mesh.vertexCount]; // } // else // { // uv2s = mesh.uv2; // } // // for (int i = 0; i <= uv2s.Length - 1; i++) // { // uv2s[i] = new Vector2(fadeOutPlane, currentLOD); // } // // mesh.uv2 = uv2s; } public void CreateGrassPlane(int currentLOD) { SetAncorPoints(); // You can change that line to provide another MeshFilter MeshFilter filter = gameObject.GetComponent(); if (filter == null) { filter = gameObject.AddComponent(); } Mesh m = filter.sharedMesh; if (m == null) m = new Mesh(); m.Clear(); int hCount2 = widthSegments + 1; int vCount2 = heightSegments + 1; int numTriangles = widthSegments * heightSegments * 6; int numVertices = hCount2 * vCount2; Vector3[] vertices = new Vector3[numVertices]; UnityEngine.Vector2[] uvs = new UnityEngine.Vector2[numVertices]; int[] triangles = new int[numTriangles]; Vector4[] tangents = new Vector4[numVertices]; Vector4 tangent = new Vector4(1f, 0f, 0f, -1f); int index = 0; float uvFactorX = 1.0f / widthSegments; float uvFactorY = 1.0f / heightSegments; float scaleX = width / widthSegments; float scaleY = height / heightSegments; for (float y = 0.0f; y < vCount2; y++) { for (float x = 0.0f; x < hCount2; x++) { float NormalizedX = x * uvFactorX; float CurrentOffset = Mathf.Lerp(Offset1, Offset2, x * uvFactorX); float ZOffset = Mathf.Lerp(0, CurrentOffset, y * uvFactorY); if ((y * scaleY) <= MinimumBendHeight) { ZOffset = 0; } float zCurveoffset = 0; if (NormalizedX <= 0.5f) { zCurveoffset = Mathf.Lerp(CurveOffset,0,NormalizedX *2); } else { zCurveoffset = Mathf.Lerp(0, CurveOffset, (NormalizedX*2) - 0.5f); } vertices[index] = new Vector3(x * scaleX - width / 2f - anchorOffset.x, y * scaleY - height / 2f - anchorOffset.y, ZOffset + zCurveoffset); tangents[index] = tangent; uvs[index++] = new UnityEngine.Vector2(x * uvFactorX, y * uvFactorY); } } index = 0; for (int y = 0; y < heightSegments; y++) { for (int x = 0; x < widthSegments; x++) { triangles[index] = (y * hCount2) + x; triangles[index + 1] = ((y + 1) * hCount2) + x; triangles[index + 2] = (y * hCount2) + x + 1; triangles[index + 3] = ((y + 1) * hCount2) + x; triangles[index + 4] = ((y + 1) * hCount2) + x + 1; triangles[index + 5] = (y * hCount2) + x + 1; index += 6; } } m.vertices = vertices; m.uv = uvs; m.triangles = triangles; m.tangents = tangents; m.RecalculateNormals(); if (GenerateBackside) { BuildBackside(m); m.RecalculateNormals(); } filter.sharedMesh = m; m.RecalculateBounds(); //if (material == null) //{ // material = (Material)Resources.Load("GrassPlaneMaterial", typeof(Material)); //} MeshRenderer meshRenderer = this.gameObject.GetComponent(); if (meshRenderer) { meshRenderer.sharedMaterial = Material; } if (BakePhase || BakeBend) ApplyPhaseAndBend(m,currentLOD); } void BuildBackside(Mesh mesh) { var vertices = mesh.vertices; var uv = mesh.uv; var normals = mesh.normals; var szV = vertices.Length; var newVerts = new Vector3[szV * 2]; var newUv = new UnityEngine.Vector2[szV * 2]; var newNorms = new Vector3[szV * 2]; for (var j = 0; j < szV; j++) { // duplicate vertices and uvs: newVerts[j] = newVerts[j + szV] = vertices[j]; newUv[j] = newUv[j + szV] = uv[j]; // copy the original normals... newNorms[j] = normals[j]; // and revert the new ones newNorms[j + szV] = -normals[j]; } var triangles = mesh.triangles; var szT = triangles.Length; var newTris = new int[szT * 2]; // double the triangles for (var i = 0; i < szT; i += 3) { // copy the original triangle newTris[i] = triangles[i]; newTris[i + 1] = triangles[i + 1]; newTris[i + 2] = triangles[i + 2]; // save the new reversed triangle var j = i + szT; newTris[j] = triangles[i] + szV; newTris[j + 2] = triangles[i + 1] + szV; newTris[j + 1] = triangles[i + 2] + szV; } mesh.vertices = newVerts; mesh.uv = newUv; mesh.normals = newNorms; mesh.triangles = newTris; // assign triangles last! } } }