using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Linq; public class BF_AddSnow : MonoBehaviour { public Material snowMaterial; public float angle = 80f; public bool isAuto = false; public float intersectionOffset = 0.25f; public bool useIntersection = false; public bool useUpdatedRotation = false; private Mesh originalMesh; private MeshFilter meshFilter; private Mesh newMesh; private GameObject newGO; private float yIntersection = 0f; private Quaternion ySlope = Quaternion.identity; private float zNormal = 0; private Vector3 normalHit = Vector3.zero; private float oldyIntersection = -1f; private int[] oldTri; private Vector3[] oldVert; private Vector3[] oldNorm; private Vector3[] oldNormWorld; private Vector2[] oldUV; private Color[] oldCol; private List triangles = new List(); private List vertexs = new List(); private List uvs = new List(); private List cols = new List(); void Start() { CheckValues(); BuildInitialGeometry(); } private void Update() { if (useIntersection) { CheckIntersection(); } if (useUpdatedRotation) { UpdateVertexColor(); } } private void CheckValues() { meshFilter = gameObject.GetComponent(); originalMesh = meshFilter.mesh; oldTri = originalMesh.triangles; oldVert = originalMesh.vertices; oldNorm = originalMesh.normals; oldNormWorld = oldNorm; if (isAuto) { int k = 0; foreach (Vector3 norm in oldNorm) { oldNormWorld[k] = this.transform.localToWorldMatrix.MultiplyVector(norm).normalized; k++; } oldCol = new Color[oldVert.Length]; } else { oldCol = originalMesh.colors; } oldUV = originalMesh.uv; } private void CheckIntersection() { int layerMask = 1 << 0 | 1 << 4; RaycastHit hit; RaycastHit hitIfHit; if (Physics.Raycast(transform.position + Vector3.up*5f, Vector3.down, out hit, 200, layerMask)) { if (hit.transform != this.transform) { yIntersection = hit.point.y + intersectionOffset; //Vector3 tangent = Vector3.ProjectOnPlane(Vector3.down, hit.normal).normalized; ySlope = Quaternion.LookRotation(hit.normal, Vector3.forward); zNormal = ((hit.normal.normalized.z) + 1f) / 2f; normalHit = hit.normal.normalized; if (yIntersection != oldyIntersection) { UpdateSlopeColor(); } oldyIntersection = yIntersection; } else { if (Physics.Raycast(hit.point + Vector3.up * -0.05f, Vector3.down, out hitIfHit, 200, layerMask)) { if (hitIfHit.transform != this.transform) { yIntersection = hitIfHit.point.y + intersectionOffset; //Vector3 tangent = Vector3.ProjectOnPlane(Vector3.down, hitIfHit.normal).normalized; ySlope = Quaternion.LookRotation(hitIfHit.normal, Vector3.forward); zNormal = ((hitIfHit.normal.normalized.z) + 1f) / 2f; normalHit = hitIfHit.normal.normalized; if (yIntersection != oldyIntersection) { UpdateSlopeColor(); } oldyIntersection = yIntersection; } } } } } private void ClearGeometry() { triangles.Clear(); triangles.TrimExcess(); vertexs.Clear(); vertexs.TrimExcess(); uvs.Clear(); uvs.TrimExcess(); cols.Clear(); cols.TrimExcess(); } private void BuildInitialGeometry() { if (meshFilter == null) { meshFilter = gameObject.GetComponent(); } newMesh = new Mesh(); newGO = new GameObject(); MeshFilter mF = newGO.AddComponent(); MeshRenderer mR = newGO.AddComponent(); mF.mesh = newMesh; mR.material = snowMaterial; snowMaterial.SetFloat("_ISADD", 1); snowMaterial.EnableKeyword("IS_ADD"); if (useIntersection) { if (snowMaterial.GetFloat("_USEINTER") == 0) { snowMaterial.SetFloat("_USEINTER", 1); snowMaterial.EnableKeyword("USE_INTER"); } } else { if (snowMaterial.GetFloat("_USEINTER") == 1) { snowMaterial.SetFloat("_USEINTER", 0); snowMaterial.DisableKeyword("USE_INTER"); } } newGO.transform.parent = this.transform; newGO.transform.localPosition = Vector3.zero; newGO.transform.localScale = Vector3.one; newGO.transform.localRotation = Quaternion.identity; int indexNewV = 0; foreach (Vector3 v in oldVert) { vertexs.Add(v + new Vector3(0,0,0)); uvs.Add(oldUV[indexNewV]); indexNewV++; } indexNewV = 0; foreach (int innt in oldTri) { triangles.Add(oldTri[indexNewV]); indexNewV++; } if (isAuto) { int j = 0; foreach (Vector3 norm in oldNormWorld) { if(j>= oldCol.Length) { break; } oldCol[j] = Color.red; float theAngle = Vector3.Angle(Vector3.up, norm); if (theAngle < (angle+10f)) { Color lerpedColor = Color.Lerp(Color.white, Color.red, Mathf.Max(0f,theAngle- angle/2f) / (angle / 2f)); oldCol[j] = lerpedColor; } j++; } } cols = oldCol.ToList(); newMesh.vertices = vertexs.ToArray(); newMesh.triangles = triangles.ToArray(); newMesh.uv = uvs.ToArray(); newMesh.colors = cols.ToArray(); newMesh.normals = originalMesh.normals; RecalculateNormalsSeamless(newMesh); newMesh.RecalculateBounds(); newMesh.Optimize(); } private void UpdateVertexColor() { Color[] updatedColors = newMesh.colors; Vector3[] newNormWorld = newMesh.normals; if (isAuto) { int k = 0; foreach (Vector3 norm in newMesh.normals) { newNormWorld[k] = this.transform.localToWorldMatrix.MultiplyVector(norm).normalized; k++; } } if (isAuto) { int j = 0; foreach (Vector3 norm in newNormWorld) { if (j >= updatedColors.Length) { break; } float theAngle = Vector3.Angle(Vector3.up, norm); if (theAngle < (angle + 10f)) { Color lerpedColor = Color.Lerp(new Color(1,1,1, updatedColors[j].a), new Color(1, 0, 0, updatedColors[j].a), Mathf.Max(0f, theAngle - angle / 2f) / (angle / 2f)); updatedColors[j].r = lerpedColor.r; updatedColors[j].g = lerpedColor.g; updatedColors[j].b = lerpedColor.b; updatedColors[j].a = lerpedColor.a; } j++; } } newMesh.colors = updatedColors; } private void UpdateSlopeColor() { // This is not perfect for now but gets the job done... // int j = 0; Color[] updatedColors = newMesh.colors; Vector2[] updatedUV4 = new Vector2[updatedColors.Count()]; Vector2[] updatedUV5 = new Vector2[updatedColors.Count()]; Vector2[] updatedUV6 = new Vector2[updatedColors.Count()]; Vector2[] updatedUV7 = new Vector2[updatedColors.Count()]; foreach (Color norm in newMesh.colors) { updatedColors[j].a = yIntersection; updatedUV4[j] = new Vector2(normalHit.x, normalHit.y); updatedUV5[j] = new Vector2(zNormal, normalHit.z); updatedUV6[j] = new Vector2(ySlope.x, ySlope.y); updatedUV7[j] = new Vector2(ySlope.z, ySlope.w); j++; } newMesh.colors = updatedColors; newMesh.uv4 = updatedUV4; newMesh.uv5 = updatedUV5; newMesh.uv6 = updatedUV6; newMesh.uv7 = updatedUV7; } private void RecalculateNormalsSeamless(Mesh mesh) { var trianglesOriginal = mesh.triangles; var triangles = trianglesOriginal.ToArray(); var vertices = mesh.vertices; var mergeIndices = new Dictionary(); for (int i = 0; i < vertices.Length; i++) { var vertexHash = vertices[i].GetHashCode(); if (mergeIndices.TryGetValue(vertexHash, out var index)) { for (int j = 0; j < triangles.Length; j++) if (triangles[j] == i) triangles[j] = index; } else mergeIndices.Add(vertexHash, i); } mesh.triangles = triangles; var normals = new Vector3[vertices.Length]; mesh.RecalculateNormals(); var newNormals = mesh.normals; for (int i = 0; i < vertices.Length; i++) if (mergeIndices.TryGetValue(vertices[i].GetHashCode(), out var index)) normals[i] = newNormals[index]; mesh.triangles = trianglesOriginal; mesh.normals = normals; } }