Hazardinho

[HELP] getScreenFromWorldPosition

Recommended Posts

Hello community, I created a script to mark the location of the location in dx, where it shows the location, how many meters are from the location, and I was looking to improve this function where, basically, if the 3D Dx image is not activated On the player screen , the image will be in the corner of the image corresponding to the side where the mark is.

Image: https://imgur.com/a1AKZ96
 

Expectation made in the Photo Shop: https://imgur.com/Tskkamp

Share this post


Link to post

I am not exactly sure what the raw mathematics are

.

But you can use for your first iteration this function:

local screenPosX, screenPosY = getScreenFromWorldPosition (  x,  y,  z, 10, true  )
if screenPosX and screenPosY then
  
  
end

It will flatten the complex 3D orientation and therefore allows you to calculate the rotation:

https://wiki.multitheftauto.com/wiki/FindRotation

 

 

 

Share this post


Link to post

You can use optional argument 'edgeTolerance' of getScreenFromWorldPosition.

If 'location point' is out of screen, function will return screen x,y position outside of screen resolution. You only have to format returned x,y to be on edge of screen resolution.

https://i.imgur.com/tkVGLBX.png

It will return false if 'location point' is behind the screen.

  • Like 1

Share this post


Link to post
Posted (edited)
5 minutos atrás, Skuleris disse:

Você pode usar o argumento opcional 'edgeTolerance' de getScreenFromWorldPosition.

Se o 'ponto de localização' estiver fora da tela, uma função retornará a posição x, e a tela fora da resolução da tela. Você precisa de formatar x, e retorna para estar na borda da resolução da tela.

https://i.imgur.com/tkVGLBX.png

Ele retornou falso se o 'ponto de localização' estiver atrás da tela.

The argument you cited is giving an error. I don't know why, am I doing it right?

getScreenFromWorldPosition (x, y, pz + 2, 0.0.3, verdadeiro)

 

Edited by Hazardinho

Share this post


Link to post
Posted (edited)

Not right. Use 10 instead of 0.0.3

Tip: use /debugscript to see detailed error code. Not hard to figure out yourself! 

 

You will have to format returned x,y to be on the edge of resolution.

Edited by Skuleris

Share this post


Link to post
Just now, Skuleris said:

Not right. Use 10 instead of 0.0.3

Tip: use /debugscript to see detailed error code. Not hard to figure out yourself! 

 

Uou will have to format returned x,y to be on the edge of resolution.

Can you give me an example?

Share this post


Link to post

Since I am not that terrible at math here is my solution:

local function get_screen_border_coordinates(wx, wy, wz)
    local camMatArray = getElementMatrix(getCamera());
    local camMat_right = camMatArray[1];
    local camMat_forward = camMatArray[2];
    local camMat_up = camMatArray[3];
    local camMat_pos = camMatArray[4];
    local camMat = Matrix();
    local sW, sH = guiGetScreenSize();
    local s_ratio = sH / sW;
    camMat:setForward(Vector3(camMat_forward[1], camMat_forward[2], camMat_forward[3]));
    camMat:setRight(Vector3(camMat_right[1], camMat_right[2], camMat_right[3]));
    camMat:setUp(Vector3(camMat_up[1] * s_ratio, camMat_up[2] * s_ratio, camMat_up[3] * s_ratio));
    camMat:setPosition(Vector3(camMat_pos[1], camMat_pos[2], camMat_pos[3]));
    
    local invCamMat = camMat:inverse();
    
    local invVec = invCamMat:transformPosition(wx, wy, wz);
    
    local function to_real_coord(val)
        return ( val / 2 ) + 1/2;
    end
    
    local ratWidth, ratHeight, depthDist = invVec.x, invVec.y, invVec.z;
    
    if (ratWidth == 0) and (ratHeight == 0) then
        return 0, 0;
    elseif (ratWidth == 0) then
        return 0, to_real_coord(1 / ratHeight);
    elseif (ratHeight == 0) then
        return to_real_coord(1 / ratWidth), 0;
    end
    
    local dist_to_width = math.abs(1 / ratWidth);
    local dist_to_height = math.abs(1 / ratHeight);
    
    local scale_dist = math.min(dist_to_width, dist_to_height);
    
    ratWidth = ratWidth * scale_dist;
    ratHeight = ratHeight * scale_dist;
    
    return to_real_coord(ratWidth), to_real_coord(-ratHeight);
