I want to make a hole of N vertices in the face of a Cube, I'm adding just four like a square in this post to be simple, but eventually I want to be able to add as many as I want and maybe in a circular shape, but I can't make the triangle part right. The code to reproduce my steps are bellow.
1- After Raycasting into a Cube, to get the mesh I do this (don't forget to add a MeshCollider to the Cube):
if (Input.GetMouseButtonUp(0))
{
RaycastHit hit;
if (!Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit))
{
return;
}
var meshCollider = hit.collider as MeshCollider;
var mesh = meshCollider.sharedMesh;
Mesh meshChanged = Instantiate(mesh);
meshChanged.name = "Changed Cube";
}
2- Them I get the vertices, normals, triangles and uv:
Vector3[] vertices = mesh.vertices.ToArray();
Vector3[] normals = mesh.normals.ToArray().ToArray();
int[] triangles = mesh.triangles.ToArray();
Vector2[] uvs = mesh.uv.ToArray();
3- I get the position of the hit.point, in the Cube local space and the normal of the triangle that the hit.point hitted.
Vector3 hitPositionOnMesh = hit.transform.InverseTransformPoint(hit.point);
Vector3 hittedTriangleNormal = CalculateNormalOfTriangle(normals, triangles, hit.triangleIndex);
3.a- CalculateNormalOfTriangle Function
private static Vector3 CalculateNormalOfTriangle(Vector3[] normals, int[] triangles, int triangleIndex)
{
var n0 = normals[triangles[triangleIndex * 3 + 0]];
var n1 = normals[triangles[triangleIndex * 3 + 1]];
var n2 = normals[triangles[triangleIndex * 3 + 2]];
return ((n0 + n1 + n2) / 3);
}
4- To be simple, I add only four vertices and add a distance (0.1f) to them.
Vector3 hitPositionOnMesh1 = new Vector3();
Vector3 hitPositionOnMesh2 = new Vector3();
Vector3 hitPositionOnMesh3 = new Vector3();
Vector3 hitPositionOnMesh4 = new Vector3();
Vector3 hitP = hit.transform.position;
Vector3 point1 = new Vector3(hitP.x + 0.1f, hitP.y + 0.1f, hitP.z);
Vector3 point2 = new Vector3(hitP.x - 0.1f, hitP.y + 0.1f, hitP.z);
Vector3 point3 = new Vector3(hitP.x - 0.1f, hitP.y - 0.1f, hitP.z);
Vector3 point4 = new Vector3(hitP.x + 0.1f, hitP.y - 0.1f, hitP.z);
hitPositionOnMesh1 = point1;
hitPositionOnMesh2 = point2;
hitPositionOnMesh3 = point3;
hitPositionOnMesh4 = point4;
5- I create the outside vertices and inside vertices lists.
List<Vector3> OutsideVerticesOfFace = new List<Vector3>();
for (int i = 0; i < normals.Length; i++)
{
if (normals[i] == hittedTriangleNormal)
{
OutsideVerticesOfFace.Add(vertices[i]);
}
}
List<Vector3> InsideVerticesOfFace = new List<Vector3>
{
hitPositionOnMesh1,
hitPositionOnMesh2,
hitPositionOnMesh3,
hitPositionOnMesh4
};
6- I remove the triangles from face of the Cube that the hit.point hitted.
triangles = RemoveFace(hittedTriangleNormal, triangles, normals);
6.a- RemoveFace Function
private static int[] RemoveFace(Vector3 normalToCompare, int[] triangles, Vector3[] normals)
{
List<int> trianglesToRemove = new List<int>();
for (int i = 0; i < triangles.Length; i += 3)
{
var p0 = normals[triangles[i + 0]];
var p1 = normals[triangles[i + 1]];
var p2 = normals[triangles[i + 2]];
if (((p0 + p1 + p2) / 3) == normalToCompare)
{
trianglesToRemove.Add(triangles[i + 0]);
trianglesToRemove.Add(triangles[i + 1]);
trianglesToRemove.Add(triangles[i + 2]);
}
}
int[] _triangles = RemoveTriangles(triangles, trianglesToRemove.ToArray());
return _triangles;
}
6.b- RemoveTriangles Function
private static int[] RemoveTriangles(int[] triangles, int[] trianglesToRemove)
{
List<int> tmpT = new List<int>(triangles.ToList());
for (int i = 0; i < triangles.ToList().Count; i += 3)
{
bool v1 = (triangles.ToList()[i + 0] == trianglesToRemove[0]);
bool v2 = (triangles.ToList()[i + 1] == trianglesToRemove[1]);
bool v3 = (triangles.ToList()[i + 2] == trianglesToRemove[2]);
if ((v1 && v2 && v3))
{
tmpT.RemoveRange(i, trianglesToRemove.Length);
}
}
triangles = tmpT.ToArray();
return triangles;
}
7- I create a new list of vertices and change the normals and uvs lists.
List<Vector3> newVertices = new List<Vector3>(vertices.ToArray());
newVertices.AddRange(InsideVerticesOfFace);
normals = (new List<Vector3>(normals)
{
hittedTriangleNormal,
hittedTriangleNormal,
hittedTriangleNormal,
hittedTriangleNormal,
}).ToArray();
uvs = (new List<Vector2>(uvs)
{
new Vector2(hitPositionOnMesh1.x, hitPositionOnMesh1.z),
new Vector2(hitPositionOnMesh2.x, hitPositionOnMesh2.z),
new Vector2(hitPositionOnMesh3.x, hitPositionOnMesh3.z),
new Vector2(hitPositionOnMesh4.x, hitPositionOnMesh4.z),
}).ToArray();
8- Now, this is the problem, this is where it isn't working, the part of making the new triangles.
List<int> outsideTriangles;
outsideTriangles = MakeOuterTriangles(_vertices.ToArray(), InsideVerticesOfFace, OutsideVerticesOfFace);
List<int> newTriangles = triangles.ToList();
newTriangles.AddRange(outsideTriangles.ToArray());
triangles = newTriangles.ToArray();
meshChanged.vertices = newVertices.ToArray();
meshChanged.normals = normals;
meshChanged.triangles = triangles;
meshChanged.uv = uvs;
8.a- I tried Trigonometry, using cosine law, c = sqrt(a^2 + b^2 - 2ab * cos(C)). c being the distance from the inside vertice (0) with second outside vertice (1), a being the distance from the outside vertice (0) with outside vertice (1) and b being the distance from the inside vertice (0) with outside vertice (0). C being the angle between inside vertice (0) and outside vertice (0), like in the image:
Firstly I rearrange the outsideVertice list, because for some reason, Unity create vertices in a weird "0, 1, 3, 2". So I rearrange it so that the last item becames the third, and the second item becames the last, "0, 1, 2 (last), 3 (third)" and I also add the first item in the last position, so that it make a loop.
Secondly I sorted the newOutside list by distance the from the insideVertice list, so that outsideVertices[0] is always the right item.
*I tried to change the variables parameters to see if I misplaced something, it didn't work at all, in the best case, the triangles of the face where deleted or there where no triangles at all.
public static List<int> MakeOuterTriangles(Vector3[] vertices, List<Vector3> insideVertices, List<Vector3> outsideVertices)
{
List<int> newTriangles = new List<int>();
List<Vector3> newOutside = new List<Vector3>() { outsideVertices[0], outsideVertices[1], outsideVertices[outsideVertices.Count - 1], outsideVertices[2], outsideVertices[0]};
for (int i = 0; i < insideVertices.Count - 1; i++)
{
newOutside = newOutside.OrderBy(x => Vector3.Distance(x, insideVertices[i])).ToList();
float side_a = Vector3.Distance(outsideVertices[0], outsideVertices[1]);
float side_b = Vector3.Distance(outsideVertices[0], insideVertices[i]);
float angle = Vector3.Angle(outsideVertices[0], insideVertices[i]);
float side_c = Mathf.Sqrt(Mathf.Pow(side_a, 2) + Mathf.Pow(side_b, 2) - 2 * (side_a * side_b) * Mathf.Cos(angle));
if (side_c == Vector3.Distance(outsideVertices[1], insideVertices[i]))
{
newTriangles.Add(vertices.ToList().IndexOf(insideVertices[i]));
}
}
return newTriangles;
}
8.b- I tried to adapt this code link1, from the video link2, the code is bellow, but I assume that the number of vertice in the inside has to be the same as the outside. So it didn't work also.
public static List<int> MakeOuterTriangles(Vector3[] vertices, List<Vector3> insideVertices, List<Vector3> outsideVertices)
{
List<int> newTriangles = new List<int>();
int sides = insideVertices.Count + outsideVertices.Count/2;
for(int i = 0; i<sides;i++)
{
int outerIndex = i;
int innerIndex = i+sides;
newTriangles.Add(outerIndex);
newTriangles.Add(innerIndex);
newTriangles.Add((i+1)%sides);
newTriangles.Add(outerIndex);
newTriangles.Add(sides+((sides+i-1)%sides));
newTriangles.Add(outerIndex+sides);
}
return newTriangles;
}
8.c- This my new try (code bellow), I get this image result:
List<int> outsideTriangles = new List<int>();
for (int i = 0; i < OutsideVerticesOfFace.Count - 1; i++)
{
if (i == OutsideVerticesOfFace.Count - 1)
{
InsideVerticesOfFace = InsideVerticesOfFace.OrderBy(x => Vector3.Distance(x, OutsideVerticesOfFace[i])).ToList();
float distanceV = Mathf.Abs(Vector3.Distance(OutsideVerticesOfFace[0], OutsideVerticesOfFace[i]));
if (distance == 1)
{
outsideTriangles.Add(newVertices.ToList().IndexOf(OutsideVerticesOfFace[0]));
outsideTriangles.Add(newVertices.ToList().IndexOf(InsideVerticesOfFace[0]));
outsideTriangles.Add(newVertices.ToList().IndexOf(OutsideVerticesOfFace[i]));
}
}
else if (i != OutsideVerticesOfFace.Count - 1)
{
InsideVerticesOfFace = InsideVerticesOfFace.OrderBy(x => Vector3.Distance(x, OutsideVerticesOfFace[i])).ToList();
float distanceV = Mathf.Abs(Vector3.Distance(OutsideVerticesOfFace[i + 1], OutsideVerticesOfFace[i]));
if (distanceV == 1)
{
outsideTriangles.Add(newVertices.ToList().IndexOf(OutsideVerticesOfFace[0]));
outsideTriangles.Add(newVertices.ToList().IndexOf(InsideVerticesOfFace[0]));
outsideTriangles.Add(newVertices.ToList().IndexOf(OutsideVerticesOfFace[i]));
}
}
}
8.d- (Almost success) First I have made a mistake, in the 4 step, I thought that I was using that piece of code, but I wasn't and I only notice it later, the rest of the could is the same thing. Here is the code that I was actually using.
Vector3 pivot = hit.transform.InverseTransformPoint(hit.transform.position);
Vector3 point = hitPositionOnMesh;
Vector3 dir = point - pivot;
dir = Quaternion.Euler(new Vector3(180, 0, 0)) * dir;
point = dir + pivot;
Vector3 point1 = new Vector3(point.x + distance, point.y + distance, point.z);
Vector3 point2 = new Vector3(point.x - distance, point.y + distance, point.z);
Vector3 point3 = new Vector3(point.x + distance, point.y - distance, point.z);
Vector3 point4 = new Vector3(point.x - distance, point.y - distance, point.z);
dir = point1 - pivot;
dir = Quaternion.Euler(new Vector3(-180, 0, 0)) * dir;
point1 = dir + pivot;
dir = point2 - pivot;
dir = Quaternion.Euler(new Vector3(-180, 0, 0)) * dir;
point2 = dir + pivot;
dir = point3 - pivot;
dir = Quaternion.Euler(new Vector3(-180, 0, 0)) * dir;
point3 = dir + pivot;
dir = point4 - pivot;
dir = Quaternion.Euler(new Vector3(-180, 0, 0)) * dir;
point4 = dir + pivot;
hitPositionOnMesh1 = point1;
hitPositionOnMesh2 = point2;
hitPositionOnMesh3 = point3;
hitPositionOnMesh4 = point4;
I was planning to use this code so that no matter what face the Raycast hitted, I would only need to rotate the face to match the positive z axis, and then I would only need to add the distance from the points and rotate it back. But currently it only works if you hit the negative z axis of the Cube, I don't know if it would work with other faces, I didn't implement it to them yet. And since my question isn't about only 4 vertices, I will not post 8.d as the answer.
public static List<int> MakeOuterTriangles(Vector3[] vertices, List<Vector3> insideVertices, List<Vector3> outsideVertices)
{
List<int> newTriangles = new List<int>();
for (int i = 0; i < OutsideVerticesOfFace.Count; i++)
{
List<Vector3> newOutsideTriangles = OutsideVerticesOfFace.Skip(i).Concat(OutsideVerticesOfFace.Take(i)).ToList();
List<Vector3> newInsideTriangles = InsideVerticesOfFace.Skip(i).Concat(InsideVerticesOfFace.Take(i)).ToList();
if (i % 2 == 0)
{
if (i % 3 == 0)
{
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[i]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[newOutsideTriangles.Count - 2]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[i]));
}
else if(i % 3 == 1)
{
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[newOutsideTriangles.Count - 2]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[newOutsideTriangles.Count - 2]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[newOutsideTriangles.Count - 3]));
}
else
{
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[newOutsideTriangles.Count - 3]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[newOutsideTriangles.Count - 1]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[newOutsideTriangles.Count - 3]));
}
}
else if (i % 2 == 1)
{
if (i % 3 == 0)
{
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[newOutsideTriangles.Count - 3]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[newOutsideTriangles.Count - 2]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[newOutsideTriangles.Count - 2]));
}
else if (i % 3 == 1)
{
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[i]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[i]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[newOutsideTriangles.Count - 2]));
}
}
}
for (int i = 0; i < OutsideVerticesOfFace.Count; i++)
{
List<Vector3> newOutsideTriangles = OutsideVerticesOfFace.Skip(i).Concat(OutsideVerticesOfFace.Take(i)).ToList();
List<Vector3> newInsideTriangles = InsideVerticesOfFace.Skip(i).Concat(InsideVerticesOfFace.Take(i)).ToList();
if (i % 2 == 0)
{
if (i % 3 == 0)
{
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[i]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[newOutsideTriangles.Count - 3]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[i]));
}
else
{
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[newOutsideTriangles.Count - 1]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[newOutsideTriangles.Count - 3]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[newOutsideTriangles.Count - 1]));
}
}
else if (i % 2 == 1)
{
if (i % 3 == 0)
{
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[newOutsideTriangles.Count - 3]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[newOutsideTriangles.Count - 1]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[i]));
}
else if (i % 3 == 1)
{
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[i]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newInsideTriangles[newOutsideTriangles.Count - 2]));
outsideTriangles.Add(_vertices.ToList().IndexOf(newOutsideTriangles[newOutsideTriangles.Count - 2]));
}
}
}
return newTriangles;
}