Mature Posted March 7, 2020 Share 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 Link to comment
Moderators IIYAMA Posted March 7, 2020 Moderators Share 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 Link to comment
Skuleris Posted March 7, 2020 Share 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 Link to comment
Mature Posted March 7, 2020 Author Share 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 Link to comment
Skuleris Posted March 7, 2020 Share 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 Link to comment
Mature Posted March 7, 2020 Author Share 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? Link to comment
The_GTA Posted March 7, 2020 Share 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. Link to comment
Mature Posted March 7, 2020 Author Share 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 Link to comment
The_GTA Posted March 7, 2020 Share 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 Link to comment
Mature Posted March 7, 2020 Author Share 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 Link to comment
The_GTA Posted March 7, 2020 Share 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 Link to comment
Mature Posted March 7, 2020 Author Share 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 Link to comment
The_GTA Posted March 8, 2020 Share 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. Link to comment
The_GTA Posted April 26, 2020 Share 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 Link to comment
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