end

addEventHandler("onClientRender", root,
    function()
        local wx, wy, wz = 0, 0, 0;
        
        local screenRelX, screenRelY = get_screen_border_coordinates(wx, wy, wz);
        
        dxDrawText( "relX: " .. screenRelX .. ", relY: " .. screenRelY, 100, 300 );
        
        local screenWidth, screenHeight = guiGetScreenSize();
        
        local screenX, screenY = screenRelX * screenWidth, screenRelY * screenHeight;
        
        dxDrawRectangle( screenX - 25, screenY - 25, 50, 50, 0xFFFFFFFF );
    end
);

The mathematical background is very much related to my work at 3D software rendering, but I do not want to overcomplicate things for you. :) 

Share this post


Link to post
Posted (edited)

 

22 minutes ago, The_GTA said:

Since I am not that terrible at math here is my solution:


local function get_screen_border_coordinates(wx, wy, wz)
    local camMatArray = getElementMatrix(getCamera());
    local camMat_right = camMatArray[1];
    local camMat_forward = camMatArray[2];
    local camMat_up = camMatArray[3];
    local camMat_pos = camMatArray[4];
    local camMat = Matrix();
    local sW, sH = guiGetScreenSize();
    local s_ratio = sH / sW;
    camMat:setForward(Vector3(camMat_forward[1], camMat_forward[2], camMat_forward[3]));
    camMat:setRight(Vector3(camMat_right[1], camMat_right[2], camMat_right[3]));
    camMat:setUp(Vector3(camMat_up[1] * s_ratio, camMat_up[2] * s_ratio, camMat_up[3] * s_ratio));
    camMat:setPosition(Vector3(camMat_pos[1], camMat_pos[2], camMat_pos[3]));
    
    local invCamMat = camMat:inverse();
    
    local invVec = invCamMat:transformPosition(wx, wy, wz);
    
    local function to_real_coord(val)
        return ( val / 2 ) + 1/2;
    end
    
    local ratWidth, ratHeight, depthDist = invVec.x, invVec.y, invVec.z;
    
    if (ratWidth == 0) and (ratHeight == 0) then
        return 0, 0;
    elseif (ratWidth == 0) then
        return 0, to_real_coord(1 / ratHeight);
    elseif (ratHeight == 0) then
        return to_real_coord(1 / ratWidth), 0;
    end
    
    local dist_to_width = math.abs(1 / ratWidth);
    local dist_to_height = math.abs(1 / ratHeight);
    
    local scale_dist = math.min(dist_to_width, dist_to_height);
    
    ratWidth = ratWidth * scale_dist;
    ratHeight = ratHeight * scale_dist;
    
    return to_real_coord(ratWidth), to_real_coord(-ratHeight);
end

addEventHandler("onClientRender", root,
    function()
        local wx, wy, wz = 0, 0, 0;
        
        local screenRelX, screenRelY = get_screen_border_coordinates(wx, wy, wz);
        
        dxDrawText( "relX: " .. screenRelX .. ", relY: " .. screenRelY, 100, 300 );
        
        local screenWidth, screenHeight = guiGetScreenSize();
        
        local screenX, screenY = screenRelX * screenWidth, screenRelY * screenHeight;
        
        dxDrawRectangle( screenX - 25, screenY - 25, 50, 50, 0xFFFFFFFF );
    end
);

The mathematical background is very much related to my work at 3D software rendering, but I do not want to overcomplicate things for you. :) 

