0
\$\begingroup\$

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:
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:

image2

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;
}

image

\$\endgroup\$
3
  • 3
    \$\begingroup\$ It's not clear to me from your comments and code whether you're aware of the concept of winding order (and are preserving it during your sort)? The order the vertices appear in is used to determine which is the "front" and "back" of a triangle (that's one reason Unity orders the verts the way it does). This is referenced in the docs (near the bottom). You could try turning off back face culling and see if the missing faces appear. If so, it's likely a winding order issue. \$\endgroup\$
    – Basic
    Oct 9 at 21:49
  • \$\begingroup\$ Instead of accreting more and more code onto the question, making it longer and more intimidating to read, you may want to edit your post to streamline it and focus solely on your new "almost successful" attempt. The less time it takes for someone to get up to speed with your current needs, the faster they can give you accurate and useful answers. \$\endgroup\$
    – DMGregory
    Oct 11 at 0:28
  • \$\begingroup\$ It isn't intimidating to read, as I said in 8.d, just use 8.d in the place of 4 and ignore the other attempts, I'm putting every attemp so that you guys know what I'm doing, since my last attemps to make a question, this was a big request, then I'm fullfiling it. Don't mind it, when I find the asnwer for myself, I will clean everything up and put the only the explanation, meanwhile if you gentleman has something that can be usefull I would be grateful. \$\endgroup\$ Oct 11 at 0:44

0

You must log in to answer this question.

Browse other questions tagged .