Pinpointing one vertice with RaycastHit

Hello,
I have a script where I click on an object using RaycastHit and it will then do something. However I have been trying to be more specific with this and instead of clicking on the entire object I would like to be able to click on one vertice. I made a Vector3 array using the MeshFilter, however I am stumped at this point because I can’t individually access one vertice let alone click on one. I searched the manuel/API and can’t anything. Is there any functions on how to do this. Thank you for your time.

To pick the closest vertex on a mesh you need to have a MeshCollider attached to the object that uses the same mesh as your visual mesh. The mesh collider must not be set to convex as this would recalculate a convex hull mesh, so the vertices wouldn’t match up at all.

A RaycastHit already gives you pretty much everything you need. To find the closest vertex you first have to determine the 3 vertices of the triangle that you hit. For this you just take the triangle index of your RaycastHit and multiply it by “3” to get the index of the first triangle index.

Now take the barycentricCoordinate of your hit and determine which component is the largest (x, y or z). If x is has the largest value, your desired vertex index is triangles[triangleIndex*3]. If the y component is the largest, you have to use triangles[triangleIndex*3 + 1] and if it’s “z” use triangles[triangleIndex*3 + 2]

The resulting index would be used to index your vertices array of your mesh.

As a straight forward utility method:

public static int GetClosestVertex(RaycastHit aHit, int[] aTriangles)
{
    var b = aHit.barycentricCoordinate;
    int index = aHit.triangleIndex * 3;
    if (aTriangles == null || index < 0 || index + 2 >= aTriangles.Length)
        return -1;

    if (b.x > b.y)
    {
        if (b.x > b.z)
            return aTriangles[index]; // x
        else
            return aTriangles[index + 2]; // z
    }
    else if (b.y > b.z)
        return aTriangles[index + 1]; // y
    else
       return aTriangles[index + 2]; // z
}

If something goes wrong the method returns “-1”, otherwise it will return the vertex index that is closest to the hit point.

This method expects your RaycastHit as parameter as well as the triangle array of your mesh.

You’re right, there is no API for such a thing. Plus, a vertex is a point in space with no bounds, so it’s infinitely small and impossible to collide with, not to mention it’s usually stored in VRAM.

What you’re going to have to do is keep a local copy of the vertex array in RAM, then write a manual raycast function against points (you can find one via google pretty easy), which will return a distance from the point to the line of the raycast. Then just pick the vertex with the smallest returned distance, and make sure you check for a threshold.

That’ll probably be wildly unoptimized for large meshes, though, so you’re definitely going to want to do an initial bounds check for the whole object, and consider a binary space partitioning algorithm to narrow down the possible vertices to actually check.

One temporary solution as I mentioned in the other post was to run a while script that cycles through all of the points in the vertex array. At every point create a sphere whose position is that point. Make the sphere invisible (no MeshFilter) and the right size depending on how packed the vertices are. Make sure the sphere has a SphereCollider so that the raycast function actually collides with it. Then, when the user left clicks on a certain sphere (Input commands) you’ll know which vertice they are selecting because that vertice’s position is the same as the position of the sphere. Cycle through the vertices array comparing the sphere’s position with the vertex and when you have the vertex record it’s index. Once you have the index you can have follow up commands change that vertice because you know its index. I am fairly confident this solution would work for the time being.

Here is a picture of what I mean: Imgur: The magic of the Internet

Do you need me to be more specific?

EDIT2: Just wrote a quick program that constructed a sphere at every vertex so it should be completely doable. Note however that if you instantiate one sphere per vertex you could be looking at a huge number of instantiated GameObjects, which may or may not be problematic depending on your computer.

Edit1: Changed MeshCollider to SphereCollider