Jump to content

[HELP] getScreenFromWorldPosition


Mature

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

Link to comment
  • Moderators

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

 

 

 

Link to comment
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
Link to comment
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?

Link to comment

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. :) 

Link to comment

 

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
Link to comment

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
Link to comment
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 

Link to comment
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 ??

Link to comment
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 <font style=?"> <font style=?">

Perfect thank you :D

Link to comment
  • 1 month later...

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
Link to comment

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.
×
×
  • Create New...