/* This file is part of the "NavMesh Extension" project by Rebound Games. * You are only allowed to use these resources if you've bought them directly or indirectly * from Rebound Games. You shall not license, sublicense, sell, resell, transfer, assign, * distribute or otherwise make available to any third party the Service or the Content. */ using UnityEngine; using System.Linq; using System.Collections; using System.Collections.Generic; namespace NavMeshExtension { /// /// Stores a NavMesh mesh object and lists to manipulate it. /// [RequireComponent(typeof(MeshFilter))] [RequireComponent(typeof(MeshRenderer))] public class NavMeshObject : MonoBehaviour { /// /// Whether new submeshes should be created automatically. /// public bool autoSplit = false; /// /// The vertex count where new submeshes should be created, if autoSplit is true. /// public int splitAt = 4; /// /// Offset on the y-axis when adding new vertices to the mesh. /// public float yOffset = 0.015f; /// /// List of relative vertex positions for this mesh. /// [HideInInspector] public List list = new List(); /// /// List of indices placed in the current submesh, pointing to the list of vertex positions. /// [HideInInspector] public List current = new List(); /// /// List of indices for each submesh, pointing to the list of vertex positions. /// [HideInInspector] public List subPoints = new List(); /// /// Reference to the mesh component of the current submesh. /// [HideInInspector] public Mesh subMesh; /// /// Wrapper class storing references to vertex positions for each submesh. /// [System.Serializable] public class SubPoints { public List list = new List(); } /// /// Combines all submeshes into the mesh on this object. /// public void Combine() { //get all mesh filters, but don't continue if there are no submeshes MeshFilter[] meshFilters = GetComponentsInChildren(); //get the mesh filter on this object MeshFilter myFilter = meshFilters[0]; List combine = new List(); //add meshes to the combine instances for (int i = 0; i < meshFilters.Length; i++) { if (meshFilters[i].sharedMesh == null) continue; CombineInstance c = new CombineInstance(); c.mesh = meshFilters[i].sharedMesh; c.transform = meshFilters[i].transform.localToWorldMatrix; combine.Add(c); } //rename mesh to a more appropriate name or keep it string meshName = "NavMesh"; if (myFilter.sharedMesh != null) meshName = myFilter.sharedMesh.name; //create new shared mesh from combined meshes myFilter.sharedMesh = new Mesh(); myFilter.sharedMesh.name = meshName; myFilter.sharedMesh.CombineMeshes(combine.ToArray()); current.Clear(); //list of vertices and triangles List vertices = new List(myFilter.sharedMesh.vertices); List triangles = new List(myFilter.sharedMesh.triangles); //convert vertex positions into relative positions for (int i = 0; i < vertices.Count; i++) vertices[i] = transform.InverseTransformPoint(vertices[i]); /* string str = ""; for (int i = 0; i < triangles.Count; i++) str += triangles[i] + " "; Debug.Log("BEFORE tris: " + str); */ //find duplicated vertex positions List dupIndices = new List(); List duplicates = vertices.GroupBy(x => x) .Where(x => x.Count() > 1) .Select(x => x.Key) .ToList(); //Debug.Log("duplicates: " + duplicates.Count); //loop over duplicates to find vertex indices, //also overwrite indices with the first occurence in the triangle array for (int i = 0; i < duplicates.Count; i++) { //get all occurences of duplicated indices List indices = vertices.Select((value, index) => new { value, index }) .Where(a => Vector3.Equals(a.value, duplicates[i])) .Select(a => a.index).ToList(); //get first occurence int unique = indices[0]; indices.RemoveAt(0); //loop over duplicated indices for (int j = 0; j < indices.Count; j++) { //get this duplicate int dupIndex = indices[j]; //get all matches in the triangle array List matches = Enumerable.Range(0, triangles.Count) .Where(v => triangles[v] == dupIndex) .ToList(); //overwrite duplicated matches with the unique index for (int k = 0; k < matches.Count; k++) { //Debug.Log("overwriting index: " + matches[j] + " with: " + first); triangles[matches[k]] = unique; } //remember for later, when we are merging vertices dupIndices.Add(dupIndex); } } //sort duplicated indices in a descending order dupIndices = dupIndices.OrderByDescending(x => x).ToList(); //loop over indices for (int i = 0; i < dupIndices.Count; i++) { //remove the vertex int dupIndex = dupIndices[i]; vertices.RemoveAt(dupIndex); //decrease indices starting after this vertex, //since we removed it and the array is smaller now for (int j = dupIndex; j < triangles.Count; j++) { if (triangles[j] >= dupIndex) triangles[j] = triangles[j] - 1; } } /* str = ""; for (int i = 0; i < triangles.Count; i++) str += triangles[i] + " "; Debug.Log("AFTER tris: " + str); */ //Debug.Log("COUNTS: " + vertices.Count + " " + triangles.Count); //assign merged vertices and triangles to the new mesh myFilter.sharedMesh.triangles = triangles.ToArray(); myFilter.sharedMesh.vertices = vertices.ToArray(); //recalculate and optimize myFilter.sharedMesh.RecalculateNormals(); myFilter.sharedMesh.RecalculateBounds(); var o_197_12_636329683418531818 = myFilter.sharedMesh; } /// /// Creates a new submesh gameobject, which will be merged into the existing one later on. /// public GameObject CreateSubMesh() { //add new entry for vertex references subPoints.Add(new SubPoints()); //create new submesh gameobject GameObject obj = new GameObject("New SubMesh"); obj.transform.parent = transform; obj.transform.localPosition = Vector3.zero; //get important components MeshFilter subFilter = obj.AddComponent(); MeshRenderer subRenderer = obj.AddComponent(); //modify material and create actual submesh subRenderer.sharedMaterial = GetComponent().sharedMaterial; subRenderer.enabled = GetComponent().enabled; subFilter.mesh = subMesh = new Mesh(); subMesh.name = "SubMesh"; current.Clear(); return obj; } /// /// Updates the mesh with new vertex positions. /// public void UpdateMesh(Vector3[] verts) { //convert passed in vertices to relative positioning MeshFilter myFilter = GetComponent(); for (int i = 0; i < verts.Length; i++) verts[i] = transform.InverseTransformPoint(verts[i]); //assign vertices myFilter.sharedMesh.vertices = verts; } /// /// Adds a new vertex to the current submesh. /// public void AddPoint(Vector3 point) { //modify point to take offset into account point = point + new Vector3(0, yOffset, 0); //re-position this object to the first point if (list.Count == 0) transform.position = point; //add new point to the list of vertices list.Add(transform.InverseTransformPoint(point)); //get the current index, //then add it to the current and actual submesh list int index = list.Count - 1; current.Add(index); subPoints[subPoints.Count - 1].list.Add(index); } /// /// Adds a reference to an existing vertex to the current submesh. /// public void AddPoint(int point) { //just add the index to the lists current.Add(point); subPoints[subPoints.Count - 1].list.Add(point); } /// /// Creates the double-sided submesh based on vertices and triangles. /// public void CreateMesh() { //clear mesh definitions if (subMesh) subMesh.Clear(); //get components MeshFilter subFilter = null; MeshFilter[] subFilters = GetComponentsInChildren(true); //find corresponding MeshFilter for (int i = 0; i < subFilters.Length; i++) { if (subFilters[i].sharedMesh == subMesh) { subFilter = subFilters[i]; break; } } //get vertex positions of current submesh Vector3[] vertex = new Vector3[current.Count]; for (int i = 0; i < current.Count; i++) vertex[i] = list[current[i]]; //don't continue without meshfilter or not enough points if (!subFilter || vertex.Length < 3) return; //set uvs of vertices Vector2[] uvs = new Vector2[vertex.Length]; for (int i = 0; i < vertex.Length; i++) { if ((i % 2) == 0) uvs[i] = new Vector2(0, 0); else uvs[i] = new Vector2(1, 1); } //assign data to mesh subMesh.vertices = vertex; subMesh.uv = uvs; subMesh.triangles = RecalculateTriangles(null); //recalculate and optimize subMesh.RecalculateNormals(); subMesh.RecalculateBounds(); ; //assign mesh to filter subFilter.mesh = subMesh; } public void CreateMeshFromPoints() { var meshFilter = GetComponent(); if (meshFilter == null) return; Vector2[] uvs = new Vector2[list.Count]; for (int i = 0; i < list.Count; i++) { if ((i % 2) == 0) uvs[i] = new Vector2(0, 0); else uvs[i] = new Vector2(1, 1); } meshFilter.mesh.vertices = list.ToArray(); meshFilter.mesh.uv = uvs; List triangleList = new List(); for (int i = 0; i < list.Count; ++i) { triangleList.Add(i); } meshFilter.mesh.triangles = RecalculateTriangles(triangleList); meshFilter.mesh.RecalculateNormals(); meshFilter.mesh.RecalculateBounds(); var o_356_12_636329683418752402 = meshFilter.mesh; } /// /// Recalculates a triangle array for a given list of vertex indices. /// public int[] RecalculateTriangles(List list) { //create triangles array //3 verts per triangle * num triangles int triLength = list == null ? current.Count - 2 : list.Count - 2; int[] tris = new int[3 * triLength * 2]; //triangle indices (forwards) int C1 = list == null ? 0 : list[0]; int C2 = list == null ? 1 : list[1]; int C3 = list == null ? 2 : list[2]; //assign triangles clockwise for (int j = 0; j < tris.Length / 2; j += 3) { tris[j] = C1; tris[j + 1] = C2; tris[j + 2] = C3; C2++; C3++; } //assign triangles counterclockwise for (int j = tris.Length / 2; j < tris.Length; j += 3) { int index = (j - tris.Length / 2) * 2; tris[j] = C1; tris[j + 1] = tris[j - index - 1]; tris[j + 2] = tris[j - index - 2]; } /* string str = ""; for (int i = 0; i < tris.Length; i++) str += tris[i] + " "; Debug.Log("Recalculated Tris: " + str); */ return tris; } } }