This will help me a lot, but a question, how can I do to identify that the markup is not showing on my screen? since 3D has certain angles that I am not visible on the screen? .

Edited by Hazardinho

Share this post


Link to post
Posted (edited)

Dear Hazardinho,

here is an extended (and bugfixed, sorry about that!) version that supports getting screen coordinates as well as the border coordinates in one function.

local function get_screen_coordinates(wx, wy, wz)
    local camMatArray = getElementMatrix(getCamera());
    local camMat_right = camMatArray[1];
    local camMat_forward = camMatArray[2];
    local camMat_up = camMatArray[3];
    local camMat_pos = camMatArray[4];
    local camMat = Matrix();
    local sW, sH = guiGetScreenSize();
    local s_ratio = sH / sW;
    camMat:setForward(Vector3(camMat_forward[1], camMat_forward[2], camMat_forward[3]));
    camMat:setRight(Vector3(camMat_right[1], camMat_right[2], camMat_right[3]));
    camMat:setUp(Vector3(camMat_up[1] * s_ratio, camMat_up[2] * s_ratio, camMat_up[3] * s_ratio));
    camMat:setPosition(Vector3(camMat_pos[1], camMat_pos[2], camMat_pos[3]));
    
    local invCamMat = camMat:inverse();
    
    local invVec = invCamMat:transformPosition(wx, wy, wz);
    
    local function to_real_coord(val)
        return ( val / 2 ) + 1/2;
    end
    
    local ratWidth, depthDist, ratHeight = invVec.x, invVec.y, invVec.z;
    
    if (depthDist > 0) then
        ratWidth = ratWidth / depthDist;
        ratHeight = ratHeight / depthDist;
        
        if (math.abs(ratWidth) <= 1) and (math.abs(ratHeight) <= 1) then
            return to_real_coord(ratWidth), to_real_coord(-ratHeight), true;
        end
    end
    
    if (ratWidth == 0) and (ratHeight == 0) then
        return 1/2, 1/2;
    elseif (ratWidth == 0) then
        return 1/2, to_real_coord(1 / ratHeight);
    elseif (ratHeight == 0) then
        return to_real_coord(1 / ratWidth), 1/2;
    end
    
    local dist_to_width = math.abs(1 / ratWidth);
    local dist_to_height = math.abs(1 / ratHeight);
    
    local scale_dist = math.min(dist_to_width, dist_to_height);
    
    ratWidth = ratWidth * scale_dist;
    ratHeight = ratHeight * scale_dist;
    
    return to_real_coord(ratWidth), to_real_coord(-ratHeight), false;
end

addEventHandler("onClientRender", root,
    function()
        local wx, wy, wz = 0, 0, 0;
        
        local screenRelX, screenRelY, isOnScreen = get_screen_coordinates(wx, wy, wz);
        
        dxDrawText( "relX: " .. screenRelX .. ", relY: " .. screenRelY, 100, 300 );
        dxDrawText( "is on screen: " .. tostring(isOnScreen), 100, 320 );
        
        local screenWidth, screenHeight = guiGetScreenSize();
        
        local screenX, screenY = screenRelX * screenWidth, screenRelY * screenHeight;
        
        dxDrawRectangle( screenX - 25, screenY - 25, 50, 50, 0xFFFFFFFF );
    end
);

In order to detect if an object is on the screen, simply use the third return value.

Edited by The_GTA
added condition to check whether on screen or not
  • Like 1

Share this post


Link to post
52 minutes ago, The_GTA said:

Caro Hazardinho,

Aqui está uma versão estendida (e com correção de erros, desculpe!) que suporta a obtenção de coordenadas de tela, bem como as coordenadas de borda em uma função.


 
     
    
    
    
    
     
     
    


   

    
    
    
    
    
     
             
    
    
    
    
       


        
                
             
        
    
    
          
          
       
          
       
          
    
    
      
      
    
     
    


    
     



    
           
        
        
        
               
              
        
         
        
        
        
              
    

