/*

Created by:

Juan Sebastian Munoz Arango

naruse@gmail.com

all rights reserved

This class creates a connection graph for neighbor vertices from the parent mesh and

subsequently generates a line mesh that highlights neighboor vertices.

*/

using System.Threading;

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class NeighborVertexHighlight : MonoBehaviour {

private Mesh highlightMesh;

private Mesh parentMesh; //refers to the mesh we are getting the indices to highlight

private List<int>[] neighborGraph;//precomputed neighbor graph

private Dictionary<Vector3, VertexNeighbor> indexNeighbors;//contains the current indices that we want to highlight

List<Vector3> vertices = new List<Vector3>();

List<int> indices = new List<int>();

Dictionary<int, int> indexRemap = new Dictionary<int, int>();

private bool finishedProcessingNeighborGraph = false;

void Start() {

indexNeighbors = new Dictionary<Vector3, VertexNeighbor>();

if (transform.parent == null)

Debug.LogError("NeighboorHighlight needs to be attached to a child of the mesh one wants to highlight");

parentMesh = transform.parent.GetComponent<MeshFilter>().mesh;

if (parentMesh == null)

Debug.LogError("Parent GameObject doesnt have a mesh");

Material meshMat = new Material(Shader.Find("Unlit/Color"));

meshMat.color = Color.red;

gameObject.AddComponent<MeshRenderer>().material = meshMat;

gameObject.AddComponent<MeshFilter>();

/* Neighbor Graph generation */

ProcessNeighborGraph();

}

private void ThreadedNeighborGraphProcess(Vector3[] parentVerts, int[] parentTriangs) {

finishedProcessingNeighborGraph = false;

Debug.Log("Generating neighbor hierarchy in other thread...");

neighborGraph = GenerateNeighborGraph(parentVerts, parentTriangs);

Debug.Log("Finished generating hierarchy!");

finishedProcessingNeighborGraph = true;

}

private void ProcessNeighborGraph() {

finishedProcessingNeighborGraph = false;

Vector3[] parentVerts = parentMesh.vertices;

int[] parentTriangs = parentMesh.triangles;

Thread graphWorker = new Thread(new ThreadStart(() => ThreadedNeighborGraphProcess(parentVerts, parentTriangs)));

graphWorker.Start();

}

public void AddIndex(int i) {

if (!finishedProcessingNeighborGraph) {

Debug.LogError("Cant add index as neighbor graph is being processed!");

return;

}

if (!indexNeighbors.ContainsKey(parentMesh.vertices[i])) {

indexNeighbors.Add(parentMesh.vertices[i], new VertexNeighbor(i, neighborGraph[i]));

AddVertexToMesh(parentMesh.vertices[i], i);

}

}

public void RemoveIndex(int i) {

if (!finishedProcessingNeighborGraph) {

Debug.LogError("Cant remove index as neighbor graph is being processed!");

return;

}

if (indexNeighbors.ContainsKey(parentMesh.vertices[i])) {

indexNeighbors.Remove(parentMesh.vertices[i]);

RemoveVertexFromMesh(parentMesh.vertices[i], i);

}

}

private void AddVertexToMesh(Vector3 vert, int index) {

highlightMesh = new Mesh();

List<int> neighborsOfIndex = neighborGraph[index];

if (!indexRemap.ContainsKey(index)) {

vertices.Add(vert);

indexRemap.Add(index, vertices.Count - 1);

}

for (int i = 0; i < neighborsOfIndex.Count; i++) {

indices.Add(indexRemap[index]);

if (indexRemap.ContainsKey(neighborsOfIndex[i])) {

indices.Add(indexRemap[neighborsOfIndex[i]]);

} else {

vertices.Add(parentMesh.vertices[neighborsOfIndex[i]]);

indexRemap.Add(neighborsOfIndex[i], vertices.Count - 1);

indices.Add(indexRemap[neighborsOfIndex[i]]);

}

}

highlightMesh.SetVertices(vertices);

highlightMesh.SetIndices(indices.ToArray(), MeshTopology.Lines, 0);

GetComponent<MeshFilter>().sharedMesh = highlightMesh;

}

private void RemoveVertexFromMesh(Vector3 vert, int index) {

highlightMesh = new Mesh();

if(indexRemap.ContainsKey(index)) {

for(int i = 0; i < indices.Count-1; i++) {

if(indices[i] == indexRemap[index]) {

indices[i] = -1;

indices[i + 1] = -1;

}

}

indices.RemoveAll(x => x == -1);

}

highlightMesh.SetVertices(vertices);

highlightMesh.SetIndices(indices.ToArray(), MeshTopology.Lines, 0);

GetComponent<MeshFilter>().sharedMesh = highlightMesh;

}

private List<int>[] GenerateNeighborGraph(Vector3[] _vertices, int[] _triangles) {

List<int>[] neighbor = new List<int>[_vertices.Length];

Dictionary<Vector3, int> currentConnections = new Dictionary<Vector3, int>();

for (int j = 0; j < _triangles.Length; j += 3) {

int j1 = _triangles[j + 0];

int j2 = _triangles[j + 1];

int j3 = _triangles[j + 2];

if (neighbor[j1] == null) neighbor[j1] = new List<int>();

if (neighbor[j2] == null) neighbor[j2] = new List<int>();

if (neighbor[j3] == null) neighbor[j3] = new List<int>();

if (!currentConnections.ContainsKey(_vertices[j1]))

currentConnections.Add(_vertices[j1], j1);

if (!currentConnections.ContainsKey(_vertices[j2]))

currentConnections.Add(_vertices[j2], j2);

if (!currentConnections.ContainsKey(_vertices[j3]))

currentConnections.Add(_vertices[j3], j3);

neighbor[currentConnections[_vertices[j1]]].Add(j2);

neighbor[currentConnections[_vertices[j1]]].Add(j3);

neighbor[currentConnections[_vertices[j2]]].Add(j1);

neighbor[currentConnections[_vertices[j2]]].Add(j3);

neighbor[currentConnections[_vertices[j3]]].Add(j1);

neighbor[currentConnections[_vertices[j3]]].Add(j2);

}

return neighbor;

}

//contains index of a vertex and its neighbor indexes

private class VertexNeighbor {

private int index; public int Index { get { return index; } }

private List<int> neighborIndexes; public List<int> NeighborIndexes { get { return neighborIndexes; } }

public VertexNeighbor(int i, List<int> indexes) {

index = i;

neighborIndexes = indexes;

}

}

}