Mature Posted March 7, 2020 Posted March 7, 2020 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
Moderators IIYAMA Posted March 7, 2020 Moderators Posted March 7, 2020 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
Skuleris Posted March 7, 2020 Posted March 7, 2020 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. 1
Mature Posted March 7, 2020 Author Posted March 7, 2020 (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 March 7, 2020 by Hazardinho
Skuleris Posted March 7, 2020 Posted March 7, 2020 (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 March 7, 2020 by Skuleris
Mature Posted March 7, 2020 Author Posted March 7, 2020 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?
The_GTA Posted March 7, 2020 Posted March 7, 2020 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.
Mature Posted March 7, 2020 Author Posted March 7, 2020 (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 March 7, 2020 by Hazardinho
The_GTA Posted March 7, 2020 Posted March 7, 2020 (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 March 7, 2020 by The_GTA added condition to check whether on screen or not 1
Mature Posted March 7, 2020 Author Posted March 7, 2020 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
The_GTA Posted March 7, 2020 Posted March 7, 2020 10 minutes ago, Hazardinho said: Incredible, thank you so much for giving me the ready function, it really helped, I am forever grateful 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: 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
Mature Posted March 7, 2020 Author Posted March 7, 2020 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: Puxa, eu me odeio às vezes ?"> ?"> Perfect thank you
The_GTA Posted March 8, 2020 Posted March 8, 2020 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.
The_GTA Posted April 26, 2020 Posted April 26, 2020 (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 April 26, 2020 by The_GTA added fixed script
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now