Para detectar se um objeto está na tela, basta usar o terceiro valor de retorno.

Incredible, thank you so much for giving me the ready function, it really helped, I am forever grateful :D 

Share this post


Link to post
10 minutes ago, Hazardinho said:

Incredible, thank you so much for giving me the ready function, it really helped, I am forever grateful :D 

No problem, I spent some time testing the thing and it turns out I forgot to multiply with the camera Field Of View angle. So here is an ingame screenshot of it working:

6yFzpG2.png

local function get_screen_coordinates(wx, wy, wz)
    local camMatArray = getElementMatrix(getCamera());
    local camMat_right = camMatArray[1];
    local camMat_forward = camMatArray[2];
    local camMat_up = camMatArray[3];
    local camMat_pos = camMatArray[4];
    local camMat = Matrix();
    local sW, sH = guiGetScreenSize();
    local s_ratio = sH / sW;
    local _,_,_,_,_,_,_,fov = getCameraMatrix();
    local fovRad = math.rad(fov/2);
    local tanfov = math.tan(fovRad);
    local farclip = getFarClipDistance();
    camMat:setForward(Vector3(camMat_forward[1], camMat_forward[2], camMat_forward[3]));
    camMat:setRight(Vector3(camMat_right[1] * tanfov, camMat_right[2] * tanfov, camMat_right[3] * tanfov));
    camMat:setUp(Vector3(camMat_up[1] * tanfov * s_ratio, camMat_up[2] * tanfov * s_ratio, camMat_up[3] * tanfov * s_ratio));
    camMat:setPosition(Vector3(camMat_pos[1], camMat_pos[2], camMat_pos[3]));
    
    local invCamMat = camMat:inverse();
    
    local invVec = invCamMat:transformPosition(wx, wy, wz);
    
    local function to_real_coord(val)
        return ( val / 2 ) + 1/2;
    end
    
    local ratWidth, depthDist, ratHeight = invVec.x, invVec.y, invVec.z;

    if (depthDist > 0) then
        ratWidth = ratWidth / depthDist;
        ratHeight = ratHeight / depthDist;
        
        if (math.abs(ratWidth) <= 1) and (math.abs(ratHeight) <= 1) then
            return to_real_coord(ratWidth), to_real_coord(-ratHeight), true;
        end
    end
    
    if (ratWidth == 0) and (ratHeight == 0) then
        return 1/2, 1/2;
    elseif (ratWidth == 0) then
        return 1/2, to_real_coord(1 / ratHeight);
    elseif (ratHeight == 0) then
        return to_real_coord(1 / ratWidth), 1/2;
    end
    
    local dist_to_width = math.abs(1 / ratWidth);
    local dist_to_height = math.abs(1 / ratHeight);
    
    local scale_dist = math.min(dist_to_width, dist_to_height);
    
    ratWidth = ratWidth * scale_dist;
    ratHeight = ratHeight * scale_dist;
    
    return to_real_coord(ratWidth), to_real_coord(-ratHeight), false;
end

createObject(1454, 0, 0, 10);

addEventHandler("onClientRender", root,
    function()
        local wx, wy, wz = 0, 0, 10;
        
        local screenRelX, screenRelY, isOnScreen = get_screen_coordinates(wx, wy, wz);
        
        dxDrawText( "relX: " .. screenRelX .. ", relY: " .. screenRelY, 100, 300 );
        dxDrawText( "is on screen: " .. tostring(isOnScreen), 100, 320 );
        
        local screenWidth, screenHeight = guiGetScreenSize();
        
        local screenX, screenY = screenRelX * screenWidth, screenRelY * screenHeight;
        
        dxDrawRectangle( screenX - 25, screenY - 25, 50, 50, 0xFFFFFFFF );
    end
);

Gosh I hate myself sometimes 😅🤣

Share this post


Link to post
2 minutes ago, The_GTA said:

