Mir
Mir

Reputation: 323

Unity - How to use mesh triangleIndex to recolor a triangle when clicked?

I am wondering how to recolor individual triangles within a mesh in Unity when they are clicked. My code below allows me to detect where clicks are landing on the surface of the mesh in 3D space. From there I attempt to get the triangle index from the mesh and set the corresponding index in the color array to red.

However, I can see that where I click (where the green cube is translated to in the image) is not even close to where the triangle is recolored on the mesh. Is there some kind of index or coordinate conversion that needs to happen in order to color the exact triangle where the raycast/click collides with the mesh?

I am using a standard unlit shader for the sphere in the image and sometimes get an index out of bounds error, which indicates that I'm overrunning the length of the color array when attempting to set it's color to red.

Any help or insight into this would be greatly appreciated. Thanks!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MyRayDraw : MonoBehaviour
{
    public GameObject cube;
    private MeshRenderer meshRenderer;
    Mesh mesh;
    Vector3[] vertices;
    Color[] colorArray;

    private void Start()
    {
        mesh = transform.GetComponent<MeshFilter>().mesh;
        vertices = mesh.vertices;

        // create new colors array where the colors will be created
        colorArray = new Color[vertices.Length];
        for (int k = 0; k < vertices.Length; k++)
        {
            colorArray[k] = Color.white;
        }
        mesh.colors = colorArray;
    }

    void Update()
    {        
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            if (Physics.Raycast(ray, out RaycastHit hit))
            {
                Debug.Log(hit.triangleIndex);
                cube.transform.position = hit.point;
                colorArray[hit.triangleIndex] = Color.red;
                mesh.colors = colorArray;
            }
            else
            {
                Debug.Log("no hit");
            }
        }
    }
}

enter image description here

Upvotes: 3

Views: 4196

Answers (1)

derHugo
derHugo

Reputation: 90679

mesh.colors are the colors of the vertices not of the triangles.

What you want to do is not coloring a single vertex but the triangle you hit.

mesh.triangles

The array is a list of triangles that contains indices into the vertex array. The size of the triangle array must always be a multiple of 3. Vertices can be shared by simply indexing into the same vertex.

In RaycastHit.triangleIndex you can find an example of how to get the according vertex indices for a certain triangle index.

For better understanding here I leave an example for a cube mesh.

The vertices are

INDEX | 3D-Position
0     | ( 0.5, -0.5,  0.5)
1     | (-0.5, -0.5,  0.5)
2     | ( 0.5,  0.5,  0.5)
3     | (-0.5,  0.5,  0.5)
4     | ( 0.5,  0.5, -0.5)
5     | (-0.5,  0.5, -0.5)
6     | ( 0.5, -0.5, -0.5)
7     | (-0.5, -0.5, -0.5)
8     | ( 0.5,  0.5,  0.5)
9     | (-0.5,  0.5,  0.5)
10    | ( 0.5,  0.5, -0.5)
11    | (-0.5,  0.5, -0.5)
12    | ( 0.5, -0.5, -0.5)
13    | ( 0.5, -0.5,  0.5)
14    | (-0.5, -0.5,  0.5)
15    | (-0.5, -0.5, -0.5)
16    | (-0.5, -0.5,  0.5)
17    | (-0.5,  0.5,  0.5)
18    | (-0.5,  0.5, -0.5)
19    | (-0.5, -0.5, -0.5)
20    | ( 0.5, -0.5, -0.5)
21    | ( 0.5,  0.5, -0.5)
22    | ( 0.5,  0.5,  0.5)
23    | ( 0.5, -0.5,  0.5)

The triangle list referencing to the vertex indexes ranging from 0 to 23 looks like

              (0, 2, 3, 0, 3, 1, 8, 4, 5, 8, 5, 9, 10, 6, 7, 10, 7, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23)

triangleIndex: 0        1        2        3        4         5          6           7           8           9           10          11

as you can see it is a plain list. Therefore in order to take a certain triangle out of that list first of all you have to "jump" over a certain amount of vertex sets using the triangleIndex where one set consists of exactly 3 vertex indices.

Then you take three sequential vertex indices and you have your 3 vertices of the target triangle.

For a sphere however there are 515 vertices but 2304 triangles! => for the most triangleIndex you got an IndexOutOfRangeException by trying to directly access mesh.verteces[some index > 511].

int[] triangles;

private void Start()
{
    ...

    triangles = mesh.triangles;
}

private void Update()
{
    ...

        var vertIndex1 = triangles[hit.triangleIndex * 3 + 0];
        var vertIndex2 = triangles[hit.triangleIndex * 3 + 1];
        var vertIndex3 = triangles[hit.triangleIndex * 3 + 2];

        colorArray[vert1] = Color.red;
        colorArray[vert2] = Color.red;
        colorArray[vert3] = Color.red;

        mesh.colors = colorArray;

    ...
}

enter image description here

Upvotes: 10

Related Questions