'LibGDX World/Screen coordinates to Viewport coordinates (with camera zoom)

I have a game map that is larger than the viewport, so the camera moves around. The player can also zoom the camera in/out. When anywhere on the screen is tapped I get the device screen coordinates. So if I have a 1920x1080 screen and tap the upper right corner i get (1920, 1080) whereas if I have a 1280x720 screen tapping at the same place gives a (1280, 720).

My viewport (virtual screen size) is set to 1280x720. If I use cam.unproject(Vector2) or viewport.unproject(Vector2) I get the corresponding world coordinates. So if the camera is not translated or zoomed. Tapping the upper left corner on any screen size gives a (1280,720). But if I scroll the map up and to the right I get bigger values than (1280,720). In fact I get (1280 + camera.position.x, 720 + camera.position.y). And using zoom complicates things even more and I have to manually calculate what I'm looking for.

My question is, how can I get the viewport coordinate and not the world coordinate? Does LibGDX have a way around this?



Solution 1:[1]

You could do something like this

    public static Vector3 MultiplyPoint(Matrix4 m, Vector3 point)
{
    Vector3 result = new Vector3(0, 0, 0);
    result.x = m.val[Matrix4.M00] * point.x + m.val[Matrix4.M01] * point.y +  m.val[Matrix4.M02]* point.z +  m.val[Matrix4.M03];
    result.y = m.val[Matrix4.M10] * point.x + m.val[Matrix4.M11] * point.y + m.val[Matrix4.M12] * point.z + m.val[Matrix4.M13];
    result.z = m.val[Matrix4.M20] * point.x + m.val[Matrix4.M21] * point.y + m.val[Matrix4.M22] * point.z + m.val[Matrix4.M23];
    float num = m.val[Matrix4.M30] * point.x + m.val[Matrix4.M31] * point.y + m.val[Matrix4.M32] * point.z + m.val[Matrix4.M33];
    num = 1f / num;
    result.x *= num;
    result.y *= num;
    result.z *= num;
    return result;
}

public static Vector2 WorldToScreenPoint(float x, float y)
{
    Matrix4 V = Constants.Camera.view;
    Matrix4 P = Constants.Camera.projection;
    
    Matrix4 MVP = P.mul(V); // Skipping M, point in world coordinates
    Vector3 screenPos = MultiplyPoint(MVP, new Vector3(x, y, 0));
    
    Vector3 screenPoint = new Vector3(screenPos.x + 1f, screenPos.y + 1f, screenPos.z + 1f).scl(0.5f); // returns x, y in [0, 1] internal. 
    
    return new Vector2(screenPoint.x * Constants.GlobalWidth, screenPoint.y * Constants.GlobalHeight); // multiply by viewport width and height to get the actual screen coordinates.
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 GabeTheApe