Search code examples
c#unity-game-enginetexturesmeshvertex

Unity3D : is it possible to dynamically change a vertex in a mesh without recalculating trianglaes, uv and normals?


I have successfully created a cube following this code : http://wiki.unity3d.com/index.php/ProceduralPrimitives and, with texture, got this :

enter image description here

But if I try to change one vertex's position, half of the triangles get inverted such as shown in the following screen.
I also tried to change all of the vertices at once by giving them the exact same values ( I copy / pasted the code used to create the vertices ) and reassign the vertices with mesh.vertices = vertices but I get the exact same triangle problem

enter image description here

All of the triangles are there but half of them end up mirrored inside of the mesh and the shape of a cube is lost.

When I also recalculate the triangles the texture is out of place and I need to recalculate the normals and UV if I want a normal looking cube.

This does not seem right to me as I am changing the positions of vertices but not changing any of the edges / triangles. I do not understand why I must recalculate them.

Is there a way I can change the shape of my cube without having to recalculate everything ?
I am not adding / deleting vertices and the cube is not being twisted / deformed ( the top left vertex remains the top left vertex and so on )

Note : I wish to change the positions of the vertices at run time

Edit : this is the code that induces the error :

    float width = 10f;
    float height = 10f;
    float length = 10f;
    Vector3 p0 = new Vector3(-width * .5f, -height * .5f, length * .5f);
    Vector3 p1 = new Vector3(width * .5f, -height * .5f, length * .5f);
    Vector3 p2 = new Vector3(width * .5f, -height * .5f, -length * .5f);
    Vector3 p3 = new Vector3(-width * .5f, -height * .5f, -length * .5f);
    Vector3 p4 = new Vector3(-width * .5f, height * .5f, length * .5f);
    Vector3 p5 = new Vector3(width * .5f, height * .5f, length * .5f);
    Vector3 p6 = new Vector3(width * .5f, height * .5f, -length * .5f);
    Vector3 p7 = new Vector3(-width * .5f, height * .5f, -length * .5f);

    Vector3[] vertices = new Vector3[] {
        // Bottom
        p0, p1, p2, p3,
        // Left
        p7, p4, p0, p3,
        // Front
        p4, p5, p1, p0,
        // Back
        p6, p7, p3, p2,
        // Right
        p5, p6, p2, p1,
        // Top
        p7, p6, p5, p4
    };
    _selectionBoxMeshFilter.mesh.vertices = vertices;

It is the exact same code as the one used to create the mesh in the first place. The mesh does weird stuff when this is called after the mesh was created.


Solution

  • mesh.Optimize reorders vertices and re-arranges triangles to make rendering more efficient.

    You aren't replacing the current values of vertices with the same values, because the ordering happens to be different in this case.

    To fix this problem, don't assume that the vertices are going to be in the same place as they are when they are assigned to mech.vertices.

    One solution could be making a mapping of which indices are for each vertex after mesh.Optimize is called, and assign/edit vertices based on that:

    private List<int> indicesOfP0;
    
    // ...
    
    _selectionBoxMeshFilter.mesh.Optimize();
    
    indicesOfP0 = new List<int>();
    for (int i = 0 ; i < _selectionBoxMeshFilter.mesh.vertices.Length ; i++)
    {
        if (_selectionBoxMeshFilter.mesh.vertices[i] == p0)
        { 
            indicesOfP0.Add(i);
        } 
    }
    
    // ...
    
    Vector3[] vertices = _selectionBoxMeshFilter.mesh.vertices;
    foreach (int i in indicesOfP0)
    {
        vertices[i] += Vector3.up * Time.deltaTime;
    }
    _selectionBoxMeshFilter.mesh.vertices = vertices;
    

    A better option would probably just be calculating what to do with each vertex based on its position:

    Vector3[] vertices = _selectionBoxMeshFilter.mesh.vertices;
    for (int i = 0; i < vertices.Length; i++)
    {
        if (vertices[i].y>0) 
        {
            vertices[i] += Vector3.up * Time.deltaTime;
        }
    }
    _selectionBoxMeshFilter.mesh.vertices = vertices;
    

    But if you must assign an array of positions, consider basing it off the positions after mesh.Optimize is called:

    private Vector3[] startPos;
    
    // ...
    
    _selectionBoxMeshFilter.mesh.Optimize();
    
    startPos = _selectionBoxMeshFilter.mesh.vertices;
    
    // ...
    
    _selectionBoxMeshFilter.mesh.vertices = startPos;