Cannot convert vertex positions of a mesh into world space

Guys, I have a problem. On this mesh model with mesh collider, I need to be able to click on any of the vertices by the mouse and a sphere should mark that point, meaning it should move there and it should be done in runtime. The problem is that I cannot match the position of the cursor with the positions of the vertices on the model.

Any ideas?

if you have vertex position relative to object, you can just add his object position, to make it wordSpace.
If you want to do it in shader, there is a function unity_ObjectToWorld

There are several things not clear. First of all you said

I want to move a sphere to the mouse
position of the vertices

This makes no sense. There is no natural connection betwen vertex positions and the mouse position. Maybe you want to know the world position of the closest vertex to the mouse position?

The next thing is since your model is a character model it’s most likely a SkinnedMeshRender. The problem here is that the mesh vertices which are defined in localspace are “unkinned”. So just translating them to worldspace may not be what you want because when the model is actually affected by skinning. To get the skinned vertex positions in local space you would need to use SkinnedMeshRenderer.BakeMesh which will apply the skinning based on the current animation state. The vertices of that baked mesh can then be translated to worldspace by using Transform.TransformPoint(). Of course you have to use the transform of the skinned mesh renderer since the vertices are relative to this coordinate space.

Though it’s still not clear how the mouse position applies here. If you want to find a point on the surface of your skinned mesh, based on where the mouse position is, that’s even trickier. To use Raycasting you need a mesh collider. However meshcolliders can not be skinned. So a MeshCollider will only have a static mesh. If you just want to pick a point once you could use BakeMesh as above, create a MeshCollider dynamically and assign the baked mesh to the mesh collider. Now you can raycast against that collider to determine the intersection point of the mouse ray and your model.

Though as i said your question is not clear at all. Please edit your question and add more details. Specifically:

  • Do you actually have a skinned mesh?
  • What role does the mouse position play in your problem?
  • What is the exact goal? Describe the whole event process (user action → program reaction)
  • Maybe add some more background information what this is all about. Do you need this at runtime? Do you try to create an editor script?

edit

Ok here we go. Since we cleared up some of the questions this is how you can select the “nearest” vertex to the mouse position for a non skinned mesh:

  • First you create a Ray with Input.mousePosition and Camera.ScreenPointToRay.
  • Now grab a reference to the MeshCollider on your object and use it’s Raycast method. This method is different from Physics.Raycast as it will only detect intersections with this specific collider. Though if you want you can use Physics.Raycast as well in case you specifically want to be able to shield parts of the mesh. Though in this case you have to verify that you actually hit the right object.
  • Now you can use the RaycastHit information to determine the closest vertex. Luckily this can be achieved quite easily with the “triangleIndex” and the “barycentricCoordinate” of the RaycastHit struct.

The implementation would look like this:

public static int GetClosestVertex(MeshCollider aCol)
{
    var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    RaycastHit hit;
    if (aCol.Raycast(ray, out hit, float.PositiveInfinity))
    {
        var triangles = aCol.sharedMesh.triangles;
        var bc = hit.barycentricCoordinate;
        if (bc.x > bc.y)
        {
            if (bc.x > bc.z)
                return triangles[hit.triangleIndex*3];
            return triangles[hit.triangleIndex*3+2];
        }
        if (bc.y > bc.z)
            return triangles[hit.triangleIndex*3+1];
        return triangles[hit.triangleIndex*3+2];
    }
    return -1;
}

This method will return the vertex index of the vertex that is closest to the hit point on the mesh. To get the actual world space position of that vertex you just need to grab that vertex from the vertices array and transform it to worldspace using Transform.TransformPoint:

public static Vector3 GetWorldspaceVertex(MeshCollider aCol, int aIndex)
{
    var verts = aCol.sharedMesh.vertices;
    return aCol.transform.TransformPoint(verts[aIndex]);
}

With those two methods you can do this:

int index = GetClosestVertex(yourMeshCollider);
if (index >= 0)
{
    Vector3 v = GetWorldspaceVertex(yourMeshCollider, index);
    // do something with "v". It's the world space position of the closest vertex.
}

Keep in mind that “GetClosestVertex” can return “-1” when your mouse is not over the mesh. This is handled with the “if (index >= 0)”.