Sem problemas, passei algum tempo testando a coisa e acabei esquecendo de me multiplicar com o ângulo do campo de visão da câmera. Então, aqui está uma imagem do jogo funcionando:

6yFzpG2.png


 
     
    
    
    
    
     
     
    
     
     
     
     

   
   

    
    
    
    
    
     
             
    
    
    

       


        
                
             
        
    
    
          
          
       
          
       
          
    
    
      
      
    
     
    


    
     


   


    
           
        
        
        
               
              
        
         
        
        
        
              
    

Puxa, eu me odeio às vezes 😅 🤣

Perfect thank you :D

Share this post


Link to post

There is a bug in getCameraMatrix() FoV value which prevents proper camera calculation if you are in a vehicle and accelerate. The FoV is not properly returned by that function. Please track this issue so we can get it fixed.

Share this post


Link to post
Posted (edited)

Please note that there exists another MTA bug: the camera matrix is sometimes wronly returned. See this Github issue for further details.

I noticed that there was a tiny bug in the get_screen_coordinates function if the world coordinate was directly on the middle vertical line of the screen. Then the screen-y coordinate would be returned as inverted. Even though that issue would be virtually never encountered I decided to give you this following script which has the issue fixed:

local function get_screen_coordinates(wx, wy, wz)
    local camMatArray = getElementMatrix(getCamera());
    local camMat_right = camMatArray[1];
    local camMat_forward = camMatArray[2];
    local camMat_up = camMatArray[3];
    local camMat_pos = camMatArray[4];
    local camMat = Matrix();
    local sW, sH = guiGetScreenSize();
    local s_ratio = sH / sW;
    local _,_,_,_,_,_,_,fov = getCameraMatrix();
    local fovRad = math.rad(fov/2);
    local tanfov = math.tan(fovRad);
    local farclip = getFarClipDistance();
    camMat:setForward(Vector3(camMat_forward[1], camMat_forward[2], camMat_forward[3]));
    camMat:setRight(Vector3(camMat_right[1] * tanfov, camMat_right[2] * tanfov, camMat_right[3] * tanfov));
    camMat:setUp(Vector3(camMat_up[1] * tanfov * s_ratio, camMat_up[2] * tanfov * s_ratio, camMat_up[3] * tanfov * s_ratio));
    camMat:setPosition(Vector3(camMat_pos[1], camMat_pos[2], camMat_pos[3]));
    
    local invCamMat = camMat:inverse();
    
    local invVec = invCamMat:transformPosition(wx, wy, wz);
    
    local function to_real_coord(val)
        return ( val / 2 ) + 1/2;
    end
    
    local ratWidth, depthDist, ratHeight = invVec.x, invVec.y, invVec.z;
  
    -- Screen top is -1 but should be 1 to match the up vector perception.
    ratHeight = -ratHeight;

    if (depthDist > 0) then
        ratWidth = ratWidth / depthDist;
        ratHeight = ratHeight / depthDist;
        
        if (math.abs(ratWidth) <= 1) and (math.abs(ratHeight) <= 1) then
            return to_real_coord(ratWidth), to_real_coord(ratHeight), true;
        end
    end
    
    if (ratWidth == 0) and (ratHeight == 0) then
        return 1/2, 1/2;
    elseif (ratWidth == 0) then
        return 1/2, to_real_coord(1 / ratHeight);
    elseif (ratHeight == 0) then
        return to_real_coord(1 / ratWidth), 1/2;
    end
    
    local dist_to_width = math.abs(1 / ratWidth);
    local dist_to_height = math.abs(1 / ratHeight);
    
    local scale_dist = math.min(dist_to_width, dist_to_height);
    
    ratWidth = ratWidth * scale_dist;
    ratHeight = ratHeight * scale_dist;
    
    return to_real_coord(ratWidth), to_real_coord(ratHeight), false;
end

 

Edited by The_GTA
added fixed script

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.